aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/routing/route_set.rb
diff options
context:
space:
mode:
authorAndrew White <andyw@pixeltrix.co.uk>2014-01-05 00:24:57 +0000
committerAndrew White <andyw@pixeltrix.co.uk>2014-01-05 00:36:25 +0000
commitd017e92e1d825c6bf9fa49c339c55ca920effe45 (patch)
tree1be932f13414cd91fe8365b3f4d2d1d05362965b /actionpack/lib/action_dispatch/routing/route_set.rb
parent339ce7fe9d09c088cfc44e78d91b0a5dd220f9f1 (diff)
downloadrails-d017e92e1d825c6bf9fa49c339c55ca920effe45.tar.gz
rails-d017e92e1d825c6bf9fa49c339c55ca920effe45.tar.bz2
rails-d017e92e1d825c6bf9fa49c339c55ca920effe45.zip
Use a custom route vistor for optimized route generation
Using a Regexp to replace dynamic segments in a path string is fraught with difficulty and can lead to odd edge cases like #13349. Since we already have a parsed representation of the path it makes sense to use that to generate an array of segments that can be used to build an optimized route's path quickly. Tests on a simple route (e.g. /posts/:id) show a speedup of 35%: https://gist.github.com/pixeltrix/8261932 Calculating ------------------------------------- Current Helper: 5274 i/100ms New Helper: 8050 i/100ms ------------------------------------------------- Current Helper: 79263.6 (±3.7%) i/s - 395550 in 4.997252s New Helper: 153464.5 (±4.9%) i/s - 772800 in 5.047834s Tests on a more complex route show even an greater performance boost: https://gist.github.com/pixeltrix/8261957 Calculating ------------------------------------- Current Helper: 2367 i/100ms New Helper: 5382 i/100ms ------------------------------------------------- Current Helper: 29506.0 (±3.2%) i/s - 149121 in 5.059294s New Helper: 78815.5 (±4.1%) i/s - 398268 in 5.062161s It also has the added benefit of fixing the edge cases described above. Fixes #13349
Diffstat (limited to 'actionpack/lib/action_dispatch/routing/route_set.rb')
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb52
1 files changed, 25 insertions, 27 deletions
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 04faabef37..bbdcc7c5d3 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -163,9 +163,10 @@ module ActionDispatch
def initialize(route, options)
super
- @path_parts = @route.required_parts
- @arg_size = @path_parts.size
- @string_route = @route.optimized_path
+ @klass = Journey::Router::Utils
+ @required_parts = @route.required_parts
+ @arg_size = @required_parts.size
+ @optimized_path = @route.optimized_path
end
def call(t, args)
@@ -182,42 +183,39 @@ module ActionDispatch
private
def optimized_helper(args)
- path = @string_route.dup
- klass = Journey::Router::Utils
+ params = Hash[parameterize_args(args)]
+ missing_keys = missing_keys(params)
- @path_parts.zip(args) do |part, arg|
- parameterized_arg = arg.to_param
+ unless missing_keys.empty?
+ raise_generation_error(params, missing_keys)
+ end
- if parameterized_arg.nil? || parameterized_arg.empty?
- raise_generation_error(args)
- end
+ @optimized_path.map{ |segment| replace_segment(params, segment) }.join
+ end
- # Replace each route parameter
- # e.g. :id for regular parameter or *path for globbing
- # with ruby string interpolation code
- path.gsub!(/(\*|:)#{part}/, klass.escape_fragment(parameterized_arg))
- end
- path
+ def replace_segment(params, segment)
+ Symbol === segment ? @klass.escape_fragment(params[segment]) : segment
end
def optimize_routes_generation?(t)
t.send(:optimize_routes_generation?)
end
- def raise_generation_error(args)
- parts, missing_keys = [], []
-
- @path_parts.zip(args) do |part, arg|
- parameterized_arg = arg.to_param
-
- if parameterized_arg.nil? || parameterized_arg.empty?
- missing_keys << part
+ def parameterize_args(args)
+ [].tap do |parameterized_args|
+ @required_parts.zip(args) do |part, arg|
+ parameterized_arg = arg.to_param
+ parameterized_args << [part, parameterized_arg]
end
-
- parts << [part, arg]
end
+ end
+
+ def missing_keys(args)
+ args.select{ |part, arg| arg.nil? || arg.empty? }.keys
+ end
- message = "No route matches #{Hash[parts].inspect}"
+ def raise_generation_error(args, missing_keys)
+ message = "No route matches #{args.inspect}"
message << " missing required keys: #{missing_keys.inspect}"
raise ActionController::UrlGenerationError, message