Ticket #9450: 9450.diff
| File 9450.diff, 17.5 kB (added by nzkoz, 3 years ago) |
|---|
-
a/actionpack/lib/action_controller/integration.rb
old new 74 74 75 75 unless defined? @named_routes_configured 76 76 # install the named routes in this session instance. 77 # But we have to disable the optimisation code so that we can 78 # generate routes without @request being initialized 79 Routing.optimise_named_routes=false 80 Routing::Routes.reload! 77 81 klass = class<<self; self; end 78 82 Routing::Routes.install_helpers(klass) 79 83 -
a/actionpack/lib/action_controller/routing.rb
old new 1 1 require 'cgi' 2 2 require 'uri' 3 3 require 'action_controller/polymorphic_routes' 4 require 'action_controller/routing_optimisation' 4 5 5 6 class Object 6 7 def to_param … … 255 256 mattr_accessor :controller_paths 256 257 self.controller_paths = [] 257 258 259 # Indicates whether or not optimise the generated named 260 # route helper methods 261 mattr_accessor :optimise_named_routes 262 self.optimise_named_routes = true 263 258 264 # A helper module to hold URL related helpers. 259 265 module Helpers 260 266 include PolymorphicRoutes … … 325 331 end 326 332 327 333 class Route #:nodoc: 328 attr_accessor :segments, :requirements, :conditions 334 attr_accessor :segments, :requirements, :conditions, :optimise 329 335 330 336 def initialize 331 337 @segments = [] 332 338 @requirements = {} 333 339 @conditions = {} 334 340 end 341 342 # Indicates whether the routes should be optimised with the string interpolation 343 # version of the named routes methods. 344 def optimise? 345 @optimise 346 end 347 348 def segment_keys 349 segments.collect do |segment| 350 segment.key if segment.respond_to? :key 351 end.compact 352 end 335 353 336 354 # Write and compile a +generate+ method for this Route. 337 355 def write_generation … … 381 399 end 382 400 requirement_conditions * ' && ' unless requirement_conditions.empty? 383 401 end 402 384 403 def generation_structure 385 404 segments.last.string_structure segments[0..-2] 386 405 end … … 977 996 requirements = assign_route_options(segments, defaults, requirements) 978 997 979 998 route = Route.new 999 980 1000 route.segments = segments 981 1001 route.requirements = requirements 982 1002 route.conditions = conditions 1003 1004 # Routes cannot use the current string interpolation method 1005 # if there are user-supplied :requirements as the interpolation 1006 # code won't raise RoutingErrors when generating 1007 route.optimise = !options.key?(:requirements) && ActionController::Routing.optimise_named_routes 983 1008 984 1009 if !route.significant_keys.include?(:action) && !route.requirements[:action] 985 1010 route.requirements[:action] = "index" … … 1056 1081 # named routes. 1057 1082 class NamedRouteCollection #:nodoc: 1058 1083 include Enumerable 1059 1084 include ActionController::Routing::Optimisation 1060 1085 attr_reader :routes, :helpers, :deprecated_named_routes 1061 1086 1062 1087 def initialize … … 1140 1165 1141 1166 def define_url_helper(route, name, kind, options) 1142 1167 selector = url_helper_name(name, kind) 1143 1144 1168 # The segment keys used for positional paramters 1145 segment_keys = route.segments.collect do |segment| 1146 segment.key if segment.respond_to? :key 1147 end.compact 1169 1148 1170 hash_access_method = hash_access_name(name, kind) 1149 1171 1150 1172 @module.send :module_eval, <<-end_eval # We use module_eval to avoid leaks 1151 1173 def #{selector}(*args) 1174 1175 #{generate_optimisation_block(route, kind)} 1176 1152 1177 opts = if args.empty? || Hash === args.first 1153 1178 args.first || {} 1154 1179 else … … 1166 1191 # foo_url(bar, baz, bang, :sort_by => 'baz') 1167 1192 # 1168 1193 options = args.last.is_a?(Hash) ? args.pop : {} 1169 args = args.zip(#{ segment_keys.inspect}).inject({}) do |h, (v, k)|1194 args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)| 1170 1195 h[k] = v 1171 1196 h 1172 1197 end … … 1179 1204 @module.send(:protected, selector) 1180 1205 helpers << selector 1181 1206 end 1182 1183 1207 end 1184 1208 1185 1209 attr_accessor :routes, :named_routes -
/dev/null
old new 1 module ActionController 2 module Routing 3 # Much of the slow performance from routes comes from the 4 # complexity of expiry, :requirements matching, defaults providing 5 # and figuring out which url pattern to use. With named routes 6 # we can avoid the expense of finding the right route. So if 7 # they've provided the right number of arguments, and have no 8 # :requirements, we can just build up a string and return it 9 # 10 # This means that we end up generating code that looks like this: 11 # map.person_friend '/people/:person_id/friend/:friend_id',... 12 # 13 # def person_friend_path(*args) 14 # if args.size == 2 15 # return "/people/#{URI.escape(args[0].to_param)}/friend/#{URI.escape(args[1].to_param)}" 16 # elsif args.size == 3 && args[3].is_a?(Hash) 17 # return "/people/#{URI.escape(args[0].to_param)}/friend/#{URI.escape(args[1].to_param)}?#{args[2].to_query_string}" 18 # end 19 # 20 # SLOW_CODE 21 # end 22 module Optimisation 23 def generate_optimisation_block(route, kind) 24 return "" unless route.optimise? 25 OPTIMISERS.inject("") do |memo, klazz| 26 optimiser = klazz.new(route, kind) 27 memo << "return #{optimiser.generation_code} if #{optimiser.guard_condition}\n" 28 end 29 end 30 31 class Optimiser 32 attr_reader :route, :kind 33 def initialize(route, kind) 34 @route = route 35 @kind = kind 36 end 37 end 38 39 class PositionalArguments < Optimiser 40 def guard_condition 41 number_of_arguments = route.segment_keys.size 42 # if they're using foo_url(:id=>2) it's one 43 # argument, but we don't want to generate /foos/id2 44 if number_of_arguments == 1 45 "args.size == 1 && !args.first.is_a?(Hash)" 46 else 47 "args.size == #{number_of_arguments}" 48 end 49 end 50 51 def generation_code 52 elements = [] 53 idx = 0 54 55 if kind == :url 56 elements << '#{request.protocol}' 57 elements << '#{request.host_with_port}' 58 end 59 60 # The last entry in route.segments appears to 61 # *always* be a 'divider segment' for '/' 62 # but we have assertions to ensure that we don't 63 # include the trailing slashes, so skip them 64 route.segments[0..-2].each do |segment| 65 if segment.is_a?(DynamicSegment) 66 elements << "\#{URI.escape(args[#{idx}].to_param, ActionController::Routing::Segment::UNSAFE_PCHAR)}" 67 idx += 1 68 else 69 elements << segment.to_s 70 end 71 end 72 %("#{elements * ''}") 73 end 74 end 75 76 77 # supports additional arguments like 78 # - person_url(@person, :q=>"some search term") 79 class PositionalArgumentsWithAdditionalParams < PositionalArguments 80 def guard_condition 81 number_of_arguments = route.segment_keys.size 82 if number_of_arguments == 1 83 "args.size == 2 && !args.first.is_a?(Hash)" 84 else 85 "args.size == #{number_of_arguments + 1}" 86 end 87 end 88 89 # Use the same code as without the arguments, but add an 90 # args.last.to_query on the end 91 def generation_code 92 orig_code = super 93 orig_code[-1] = '?#{args.last.to_query}"' 94 orig_code 95 end 96 end 97 98 OPTIMISERS = [PositionalArguments, PositionalArgumentsWithAdditionalParams] 99 end 100 end 101 end -
a/actionpack/test/controller/resources_test.rb
old new 26 26 end 27 27 28 28 class ResourcesTest < Test::Unit::TestCase 29 30 31 # The assertions in these tests are incompatible with the hash method 32 # optimisation. This could indicate user level problems 33 def setup 34 ActionController::Routing.optimise_named_routes = false 35 end 36 37 def tear_down 38 ActionController::Routing.optimise_named_routes = true 39 end 40 29 41 def test_should_arrange_actions 30 42 resource = ActionController::Resources::Resource.new(:messages, 31 43 :collection => { :rss => :get, :reorder => :post, :csv => :post }, -
a/actionpack/test/controller/routing_test.rb
old new 47 47 class LegacyRouteSetTests < Test::Unit::TestCase 48 48 attr_reader :rs 49 49 def setup 50 # These tests assume optimisation is on, so re-enable it. 51 ActionController::Routing.optimise_named_routes = true 50 52 @rs = ::ActionController::Routing::RouteSet.new 51 53 @rs.draw {|m| m.connect ':controller/:action/:id' } 52 54 ActionController::Routing.use_controllers! %w(content admin/user admin/news_feed) … … 166 168 167 169 def test_basic_named_route 168 170 rs.add_named_route :home, '', :controller => 'content', :action => 'list' 169 x = setup_for_named_route .new170 assert_equal( {:controller => 'content', :action => 'list', :use_route => :home, :only_path => false},171 x = setup_for_named_route 172 assert_equal("http://named.route.test", 171 173 x.send(:home_url)) 172 174 end 173 175 174 176 def test_named_route_with_option 175 177 rs.add_named_route :page, 'page/:title', :controller => 'content', :action => 'show_page' 176 x = setup_for_named_route .new177 assert_equal( {:controller => 'content', :action => 'show_page', :title => 'new stuff', :use_route => :page, :only_path => false},178 x = setup_for_named_route 179 assert_equal("http://named.route.test/page/new%20stuff", 178 180 x.send(:page_url, :title => 'new stuff')) 179 181 end 180 182 181 183 def test_named_route_with_default 182 184 rs.add_named_route :page, 'page/:title', :controller => 'content', :action => 'show_page', :title => 'AboutPage' 183 x = setup_for_named_route.new 184 assert_equal({:controller => 'content', :action => 'show_page', :title => 'AboutPage', :use_route => :page, :only_path => false}, 185 x.send(:page_url)) 186 assert_equal({:controller => 'content', :action => 'show_page', :title => 'AboutRails', :use_route => :page, :only_path => false}, 185 x = setup_for_named_route 186 assert_equal("http://named.route.test/page/AboutRails", 187 187 x.send(:page_url, :title => "AboutRails")) 188 188 189 189 end 190 190 191 191 def test_named_route_with_nested_controller 192 192 rs.add_named_route :users, 'admin/user', :controller => '/admin/user', :action => 'index' 193 x = setup_for_named_route .new194 assert_equal( {:controller => '/admin/user', :action => 'index', :use_route => :users, :only_path => false},193 x = setup_for_named_route 194 assert_equal("http://named.route.test/admin/user", 195 195 x.send(:users_url)) 196 196 end 197 197 198 198 def setup_for_named_route 199 x = Class.new 200 x.send(:define_method, :url_for) {|x| x} 201 rs.install_helpers(x) 202 x 199 klass = Class.new(MockController) 200 rs.install_helpers(klass) 201 klass.new(rs) 203 202 end 204 203 205 204 def test_named_route_without_hash … … 214 213 :year => /\d+/, :month => /\d+/, :day => /\d+/ 215 214 map.connect ':controller/:action/:id' 216 215 end 217 x = setup_for_named_route .new218 assert_equal(219 {:controller => 'page', :action => 'show', :title => 'hi', :use_route => :article, :only_path => false},220 x.send(:article_url, :title => 'hi')221 )216 x = setup_for_named_route 217 # assert_equal( 218 # {:controller => 'page', :action => 'show', :title => 'hi', :use_route => :article, :only_path => false}, 219 # x.send(:article_url, :title => 'hi') 220 # ) 222 221 assert_equal( 223 {:controller => 'page', :action => 'show', :title => 'hi', :day => 10, :year => 2005, :month => 6, :use_route => :article, :only_path => false},222 "http://named.route.test/page/2005/6/10/hi", 224 223 x.send(:article_url, :title => 'hi', :day => 10, :year => 2005, :month => 6) 225 224 ) 226 225 end … … 408 407 assert_equal '/test', rs.generate(:controller => 'post', :action => 'show') 409 408 assert_equal '/test', rs.generate(:controller => 'post', :action => 'show', :year => nil) 410 409 411 x = setup_for_named_route .new412 assert_equal( {:controller => 'post', :action => 'show', :use_route => :blog, :only_path => false},410 x = setup_for_named_route 411 assert_equal("http://named.route.test/test", 413 412 x.send(:blog_url)) 414 413 end 415 414 … … 455 454 assert_equal '/', rs.generate(:controller => 'content', :action => 'index') 456 455 assert_equal '/', rs.generate(:controller => 'content') 457 456 458 x = setup_for_named_route .new459 assert_equal( {:controller => 'content', :action => 'index', :use_route => :home, :only_path => false},457 x = setup_for_named_route 458 assert_equal("http://named.route.test", 460 459 x.send(:home_url)) 461 460 end 462 461 … … 571 570 ensure 572 571 Object.send(:remove_const, :SubpathBooksController) rescue nil 573 572 end 573 574 def test_failed_requirements_raises_exception_with_violated_requirements 575 rs.draw do |r| 576 r.foo_with_requirement 'foos/:id', :controller=>'foos', :requirements=>{:id=>/\d+/} 577 end 578 579 x = setup_for_named_route 580 assert_raises(ActionController::RoutingError) do 581 x.send(:foo_with_requirement_url, "I am Against the requirements") 582 end 583 end 574 584 end 575 585 576 586 class SegmentTest < Test::Unit::TestCase … … 840 850 end 841 851 842 852 uses_mocha 'RouteTest' do 843 853 854 class MockController 855 attr_accessor :routes 856 857 def initialize(routes) 858 self.routes = routes 859 end 860 861 def url_for(options) 862 only_path = options.delete(:only_path) 863 path = routes.generate(options) 864 only_path ? path : "http://named.route.test#{path}" 865 end 866 867 def request 868 @request ||= MockRequest.new(:host => "named.route.test", :method => :get) 869 end 870 end 871 872 class MockRequest 873 attr_accessor :path, :path_parameters, :host, :subdomains, :domain, :method 874 875 def initialize(values={}) 876 values.each { |key, value| send("#{key}=", value) } 877 if values[:host] 878 subdomain, self.domain = values[:host].split(/\./, 2) 879 self.subdomains = [subdomain] 880 end 881 end 882 883 def protocol 884 "http://" 885 end 886 887 def host_with_port 888 (subdomains * '.') + '.' + domain 889 end 890 891 end 892 844 893 class RouteTest < Test::Unit::TestCase 845 894 846 895 def setup … … 1354 1403 1355 1404 end 1356 1405 1357 class RouteSetTest < Test::Unit::TestCase1358 class MockController1359 attr_accessor :routes1360 1361 def initialize(routes)1362 self.routes = routes1363 end1364 1406 1365 def url_for(options)1366 only_path = options.delete(:only_path)1367 path = routes.generate(options)1368 only_path ? path : "http://named.route.test#{path}"1369 end1370 end1371 1407 1372 class MockRequest1373 attr_accessor :path, :path_parameters, :host, :subdomains, :domain, :method1374 1408 1375 def initialize(values={}) 1376 values.each { |key, value| send("#{key}=", value) } 1377 if values[:host] 1378 subdomain, self.domain = values[:host].split(/\./, 2) 1379 self.subdomains = [subdomain] 1380 end 1381 end 1382 end 1409 class RouteSetTest < Test::Unit::TestCase 1383 1410 1384 1411 def set 1385 1412 @set ||= ROUTING::RouteSet.new … … 1510 1537 controller.send(:multi_url, 7, "hello", 5, :baz => "bar") 1511 1538 end 1512 1539 1513 def test_named_route_url_method_with_ ordered_parameters_and_hash_ordered_parameters_override_hash1540 def test_named_route_url_method_with_no_positional_arguments 1514 1541 controller = setup_named_route_test 1515 assert_equal "http://named.route.test/people /go/7/hello/joe/5?baz=bar",1516 controller.send(: multi_url, 7, "hello", 5, :foo => 666, :baz => "bar")1542 assert_equal "http://named.route.test/people?baz=bar", 1543 controller.send(:index_url, :baz => "bar") 1517 1544 end 1518 1545 1519 1546 def test_draw_default_route