diff options
Diffstat (limited to 'actionpack/lib')
-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 |