diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/routing/polymorphic_routes.rb')
-rw-r--r-- | actionpack/lib/action_dispatch/routing/polymorphic_routes.rb | 301 |
1 files changed, 164 insertions, 137 deletions
diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb index 9934f5547a..4de5f9e2f7 100644 --- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb +++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + module ActionDispatch module Routing # Polymorphic URL helpers are methods for smart resolution to a named route call when # 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: @@ -40,7 +42,7 @@ module ActionDispatch # # Example usage: # - # edit_polymorphic_path(@post) # => "/posts/1/edit" + # edit_polymorphic_path(@post) # => "/posts/1/edit" # polymorphic_path(@post, format: :pdf) # => "/posts/1.pdf" # # == Usage with mounted engines @@ -79,7 +81,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 # @@ -103,6 +105,10 @@ module ActionDispatch return polymorphic_url record, options end + if mapping = polymorphic_mapping(record_or_hash_or_array) + return mapping.call(self, [record_or_hash_or_array, options], false) + end + opts = options.dup action = opts.delete :action type = opts.delete(:routing_type) || :url @@ -114,8 +120,7 @@ module ActionDispatch opts end - # Returns the path component of a URL for the given record. It uses - # <tt>polymorphic_url</tt> with <tt>routing_type: :path</tt>. + # Returns the path component of a URL for the given record. def polymorphic_path(record_or_hash_or_array, options = {}) if Hash === record_or_hash_or_array options = record_or_hash_or_array.merge(options) @@ -123,6 +128,10 @@ module ActionDispatch return polymorphic_path record, options end + if mapping = polymorphic_mapping(record_or_hash_or_array) + return mapping.call(self, [record_or_hash_or_array, options], true) + end + opts = options.dup action = opts.delete :action type = :path @@ -134,7 +143,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 +157,195 @@ 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_path_for_action(action, record_or_hash, options) - polymorphic_path(record_or_hash, options.merge(:action => action)) - end - - class HelperMethodBuilder # :nodoc: - CACHE = { 'path' => {}, 'url' => {} } - - def self.get(action, type) - type = type.to_s - CACHE[type].fetch(action) { build action, type } + def polymorphic_url_for_action(action, record_or_hash, options) + polymorphic_url(record_or_hash, options.merge(action: action)) end - def self.url; CACHE['url'.freeze][nil]; end - def self.path; CACHE['path'.freeze][nil]; end + def polymorphic_path_for_action(action, record_or_hash, options) + polymorphic_path(record_or_hash, options.merge(action: action)) + end - def self.build(action, type) - prefix = action ? "#{action}_" : "" - suffix = type - if action.to_s == 'new' - HelperMethodBuilder.singular prefix, suffix + def polymorphic_mapping(record) + if record.respond_to?(:to_model) + _routes.polymorphic_mappings[record.to_model.model_name.name] else - HelperMethodBuilder.plural prefix, suffix + _routes.polymorphic_mappings[record.class.name] end end - def self.singular(prefix, suffix) - new(->(name) { name.singular_route_key }, prefix, suffix) - end + class HelperMethodBuilder # :nodoc: + CACHE = { "path" => {}, "url" => {} } - def self.plural(prefix, suffix) - new(->(name) { name.route_key }, prefix, suffix) - end + def self.get(action, type) + type = type.to_s + CACHE[type].fetch(action) { build action, type } + end - def self.polymorphic_method(recipient, record_or_hash_or_array, action, type, options) - builder = get action, type + def self.url; CACHE["url"][nil]; end + def self.path; CACHE["path"][nil]; end - 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 + 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 - - 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 end + def self.singular(prefix, suffix) + new(->(name) { name.singular_route_key }, prefix, suffix) + end - if options.empty? - recipient.send(method, *args) - else - recipient.send(method, *args, options) + def self.plural(prefix, suffix) + new(->(name) { name.route_key }, prefix, suffix) end - end - attr_reader :suffix, :prefix + 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 - def initialize(key_strategy, prefix, suffix) - @key_strategy = key_strategy - @prefix = prefix - @suffix = suffix - end + when nil + raise ArgumentError, "Nil location provided. Can't build URI." + else + method, args = builder.handle_model record_or_hash_or_array + end - def handle_string(record) - [get_method_for_string(record), []] - end + if options.empty? + recipient.send(method, *args) + else + recipient.send(method, *args, options) + end + end - def handle_string_call(target, str) - target.send get_method_for_string str - end + attr_reader :suffix, :prefix - def handle_class(klass) - [get_method_for_class(klass), []] - end + def initialize(key_strategy, prefix, suffix) + @key_strategy = key_strategy + @prefix = prefix + @suffix = suffix + end - def handle_class_call(target, klass) - target.send get_method_for_class klass - end + def handle_string(record) + [get_method_for_string(record), []] + end - def handle_model(record) - args = [] + def handle_string_call(target, str) + target.send get_method_for_string str + 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(klass) + [get_method_for_class(klass), []] + end - [named_route, args] - end + def handle_class_call(target, klass) + target.send get_method_for_class klass + end - def handle_model_call(target, model) - method, args = handle_model model - target.send(method, *args) - end + def handle_model(record) + args = [] - def handle_list(list) - record_list = list.dup - record = record_list.pop + 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 - args = [] + [named_route, args] + end - route = record_list.map { |parent| - case parent - when Symbol, String - parent.to_s - when Class - args << parent - parent.model_name.singular_route_key + def handle_model_call(target, record) + if mapping = polymorphic_mapping(target, record) + mapping.call(target, [record], suffix == "path") else - args << parent.to_model - parent.to_model.model_name.singular_route_key + method, args = handle_model(record) + target.send(method, *args) 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 + end + + 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 + record.to_s + when Class + @key_strategy.call record.model_name else - @key_strategy.call model.model_name + model = record.to_model + if model.persisted? + args << model + model.model_name.singular_route_key + else + @key_strategy.call model.model_name + end 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 polymorphic_mapping(target, record) + if record.respond_to?(:to_model) + target._routes.polymorphic_mappings[record.to_model.model_name.name] + else + target._routes.polymorphic_mappings[record.class.name] + end + end - def get_method_for_string(str) - "#{prefix}#{str}_#{suffix}" - end + def get_method_for_class(klass) + name = @key_strategy.call klass.model_name + get_method_for_string name + end - [nil, 'new', 'edit'].each do |action| - CACHE['url'][action] = build action, 'url' - CACHE['path'][action] = build action, 'path' + 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" + end end - end end end end |