method
build
v6.1.7.7 -
Show latest stable
- Class:
ActionDispatch::Routing::Mapper::Mapping
build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)public
No documentation available.
# File actionpack/lib/action_dispatch/routing/mapper.rb, line 77
def self.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
scope_params = {
blocks: scope[:blocks] || [],
constraints: scope[:constraints] || {},
defaults: (scope[:defaults] || {}).dup,
module: scope[:module],
options: scope[:options] || {}
}
new set: set, ast: ast, controller: controller, default_action: default_action,
to: to, formatted: formatted, via: via, options_constraints: options_constraints,
anchor: anchor, scope_params: scope_params, options: scope_params[:options].merge(options)
end
def self.check_via(via)
if via.empty?
msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" "If you want to expose your action to both GET and POST, add `via: [:get, :post]` option.\n" "If you want to expose your action to GET, use `get` in the router:\n" " Instead of: match \"controller#action\"\n" " Do: get \"controller#action\""
raise ArgumentError, msg
end
via
end
def self.normalize_path(path, format)
path = Mapper.normalize_path(path)
if format == true
"#{path}.:format"
elsif optional_format?(path, format)
"#{path}(.:format)"
else
path
end
end
def self.optional_format?(path, format)
format != false && !path.match?(OPTIONAL_FORMAT_REGEX)
end
def initialize(set,, ast,, controller,, default_action,, to,, formatted,, via,, options_constraints,, anchor,, scope_params,, options))
@defaults = scope_params[:defaults]
@set = set
@to = intern(to)
@default_controller = intern(controller)
@default_action = intern(default_action)
@ast = ast
@anchor = anchor
@via = via
@internal = options.delete(:internal)
@scope_options = scope_params[:options]
path_params = []
wildcard_options = {}
ast.each do |node|
if node.symbol?
path_params << node.to_sym
elsif formatted != false && node.star?
# Add a constraint for wildcard route to make it non-greedy and match the
# optional format part of the route by default.
wildcard_options[node.name.to_sym] ||= /.+?/
elsif node.cat?
alter_regex_for_custom_routes(node)
end
end
options = wildcard_options.merge!(options)
options = normalize_options!(options, path_params, scope_params[:module])
split_options = constraints(options, path_params)
constraints = scope_params[:constraints].merge Hash[split_options[:constraints] || []]
if options_constraints.is_a?(Hash)
@defaults = Hash[options_constraints.find_all { |key, default|
URL_OPTIONS.include?(key) && (String === default || Integer === default)
}].merge @defaults
@blocks = scope_params[:blocks]
constraints.merge! options_constraints
else
@blocks = blocks(options_constraints)
end
requirements, conditions = split_constraints path_params, constraints
verify_regexp_requirements requirements.map(&:last).grep(Regexp)
formats = normalize_format(formatted)
@requirements = formats[:requirements].merge Hash[requirements]
@conditions = Hash[conditions]
@defaults = formats[:defaults].merge(@defaults).merge(normalize_defaults(options))
if path_params.include?(:action) && [email protected]?(:action)
@defaults[:action] ||= "index"
end
@required_defaults = (split_options[:required_defaults] || []).map(&:first)
end
def make_route(name, precedence)
Journey::Route.new(name: name, app: application, path: path, constraints: conditions,
required_defaults: required_defaults, defaults: defaults,
request_method_match: request_method, precedence: precedence,
scope_options: scope_options, internal: @internal)
end
def application
app(@blocks)
end
JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
def path
Journey::Path::Pattern.new(@ast, requirements, JOINED_SEPARATORS, @anchor)
end
def conditions
build_conditions @conditions, @set.request_class
end
def build_conditions(current_conditions, request_class)
conditions = current_conditions.dup
conditions.keep_if do |k, _|
request_class.public_method_defined?(k)
end
end
private :build_conditions
def request_method
@via.map { |x| Journey::Route.verb_matcher(x) }
end
private :request_method
private
# Find all the symbol nodes that are adjacent to literal nodes and alter
# the regexp so that Journey will partition them into custom routes.
def alter_regex_for_custom_routes(node)
if node.left.literal? && node.right.symbol?
symbol = node.right
elsif node.left.literal? && node.right.cat? && node.right.left.symbol?
symbol = node.right.left
elsif node.left.symbol? && node.right.literal?
symbol = node.left
elsif node.left.symbol? && node.right.cat? && node.right.left.literal?
symbol = node.left
end
if symbol
symbol.regexp = /(?:#{Regexp.union(symbol.regexp, '-')})+/
end
end
def intern(object)
object.is_a?(String) ? -object : object
end
def normalize_options!(options, path_params, modyoule)
if path_params.include?(:controller)
raise ArgumentError, ":controller segment is not allowed within a namespace block" if modyoule
# Add a default constraint for :controller path segments that matches namespaced
# controllers with default routes like :controller/:action/:id(.:format), e.g:
# GET /admin/products/show/1
# => { controller: 'admin/products', action: 'show', id: '1' }
options[:controller] ||= /.+?/
end
if to.respond_to?(:action) || to.respond_to?(:call)
options
else
to_endpoint = split_to to
controller = to_endpoint[0] || default_controller
action = to_endpoint[1] || default_action
controller = add_controller_module(controller, modyoule)
options.merge! check_controller_and_action(path_params, controller, action)
end
end
def split_constraints(path_params, constraints)
constraints.partition do |key, requirement|
path_params.include?(key) || key == :controller
end
end
def normalize_format(formatted)
case formatted
when true
{ requirements: { format: /.+/ },
defaults: {} }
when Regexp
{ requirements: { format: formatted },
defaults: { format: nil } }
when String
{ requirements: { format: Regexp.compile(formatted) },
defaults: { format: formatted } }
else
{ requirements: {}, defaults: {} }
end
end
def verify_regexp_requirements(requirements)
requirements.each do |requirement|
if ANCHOR_CHARACTERS_REGEX.match?(requirement.source)
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
end
if requirement.multiline?
raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
end
end
end
def normalize_defaults(options)
Hash[options.reject { |_, default| Regexp === default }]
end
def app(blocks)
if to.respond_to?(:action)
Routing::RouteSet::StaticDispatcher.new to
elsif to.respond_to?(:call)
Constraints.new(to, blocks, Constraints::CALL)
elsif blocks.any?
Constraints.new(dispatcher(defaults.key?(:controller)), blocks, Constraints::SERVE)
else
dispatcher(defaults.key?(:controller))
end
end
def check_controller_and_action(path_params, controller, action)
hash = check_part(:controller, controller, path_params, {}) do |part|
translate_controller(part) {
message = +"'#{part}' is not a supported controller name. This can lead to potential routing problems."
message << " See https://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use"
raise ArgumentError, message
}
end
check_part(:action, action, path_params, hash) { |part|
part.is_a?(Regexp) ? part : part.to_s
}
end
def check_part(name, part, path_params, hash)
if part
hash[name] = yield(part)
else
unless path_params.include?(name)
message = "Missing :#{name} key on routes definition, please check your routes."
raise ArgumentError, message
end
end
hash
end
def split_to(to)
if /#/.match?(to)
to.split("#").map!(&:-@)
else
[]
end
end
def add_controller_module(controller, modyoule)
if modyoule && !controller.is_a?(Regexp)
if controller&.start_with?("/")
-controller[1..-1]
else
-[modyoule, controller].compact.join("/")
end
else
controller
end
end
def translate_controller(controller)
return controller if Regexp === controller
return controller.to_s if /\A[a-z_0-9][a-z_0-9\/]*\z/.match?(controller)
yield
end
def blocks(callable_constraint)
unless callable_constraint.respond_to?(:call) || callable_constraint.respond_to?(:matches?)
raise ArgumentError, "Invalid constraint: #{callable_constraint.inspect} must respond to :call or :matches?"
end
[callable_constraint]
end
def constraints(options, path_params)
options.group_by do |key, option|
if Regexp === option
:constraints
else
if path_params.include?(key)
:path_params
else
:required_defaults
end
end
end
end
def dispatcher(raise_on_name_error)
Routing::RouteSet::Dispatcher.new raise_on_name_error
end
end