Changeset 678

Show
Ignore:
Timestamp:
09/23/07 08:03:23 (1 year ago)
Author:
r.@tinyclouds.org
Message:

Added an "action class". there is a target_api.rb file which shows what I'm aiming for. Replaced router with a simple module traversing one. Nothing works.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/ry_routes/app_generators/merb_plugin/templates/config/router.rb

    r453 r678  
    1 # Merb::Router is the request routing mapper for the merb framework. 
    2 # 
    3 # You can route a specific URL to a controller / action pair: 
    4 # 
    5 #   r.match("/contact"). 
    6 #     to(:controller => "info", :action => "contact") 
    7 # 
    8 # You can define placeholder parts of the url with the :symbol notation. These 
    9 # placeholders will be available in the params hash of your controllers. For example: 
    10 # 
    11 #   r.match("/books/:book_id/:action"). 
    12 #     to(:controller => "books") 
    13 #    
    14 # Or, use placeholders in the "to" results for more complicated routing, e.g.: 
    15 # 
    16 #   r.match("/admin/:module/:controller/:action/:id"). 
    17 #     to(:controller => ":module/:controller") 
    18 # 
    19 # You can also use regular expressions, deferred routes, and many other options. 
    20 # See merb/specs/merb/merb_router_spec.rb for a fairly complete usage sample. 
    21  
    22 puts "Compiling routes.." 
    231Merb::Router.prepare do |r| 
    24   # RESTful routes 
    25   # r.resources :posts 
    26  
     2   
     3  r.map 'products/trainers', :controller => TrainersController 
     4  r.map ':locale/blah', :controller => BlahCont 
    275  # Default route, usually you don't want to change this 
    286  r.default_routes 
    29    
     7  r.map '/:controller' do |controller| 
     8    controller.camel_case 
     9  end 
    3010  # Change this for your home page to be avaiable at / 
    3111  # r.match('/').to(:controller => 'whatever', :action =>'index') 
  • branches/ry_routes/lib/merb/constants.rb

    r573 r678  
    99      "'" => ''', 
    1010    }.freeze 
     11     
     12    TYPES = { 
     13      :yaml => %w[application/x-yaml text/yaml], 
     14      :text => %w[text/plain], 
     15      :html => %w[text/html application/xhtml+xml application/html], 
     16      :xml  => %w[application/xml text/xml application/x-xml], 
     17      :js   => %w[application/json text/x-json text/javascript application/javascript application/x-javascript] 
     18    }.to_mash.freeze 
    1119     
    1220    DEFAULT_SEND_FILE_OPTIONS = { 
  • branches/ry_routes/lib/merb/controller.rb

    r647 r678  
    3838      end 
    3939       
     40      def setup_routes 
     41        map '', :index 
     42        public_instance_methods.each do |m| 
     43          if m[-1] == ?! # if the last character is ! 
     44            map m.chop { |req| m if req.post? or req.put? or req.delete? } 
     45          else 
     46            map m { |req| m if req.get? or req.head? } 
     47          end 
     48        end 
     49      end 
     50       
     51      def map(*args, &b) 
     52        @@routes.map_action(*args, &b) 
     53      end 
     54       
    4055      # returns the action for a request 
    41       def route(request) 
     56      def route(path, request) 
    4257         
    4358      end 
  • branches/ry_routes/lib/merb/core_ext/module.rb

    r400 r678  
    11class Module 
     2   
     3  def submodules 
     4    constants.map do |const| 
     5      c = const_get(const) 
     6      c if c.kind_of?(Module) 
     7    end.compact 
     8  end 
    29   
    310  # alias_method_chain :foo, :bar produces the following pattern: 
  • branches/ry_routes/lib/merb/dispatcher.rb

    r647 r678  
    2424        MERB_LOGGER.info("Params: #{request.params.inspect}") 
    2525        MERB_LOGGER.info("Cookies: #{request.cookies.inspect}") 
    26         # user friendly error messages 
    27         if request.route_params.empty? 
    28           raise ControllerExceptions::NotFound, "No routes match the request" 
    29         elsif request.controller_class.nil? 
    30           raise ControllerExceptions::NotFound, "Route matched, but route did not specify a controller"  
    31         end 
    32         MERB_LOGGER.debug("Routed to: #{request.route_params.inspect}") 
    33         klass = request.controller_class 
    34         action = klass.route(request) 
    35         dispatch_action(klass, action, request, response) 
     26        action_class = Router.match(request.path) 
     27        action_class.dispatch(request, status) 
    3628      rescue => exception 
    3729        exception = controller_exception(exception) 
     
    3931      end 
    4032         
    41       private  
    42        
    43       # setup the controller and call the chosen action  
    44       def dispatch_action(klass, action, request, response, status=200) 
    45         # build controller 
    46         controller = klass.build(request, response, status) 
    47         # complete setup benchmarking 
    48         #controller._benchmarks[:setup_time] = Time.now - start 
    49         if @@use_mutex 
    50           @@mutex.synchronize { controller.dispatch(action) } 
    51         else 
    52           controller.dispatch(action) 
    53         end 
    54         [controller, action] 
    55       end 
     33      private 
    5634       
    5735      # Re-route the current request to the Exception controller 
  • branches/ry_routes/lib/merb/request.rb

    r647 r678  
    9292        h = body_and_query_params 
    9393        h.merge!(multipart_params) if multipart_params 
    94         h.merge!(route_params) if route_params 
    9594        h.merge!(json_params) if json_params 
    9695        h.merge!(xml_params) if xml_params 
     
    9998    end 
    10099     
     100    def session 
     101      @session ||= begin 
     102        CookieSession.new(cookies[:session_id], Merb::Server.config[:session_secret_key]) 
     103      end 
     104    end 
     105     
    101106    def cookies 
    102107      @cookies ||= self.class.query_parse(@env[Merb::Const::HTTP_COOKIE], ';,') 
    103     end 
    104      
    105     def route_match 
    106       @route_match ||= Router.match(path_info) 
    107     end 
    108     private :route_match 
    109      
    110     def controller_class 
    111       route_match.first 
    112     end 
    113      
    114     def route_params 
    115       route_match.second 
    116108    end 
    117109     
  • branches/ry_routes/lib/merb/router.rb

    r647 r678  
     1module Actions 
     2end 
     3 
    14module Merb 
    2  
    3   class Router 
    4      
    5     SECTION_REGEXP = /(?::([a-z*_]+))/.freeze 
     5  class SimpleRouter 
    66     
    77    class << self 
    88       
    9       def prepare 
    10         @@matcher = RouteMatcher.new 
    11         @@generator = RouteGenerator.new 
    12        
    13         yield self 
    14        
    15         @@matcher.compile_router 
     9      def table 
     10        @@table ||= module_route_table(Actions) 
    1611      end 
    1712       
    18       def add(*route) 
    19         @@matcher.add(*route) 
    20       end 
    21        
    22       def matcher 
    23         @@matcher 
    24       end 
    25        
    26       def generator 
    27         @@generator 
     13      def module_route_table(mod) 
     14        hash = {} 
     15        mod.submodules.each do |sub| 
     16          hash[sub.base_url] = sub if sub.respond_to?(:base_url) 
     17          hash.update module_route_table(sub) 
     18        end 
     19        hash 
    2820      end 
    2921       
    3022      def match(path) 
    31         @@matcher.route_request(path) 
    32       end 
    33        
    34       def generate(method, *args) 
    35         @@generator.generate(method, *args) 
    36       end 
    37        
    38       def resources(res, opts={}) 
    39         opts[:prefix] ||= "" 
    40         if block_given? 
    41           procs = [] 
    42           yield Resource.new(res, procs, opts) 
    43           procs.reverse.each &:call 
    44         else 
    45           generate_resources_routes(res,opts) 
    46         end 
    47       end 
    48        
    49       def resource(res, opts={}) 
    50         opts[:prefix] ||= "" 
    51         if block_given? 
    52           procs = [] 
    53           yield Resource.new(res, procs, opts) 
    54           procs.reverse.each &:call 
    55         else 
    56           generate_singleton_routes(res,opts) 
    57         end 
    58       end 
    59        
    60       def default_routes(*a) 
    61         @@matcher.default_routes(*a) 
    62       end 
    63        
    64       def compiled_statement 
    65         @@matcher.compiled_statement 
    66       end 
    67        
    68       def compiled_regexen 
    69         @@matcher.compiled_regexen 
    70       end 
    71          
    72       def generate_resources_routes(res, opts) 
    73         [@@matcher,@@generator].each { |r| r.generate_resources_routes(res, opts) } 
    74       end 
    75        
    76       def generate_singleton_routes(res, opts) 
    77         [@@matcher,@@generator].each { |r| r.generate_singleton_routes(res, opts) } 
    78       end 
    79  
    80     end # class << self 
    81      
    82     class RouteMatcher 
    83       attr_reader :routes, :compiled_statement, :compiled_regexen 
    84  
    85       def initialize 
    86         @routes = Array.new 
    87         # The final compiled lambda that gets used as the body of the route_request method. 
    88         @compiled_statement = String.new 
    89         @compiled_regexen   = Array.new 
    90       end 
    91  
    92       # Add a route to be compiled. 
    93       def add(*route) 
    94         opt = Hash === route.last ? route.pop : {} 
    95         if n = opt[:namespace] 
    96           path = "/#{n}#{route[0]}"  
    97         else 
    98           path = route[0] 
    99         end     
    100         @routes << [path, opt] 
    101       end 
    102        
    103       def raw_add(*route) 
    104         @routes << [route[0], (route[1]||{})] 
    105       end 
    106        
    107       # Build up a string that defines a lambda that does a case statement on 
    108       # the PATH_INFO against each of the compiled routes in turn. First route 
    109       # that matches wins. 
    110       def compile_router 
    111         router_lambda = @routes.inject("lambda{|path| \n  sections={}\n  case path\n") { |m,r| 
    112           m << compile(r) 
    113         } << "  else\n    return {:controller=>'Noroutefound', :action=>'noroute'}\n  end\n}" 
    114         @compiled_statement = router_lambda 
    115         meta_def(:route_request, &eval(router_lambda)) 
    116       end   
    117        
    118       # Compile each individual route into a when /.../ component of the case 
    119       # statement. Takes /:sections of the route def that start with : and 
    120       # turns them into placeholders for whatever urls match against the route 
    121       # in question. 
    122       def compile(route) 
    123         raise ArgumentError unless String === route[0] 
    124         code, count = '', 0 
    125         while route[0] =~ Router::SECTION_REGEXP 
    126           route[0] = route[0].dup 
    127           name = $1 
    128           (name =~ /(\*+)(\w+)/) ? (flag = true; name = $2) : (flag = false) 
    129           count += 1 
    130           if flag 
    131             route[0].sub!(Router::SECTION_REGEXP, "([^,?]+)") 
    132           else   
    133             route[0].sub!(Router::SECTION_REGEXP, "([^\/,?]+)") 
    134           end 
    135           code << "    sections[:#{name}] = $#{count}\n" 
    136         end 
    137         @compiled_regexen << Regexp.new(route[0]) 
    138         index = @compiled_regexen.size - 1 
    139         condition = "  when @compiled_regexen[#{index}] " 
    140         statement = "#{condition}\n#{code}" 
    141         statement << "    return #{route[1].inspect}.update(sections)\n" 
    142         statement 
    143       end 
    144        
    145       def generate_resources_routes(res,opt) 
    146         with_options opt.merge(:controller => res.to_s, :rest => true) do |r| 
    147           r.raw_add "#{opt[:prefix]}/#{res}/:id[;/]edit", :allowed => {:get => 'edit'} 
    148           r.raw_add "#{opt[:prefix]}/#{res}/new[;/]:action", :allowed => {:get => 'new', :post => 'new', :put => 'new', :delete => 'new'} 
    149           r.raw_add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'} 
    150           if mem = opt[:member] 
    151             mem.keys.sort_by{|x| "#{x}"}.each {|action| 
    152               allowed = mem[action].injecting({}) {|h, verb| h[verb] = "#{action}"} 
    153               r.raw_add "#{opt[:prefix]}/#{res}/:id[;/]+#{action}", :allowed => allowed 
    154             } 
    155           end 
    156           if coll = opt[:collection] 
    157             coll.keys.sort_by{|x| "#{x}"}.each {|action| 
    158               allowed = coll[action].injecting({}) {|h, verb| h[verb] = "#{action}"} 
    159               r.raw_add "#{opt[:prefix]}/#{res}[;/]#{action}", :allowed => allowed 
    160             } 
    161           end   
    162           r.raw_add "#{opt[:prefix]}/#{res}/:id\\.:format", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'} 
    163           r.raw_add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'index', :post => 'create'} 
    164           r.raw_add "#{opt[:prefix]}/#{res}/:id", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'} 
    165           r.raw_add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'index', :post => 'create'} 
    166         end 
    167       end 
    168        
    169       def generate_singleton_routes(res,opt) 
    170         with_options opt.merge(:controller => res.to_s, :rest => true ) do |r| 
    171           r.raw_add "#{opt[:prefix]}/#{res}[;/]edit", :allowed => {:get => 'edit'} 
    172           r.raw_add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'show'} 
    173           r.raw_add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'} 
    174           r.raw_add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'show', :post => 'create', :put => 'update', :delete => 'destroy'} 
    175         end 
    176       end 
    177        
    178       def default_routes(opt={}) 
    179         namespace = opt[:namespace] ? "/#{opt[:namespace]}" : "" 
    180         with_options opt do |r| 
    181           r.raw_add namespace + "/:controller/:action/:id\\.:format" 
    182           r.raw_add namespace + "/:controller/:action/:id" 
    183           r.raw_add namespace + "/:controller/:action\\.:format" 
    184           r.raw_add namespace + "/:controller/:action" 
    185           r.raw_add namespace + "/:controller\\.:format", :action => 'index' 
    186           r.raw_add namespace + "/:controller", :action => 'index' 
    187         end   
    188       end 
    189     end 
    190  
    191     class RouteGenerator 
    192  
    193       attr_accessor :paths 
    194  
    195       def initialize 
    196         @paths = {} 
    197       end 
    198  
    199       def add(name, path) 
    200         @paths[name.to_sym] = path 
    201       end 
    202        
    203       def generate(name, *args) 
    204         options = Hash === args.last ? args.pop : {} 
    205         obj = args[0] 
    206         options.each do |key, value| 
    207           next unless value.respond_to?(:to_param) 
    208           unless key.to_s =~ /_?id$/ 
    209             old_key = key 
    210             options[old_key] = value.to_param 
    211             key = "#{key}_id".intern 
    212           end 
    213           options[key] = value.to_param 
    214         end   
    215          
    216         path = @paths[name].dup 
    217         while path =~ Router::SECTION_REGEXP 
    218           if obj.respond_to?($1) && ! obj.nil? 
    219             path.sub!(Router::SECTION_REGEXP, obj.send($1).to_s) 
    220           else   
    221             path.sub!(Router::SECTION_REGEXP, options[$1.intern].to_s) 
    222           end 
    223         end 
    224         if f = options[:format] 
    225           "#{path}.#{f}" 
    226         else 
    227           path 
    228         end    
    229       end 
    230        
    231       def generate_singleton_routes(res,opt) 
    232         res = res.to_s 
    233         if opt[:namespace] 
    234           namespace = "#{opt[:namespace]}_" 
    235           name = "/#{opt[:namespace]}" 
    236         else 
    237           namespace = '' 
    238           name = '' 
    239         end 
    240         add namespace + "edit_#{res}", name + "#{opt[:prefix]}/#{res}/edit" 
    241         add namespace + "new_#{res}", name + "#{opt[:prefix]}/#{res}/new" 
    242         add namespace + res, name + "#{opt[:prefix]}/#{res}" 
    243       end 
    244        
    245       def generate_resources_routes(res,opt) 
    246         res = res.to_s 
    247         res_singular = res.singularize 
    248         if opt[:namespace] 
    249           namespace = "#{opt[:namespace]}_" 
    250           name = "/#{opt[:namespace]}" 
    251         else 
    252           namespace = '' 
    253           name = '' 
    254         end   
    255         add namespace + res,                    name + "#{opt[:prefix]}/#{res}" 
    256         add namespace + res_singular,           name + "#{opt[:prefix]}/#{res}/:id" 
    257         add namespace + "new_#{res_singular}",  name + "#{opt[:prefix]}/#{res}/new" 
    258         add namespace + "custom_new_#{res_singular}",  name + "#{opt[:prefix]}/#{res}/new/:action" 
    259         add namespace + "edit_#{res_singular}", name + "#{opt[:prefix]}/#{res}/:id/edit" 
    260         if mem = opt[:member] 
    261           mem.keys.sort_by{|x| "#{x}"}.each {|action| 
    262             add namespace + "#{action}_#{res_singular}", name + "#{opt[:prefix]}/#{res}/:id/#{action}" 
    263           } 
    264         end 
    265         if coll = opt[:collection] 
    266           coll.keys.sort_by{|x| "#{x}"}.each {|action| 
    267             add namespace + "#{action}_#{res_singular}", name + "#{opt[:prefix]}/#{res}/#{action}" 
    268           } 
    269         end 
    270       end 
    271     end 
    272  
    273     class Resource 
    274        
    275       def initialize(resource, procs=[], opts={}) 
    276         @resource, @procs, @opts = resource, procs, opts 
    277         @procs << proc do 
    278            [::Merb::Router.matcher, ::Merb::Router.generator].each {|r| r.generate_resources_routes(@resource, @opts) }  
    279         end 
    280       end 
    281  
    282       def resources(res, opts={}) 
    283         (opts[:prefix]||='') << "/#{@resource}/:#{@resource.to_s.singularize}_id" 
    284  
    285         opts[:prefix] = @opts[:prefix] + opts[:prefix] 
    286         if block_given? 
    287           yield self.class.new(res, @procs, opts) 
    288         else 
    289           @procs << proc do 
    290              [::Merb::Router.matcher, ::Merb::Router.generator].each {|r| r.generate_resources_routes(res, opts) } 
    291           end 
    292         end 
    293       end 
    294        
    295       def resource(res, opts={}) 
    296         (opts[:prefix]||='') << "/#{@resource}/:#{@resource.to_s.singularize}_id" 
    297  
    298         opts[:prefix] = @opts[:prefix] + opts[:prefix] 
    299         if block_given? 
    300           yield self.class.new(res, @procs, opts) 
    301         else 
    302           @procs << proc do 
    303              [::Merb::Router.matcher, ::Merb::Router.generator].each { |r| r.generate_singleton_routes(res, opts) } 
    304           end    
     23        # this can be optimized a lot. 
     24        sections = path.split('/') 
     25        sections.length.times do  
     26          j = sections.join('/') 
     27          return table[j] if table.has_key?(j) 
     28          sections.pops 
    30529        end 
    30630      end