Changeset 7421
- Timestamp:
- 09/09/07 00:18:55 (1 year ago)
- Files:
-
- trunk/actionpack/CHANGELOG (modified) (1 diff)
- trunk/actionpack/lib/action_controller/integration.rb (modified) (1 diff)
- trunk/actionpack/lib/action_controller/routing_optimisation.rb (added)
- trunk/actionpack/lib/action_controller/routing.rb (modified) (10 diffs)
- trunk/actionpack/test/controller/resources_test.rb (modified) (1 diff)
- trunk/actionpack/test/controller/routing_test.rb (modified) (12 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/CHANGELOG
r7419 r7421 1 1 *SVN* 2 3 * Optimise named route generation when using positional arguments. [Koz] 4 5 This change delivers significant performance benefits for the most 6 common usage scenarios for modern rails applications by avoiding the 7 costly trip through url_for. Initial benchmarks indicate this is 8 between 6 and 20 times as fast. 2 9 3 10 * Explicitly require active_record/query_cache before using it. [Jeremy Kemper] trunk/actionpack/lib/action_controller/integration.rb
r7383 r7421 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) trunk/actionpack/lib/action_controller/routing.rb
r7415 r7421 2 2 require 'uri' 3 3 require 'action_controller/polymorphic_routes' 4 require 'action_controller/routing_optimisation' 4 5 5 6 class Object … … 255 256 mattr_accessor :controller_paths 256 257 self.controller_paths = [] 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 257 263 258 264 # A helper module to hold URL related helpers. … … 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 … … 332 338 @requirements = {} 333 339 @conditions = {} 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 334 352 end 335 353 … … 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] … … 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] … … 1052 1077 class NamedRouteCollection #:nodoc: 1053 1078 include Enumerable 1054 1079 include ActionController::Routing::Optimisation 1055 1080 attr_reader :routes, :helpers 1056 1081 … … 1129 1154 def define_url_helper(route, name, kind, options) 1130 1155 selector = url_helper_name(name, kind) 1156 # The segment keys used for positional paramters 1131 1157 1132 # The segment keys used for positional paramters1133 segment_keys = route.segments.collect do |segment|1134 segment.key if segment.respond_to? :key1135 end.compact1136 1158 hash_access_method = hash_access_name(name, kind) 1137 1159 1138 1160 @module.send :module_eval, <<-end_eval # We use module_eval to avoid leaks 1139 1161 def #{selector}(*args) 1162 1163 #{generate_optimisation_block(route, kind)} 1164 1140 1165 opts = if args.empty? || Hash === args.first 1141 1166 args.first || {} … … 1155 1180 # 1156 1181 options = args.last.is_a?(Hash) ? args.pop : {} 1157 args = args.zip(#{ segment_keys.inspect}).inject({}) do |h, (v, k)|1182 args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)| 1158 1183 h[k] = v 1159 1184 h … … 1168 1193 helpers << selector 1169 1194 end 1170 1171 1195 end 1172 1196 trunk/actionpack/test/controller/resources_test.rb
r7415 r7421 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, trunk/actionpack/test/controller/routing_test.rb
r7415 r7421 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' } … … 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 … … 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 … … 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 … … 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 198 uses_mocha "named route optimisation" do 199 def test_optimised_named_route_call_never_uses_url_for 200 rs.add_named_route :users, 'admin/user', :controller => '/admin/user', :action => 'index' 201 x = setup_for_named_route 202 x.expects(:url_for).never 203 x.send(:users_url) 204 x.send(:users_path) 205 x.send(:users_url, :some_arg=>"some_value") 206 x.send(:users_path, :some_arg=>"some_value") 207 end 208 end 197 209 198 210 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 211 klass = Class.new(MockController) 212 rs.install_helpers(klass) 213 klass.new(rs) 203 214 end 204 215 … … 215 226 map.connect ':controller/:action/:id' 216 227 end 217 x = setup_for_named_route.new 228 x = setup_for_named_route 229 # assert_equal( 230 # {:controller => 'page', :action => 'show', :title => 'hi', :use_route => :article, :only_path => false}, 231 # x.send(:article_url, :title => 'hi') 232 # ) 218 233 assert_equal( 219 {:controller => 'page', :action => 'show', :title => 'hi', :use_route => :article, :only_path => false}, 220 x.send(:article_url, :title => 'hi') 221 ) 222 assert_equal( 223 {:controller => 'page', :action => 'show', :title => 'hi', :day => 10, :year => 2005, :month => 6, :use_route => :article, :only_path => false}, 234 "http://named.route.test/page/2005/6/10/hi", 224 235 x.send(:article_url, :title => 'hi', :day => 10, :year => 2005, :month => 6) 225 236 ) … … 409 420 assert_equal '/test', rs.generate(:controller => 'post', :action => 'show', :year => nil) 410 421 411 x = setup_for_named_route .new412 assert_equal( {:controller => 'post', :action => 'show', :use_route => :blog, :only_path => false},422 x = setup_for_named_route 423 assert_equal("http://named.route.test/test", 413 424 x.send(:blog_url)) 414 425 end … … 456 467 assert_equal '/', rs.generate(:controller => 'content') 457 468 458 x = setup_for_named_route .new459 assert_equal( {:controller => 'content', :action => 'index', :use_route => :home, :only_path => false},469 x = setup_for_named_route 470 assert_equal("http://named.route.test", 460 471 x.send(:home_url)) 461 472 end … … 571 582 ensure 572 583 Object.send(:remove_const, :SubpathBooksController) rescue nil 584 end 585 586 def test_failed_requirements_raises_exception_with_violated_requirements 587 rs.draw do |r| 588 r.foo_with_requirement 'foos/:id', :controller=>'foos', :requirements=>{:id=>/\d+/} 589 end 590 591 x = setup_for_named_route 592 assert_raises(ActionController::RoutingError) do 593 x.send(:foo_with_requirement_url, "I am Against the requirements") 594 end 573 595 end 574 596 end … … 841 863 842 864 uses_mocha 'RouteTest' do 843 865 866 class MockController 867 attr_accessor :routes 868 869 def initialize(routes) 870 self.routes = routes 871 end 872 873 def url_for(options) 874 only_path = options.delete(:only_path) 875 path = routes.generate(options) 876 only_path ? path : "http://named.route.test#{path}" 877 end 878 879 def request 880 @request ||= MockRequest.new(:host => "named.route.test", :method => :get) 881 end 882 end 883 884 class MockRequest 885 attr_accessor :path, :path_parameters, :host, :subdomains, :domain, :method 886 887 def initialize(values={}) 888 values.each { |key, value| send("#{key}=", value) } 889 if values[:host] 890 subdomain, self.domain = values[:host].split(/\./, 2) 891 self.subdomains = [subdomain] 892 end 893 end 894 895 def protocol 896 "http://" 897 end 898 899 def host_with_port 900 (subdomains * '.') + '.' + domain 901 end 902 903 end 904 844 905 class RouteTest < Test::Unit::TestCase 845 906 … … 1303 1364 end 1304 1365 1366 1367 1368 1305 1369 class RouteSetTest < Test::Unit::TestCase 1306 class MockController1307 attr_accessor :routes1308 1309 def initialize(routes)1310 self.routes = routes1311 end1312 1313 def url_for(options)1314 only_path = options.delete(:only_path)1315 path = routes.generate(options)1316 only_path ? path : "http://named.route.test#{path}"1317 end1318 end1319 1320 class MockRequest1321 attr_accessor :path, :path_parameters, :host, :subdomains, :domain, :method1322 1323 def initialize(values={})1324 values.each { |key, value| send("#{key}=", value) }1325 if values[:host]1326 subdomain, self.domain = values[:host].split(/\./, 2)1327 self.subdomains = [subdomain]1328 end1329 end1330 end1331 1370 1332 1371 def set … … 1459 1498 end 1460 1499 1461 def test_named_route_url_method_with_ ordered_parameters_and_hash_ordered_parameters_override_hash1500 def test_named_route_url_method_with_no_positional_arguments 1462 1501 controller = setup_named_route_test 1463 assert_equal "http://named.route.test/people /go/7/hello/joe/5?baz=bar",1464 controller.send(: multi_url, 7, "hello", 5, :foo => 666, :baz => "bar")1502 assert_equal "http://named.route.test/people?baz=bar", 1503 controller.send(:index_url, :baz => "bar") 1465 1504 end 1466 1505