123456789_123456789_123456789_123456789_123456789_

Class: ActionDispatch::Routing::Mapper::Mapping

Do not use. This class is for internal use only.
Relationships & Source Files
Inherits: Object
Defined in: actionpack/lib/action_dispatch/routing/mapper.rb

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(set:, ast:, controller:, default_action:, to:, formatted:, via:, options_constraints:, anchor:, scope_params:, options:) ⇒ Mapping

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 132

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) && !@requirements.key?(: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

Class Method Details

.build(scope, set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 90

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

.check_via(via)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 104

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

.normalize_path(path, format)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 116

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

.optional_format?(path, format) ⇒ Boolean

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 128

def self.optional_format?(path, format)
  format != false && !path.match?(OPTIONAL_FORMAT_REGEX)
end

Instance Attribute Details

#ast (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 87

attr_reader :path, :requirements, :defaults, :to, :default_controller,
            :default_action, :required_defaults, :ast, :scope_options

#default_action (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 87

attr_reader :path, :requirements, :defaults, :to, :default_controller,
            :default_action, :required_defaults, :ast, :scope_options

#default_controller (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 87

attr_reader :path, :requirements, :defaults, :to, :default_controller,
            :default_action, :required_defaults, :ast, :scope_options

#defaults (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 87

attr_reader :path, :requirements, :defaults, :to, :default_controller,
            :default_action, :required_defaults, :ast, :scope_options

#path (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 87

attr_reader :path, :requirements, :defaults, :to, :default_controller,
            :default_action, :required_defaults, :ast, :scope_options

#required_defaults (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 87

attr_reader :path, :requirements, :defaults, :to, :default_controller,
            :default_action, :required_defaults, :ast, :scope_options

#requirements (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 87

attr_reader :path, :requirements, :defaults, :to, :default_controller,
            :default_action, :required_defaults, :ast, :scope_options

#scope_options (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 87

attr_reader :path, :requirements, :defaults, :to, :default_controller,
            :default_action, :required_defaults, :ast, :scope_options

#to (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 87

attr_reader :path, :requirements, :defaults, :to, :default_controller,
            :default_action, :required_defaults, :ast, :scope_options

Instance Method Details

#add_controller_module(controller, modyoule) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 334

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

#app(blocks) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 295

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

#application

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 190

def application
  app(@blocks)
end

#blocks(callable_constraint) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 353

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

#build_conditions(current_conditions, request_class) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 198

def build_conditions(current_conditions, request_class)
  conditions = current_conditions.dup

  conditions.keep_if do |k, _|
    request_class.public_method_defined?(k)
  end
end

#check_controller_and_action(path_params, controller, action) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 307

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

#check_part(name, part, path_params, hash) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 322

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

#conditions

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 194

def conditions
  build_conditions @conditions, @set.request_class
end

#constraints(options, path_params) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 360

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

#dispatcher(raise_on_name_error) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 374

def dispatcher(raise_on_name_error)
  Routing::RouteSet::Dispatcher.new raise_on_name_error
end

#intern(object) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 213

def intern(object)
  object.is_a?(String) ? -object : object
end

#make_route(name, precedence)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 183

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

#normalize_defaults(options) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 291

def normalize_defaults(options)
  Hash[options.reject { |_, default| Regexp === default }]
end

#normalize_format(formatted) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 259

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

#normalize_options!(options, path_params, modyoule) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 217

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)
      if to.include?("#")
        to_endpoint = to.split("#").map!(&:-@)
        controller  = to_endpoint[0]
        action      = to_endpoint[1]
      else
        controller = default_controller
        action = to
      end
    else
      raise ArgumentError, ":to must respond to `action` or `call`, or it must be a String that includes '#', or the controller should be implicit"
    end

    controller = add_controller_module(controller, modyoule)

    options.merge! check_controller_and_action(path_params, controller, action)
  end
end

#request_method (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 207

def request_method
  @via.map { |x| Journey::Route.verb_matcher(x) }
end

#route_source_location (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 378

def route_source_location
  if Mapper.route_source_locations
    action_dispatch_dir = File.expand_path("..", __dir__)
    Thread.each_caller_location do |location|
      next if location.path.start_with?(action_dispatch_dir)

      cleaned_path = Mapper.backtrace_cleaner.clean_frame(location.path)
      next if cleaned_path.nil?

      return "#{cleaned_path}:#{location.lineno}"
    end
    nil
  end
end

#split_constraints(path_params, constraints) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 253

def split_constraints(path_params, constraints)
  constraints.partition do |key, requirement|
    path_params.include?(key) || key == :controller
  end
end

#translate_controller(controller) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 346

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

#verify_regexp_requirements(requirements, wildcard_options) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 275

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