module Odex # Odex Builder class OdexBuilder < Rack::Builder end # Odex Request class OdexRequest < ActionDispatch::Journey::Routes end # Odex Router class OdexRouter < ActionDispatch::Journey::Router end # Odex Pattern class OdexPattern < ActionDispatch::Journey::Path::Pattern end # Request class Req extend Forwardable attr_reader :request, :response def_delegators :request, :session, :params def_delegators :response, :headers def initialize(env) @request = Request.new(env) @request.params.merge!(env['odex.params']) @response = Response.new([], 200, {'Content-Type' => 'text/html'}) end def cookies @cookies ||= Rack::Cookies::CookieJar.new(request.cookies) end def status(code) response.status = code end def halt(status, headers={}, body='') @response = Response.new(body, status, headers) cookies.finish!(response) if @cookies throw(:halt, response) end def system_redirect_to(uri, status=302) halt(status, {'Location' => uri}) end alias_method :redirect, :system_redirect_to def apply_to(&handler) data = instance_eval(&handler) data.respond_to?(:each) ? response.body = data : response.write(data) cookies.finish!(response) if @cookies response end end # Router class Router attr_reader :c, :od_jr, :hook_before, :hook_after, :fallback def initialize(options) @c = options[:c] @hook_before = options[:hook_before] @hook_after = options[:hook_after] @fallback = options[:fallback] prepare_for_od_jr(options[:route_defs]) end def call(env) response = od_jr.call(env) if response[0] == 40 and fallback.call(env) else response end end def prepare_for_od_jr(route_defs) routes = Odex::OdexRequest.new @od_jr = Odex::OdexRouter.new(routes, { :parameters_key => 'odex.params', :request_class => Odex::Request }) route_defs.each do |path, options, handler| pat = Odex::OdexPattern.new(path) constraints = options.fetch(:constraints, {}) defaults = options.fetch(:defaults, {}) @od_jr.routes.add_route compile(handler), pat, constraints, defaults end end def compile(handler) Proc.new do |env| scope = c.new(env) response = catch (:halt) do hook_before.each {|h| scope.instance_eval &h } scope.apply_to &handler end catch (:halt) do hook_after.each {|h| scope.instance_eval &h } end response.finish end end end # Initializer module Init def self.included(base) base.class_eval do def self.init_methods name, value @_methods ||= [] @_methods << name self.class.send(:attr_accessor, name) self.send("#{name}=", value) end def self.inherited(subclass) @_methods.each do |attr| subclass.init_methods attr, self.send(attr).deep_dup end end end end end # Rack Request class Request < Rack::Request def params @params ||= ActiveSupport::HashWithIndifferentAccess.new(super) end end # Rack Response class Response < Rack::Response end # Odex Base class Base include Odex::Init METHODS = [:options, :patch, :post, :put, :trace, :delete, :get, :head] init_methods :builder, Odex::OdexBuilder.new init_methods :c, Class.new(Req) init_methods :route_defs, [] init_methods :hook_before, [] init_methods :hook_after, [] init_methods :default_constraints, {} init_methods :map, {} def initialize(app=nil) builder = odex_rack_builder builder.run Router.new({ :c => self.class.c, :route_defs => self.class.route_defs, :hook_before => self.class.hook_before, :hook_after => self.class.hook_after, :fallback => app }) @app = builder.to_app end def odex_rack_builder rack_builder = Odex::OdexBuilder.new self.class.map.each do |url, map_class| rack_builder.map(url) do use map_class end end rack_builder end def call env @app.call env end class << self METHODS.each do |method| define_method method do |path, options={}, &block| options[:constraints] = options[:constraints] || {} options[:constraints].merge!(:request_method => method.to_s.upcase) route path, options, &block end end def group(url, app=nil, &block) scope = self.c app ||= Class.new(self.superclass) do self.c = scope class_eval(&block) end map[url] = app end def before_proc(&block) hook_before << Proc.new(&block) end def after_proc(&block) hook_after << Proc.new(&block) end def use(middleware, *args, &block) builder.use(middleware, *args, &block) end def ext(*extensions) extensions.each do |ext| extend ext ext.module_is_registered(self) if ext.respond_to?(:module_is_registered) end end def helpers(*args, &block) args << Module.new(&block) if block_given? args.each do |module| c.send(:include, module) end end def route(path, options, &block) self.route_defs << [path, options, Proc.new(&block)] end end end class App < Base class << self def go!(port = 1453) Rack::Handler.pick(['thin','webrick']).run new, :Port => port end end end end