# Stuff from playing around with rack.rubyforge.org, do what you want with it # Johan Sørensen require "rubygems" require "pp" require "../rack/lib/rack" require "erubis" # See, I resisted calling it "wunderbra" or "cup" module Cabinet class Controller def initialize(request, response) @request, @response = request, response end def render_text(text) @response.body << text end def render_template(path, context={}) Template.new(path, context).render(@response.body) end # XXX needs work def redirect_to(location) @response.body.clear # so we can give a proper http redirect response @response.status = 302 location = "/#{location}" unless location[0] == ?/ @response.header["Location"] = "#{@request.scheme}://#{@request.host}#{location}" return end def process(action, args=[]) self.__send__(action, *args) @response.finish end end class HttpError < Controller def http_404 @response.status = 404 render_text("

404 Not Found

") render_text("
#{@request.path_info.inspect}
") end def http_500(exception) @response.status = 500 if $DEBUG raise exception else render_text("

500 Internal Server Error

") end end end class Template def initialize(path, context) @context = context input = File.read(path) @template = Erubis::Eruby.new(input) end def render(out) out << @template.evaluate(@context) end end class Dispatcher def initialize(routes) @routes = Cabinet::URLResolver.new(routes) end def call(env) request = Rack::Request.new(env) response = Rack::Response.new @routes.recognize_and_process!(request, response) end end class URLResolver def initialize(routes) @routes = routes end def recognize_and_process!(request, response) @routes.each do |path_regex, class_path| request_path = request.path_info.chomp("/") if md = request_path.match(path_regex) if $DEBUG puts "got match for #{path_regex.inspect}, as #{class_path.inspect}, with args: #{md.captures.inspect}" end controller, action = class_path.split(/\.|#/) controller = Object.module_eval("::#{controller}", __FILE__, __LINE__) begin callable = controller.new(request, response) if callable.respond_to?(action) return callable.process(action, md.captures) else return HttpError.new(request, response).process(:http_404) end rescue => e return HttpError.new(request, response).process(:http_500, e) end end end return HttpError.new(request, response).process(:http_404) end end end class Hello < Cabinet::Controller def index #render("hello index") end def world not_a_method() render_text("Hello world") end def you render_text("Hello you there") end def any(whom) render_text("Hello #{whom}, says params") end def many(*args) render_text("args: #{args.inspect}, GET: #{@request.GET.inspect}") end def template(who) render_text("who: #{who.inspect}") render_template("test.erb", {:who => who}) end def redir redirect_to("/hello/you") end end module Brave class World < Cabinet::Controller def hello render_text("Hello Brave new world!") end end end mappings = { '^/hello/braveworld$' => "Brave::World.hello", '^/hello/world$' => "Hello.world", '^/hello/you$' => "Hello.you", '^/hello$' => "Hello.nothere", '^/hello/(.*)$' => "Hello.any", '^/foo/(.*)$' => "Hello#many", '^/bar/(.+)$' => "Hello.template", "^/redir" => "Hello#redir", } HOST_AND_PORT = {:Host => "127.0.0.1", :Port => 8080} app = Rack::Builder.new { if $DEBUG use Rack::CommonLogger, STDERR use Rack::ShowExceptions use Rack::Reloader use Rack::Lint end run Cabinet::Dispatcher.new(mappings) }.to_app Rack::Handler::Mongrel.run(app, HOST_AND_PORT)