diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/routing')
-rw-r--r-- | actionpack/lib/action_dispatch/routing/inspector.rb | 78 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 305 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/polymorphic_routes.rb | 270 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/redirection.rb | 40 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/route_set.rb | 185 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/routes_proxy.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/url_for.rb | 26 |
7 files changed, 457 insertions, 451 deletions
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb index 983f1daeb3..b91ffb8419 100644 --- a/actionpack/lib/action_dispatch/routing/inspector.rb +++ b/actionpack/lib/action_dispatch/routing/inspector.rb @@ -1,5 +1,5 @@ -require 'delegate' -require 'active_support/core_ext/string/strip' +require "delegate" +require "active_support/core_ext/string/strip" module ActionDispatch module Routing @@ -33,15 +33,15 @@ module ActionDispatch end def controller - requirements[:controller] || ':controller' + parts.include?(:controller) ? ":controller" : requirements[:controller] end def action - requirements[:action] || ':action' + parts.include?(:action) ? ":action" : requirements[:action] end def internal? - controller.to_s =~ %r{\Arails/(info|mailers|welcome)} + internal end def engine? @@ -51,7 +51,7 @@ module ActionDispatch ## # This class is just used for displaying route information when someone - # executes `rake routes` or looks at the RoutingError page. + # executes `rails routes` or looks at the RoutingError page. # People should not use this class. class RoutesInspector # :nodoc: def initialize(routes) @@ -80,48 +80,48 @@ module ActionDispatch private - def normalize_filter(filter) - if filter.is_a?(Hash) && filter[:controller] - { controller: /#{filter[:controller].downcase.sub(/_?controller\z/, '').sub('::', '/')}/ } - elsif filter - { controller: /#{filter}/, action: /#{filter}/, verb: /#{filter}/, name: /#{filter}/, path: /#{filter}/ } + def normalize_filter(filter) + if filter.is_a?(Hash) && filter[:controller] + { controller: /#{filter[:controller].downcase.sub(/_?controller\z/, '').sub('::', '/')}/ } + elsif filter + { controller: /#{filter}/, action: /#{filter}/, verb: /#{filter}/, name: /#{filter}/, path: /#{filter}/ } + end end - end - def filter_routes(filter) - if filter - @routes.select do |route| - route_wrapper = RouteWrapper.new(route) - filter.any? { |default, value| route_wrapper.send(default) =~ value } + def filter_routes(filter) + if filter + @routes.select do |route| + route_wrapper = RouteWrapper.new(route) + filter.any? { |default, value| route_wrapper.send(default) =~ value } + end + else + @routes end - else - @routes end - end - def collect_routes(routes) - routes.collect do |route| - RouteWrapper.new(route) - end.reject(&:internal?).collect do |route| - collect_engine_routes(route) + def collect_routes(routes) + routes.collect do |route| + RouteWrapper.new(route) + end.reject(&:internal?).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 } + end end - end - def collect_engine_routes(route) - name = route.endpoint - return unless route.engine? - return if @engines[name] + def collect_engine_routes(route) + name = route.endpoint + return unless route.engine? + return if @engines[name] - routes = route.rack_app.routes - if routes.is_a?(ActionDispatch::Routing::RouteSet) - @engines[name] = collect_routes(routes.routes) + routes = route.rack_app.routes + if routes.is_a?(ActionDispatch::Routing::RouteSet) + @engines[name] = collect_routes(routes.routes) + end end - end end class ConsoleFormatter @@ -161,7 +161,7 @@ module ActionDispatch private def draw_section(routes) - header_lengths = ['Prefix', 'Verb', 'URI Pattern'].map(&:length) + header_lengths = ["Prefix", "Verb", "URI Pattern"].map(&:length) name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max) routes.map do |r| diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index afbaa45d20..c412484895 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1,10 +1,8 @@ -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/core_ext/regexp' -require 'action_dispatch/routing/redirection' -require 'action_dispatch/routing/endpoint' +require "active_support/core_ext/hash/slice" +require "active_support/core_ext/enumerable" +require "active_support/core_ext/array/extract_options" +require "action_dispatch/routing/redirection" +require "action_dispatch/routing/endpoint" module ActionDispatch module Routing @@ -42,7 +40,7 @@ module ActionDispatch end def serve(req) - return [ 404, {'X-Cascade' => 'pass'}, [] ] unless matches?(req) + return [ 404, { "X-Cascade" => "pass" }, [] ] unless matches?(req) @strategy.call @app, req end @@ -94,7 +92,7 @@ module ActionDispatch end def self.optional_format?(path, format) - format != false && !path.include?(':format') && !path.end_with?('/') + format != false && !path.include?(":format") && !path.end_with?("/") end def initialize(set, ast, defaults, controller, default_action, modyoule, to, formatted, scope_constraints, blocks, via, options_constraints, anchor, options) @@ -107,6 +105,7 @@ module ActionDispatch @ast = ast @anchor = anchor @via = via + @internal = options.delete(:internal) path_params = ast.find_all(&:symbol?).map(&:to_sym) @@ -120,7 +119,7 @@ module ActionDispatch if options_constraints.is_a?(Hash) @defaults = Hash[options_constraints.find_all { |key, default| - URL_OPTIONS.include?(key) && (String === default || Fixnum === default) + URL_OPTIONS.include?(key) && (String === default || Integer === default) }].merge @defaults @blocks = blocks constraints.merge! options_constraints @@ -137,6 +136,10 @@ module ActionDispatch @conditions = Hash[conditions] @defaults = formats[:defaults].merge(@defaults).merge(normalize_defaults(options)) + if path_params.include?(:action) && !@requirements.key?(:action) + @defaults[:action] ||= "index" + end + @required_defaults = (split_options[:required_defaults] || []).map(&:first) end @@ -148,7 +151,8 @@ module ActionDispatch required_defaults, defaults, request_method, - precedence) + precedence, + @internal) route end @@ -265,7 +269,7 @@ module ActionDispatch { requirements: { format: Regexp.compile(formatted) }, defaults: { format: formatted } } else - { requirements: { }, defaults: { } } + { requirements: {}, defaults: {} } end end @@ -328,7 +332,7 @@ module ActionDispatch def split_to(to) if to =~ /#/ - to.split('#') + to.split("#") else [] end @@ -609,7 +613,7 @@ module ActionDispatch target_as = name_for_action(options[:as], path) options[:via] ||= :all - match(path, options.merge(:to => app, :anchor => false, :format => false)) + match(path, options.merge(to: app, anchor: false, format: false)) define_generate_prefix(app, target_as) if rails_app self @@ -656,7 +660,7 @@ module ActionDispatch super(options) else prefix_options = options.slice(*_route.segment_keys) - prefix_options[:relative_url_root] = ''.freeze + prefix_options[:relative_url_root] = "".freeze # we must actually delete prefix segment keys to avoid passing them to next url_for _route.segment_keys.each { |k| options.delete(k) } _routes.url_helpers.send("#{name}_path", prefix_options) @@ -711,11 +715,7 @@ module ActionDispatch def map_method(method, args, &block) options = args.extract_options! options[:via] = method - if options.key?(:defaults) - defaults(options.delete(:defaults)) { match(*args, options, &block) } - else - match(*args, options, &block) - end + match(*args, options, &block) self end end @@ -809,7 +809,7 @@ module ActionDispatch options = args.extract_options!.dup scope = {} - options[:path] = args.flatten.join('/') if args.any? + options[:path] = args.flatten.join("/") if args.any? options[:constraints] ||= {} unless nested_scope? @@ -819,10 +819,10 @@ module ActionDispatch if options[:constraints].is_a?(Hash) defaults = options[:constraints].select do |k, v| - URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum)) + URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Integer)) end - (options[:defaults] ||= {}).reverse_merge!(defaults) + options[:defaults] = defaults.merge(options[:defaults] || {}) else block, options[:constraints] = options[:constraints], {} end @@ -833,7 +833,7 @@ module ActionDispatch end if options.key? :anchor - raise ArgumentError, 'anchor is ignored unless passed to `match`' + raise ArgumentError, "anchor is ignored unless passed to `match`" end @scope.options.each do |option| @@ -980,7 +980,7 @@ module ActionDispatch # resources :iphones # end def constraints(constraints = {}) - scope(:constraints => constraints) { yield } + scope(constraints: constraints) { yield } end # Allows you to set default parameters for a route, such as this: @@ -1057,6 +1057,10 @@ module ActionDispatch def merge_shallow_scope(parent, child) #:nodoc: child ? true : false end + + def merge_to_scope(parent, child) + child + end end # Resource routing allows you to quickly declare all of the common routes @@ -1548,11 +1552,13 @@ module ActionDispatch # match 'path' => 'controller#action', via: patch # match 'path', to: 'controller#action', via: :post # match 'path', 'otherpath', on: :member, via: :get - def match(path, *rest) + def match(path, *rest, &block) if rest.empty? && Hash === path options = path path, to = options.find { |name, _value| name.is_a?(String) } + raise ArgumentError, "Route path not specified" if path.nil? + case to when Symbol options[:action] = to @@ -1573,110 +1579,13 @@ module ActionDispatch paths = [path] + rest end - if options[:on] && !VALID_ON_OPTIONS.include?(options[:on]) - raise ArgumentError, "Unknown scope #{on.inspect} given to :on" - end - - if @scope[:controller] && @scope[:action] - options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}" - end - - controller = options.delete(:controller) || @scope[:controller] - option_path = options.delete :path - to = options.delete :to - via = Mapping.check_via Array(options.delete(:via) { - @scope[:via] - }) - formatted = options.delete(:format) { @scope[:format] } - anchor = options.delete(:anchor) { true } - options_constraints = options.delete(:constraints) || {} - - path_types = paths.group_by(&:class) - path_types.fetch(String, []).each do |_path| - route_options = options.dup - if _path && option_path - ActiveSupport::Deprecation.warn <<-eowarn -Specifying strings for both :path and the route path is deprecated. Change things like this: - - match #{_path.inspect}, :path => #{option_path.inspect} - -to this: - - match #{option_path.inspect}, :as => #{_path.inspect}, :action => #{path.inspect} - eowarn - route_options[:action] = _path - route_options[:as] = _path - _path = option_path - end - to = get_to_from_path(_path, to, route_options[:action]) - decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints) - end - - path_types.fetch(Symbol, []).each do |action| - route_options = options.dup - decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints) - end - - self - end - - def get_to_from_path(path, to, action) - return to if to || action - - path_without_format = path.sub(/\(\.:format\)$/, '') - if using_match_shorthand?(path_without_format) - path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1').tr("-", "_") - else - nil - end - end - - def using_match_shorthand?(path) - path =~ %r{^/?[-\w]+/[-\w/]+$} - end - - def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc: - if on = options.delete(:on) - send(on) { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) } + if options.key?(:defaults) + defaults(options.delete(:defaults)) { map_match(paths, options, &block) } else - case @scope.scope_level - when :resources - nested { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) } - when :resource - member { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) } - else - add_route(path, controller, options, _path, to, via, formatted, anchor, options_constraints) - end + map_match(paths, options, &block) end end - def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc: - path = path_for_action(action, _path) - raise ArgumentError, "path is required" if path.blank? - - action = action.to_s - - default_action = options.delete(:action) || @scope[:action] - - if action =~ /^[\w\-\/]+$/ - default_action ||= action.tr('-', '_') unless action.include?("/") - else - action = nil - end - - as = if !options.fetch(:as, true) # if it's set to nil or false - options.delete(:as) - else - name_for_action(options.delete(:as), action) - end - - path = Mapping.normalize_path URI.parser.escape(path), formatted - ast = Journey::Parser.parse path - - mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options) - @set.add_route(mapping, ast, as, anchor) - end - # You can specify what Rails should route "/" to with the root method: # # root to: 'pages#main' @@ -1693,7 +1602,7 @@ to this: def root(path, options = {}) if path.is_a?(String) options[:to] = path - elsif path.is_a?(Hash) and options.empty? + elsif path.is_a?(Hash) && options.empty? options = path else raise ArgumentError, "must be called with a path and/or options" @@ -1782,7 +1691,7 @@ to this: end def resource_scope(resource) #:nodoc: - @scope = @scope.new(:scope_level_resource => resource) + @scope = @scope.new(scope_level_resource: resource) controller(resource.resource_scope) { yield } ensure @@ -1790,7 +1699,7 @@ to this: end def nested_options #:nodoc: - options = { :as => parent_resource.member_name } + options = { as: parent_resource.member_name } options[:constraints] = { parent_resource.nested_param => param_constraint } if param_constraint? @@ -1817,8 +1726,8 @@ to this: end def shallow_scope #:nodoc: - scope = { :as => @scope[:shallow_prefix], - :path => @scope[:shallow_path] } + scope = { as: @scope[:shallow_prefix], + path: @scope[:shallow_path] } @scope = @scope.new scope yield @@ -1847,8 +1756,8 @@ to this: prefix = action end - if prefix && prefix != '/' && !prefix.empty? - Mapper.normalize_name prefix.to_s.tr('-', '_') + if prefix && prefix != "/" && !prefix.empty? + Mapper.normalize_name prefix.to_s.tr("-", "_") end end @@ -1864,7 +1773,7 @@ to this: end action_name = @scope.action_name(name_prefix, prefix, collection_name, member_name) - candidate = action_name.select(&:present?).join('_') + candidate = action_name.select(&:present?).join("_") unless candidate.empty? # If a name was not explicitly given, we check if it is valid @@ -1893,19 +1802,120 @@ to this: def api_only? @set.api_only? end + private - def path_scope(path) - @scope = @scope.new(path: merge_path_scope(@scope[:path], path)) - yield - ensure - @scope = @scope.parent - end + def path_scope(path) + @scope = @scope.new(path: merge_path_scope(@scope[:path], path)) + yield + ensure + @scope = @scope.parent + end - def match_root_route(options) - name = has_named_route?(:root) ? nil : :root - match '/', { :as => name, :via => :get }.merge!(options) - end + def map_match(paths, options) + if options[:on] && !VALID_ON_OPTIONS.include?(options[:on]) + raise ArgumentError, "Unknown scope #{on.inspect} given to :on" + end + + if @scope[:to] + options[:to] ||= @scope[:to] + end + + if @scope[:controller] && @scope[:action] + options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}" + end + + controller = options.delete(:controller) || @scope[:controller] + option_path = options.delete :path + to = options.delete :to + via = Mapping.check_via Array(options.delete(:via) { + @scope[:via] + }) + formatted = options.delete(:format) { @scope[:format] } + anchor = options.delete(:anchor) { true } + options_constraints = options.delete(:constraints) || {} + + path_types = paths.group_by(&:class) + path_types.fetch(String, []).each do |_path| + route_options = options.dup + if _path && option_path + raise ArgumentError, "Ambigous route definition. Both :path and the route path where specified as strings." + end + to = get_to_from_path(_path, to, route_options[:action]) + decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints) + end + + path_types.fetch(Symbol, []).each do |action| + route_options = options.dup + decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints) + end + + self + end + + def get_to_from_path(path, to, action) + return to if to || action + + path_without_format = path.sub(/\(\.:format\)$/, "") + if using_match_shorthand?(path_without_format) + path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1').tr("-", "_") + else + nil + end + end + + def using_match_shorthand?(path) + path =~ %r{^/?[-\w]+/[-\w/]+$} + end + + def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc: + if on = options.delete(:on) + send(on) { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) } + else + case @scope.scope_level + when :resources + nested { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) } + when :resource + member { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) } + else + add_route(path, controller, options, _path, to, via, formatted, anchor, options_constraints) + end + end + end + + def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints) # :nodoc: + path = path_for_action(action, _path) + raise ArgumentError, "path is required" if path.blank? + + action = action.to_s + + default_action = options.delete(:action) || @scope[:action] + + if action =~ /^[\w\-\/]+$/ + default_action ||= action.tr("-", "_") unless action.include?("/") + else + action = nil + end + + as = if !options.fetch(:as, true) # if it's set to nil or false + options.delete(:as) + else + name_for_action(options.delete(:as), action) + end + + path = Mapping.normalize_path URI.parser.escape(path), formatted + ast = Journey::Parser.parse path + + mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options) + @set.add_route(mapping, ast, as, anchor) + end + + def match_root_route(options) + name = has_named_route?(name_for_action(:root, nil)) ? nil : :root + args = ["/", { as: name, via: :get }.merge!(options)] + + match(*args) + end end # Routing Concerns allow you to declare common routes that can be reused @@ -2016,7 +2026,7 @@ to this: class Scope # :nodoc: OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module, :controller, :action, :path_names, :constraints, - :shallow, :blocks, :defaults, :via, :format, :options] + :shallow, :blocks, :defaults, :via, :format, :options, :to] RESOURCE_SCOPES = [:resource, :resources] RESOURCE_METHOD_SCOPES = [:collection, :member, :new] @@ -2083,8 +2093,7 @@ to this: def each node = self - loop do - break if node.equal? NULL + until node.equal? NULL yield node node = node.parent end @@ -2097,7 +2106,7 @@ to this: def initialize(set) #:nodoc: @set = set - @scope = Scope.new({ :path_names => @set.resources_path_names }) + @scope = Scope.new(path_names: @set.resources_path_names) @concerns = {} end diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index 9934f5547a..4f1aaeefc8 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -4,7 +4,7 @@ module ActionDispatch # given an Active Record model instance. They are to be used in combination with # ActionController::Resources. # - # These methods are useful when you want to generate correct URL or path to a RESTful + # These methods are useful when you want to generate the correct URL or path to a RESTful # resource without having to know the exact type of the record in question. # # Nested resources and/or namespaces are also supported, as illustrated in the example: @@ -79,7 +79,7 @@ module ActionDispatch # polymorphic_url([blog, post], anchor: 'my_anchor', script_name: "/my_app") # # => "http://example.com/my_app/blogs/1/posts/1#my_anchor" # - # For all of these options, see the documentation for <tt>url_for</tt>. + # For all of these options, see the documentation for {url_for}[rdoc-ref:ActionDispatch::Routing::UrlFor]. # # ==== Functionality # @@ -134,7 +134,6 @@ module ActionDispatch opts end - %w(edit new).each do |action| module_eval <<-EOT, __FILE__, __LINE__ + 1 def #{action}_polymorphic_url(record_or_hash, options = {}) @@ -149,176 +148,175 @@ module ActionDispatch private - def polymorphic_url_for_action(action, record_or_hash, options) - polymorphic_url(record_or_hash, options.merge(:action => action)) - end + def polymorphic_url_for_action(action, record_or_hash, options) + polymorphic_url(record_or_hash, options.merge(action: action)) + end - def polymorphic_path_for_action(action, record_or_hash, options) - polymorphic_path(record_or_hash, options.merge(:action => action)) - end + def polymorphic_path_for_action(action, record_or_hash, options) + polymorphic_path(record_or_hash, options.merge(action: action)) + end - class HelperMethodBuilder # :nodoc: - CACHE = { 'path' => {}, 'url' => {} } + class HelperMethodBuilder # :nodoc: + CACHE = { "path" => {}, "url" => {} } - def self.get(action, type) - type = type.to_s - CACHE[type].fetch(action) { build action, type } - end + def self.get(action, type) + type = type.to_s + CACHE[type].fetch(action) { build action, type } + end - def self.url; CACHE['url'.freeze][nil]; end - def self.path; CACHE['path'.freeze][nil]; end + def self.url; CACHE["url".freeze][nil]; end + def self.path; CACHE["path".freeze][nil]; end - def self.build(action, type) - prefix = action ? "#{action}_" : "" - suffix = type - if action.to_s == 'new' - HelperMethodBuilder.singular prefix, suffix - else - HelperMethodBuilder.plural prefix, suffix + def self.build(action, type) + prefix = action ? "#{action}_" : "" + suffix = type + if action.to_s == "new" + HelperMethodBuilder.singular prefix, suffix + else + HelperMethodBuilder.plural prefix, suffix + end end - end - def self.singular(prefix, suffix) - new(->(name) { name.singular_route_key }, prefix, suffix) - end + def self.singular(prefix, suffix) + new(->(name) { name.singular_route_key }, prefix, suffix) + end - def self.plural(prefix, suffix) - new(->(name) { name.route_key }, prefix, suffix) - end + def self.plural(prefix, suffix) + new(->(name) { name.route_key }, prefix, suffix) + end - def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options) - builder = get action, type + def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options) + builder = get action, type + + case record_or_hash_or_array + when Array + record_or_hash_or_array = record_or_hash_or_array.compact + if record_or_hash_or_array.empty? + raise ArgumentError, "Nil location provided. Can't build URI." + end + if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy) + recipient = record_or_hash_or_array.shift + end + + method, args = builder.handle_list record_or_hash_or_array + when String, Symbol + method, args = builder.handle_string record_or_hash_or_array + when Class + method, args = builder.handle_class record_or_hash_or_array - case record_or_hash_or_array - when Array - record_or_hash_or_array = record_or_hash_or_array.compact - if record_or_hash_or_array.empty? + when nil raise ArgumentError, "Nil location provided. Can't build URI." + else + method, args = builder.handle_model record_or_hash_or_array end - if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy) - recipient = record_or_hash_or_array.shift - end - - method, args = builder.handle_list record_or_hash_or_array - when String, Symbol - method, args = builder.handle_string record_or_hash_or_array - when Class - method, args = builder.handle_class record_or_hash_or_array - when nil - raise ArgumentError, "Nil location provided. Can't build URI." - else - method, args = builder.handle_model record_or_hash_or_array + if options.empty? + recipient.send(method, *args) + else + recipient.send(method, *args, options) + end end + attr_reader :suffix, :prefix - if options.empty? - recipient.send(method, *args) - else - recipient.send(method, *args, options) + def initialize(key_strategy, prefix, suffix) + @key_strategy = key_strategy + @prefix = prefix + @suffix = suffix end - end - - attr_reader :suffix, :prefix - - def initialize(key_strategy, prefix, suffix) - @key_strategy = key_strategy - @prefix = prefix - @suffix = suffix - end - - def handle_string(record) - [get_method_for_string(record), []] - end - - def handle_string_call(target, str) - target.send get_method_for_string str - end - def handle_class(klass) - [get_method_for_class(klass), []] - end + def handle_string(record) + [get_method_for_string(record), []] + end - def handle_class_call(target, klass) - target.send get_method_for_class klass - end + def handle_string_call(target, str) + target.send get_method_for_string str + end - def handle_model(record) - args = [] + def handle_class(klass) + [get_method_for_class(klass), []] + end - model = record.to_model - named_route = if model.persisted? - args << model - get_method_for_string model.model_name.singular_route_key - else - get_method_for_class model - end + def handle_class_call(target, klass) + target.send get_method_for_class klass + end - [named_route, args] - end + def handle_model(record) + args = [] - def handle_model_call(target, model) - method, args = handle_model model - target.send(method, *args) - end + model = record.to_model + named_route = if model.persisted? + args << model + get_method_for_string model.model_name.singular_route_key + else + get_method_for_class model + end - def handle_list(list) - record_list = list.dup - record = record_list.pop + [named_route, args] + end - args = [] + def handle_model_call(target, model) + method, args = handle_model model + target.send(method, *args) + end - route = record_list.map { |parent| - case parent + def handle_list(list) + record_list = list.dup + record = record_list.pop + + args = [] + + route = record_list.map { |parent| + case parent + when Symbol, String + parent.to_s + when Class + args << parent + parent.model_name.singular_route_key + else + args << parent.to_model + parent.to_model.model_name.singular_route_key + end + } + + route << + case record when Symbol, String - parent.to_s + record.to_s when Class - args << parent - parent.model_name.singular_route_key + @key_strategy.call record.model_name else - args << parent.to_model - parent.to_model.model_name.singular_route_key + model = record.to_model + if model.persisted? + args << model + model.model_name.singular_route_key + else + @key_strategy.call model.model_name + end end - } - - route << - case record - when Symbol, String - record.to_s - when Class - @key_strategy.call record.model_name - else - model = record.to_model - if model.persisted? - args << model - model.model_name.singular_route_key - else - @key_strategy.call model.model_name - end - end - route << suffix + route << suffix - named_route = prefix + route.join("_") - [named_route, args] - end + named_route = prefix + route.join("_") + [named_route, args] + end - private + private - def get_method_for_class(klass) - name = @key_strategy.call klass.model_name - get_method_for_string name - end + def get_method_for_class(klass) + name = @key_strategy.call klass.model_name + get_method_for_string name + end - def get_method_for_string(str) - "#{prefix}#{str}_#{suffix}" - end + def get_method_for_string(str) + "#{prefix}#{str}_#{suffix}" + end - [nil, 'new', 'edit'].each do |action| - CACHE['url'][action] = build action, 'url' - CACHE['path'][action] = build action, 'path' + [nil, "new", "edit"].each do |action| + CACHE["url"][action] = build action, "url" + CACHE["path"][action] = build action, "path" + end end - end end end end diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb index d6987f4d09..87bcceccc0 100644 --- a/actionpack/lib/action_dispatch/routing/redirection.rb +++ b/actionpack/lib/action_dispatch/routing/redirection.rb @@ -1,9 +1,9 @@ -require 'action_dispatch/http/request' -require 'active_support/core_ext/uri' -require 'active_support/core_ext/array/extract_options' -require 'rack/utils' -require 'action_controller/metal/exceptions' -require 'action_dispatch/routing/endpoint' +require "action_dispatch/http/request" +require "active_support/core_ext/uri" +require "active_support/core_ext/array/extract_options" +require "rack/utils" +require "action_controller/metal/exceptions" +require "action_dispatch/routing/endpoint" module ActionDispatch module Routing @@ -22,7 +22,6 @@ module ActionDispatch end def serve(req) - req.check_path_parameters! uri = URI.parse(path(req.path_parameters, req)) unless uri.host @@ -40,9 +39,9 @@ module ActionDispatch body = %(<html><body>You are being <a href="#{ERB::Util.unwrapped_html_escape(uri.to_s)}">redirected</a>.</body></html>) headers = { - 'Location' => uri.to_s, - 'Content-Type' => 'text/html', - 'Content-Length' => body.length.to_s + "Location" => uri.to_s, + "Content-Type" => "text/html", + "Content-Length" => body.length.to_s } [ status, headers, [body] ] @@ -58,19 +57,19 @@ module ActionDispatch private def relative_path?(path) - path && !path.empty? && path[0] != '/' + path && !path.empty? && path[0] != "/" end def escape(params) - Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }] + Hash[params.map { |k,v| [k, Rack::Utils.escape(v)] }] end def escape_fragment(params) - Hash[params.map{ |k,v| [k, Journey::Router::Utils.escape_fragment(v)] }] + Hash[params.map { |k,v| [k, Journey::Router::Utils.escape_fragment(v)] }] end def escape_path(params) - Hash[params.map{ |k,v| [k, Journey::Router::Utils.escape_path(v)] }] + Hash[params.map { |k,v| [k, Journey::Router::Utils.escape_path(v)] }] end end @@ -104,11 +103,11 @@ module ActionDispatch def path(params, request) url_options = { - :protocol => request.protocol, - :host => request.host, - :port => request.optional_port, - :path => request.path, - :params => request.query_parameters + protocol: request.protocol, + host: request.host, + port: request.optional_port, + path: request.path, + params: request.query_parameters }.merge! options if !params.empty? && url_options[:path].match(/%\{\w*\}/) @@ -129,12 +128,11 @@ module ActionDispatch end def inspect - "redirect(#{status}, #{options.map{ |k,v| "#{k}: #{v}" }.join(', ')})" + "redirect(#{status}, #{options.map { |k,v| "#{k}: #{v}" }.join(', ')})" end end module Redirection - # Redirect any path to another path: # # get "/stories" => redirect("/posts") diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 846b5fa1fc..326329c65f 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -1,12 +1,11 @@ -require 'action_dispatch/journey' -require 'active_support/concern' -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' -require 'action_dispatch/http/request' -require 'action_dispatch/routing/endpoint' +require "action_dispatch/journey" +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" +require "action_dispatch/http/request" +require "action_dispatch/routing/endpoint" module ActionDispatch module Routing @@ -34,7 +33,7 @@ module ActionDispatch if @raise_on_name_error raise else - return [404, {'X-Cascade' => 'pass'}, []] + return [404, { "X-Cascade" => "pass" }, []] end end @@ -59,7 +58,7 @@ module ActionDispatch private - def controller(_); @controller_class; end + def controller(_); @controller_class; end end # A NamedRouteCollection instance is a collection of named routes, and also @@ -180,40 +179,40 @@ module ActionDispatch private - def optimized_helper(args) - params = parameterize_args(args) do - raise_generation_error(args) - end + def optimized_helper(args) + params = parameterize_args(args) do + raise_generation_error(args) + end - @route.format params - end + @route.format params + end - def optimize_routes_generation?(t) - t.send(:optimize_routes_generation?) - end + def optimize_routes_generation?(t) + t.send(:optimize_routes_generation?) + end - def parameterize_args(args) - params = {} - @arg_size.times { |i| - key = @required_parts[i] - value = args[i].to_param - yield key if value.nil? || value.empty? - params[key] = value - } - params - end + def parameterize_args(args) + params = {} + @arg_size.times { |i| + key = @required_parts[i] + value = args[i].to_param + yield key if value.nil? || value.empty? + params[key] = value + } + params + end - def raise_generation_error(args) - missing_keys = [] - params = parameterize_args(args) { |missing_key| - missing_keys << missing_key - } - constraints = Hash[@route.requirements.merge(params).sort_by{|k,v| k.to_s}] - message = "No route matches #{constraints.inspect}" - message << " missing required keys: #{missing_keys.sort.inspect}" + def raise_generation_error(args) + missing_keys = [] + params = parameterize_args(args) { |missing_key| + missing_keys << missing_key + } + constraints = Hash[@route.requirements.merge(params).sort_by { |k,v| k.to_s }] + message = "No route matches #{constraints.inspect}" + message << ", missing required keys: #{missing_keys.sort.inspect}" - raise ActionController::UrlGenerationError, message - end + raise ActionController::UrlGenerationError, message + end end def initialize(route, options, route_name, url_strategy) @@ -264,38 +263,39 @@ module ActionDispatch 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(mod, route, name, opts, route_key, url_strategy) - helper = UrlHelper.create(route, opts, route_key, url_strategy) - mod.module_eval do - define_method(name) do |*args| - last = args.last - options = case last - when Hash - args.pop - when ActionController::Parameters - if last.permitted? - args.pop.to_h - else - raise ArgumentError, "Generating an URL from non sanitized request parameters is insecure!" - end - end - helper.call self, args, options + # 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(mod, route, name, opts, route_key, url_strategy) + helper = UrlHelper.create(route, opts, route_key, url_strategy) + mod.module_eval do + define_method(name) do |*args| + last = args.last + options = \ + case last + when Hash + args.pop + when ActionController::Parameters + if last.permitted? + args.pop.to_h + else + raise ArgumentError, ActionDispatch::Routing::INSECURE_URL_PARAMETERS_MESSAGE + end + end + helper.call self, args, options + end end end - end end # strategy for building urls to send to the client @@ -310,7 +310,7 @@ module ActionDispatch alias :routes :set def self.default_resources_path_names - { :new => 'new', :edit => 'edit' } + { new: "new", edit: "edit" } end def self.new_with_config(config) @@ -513,6 +513,21 @@ module ActionDispatch route = @set.add_route(name, mapping) named_routes[name] = route if name + + if route.segment_keys.include?(:controller) + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Using a dynamic :controller segment in a route is deprecated and + will be removed in Rails 5.1. + MSG + end + + if route.segment_keys.include?(:action) + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Using a dynamic :action segment in a route is deprecated and + will be removed in Rails 5.1. + MSG + end + route end @@ -533,12 +548,10 @@ module ActionDispatch @recall = recall @set = set - normalize_recall! normalize_options! normalize_controller_action_id! use_relative_controller! normalize_controller! - normalize_action! end def controller @@ -557,11 +570,6 @@ module ActionDispatch end end - # Set 'index' as default action for recall - def normalize_recall! - @recall[:action] ||= 'index' - end - def normalize_options! # If an explicit :controller was given, always make :action explicit # too, so that action expiry works as expected for things like @@ -573,12 +581,12 @@ module ActionDispatch # be "index", not the recalled action of "show". if options[:controller] - options[:action] ||= 'index' + options[:action] ||= "index" options[:controller] = options[:controller].to_s end if options.key?(:action) - options[:action] = (options[:action] || 'index').to_s + options[:action] = (options[:action] || "index").to_s end end @@ -588,8 +596,8 @@ module ActionDispatch # :controller, :action or :id is not found, don't pull any # more keys from the recall. def normalize_controller_action_id! - use_recall_for(:controller) or return - use_recall_for(:action) or return + use_recall_for(:controller) || return + use_recall_for(:action) || return use_recall_for(:id) end @@ -597,7 +605,7 @@ module ActionDispatch # is specified, the controller becomes "foo/baz/bat" def use_relative_controller! if !named_route && different_controller? && !controller.start_with?("/") - old_parts = current_controller.split('/') + old_parts = current_controller.split("/") size = controller.count("/") + 1 parts = old_parts[0...-size] << controller @options[:controller] = parts.join("/") @@ -615,13 +623,6 @@ module ActionDispatch end end - # Move 'index' action from options to recall - def normalize_action! - if @options[:action] == 'index'.freeze - @recall[:action] = @options.delete(:action) - end - end - # Generates a path from routes, returns [path, params]. # If no route is generated the formatter will raise ActionController::UrlGenerationError def generate @@ -669,7 +670,7 @@ module ActionDispatch end def find_script_name(options) - options.delete(:script_name) || find_relative_url_root(options) || '' + options.delete(:script_name) || find_relative_url_root(options) || "" end def find_relative_url_root(options) @@ -730,7 +731,7 @@ module ActionDispatch extras = environment[:extras] || {} begin - env = Rack::MockRequest.env_for(path, {:method => method}) + env = Rack::MockRequest.env_for(path, method: method) rescue URI::InvalidURIError => e raise ActionController::RoutingError, e.message end diff --git a/actionpack/lib/action_dispatch/routing/routes_proxy.rb b/actionpack/lib/action_dispatch/routing/routes_proxy.rb index 040ea04046..ee847eaeed 100644 --- a/actionpack/lib/action_dispatch/routing/routes_proxy.rb +++ b/actionpack/lib/action_dispatch/routing/routes_proxy.rb @@ -1,4 +1,4 @@ -require 'active_support/core_ext/array/extract_options' +require "active_support/core_ext/array/extract_options" module ActionDispatch module Routing @@ -19,7 +19,7 @@ module ActionDispatch end end - def respond_to?(method, include_private = false) + def respond_to_missing?(method, include_private = false) super || @helpers.respond_to?(method) end diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index f91679593e..a1ac5a2b6c 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -173,7 +173,7 @@ module ActionDispatch route_name) when ActionController::Parameters unless options.permitted? - raise ArgumentError.new("Generating an URL from non sanitized request parameters is insecure!") + raise ArgumentError.new(ActionDispatch::Routing::INSECURE_URL_PARAMETERS_MESSAGE) end route_name = options.delete :use_route _routes.url_for(options.to_h.symbolize_keys. @@ -194,20 +194,20 @@ module ActionDispatch protected - def optimize_routes_generation? - _routes.optimize_routes_generation? && default_url_options.empty? - end + def optimize_routes_generation? + _routes.optimize_routes_generation? && default_url_options.empty? + end - def _with_routes(routes) - old_routes, @_routes = @_routes, routes - yield - ensure - @_routes = old_routes - end + def _with_routes(routes) + old_routes, @_routes = @_routes, routes + yield + ensure + @_routes = old_routes + end - def _routes_context - self - end + def _routes_context + self + end end end end |