aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb41
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb75
2 files changed, 116 insertions, 0 deletions
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 8d9f70e3c6..329a374d1e 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -2020,6 +2020,46 @@ module ActionDispatch
end
end
+ module UrlHelpers
+ # Define a custom url helper that will be added to the url helpers
+ # module. This allows you override and/or replace the default behavior
+ # of routing helpers, e.g:
+ #
+ # url_helper :homepage do
+ # "http://www.rubyonrails.org"
+ # end
+ #
+ # url_helper :commentable do |model|
+ # [ model, anchor: model.dom_id ]
+ # end
+ #
+ # url_helper :main do
+ # { controller: 'pages', action: 'index', subdomain: 'www' }
+ # end
+ #
+ # The return value must be a valid set of arguments for `url_for` which
+ # will actually build the url string. This can be one of the following:
+ #
+ # * A string, which is treated as a generated url
+ # * A hash, e.g. { controller: 'pages', action: 'index' }
+ # * An array, which is passed to `polymorphic_url`
+ # * An Active Model instance
+ # * An Active Model class
+ #
+ # You can also specify default options that will be passed through to
+ # your url helper definition, e.g:
+ #
+ # url_helper :browse, page: 1, size: 10 do |options|
+ # [ :products, options.merge(params.permit(:page, :size)) ]
+ # end
+ #
+ # NOTE: It is the url helper's responsibility to return the correct
+ # set of options to be passed to the `url_for` call.
+ def url_helper(name, options = {}, &block)
+ @set.add_url_helper(name, options, &block)
+ end
+ end
+
class Scope # :nodoc:
OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module,
:controller, :action, :path_names, :constraints,
@@ -2113,6 +2153,7 @@ module ActionDispatch
include Scoping
include Concerns
include Resources
+ include UrlHelpers
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 8ccfab56cf..b1f7cd30fc 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -73,6 +73,7 @@ module ActionDispatch
@routes = {}
@path_helpers = Set.new
@url_helpers = Set.new
+ @custom_helpers = Set.new
@url_helpers_module = Module.new
@path_helpers_module = Module.new
end
@@ -95,9 +96,23 @@ module ActionDispatch
@url_helpers_module.send :undef_method, helper
end
+ @custom_helpers.each do |helper|
+ path_name = :"#{helper}_path"
+ url_name = :"#{helper}_url"
+
+ if @path_helpers_module.method_defined?(path_name)
+ @path_helpers_module.send :undef_method, path_name
+ end
+
+ if @url_helpers_module.method_defined?(url_name)
+ @url_helpers_module.send :undef_method, url_name
+ end
+ end
+
@routes.clear
@path_helpers.clear
@url_helpers.clear
+ @custom_helpers.clear
end
def add(name, route)
@@ -143,6 +158,62 @@ module ActionDispatch
routes.length
end
+ def add_url_helper(name, defaults, &block)
+ @custom_helpers << name
+ helper = CustomUrlHelper.new(name, defaults, &block)
+
+ @path_helpers_module.module_eval do
+ define_method(:"#{name}_path") do |*args|
+ options = args.extract_options!
+ helper.call(self, args, options, only_path: true)
+ end
+ end
+
+ @url_helpers_module.module_eval do
+ define_method(:"#{name}_url") do |*args|
+ options = args.extract_options!
+ helper.call(self, args, options)
+ end
+ end
+ end
+
+ class CustomUrlHelper
+ attr_reader :name, :defaults, :block
+
+ def initialize(name, defaults, &block)
+ @name = name
+ @defaults = defaults
+ @block = block
+ end
+
+ def call(t, args, options, outer_options = {})
+ url_options = eval_block(t, args, options)
+
+ case url_options
+ when String
+ t.url_for(url_options)
+ when Hash
+ t.url_for(url_options.merge(outer_options))
+ when ActionController::Parameters
+ if url_options.permitted?
+ t.url_for(url_options.to_h.merge(outer_options))
+ else
+ raise ArgumentError, "Generating an URL from non sanitized request parameters is insecure!"
+ end
+ when Array
+ opts = url_options.extract_options!
+ t.url_for(url_options.push(opts.merge(outer_options)))
+ else
+ t.url_for([url_options, outer_options])
+ end
+ end
+
+ private
+ def eval_block(t, args, options)
+ t.instance_exec(*args, defaults.merge(options), &block)
+ end
+ end
+
class UrlHelper
def self.create(route, options, route_name, url_strategy)
if optimize_helper?(route)
@@ -554,6 +625,10 @@ module ActionDispatch
route
end
+ def add_url_helper(name, options, &block)
+ named_routes.add_url_helper(name, options, &block)
+ end
+
class Generator
PARAMETERIZE = lambda do |name, value|
if name == :controller