diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/routing')
-rw-r--r-- | actionpack/lib/action_dispatch/routing/inspector.rb | 40 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 12 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/route_set.rb | 181 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/routes_proxy.rb | 2 |
4 files changed, 156 insertions, 79 deletions
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb index ea3e8357d4..bc6dd7145c 100644 --- a/actionpack/lib/action_dispatch/routing/inspector.rb +++ b/actionpack/lib/action_dispatch/routing/inspector.rb @@ -34,6 +34,23 @@ module ActionDispatch super.to_s end + def regexp + __getobj__.path.to_regexp + end + + def json_regexp + str = regexp.inspect. + sub('\\A' , '^'). + sub('\\Z' , '$'). + sub('\\z' , '$'). + sub(/^\// , ''). + sub(/\/[a-z]*$/ , ''). + gsub(/\(\?#.+\)/ , ''). + gsub(/\(\?-\w+:/ , '('). + gsub(/\s/ , '') + Regexp.new(str).source + end + def reqs @reqs ||= begin reqs = endpoint @@ -73,10 +90,11 @@ module ActionDispatch routes_to_display = filter_routes(filter) routes = collect_routes(routes_to_display) - formatter.section :application, 'Application routes', routes + formatter.section routes @engines.each do |name, engine_routes| - formatter.section :engine, "Routes for #{name}", engine_routes + formatter.section_title "Routes for #{name}" + formatter.section engine_routes end formatter.result @@ -100,7 +118,11 @@ module ActionDispatch end.collect do |route| collect_engine_routes(route) - { name: route.name, verb: route.verb, path: route.path, reqs: route.reqs } + { name: route.name, + verb: route.verb, + path: route.path, + reqs: route.reqs, + regexp: route.json_regexp } end end @@ -125,8 +147,11 @@ module ActionDispatch @buffer.join("\n") end - def section(type, title, routes) - @buffer << "\n#{title}:" unless type == :application + def section_title(title) + @buffer << "\n#{title}:" + end + + def section(routes) @buffer << draw_section(routes) end @@ -148,8 +173,11 @@ module ActionDispatch @buffer = [] end - def section(type, title, routes) + def section_title(title) @buffer << %(<tr><th colspan="4">#{title}</th></tr>) + end + + def section(routes) @buffer << @view.render(partial: "routes/route", collection: routes) end diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 6d93f609a6..0a41ed0fcf 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/hash/except' require 'active_support/core_ext/hash/reverse_merge' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/array/extract_options' require 'active_support/inflector' require 'action_dispatch/routing/redirection' @@ -245,6 +246,12 @@ module ActionDispatch raise ArgumentError, "missing :action" end + if controller.is_a?(String) && controller !~ /\A[a-z_0-9\/]*\z/ + message = "'#{controller}' is not a supported controller name. This can lead to potential routing problems." + message << " See http://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use" + raise ArgumentError, message + end + hash = {} hash[:controller] = controller unless controller.blank? hash[:action] = action unless action.blank? @@ -1403,9 +1410,10 @@ module ActionDispatch def add_route(action, options) # :nodoc: path = path_for_action(action, options.delete(:path)) + action = action.to_s.dup - if action.to_s =~ /^[\w\/]+$/ - options[:action] ||= action unless action.to_s.include?("/") + if action =~ /^[\w\/]+$/ + options[:action] ||= action unless action.include?("/") else action = nil end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index c72310cca3..ff86f87d49 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -4,6 +4,7 @@ require 'thread_safe' require 'active_support/core_ext/object/to_query' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/module/remove_method' +require 'active_support/core_ext/array/extract_options' require 'action_controller/metal/exceptions' module ActionDispatch @@ -100,36 +101,16 @@ module ActionDispatch def initialize @routes = {} @helpers = [] - @module = Module.new do - protected - - def handle_positional_args(args, options, segment_keys) - inner_options = args.extract_options! - result = options.dup - - if args.size > 0 - keys = segment_keys - if args.size < keys.size - 1 # take format into account - keys -= self.url_options.keys if self.respond_to?(:url_options) - keys -= options.keys - end - result.merge!(Hash[keys.zip(args)]) - end - - result.merge!(inner_options) - end - end + @module = Module.new end def helper_names - self.module.instance_methods.map(&:to_s) + @helpers.map(&:to_s) end def clear! @helpers.each do |helper| - @module.module_eval do - remove_possible_method helper - end + @module.remove_possible_method helper end @routes.clear @@ -162,68 +143,126 @@ module ActionDispatch routes.length end - private + class UrlHelper # :nodoc: + def self.create(route, options) + if optimize_helper?(route) + OptimizedUrlHelper.new(route, options) + else + new route, options + end + end - def define_named_route_methods(name, route) - define_url_helper route, :"#{name}_path", - route.defaults.merge(:use_route => name, :only_path => true) - define_url_helper route, :"#{name}_url", - route.defaults.merge(:use_route => name, :only_path => false) + def self.optimize_helper?(route) + route.requirements.except(:controller, :action).empty? end - # Create a url helper allowing ordered parameters to be associated - # with corresponding dynamic segments, so you can do: - # - # foo_url(bar, baz, bang) - # - # Instead of: - # - # foo_url(bar: bar, baz: baz, bang: bang) - # - # Also allow options hash, so you can do: - # - # foo_url(bar, baz, bang, sort_by: 'baz') - # - def define_url_helper(route, name, options) - @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1 - remove_possible_method :#{name} - def #{name}(*args) - if #{optimize_helper?(route)} && args.size == #{route.required_parts.size} && !args.last.is_a?(Hash) && optimize_routes_generation? - options = #{options.inspect} - options.merge!(url_options) if respond_to?(:url_options) - options[:path] = "#{optimized_helper(route)}" - ActionDispatch::Http::URL.url_for(options) - else - url_for(handle_positional_args(args, #{options.inspect}, #{route.segment_keys.inspect})) - end + class OptimizedUrlHelper < UrlHelper # :nodoc: + attr_reader :arg_size + + def initialize(route, options) + super + @path_parts = @route.required_parts + @arg_size = @path_parts.size + @string_route = string_route(route) + end + + def call(t, args) + if args.size == arg_size && !args.last.is_a?(Hash) && optimize_routes_generation?(t) + @options.merge!(t.url_options) if t.respond_to?(:url_options) + @options[:path] = optimized_helper(args) + ActionDispatch::Http::URL.url_for(@options) + else + super end - END_EVAL + end + + private + + def string_route(route) + string_route = route.ast.to_s.dup + while string_route.gsub!(/\([^\)]*\)/, "") + true + end + string_route + end + + def optimized_helper(args) + path = @string_route.dup + klass = Journey::Router::Utils - helpers << name + @path_parts.zip(args) do |part, arg| + # 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(arg.to_param)) + end + path + end + + def optimize_routes_generation?(t) + t.send(:optimize_routes_generation?) + end end - # Clause check about when we need to generate an optimized helper. - def optimize_helper?(route) #:nodoc: - route.requirements.except(:controller, :action).empty? + def initialize(route, options) + @options = options + @segment_keys = route.segment_keys + @route = route + end + + def call(t, args) + t.url_for(handle_positional_args(t, args, @options, @segment_keys)) end - # Generates the interpolation to be used in the optimized helper. - def optimized_helper(route) - string_route = route.ast.to_s + def handle_positional_args(t, args, options, keys) + inner_options = args.extract_options! + result = options.dup - while string_route.gsub!(/\([^\)]*\)/, "") - true + if args.size > 0 + if args.size < keys.size - 1 # take format into account + keys -= t.url_options.keys if t.respond_to?(:url_options) + keys -= options.keys + end + result.merge!(Hash[keys.zip(args)]) end - route.required_parts.each_with_index do |part, i| - # Replace each route parameter - # e.g. :id for regular parameter or *path for globbing - # with ruby string interpolation code - string_route.gsub!(/(\*|:)#{part}/, "\#{Journey::Router::Utils.escape_fragment(args[#{i}].to_param)}") - end + result.merge!(inner_options) + end + end + + private + # Create a url helper allowing ordered parameters to be associated + # with corresponding dynamic segments, so you can do: + # + # foo_url(bar, baz, bang) + # + # Instead of: + # + # foo_url(bar: bar, baz: baz, bang: bang) + # + # Also allow options hash, so you can do: + # + # foo_url(bar, baz, bang, sort_by: 'baz') + # + def define_url_helper(route, name, options) + helper = UrlHelper.create(route, options.dup) - string_route + @module.remove_possible_method name + @module.module_eval do + define_method(name) do |*args| + helper.call self, args + end end + + helpers << name + end + + def define_named_route_methods(name, route) + define_url_helper route, :"#{name}_path", + route.defaults.merge(:use_route => name, :only_path => true) + define_url_helper route, :"#{name}_url", + route.defaults.merge(:use_route => name, :only_path => false) + end end attr_accessor :formatter, :set, :named_routes, :default_scope, :router diff --git a/actionpack/lib/action_dispatch/routing/routes_proxy.rb b/actionpack/lib/action_dispatch/routing/routes_proxy.rb index 73af5920ed..e2393d3799 100644 --- a/actionpack/lib/action_dispatch/routing/routes_proxy.rb +++ b/actionpack/lib/action_dispatch/routing/routes_proxy.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/extract_options' + module ActionDispatch module Routing class RoutesProxy #:nodoc: |