diff options
author | Andrew White <andrew.white@unboxedconsulting.com> | 2016-01-20 15:03:10 +0000 |
---|---|---|
committer | Andrew White <andrew.white@unboxed.co> | 2017-02-21 15:30:46 +0000 |
commit | ce7d5fb2e6ffa9ec323510aaff51f10b15f1649a (patch) | |
tree | b732091b7fc3b22edd3dabb5b4600ccd1dcb0cd1 /actionpack/lib/action_dispatch | |
parent | 31dc46cb9c8aa3e05dc955ae50ec53421951b4a5 (diff) | |
download | rails-ce7d5fb2e6ffa9ec323510aaff51f10b15f1649a.tar.gz rails-ce7d5fb2e6ffa9ec323510aaff51f10b15f1649a.tar.bz2 rails-ce7d5fb2e6ffa9ec323510aaff51f10b15f1649a.zip |
Add support for defining custom url helpers in routes.rb
Allow the definition of custom url helpers that will be available
automatically wherever standard url helpers are available. The
current solution is to create helper methods in ApplicationHelper
or some other helper module and this isn't a great solution since
the url helper module can be called directly or included in another
class which doesn't include the normal helper modules.
Reference #22512.
Diffstat (limited to 'actionpack/lib/action_dispatch')
-rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 41 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/route_set.rb | 75 |
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 |