method
build
v7.1.3.4 -
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 79
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)
@anchor = anchor
@via = via
@internal = options.delete(:internal)
@scope_options = scope_params[:options]
ast = Journey::Ast.new(ast, formatted)
options = ast.wildcard_options.merge!(options)
options = normalize_options!(options, ast.path_params, scope_params[:module])
split_options = constraints(options, ast.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 ast.path_params, constraints
verify_regexp_requirements requirements, ast.wildcard_options
formats = normalize_format(formatted)
@requirements = formats[:requirements].merge Hash[requirements]
@conditions = Hash[conditions]
@defaults = formats[:defaults].merge(@defaults).merge(normalize_defaults(options))
if ast.path_params.include?(:action) && [email protected]?(:action)
@defaults[:action] ||= "index"
end
@required_defaults = (split_options[:required_defaults] || []).map(&:first)
ast.requirements = @requirements
@path = Journey::Path::Pattern.new(ast, @requirements, JOINED_SEPARATORS, @anchor)
end
JOINED_SEPARATORS = SEPARATORS.join # :nodoc:
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, source_location: route_source_location)
end
def application
app(@blocks)
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
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
if to.nil?
controller = default_controller
action = default_action
elsif to.is_a?(String) && to.include?("#")
to_endpoint = to.split("#").map!(&:-@)
controller = to_endpoint[0]
action = to_endpoint[1]
else
raise ArgumentError, ":to must respond to `action` or `call`, or it must be a String that includes '#'"
end
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, wildcard_options)
requirements.each do |requirement, regex|
next unless regex.is_a? Regexp
if ANCHOR_CHARACTERS_REGEX.match?(regex.source)
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
end
if regex.multiline?
next if wildcard_options.key?(requirement)
raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{regex.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 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
def route_source_location
if Mapper.route_source_locations
action_dispatch_dir = File.expand_path("..", __dir__)
caller_location = caller_locations.find { |location| !location.path.include?(action_dispatch_dir) }
cleaned_path = Mapper.backtrace_cleaner.clean([caller_location.path]).first
"#{cleaned_path}:#{caller_location.lineno}" if cleaned_path
end
end
end