basyura's blog

あしたになったらほんきだす。

cgi で rails みたいに - その3

レンタルサーバで rails 使えなくても 、同じ url ルールで mvc したい。
ちょっと機能追加して簡易掲示板を作ってみた。

ディレクトリ構成

 gup.cgi
  + app
    + contollers
      + board.rb
    + views
      + board
        + index.rhtml
  + data
    + board.log

dispatcher - gup.cgi

#!/usr/bin/ruby -Ku

require 'cgi'
require 'cgi/session'
require 'cgi/session/pstore'
require 'erb'

CONTROLLER_PATH = "app/controllers/"
VIEW_PATH       = "app/views/"

class GupDispatchError < StandardError ; end

class GupBase
  def puts(msg)
    $stderr.puts msg
  end
  def escape(html)
    CGI.escapeHTML(html)
  end
  def render(option={})
    @__gup_render_option__ = option
  end
  def redirect_to(option={})
    @__gup_recirect_option__ ||= option
  end
  def __response__(cgi , category , action)
    if @__gup_recirect_option__  
      action = @__gup_recirect_option__[:action] || action
      url = cgi.script_name.split[0] + "/" + category
      url << "/" + action if action != "index"
      print cgi.header({'status' => '302 Found', 'Location' => url })
    else
      @__gup_render_option__ ||= {}
      action = @__gup_render_option__[:action] || action
      path = VIEW_PATH + category + "/" + action + ".rhtml"
      begin
        cgi.out({
          "type"       => "text/html",
          "charset"    => "utf-8",
          "connection" => "close",
        }){ ERB.new(open(path){|f| f.read}).result(binding) }
      rescue Errno::ENOENT => e
        e.to_s
      end
    end
  end
end

def create_session(cgi)
  CGI::Session::new(cgi , 
    { 
      "database_manager" => CGI::Session::PStore ,
      "prefix" => "gup_" ,
      "tmpdir" => "/tmp"
    })
end

def dispatch(cgi , session)
  info = cgi.path_info.split("/")
  raise GupDispatchError.new "welcome to gup" if info.length == 0

  info[2] = "index" if info.length < 3 || !info[2]
  begin
    load CONTROLLER_PATH + info[1] + ".rb"
    #require CONTROLLER_PATH + info[1]
  rescue LoadError => e
    raise GupDispatchError.new "unknown => #{info[1].capitalize}##{info[2]}"
  end
  app = eval(info[1].capitalize + ".new")
  raise GupDispatchError.new "#{info[1].capitalize} must extends GupBase" unless app.kind_of? GupBase

  app.instance_variable_set("@params"  , cgi.params)
  app.instance_variable_set("@session" , session)
  begin
    app.__send__(info[2])
  rescue NoMethodError => e
    raise GupDispatchError.new "unknown #{info[1].capitalize}##{info[2]}"
  end
  app.__response__(cgi , info[1] , info[2])
end

cgi = CGI.new
session = create_session(cgi)
begin
  dispatch(cgi , session)
rescue GupDispatchError => e
  cgi.out({"type"=>"text/html","charset"=>"utf-8","connection"=>"close"}){e.to_s}
end

exit

controller - board.rb

class Board < GupBase
  def index
    begin
      @list = open("data/board.log") {|f| f.read.split("\n")}.reverse
    rescue Errno::ENOENT
      @list = []
    end
  end
  def post
    name    = @params["name"][0]
    email   = @params["email"][0]
    comment = @params["comment"][0]

    if name.length != 0 && email.length != 0 && comment.length != 0
      open("data/board.log" , "a"){|f| 
        f.puts name + "," + email + "," + comment
      }
    end

    redirect_to :action => "index"
  end
end

view - index.rhtml

<%
  def escape(msg)
    msg ? CGI.escapeHTML(msg) : ""
  end
%>
<html>
  <head><title>board</title></head>
<body>
  <form action="/gup/board/post" method="post">
    <table>
      <tr><td>name   </td><td><input type="text" name="name"    size="50"></td></tr>
      <tr><td>email  </td><td><input type="text" name="email"   size="50"></td></tr>
      <tr><td>comment</td><td><input type="text" name="comment" size="100"></td></tr>
    </table>
    <input type="submit" value="submit">
  </form>
  <hr>
  <% 
    for i in 0...(@list.length < 10 ? @list.length : 10)
      info = @list[i].split(",")
  %>
     <span style="background-color:#C8E3FF;padding:1"><%= escape(info[0]) %> - <%= escape(info[1]) %></span><br>
     <%= escape(info[2]) %>
     <hr>
  <% end %>
</body>
</html>