Changeset 4242
- Timestamp:
- 04/21/06 15:17:02 (4 years ago)
- Files:
-
- trunk/actionpack/CHANGELOG (modified) (1 diff)
- trunk/actionpack/lib/action_controller/code_generation.rb (modified) (8 diffs)
- trunk/actionpack/lib/action_controller/routing.rb (modified) (8 diffs)
- trunk/actionpack/test/controller/routing_test.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/CHANGELOG
r4235 r4242 1 1 *SVN* 2 3 * Add support in routes for semicolon delimited "subpaths", like /books/:id;:action [Jamis Buck] 2 4 3 5 * 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 66 66 self.class::FieldsToDuplicate.each do |sym| 67 67 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) 69 69 copy.send("#{sym}=", value) 70 70 end … … 74 74 75 75 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] 77 77 attr_accessor(*Attributes) 78 78 FieldsToDuplicate = CodeGenerator::FieldsToDuplicate + Attributes … … 86 86 @move_ahead = nil 87 87 @finish_statement = Proc.new {|hash_expr| hash_expr} 88 @path_name = :path 89 @base_segment_name = :segment 90 @base_index_name = :index 88 91 end 89 92 … … 119 122 end 120 123 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 123 125 def index_name 124 126 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 126 128 end 127 129 … … 163 165 end 164 166 end 165 167 166 168 class GenerationGenerator < CodeGenerator #:nodoc: 167 Attributes = [:after, :before, :current, :segments ]169 Attributes = [:after, :before, :current, :segments, :subpath_at] 168 170 attr_accessor(*Attributes) 169 171 FieldsToDuplicate = CodeGenerator::FieldsToDuplicate + Attributes … … 174 176 @current = nil 175 177 @segments = [] 178 @subpath_at = nil 176 179 end 177 180 … … 203 206 yield d 204 207 end 205 208 206 209 def go 207 210 if current then current.write_generation(self) … … 216 219 d.go 217 220 end 218 221 222 def start_subpath! 223 @subpath_at ||= segments.length 224 end 225 219 226 def finish 227 segments[subpath_at..-1] = [segments[subpath_at..-1].join(";")] if subpath_at 220 228 line %("/#{segments.join('/')}") 221 229 end trunk/actionpack/lib/action_controller/routing.rb
r4209 r4242 61 61 return super(string, *args) unless self == Component 62 62 case string 63 when /.*;.*/ then SubpathComponent.new(string.split(/;/), *args) 63 64 when ':controller' then ControllerComponent.new(:controller, *args) 64 65 when /^:(\w+)$/ then DynamicComponent.new($1, *args) … … 67 68 end 68 69 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 69 117 end 70 118 … … 338 386 g.share_locals_with generator 339 387 g.before, g.current, g.after = [], components.first, (components[1..-1] || []) 340 388 341 389 known.each do |key, value| 342 390 if key == :controller then ControllerComponent.assign_controller(g, value) … … 355 403 356 404 def initialize_keys 357 @keys = (components.collect {|c| c.key} + known.keys). compact405 @keys = (components.collect {|c| c.key} + known.keys).flatten.compact 358 406 @keys.freeze 359 407 end … … 380 428 381 429 def initialize_hashes(options) 382 path_keys = components.collect {|c| c.key }. compact430 path_keys = components.collect {|c| c.key }.flatten.compact 383 431 self.known = {} 384 432 defaults = options.delete(:defaults) || {} 385 433 conditions = options.delete(:require) || {} 386 434 conditions.update(options.delete(:requirements) || {}) 387 435 388 436 options.each do |k, v| 389 437 if path_keys.include?(k) then (v.is_a?(Regexp) ? conditions : defaults)[k] = v … … 395 443 396 444 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| 398 447 if defaults.key?(component.key) then component.default = defaults[component.key] 399 448 elsif component.key == :action then component.default = 'index' … … 406 455 407 456 def add_default_requirements 408 component_keys = components.collect {|c| c.key} 457 component_keys = components.collect {|c| c.key}.flatten 409 458 known[:action] ||= 'index' unless component_keys.include? :action 410 459 end … … 506 555 end 507 556 end 508 557 509 558 eval g.to_s, nil, 'generated/routing/recognition.rb' 510 559 return g.to_s trunk/actionpack/test/controller/routing_test.rb
r3542 r4242 971 971 assert_equal ['/journal', []], rs.generate(:controller => 'content', :action => 'list_journal', :date => nil, :user_id => nil) 972 972 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 973 1049 end 974 1050