Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 4242

Show
Ignore:
Timestamp:
04/21/06 15:17:02 (4 years ago)
Author:
minam
Message:

Add support in routes for semicolon delimited "subpaths", like /books/:id;:action

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/actionpack/CHANGELOG

    r4235 r4242  
    11*SVN* 
     2 
     3* Add support in routes for semicolon delimited "subpaths", like /books/:id;:action [Jamis Buck] 
    24 
    35* Change link_to_function and button_to_function to (optionally) take an update_page block instead of a JavaScript string. Closes #4804. [zraii@comcast.net, Sam Stephenson] 
  • trunk/actionpack/lib/action_controller/code_generation.rb

    r1731 r4242  
    6666        self.class::FieldsToDuplicate.each do |sym| 
    6767          value = self.send(sym) 
    68           value = value.dup unless value.nil? || value.is_a?(Numeric) 
     68          value = value.dup unless value.nil? || value.is_a?(Numeric) || value.is_a?(Symbol) 
    6969          copy.send("#{sym}=", value) 
    7070        end 
     
    7474 
    7575    class RecognitionGenerator < CodeGenerator #:nodoc: 
    76       Attributes = [:after, :before, :current, :results, :constants, :depth, :move_ahead, :finish_statement
     76      Attributes = [:after, :before, :current, :results, :constants, :depth, :move_ahead, :finish_statement, :path_name, :base_segment_name, :base_index_name
    7777      attr_accessor(*Attributes) 
    7878      FieldsToDuplicate = CodeGenerator::FieldsToDuplicate + Attributes 
     
    8686        @move_ahead = nil 
    8787        @finish_statement = Proc.new {|hash_expr| hash_expr} 
     88        @path_name = :path 
     89        @base_segment_name = :segment 
     90        @base_index_name = :index 
    8891      end 
    8992     
     
    119122      end 
    120123     
    121       def segment_name() "segment#{depth}".to_sym end 
    122       def path_name() :path end 
     124      def segment_name() "#{base_segment_name}#{depth}".to_sym end 
    123125      def index_name 
    124126        move_ahead, @move_ahead = @move_ahead, nil 
    125         move_ahead ? "index += #{move_ahead}" : 'index' 
     127        move_ahead ? "#{base_index_name} += #{move_ahead}" : base_index_name 
    126128      end 
    127129     
     
    163165      end 
    164166    end 
    165    
     167 
    166168    class GenerationGenerator < CodeGenerator #:nodoc: 
    167       Attributes = [:after, :before, :current, :segments
     169      Attributes = [:after, :before, :current, :segments, :subpath_at
    168170      attr_accessor(*Attributes) 
    169171      FieldsToDuplicate = CodeGenerator::FieldsToDuplicate + Attributes 
     
    174176        @current = nil 
    175177        @segments = [] 
     178        @subpath_at = nil 
    176179      end 
    177180     
     
    203206        yield d 
    204207      end 
    205      
     208 
    206209      def go 
    207210        if current then current.write_generation(self) 
     
    216219        d.go 
    217220      end 
    218      
     221 
     222      def start_subpath! 
     223        @subpath_at ||= segments.length 
     224      end 
     225 
    219226      def finish 
     227        segments[subpath_at..-1] = [segments[subpath_at..-1].join(";")] if subpath_at 
    220228        line %("/#{segments.join('/')}") 
    221229      end 
  • trunk/actionpack/lib/action_controller/routing.rb

    r4209 r4242  
    6161        return super(string, *args) unless self == Component 
    6262        case string 
     63          when /.*;.*/       then SubpathComponent.new(string.split(/;/), *args) 
    6364          when ':controller' then ControllerComponent.new(:controller, *args) 
    6465          when /^:(\w+)$/    then DynamicComponent.new($1, *args) 
     
    6768        end 
    6869      end  
     70    end 
     71 
     72    class SubpathComponent < Component #:nodoc: 
     73      attr_reader :parts 
     74 
     75      def initialize(parts, *args) 
     76        @parts = parts.map { |part| Component.new(part, *args) } 
     77      end 
     78 
     79      def write_recognition(g) 
     80        raise RoutingError, "Subpath components must occur last" unless g.after.empty? 
     81        g.next_segment 
     82        g.line "subindex, subpath = 0, #{g.next_segment}.split(/;/)" 
     83        tweak_recognizer(g).go 
     84        g.move_forward { |gg| gg.continue } 
     85      end 
     86 
     87      def write_generation(g) 
     88        raise RoutingError, "Subpath components must occur last" unless g.after.empty? 
     89        tweak_generator(g).go 
     90      end 
     91 
     92      def key 
     93        parts.map { |p| p.key } 
     94      end 
     95 
     96      private 
     97 
     98        def tweak_recognizer(g) 
     99          gg = g.dup 
     100 
     101          gg.path_name = :subpath 
     102          gg.base_segment_name = :subsegment 
     103          gg.base_index_name = :subindex 
     104          gg.depth = 0 
     105 
     106          gg.before, gg.current, gg.after = [], parts.first, (parts[1..-1] || []) 
     107 
     108          gg 
     109        end 
     110 
     111        def tweak_generator(g) 
     112          gg = g.dup 
     113          gg.before, gg.current, gg.after = [], parts.first, (parts[1..-1] || []) 
     114          gg.start_subpath! 
     115          gg 
     116        end 
    69117    end 
    70118 
     
    338386        g.share_locals_with generator 
    339387        g.before, g.current, g.after = [], components.first, (components[1..-1] || []) 
    340      
     388 
    341389        known.each do |key, value| 
    342390          if key == :controller then ControllerComponent.assign_controller(g, value) 
     
    355403 
    356404      def initialize_keys 
    357         @keys = (components.collect {|c| c.key} + known.keys).compact 
     405        @keys = (components.collect {|c| c.key} + known.keys).flatten.compact 
    358406        @keys.freeze 
    359407      end 
     
    380428     
    381429        def initialize_hashes(options) 
    382           path_keys = components.collect {|c| c.key }.compact  
     430          path_keys = components.collect {|c| c.key }.flatten.compact 
    383431          self.known = {} 
    384432          defaults = options.delete(:defaults) || {} 
    385433          conditions = options.delete(:require) || {} 
    386434          conditions.update(options.delete(:requirements) || {}) 
    387        
     435 
    388436          options.each do |k, v| 
    389437            if path_keys.include?(k) then (v.is_a?(Regexp) ? conditions : defaults)[k] = v 
     
    395443     
    396444        def configure_components(defaults, conditions) 
    397           components.each do |component| 
     445          all_components = components.map { |c| SubpathComponent === c ? c.parts : c }.flatten 
     446          all_components.each do |component| 
    398447            if defaults.key?(component.key) then component.default = defaults[component.key] 
    399448            elsif component.key == :action  then component.default = 'index' 
     
    406455         
    407456        def add_default_requirements 
    408           component_keys = components.collect {|c| c.key} 
     457          component_keys = components.collect {|c| c.key}.flatten 
    409458          known[:action] ||= 'index' unless component_keys.include? :action 
    410459        end 
     
    506555          end 
    507556        end 
    508          
     557 
    509558        eval g.to_s, nil, 'generated/routing/recognition.rb' 
    510559        return g.to_s 
  • trunk/actionpack/test/controller/routing_test.rb

    r3542 r4242  
    971971    assert_equal ['/journal', []], rs.generate(:controller => 'content', :action => 'list_journal', :date => nil, :user_id => nil) 
    972972  end 
     973 
     974  def setup_request_method_routes_for(method) 
     975    @request = ActionController::TestRequest.new 
     976    @request.env["REQUEST_METHOD"] = method 
     977    @request.request_uri = "/match" 
     978 
     979    rs.draw do |r| 
     980      r.connect '/match', :controller => 'books', :action => 'get', :require => { :method => :get } 
     981      r.connect '/match', :controller => 'books', :action => 'post', :require => { :method => :post } 
     982      r.connect '/match', :controller => 'books', :action => 'put', :require => { :method => :put } 
     983      r.connect '/match', :controller => 'books', :action => 'delete', :require => { :method => :delete } 
     984    end 
     985  end 
     986 
     987  %w(GET POST PUT DELETE).each do |request_method| 
     988    define_method("test_request_method_recognized_with_#{request_method}") do 
     989      begin 
     990        Object.const_set(:BooksController, Class.new(ActionController::Base)) 
     991 
     992        setup_request_method_routes_for(request_method) 
     993 
     994        assert_nothing_raised { rs.recognize(@request) } 
     995        assert_equal request_method.downcase, @request.path_parameters["action"] 
     996      ensure 
     997        Object.send(:remove_const, :BooksController) rescue nil 
     998      end 
     999    end 
     1000  end 
     1001 
     1002  def test_subpath_recognized 
     1003    Object.const_set(:SubpathBooksController, Class.new(ActionController::Base)) 
     1004 
     1005    rs.draw do |r| 
     1006      r.connect '/books/:id;edit', :controller => 'subpath_books', :action => 'edit' 
     1007      r.connect '/items/:id;:action', :controller => 'subpath_books' 
     1008      r.connect '/posts/new;:action', :controller => 'subpath_books' 
     1009    end 
     1010 
     1011    hash = rs.recognize_path %w(books 17;edit) 
     1012    assert_not_nil hash 
     1013    assert_equal %w(subpath_books 17 edit), [hash["controller"].controller_name, hash["id"], hash["action"]] 
     1014     
     1015    hash = rs.recognize_path %w(items 3;complete) 
     1016    assert_not_nil hash 
     1017    assert_equal %w(subpath_books 3 complete), [hash["controller"].controller_name, hash["id"], hash["action"]] 
     1018     
     1019    hash = rs.recognize_path %w(posts new;preview) 
     1020    assert_not_nil hash 
     1021    assert_equal %w(subpath_books preview), [hash["controller"].controller_name, hash["action"]] 
     1022 
     1023    # for now, low-hanging fruit only. We don't allow subpath components anywhere 
     1024    # except at the end of the path 
     1025    assert_raises(ActionController::RoutingError) do 
     1026      rs.draw do |r| 
     1027        r.connect '/books;german/new', :controller => 'subpath_books', :action => "new" 
     1028      end 
     1029    end 
     1030  ensure 
     1031    Object.send(:remove_const, :SubpathBooksController) rescue nil 
     1032  end 
     1033 
     1034  def test_subpath_generated 
     1035    Object.const_set(:SubpathBooksController, Class.new(ActionController::Base)) 
     1036 
     1037    rs.draw do |r| 
     1038      r.connect '/books/:id;edit', :controller => 'subpath_books', :action => 'edit' 
     1039      r.connect '/items/:id;:action', :controller => 'subpath_books' 
     1040      r.connect '/posts/new;:action', :controller => 'subpath_books' 
     1041    end 
     1042 
     1043    assert_equal ["/books/7;edit", []], rs.generate(:controller => "subpath_books", :id => 7, :action => "edit") 
     1044    assert_equal ["/items/15;complete", []], rs.generate(:controller => "subpath_books", :id => 15, :action => "complete") 
     1045    assert_equal ["/posts/new;preview", []], rs.generate(:controller => "subpath_books", :action => "preview") 
     1046  ensure 
     1047    Object.send(:remove_const, :SubpathBooksController) rescue nil 
     1048  end 
    9731049end 
    9741050