method
build
v6.1.3.1 -
Show latest stable
-
0 notes -
Class: ActionDispatch::Routing::Mapper::Mapping
- 1.0.0
- 1.1.6
- 1.2.6
- 2.0.3
- 2.1.0
- 2.2.1
- 2.3.8
- 3.0.0
- 3.0.9
- 3.1.0
- 3.2.1
- 3.2.8
- 3.2.13
- 4.0.2
- 4.1.8
- 4.2.1 (0)
- 4.2.7 (0)
- 4.2.9 (0)
- 5.0.0.1 (0)
- 5.1.7 (0)
- 5.2.3 (0)
- 6.0.0 (0)
- 6.1.3.1 (0)
- 6.1.7.7 (0)
- 7.0.0 (0)
- 7.1.3.2 (0)
- 7.1.3.4 (0)
- What's this?
build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
public
Hide source
# 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) && !@requirements.key?(: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