module ActionDispatch::Routing::Mapper::Resources

def action_options?(options)

def action_options?(options)
  options[:only] || options[:except]
end

def action_path(name)

def action_path(name)
  @scope[:path_names][name.to_sym] || name
end

def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints)

def add_route(action, controller, options, _path, to, via, formatted, anchor, options_constraints)
  path = path_for_action(action, _path)
  raise ArgumentError, "path is required" if path.blank?
  action = action.to_s
  default_action = options.delete(:action) || @scope[:action]
  if /^[\w\-\/]+$/.match?(action)
    default_action ||= action.tr("-", "_") unless action.include?("/")
  else
    action = nil
  end
  as = if !options.fetch(:as, true) # if it's set to nil or false
    options.delete(:as)
  else
    name_for_action(options.delete(:as), action)
  end
  path = Mapping.normalize_path URI::DEFAULT_PARSER.escape(path), formatted
  ast = Journey::Parser.parse path
  mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
  @set.add_route(mapping, as)
end

def api_only? # :doc:

:doc:
def api_only? # :doc:
  @set.api_only?
end

def apply_action_options(options)

def apply_action_options(options)
  return options if action_options? options
  options.merge scope_action_options
end

def apply_common_behavior_for(method, resources, options, &block)

def apply_common_behavior_for(method, resources, options, &block)
  if resources.length > 1
    resources.each { |r| public_send(method, r, options, &block) }
    return true
  end
  if options[:shallow]
    options.delete(:shallow)
    shallow do
      public_send(method, resources.pop, options, &block)
    end
    return true
  end
  if resource_scope?
    nested { public_send(method, resources.pop, options, &block) }
    return true
  end
  options.keys.each do |k|
    (options[:constraints] ||= {})[k] = options.delete(k) if options[k].is_a?(Regexp)
  end
  scope_options = options.slice!(*RESOURCE_OPTIONS)
  unless scope_options.empty?
    scope(scope_options) do
      public_send(method, resources.pop, options, &block)
    end
    return true
  end
  false
end

def canonical_action?(action)

def canonical_action?(action)
  resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
end

def collection(&block)

route helpers.
create the search_photos_url and search_photos_path
with GET, and route to the search action of +PhotosController+. It will also
This will enable Rails to recognize paths such as /photos/search

end
end
get 'search'
collection do
resources :photos do

To add a route to the collection:
def collection(&block)
  unless resource_scope?
    raise ArgumentError, "can't use collection outside resource(s) scope"
  end
  with_scope_level(:collection) do
    path_scope(parent_resource.collection_scope, &block)
  end
end

def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints)

def decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints)
  if on = options.delete(:on)
    send(on) { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
  else
    case @scope.scope_level
    when :resources
      nested { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
    when :resource
      member { decomposed_match(path, controller, options, _path, to, via, formatted, anchor, options_constraints) }
    else
      add_route(path, controller, options, _path, to, via, formatted, anchor, options_constraints)
    end
  end
end

def draw(name)

developers to have a single routes file.
applications — even those with a few hundred routes — it's easier for
files can negatively impact discoverability and readability. For most
CAUTION: Use this feature with care. Having multiple routes

mount SomeGem::Engine, at: "/some_gem"
# config/routes/third_party/some_gem.rb

end
resources :accounts
namespace :admin do
# config/routes/admin.rb

end
draw "third_party/some_gem" # Loads `config/routes/third_party/some_gem.rb`
draw :admin # Loads `config/routes/admin.rb`
Rails.application.routes.draw do
# config/routes.rb

+Rails.application.routes.draw+ block.
routing DSL, but do not surround it with a
+config/routes+ directory. In that file, you can use the normal
Loads another routes file with the given +name+ located inside the
def draw(name)
  path = @draw_paths.find do |_path|
    File.exist? "#{_path}/#{name}.rb"
  end
  unless path
    msg  = "Your router tried to #draw the external file #{name}.rb,\n" \
           "but the file was not found in:\n\n"
    msg += @draw_paths.map { |_path| " * #{_path}" }.join("\n")
    raise ArgumentError, msg
  end
  route_path = "#{path}/#{name}.rb"
  instance_eval(File.read(route_path), route_path.to_s)
end

def get_to_from_path(path, to, action)

def get_to_from_path(path, to, action)
  return to if to || action
  path_without_format = path.sub(/\(\.:format\)$/, "")
  if using_match_shorthand?(path_without_format)
    path_without_format.delete_prefix("/").sub(%r{/([^/]*)$}, '#\1').tr("-", "_")
  else
    nil
  end
end

def map_match(paths, options)

def map_match(paths, options)
  if (on = options[:on]) && !VALID_ON_OPTIONS.include?(on)
    raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
  end
  if @scope[:to]
    options[:to] ||= @scope[:to]
  end
  if @scope[:controller] && @scope[:action]
    options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}"
  end
  controller = options.delete(:controller) || @scope[:controller]
  option_path = options.delete :path
  to = options.delete :to
  via = Mapping.check_via Array(options.delete(:via) {
    @scope[:via]
  })
  formatted = options.delete(:format) { @scope[:format] }
  anchor = options.delete(:anchor) { true }
  options_constraints = options.delete(:constraints) || {}
  path_types = paths.group_by(&:class)
  (path_types[String] || []).each do |_path|
    route_options = options.dup
    if _path && option_path
      raise ArgumentError, "Ambiguous route definition. Both :path and the route path were specified as strings."
    end
    to = get_to_from_path(_path, to, route_options[:action])
    decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
  end
  (path_types[Symbol] || []).each do |action|
    route_options = options.dup
    decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints)
  end
  self
end

def match(path, *rest, &block)

match 'path', 'otherpath', on: :member, via: :get
match 'path', to: 'controller#action', via: :post
match 'path' => 'controller#action', via: :patch

For more information, see match[rdoc-ref:Base#match].
Matches a URL pattern to one or more routes.
def match(path, *rest, &block)
  if rest.empty? && Hash === path
    options  = path
    path, to = options.find { |name, _value| name.is_a?(String) }
    raise ArgumentError, "Route path not specified" if path.nil?
    case to
    when Symbol
      options[:action] = to
    when String
      if /#/.match?(to)
        options[:to] = to
      else
        options[:controller] = to
      end
    else
      options[:to] = to
    end
    options.delete(path)
    paths = [path]
  else
    options = rest.pop || {}
    paths = [path] + rest
  end
  if options.key?(:defaults)
    defaults(options.delete(:defaults)) { map_match(paths, options, &block) }
  else
    map_match(paths, options, &block)
  end
end

def match_root_route(options)

def match_root_route(options)
  args = ["/", { as: :root, via: :get }.merge(options)]
  match(*args)
end

def member(&block)

preview_photo_url and preview_photo_path helpers.
preview action of +PhotosController+. It will also create the
This will recognize /photos/1/preview with GET, and route to the

end
end
get 'preview'
member do
resources :photos do

To add a member route, add a member block into the resource block:
def member(&block)
  unless resource_scope?
    raise ArgumentError, "can't use member outside resource(s) scope"
  end
  with_scope_level(:member) do
    if shallow?
      shallow_scope {
        path_scope(parent_resource.member_scope, &block)
      }
    else
      path_scope(parent_resource.member_scope, &block)
    end
  end
end

def name_for_action(as, action)

def name_for_action(as, action)
  prefix = prefix_name_for_action(as, action)
  name_prefix = @scope[:as]
  if parent_resource
    return nil unless as || action
    collection_name = parent_resource.collection_name
    member_name = parent_resource.member_name
  end
  action_name = @scope.action_name(name_prefix, prefix, collection_name, member_name)
  candidate = action_name.select(&:present?).join("_")
  unless candidate.empty?
    # If a name was not explicitly given, we check if it is valid
    # and return nil in case it isn't. Otherwise, we pass the invalid name
    # forward so the underlying router engine treats it and raises an exception.
    if as.nil?
      candidate unless !candidate.match?(/\A[_a-z]/i) || has_named_route?(candidate)
    else
      candidate
    end
  end
end

def namespace(path, options = {})

See ActionDispatch::Routing::Mapper::Scoping#namespace.
def namespace(path, options = {})
  if resource_scope?
    nested { super }
  else
    super
  end
end

def nested(&block)

def nested(&block)
  unless resource_scope?
    raise ArgumentError, "can't use nested outside resource(s) scope"
  end
  with_scope_level(:nested) do
    if shallow? && shallow_nesting_depth >= 1
      shallow_scope do
        path_scope(parent_resource.nested_scope) do
          scope(nested_options, &block)
        end
      end
    else
      path_scope(parent_resource.nested_scope) do
        scope(nested_options, &block)
      end
    end
  end
end

def nested_options

def nested_options
  options = { as: parent_resource.member_name }
  options[:constraints] = {
    parent_resource.nested_param => param_constraint
  } if param_constraint?
  options
end

def nested_scope?

def nested_scope?
  @scope.nested?
end

def new(&block)

def new(&block)
  unless resource_scope?
    raise ArgumentError, "can't use new outside resource(s) scope"
  end
  with_scope_level(:new) do
    path_scope(parent_resource.new_scope(action_path(:new)), &block)
  end
end

def param_constraint

def param_constraint
  @scope[:constraints][parent_resource.param]
end

def param_constraint?

def param_constraint?
  @scope[:constraints] && @scope[:constraints][parent_resource.param].is_a?(Regexp)
end

def parent_resource

def parent_resource
  @scope[:scope_level_resource]
end

def path_for_action(action, path)

def path_for_action(action, path)
  return "#{@scope[:path]}/#{path}" if path
  if canonical_action?(action)
    @scope[:path].to_s
  else
    "#{@scope[:path]}/#{action_path(action)}"
  end
end

def path_scope(path)

def path_scope(path)
  @scope = @scope.new(path: merge_path_scope(@scope[:path], path))
  yield
ensure
  @scope = @scope.parent
end

def prefix_name_for_action(as, action)

def prefix_name_for_action(as, action)
  if as
    prefix = as
  elsif !canonical_action?(action)
    prefix = action
  end
  if prefix && prefix != "/" && !prefix.empty?
    Mapper.normalize_name prefix.to_s.tr("-", "_")
  end
end

def resource(*resources, &block)

Takes same options as resources[rdoc-ref:#resources]
=== Options

form_with(model: @profile) {}
# Enables this to work with singular routes:

resolve('Profile') { [:profile] }
resource :profile

will need to call resolve[rdoc-ref:CustomUrls#resolve]:
record identification (e.g. in +form_with+ or +redirect_to+), you
If you want instances of a model to work with this resource via

POST /profile
DELETE /profile
PATCH/PUT /profile
GET /profile/edit
GET /profile
GET /profile/new

the plural):
the +Profiles+ controller (note that the controller is named after
This creates six different routes in your application, all mapping to

resource :profile

the show action:
a singular resource to map /profile (rather than /profile/:id) to
profile of the currently logged in user. In this case, you can use
referencing an ID. A common example, /profile always shows the
Sometimes, you have a resource that clients always look up without
def resource(*resources, &block)
  options = resources.extract_options!.dup
  if apply_common_behavior_for(:resource, resources, options, &block)
    return self
  end
  with_scope_level(:resource) do
    options = apply_action_options options
    resource_scope(SingletonResource.new(resources.pop, api_only?, @scope[:shallow], options)) do
      yield if block_given?
      concerns(options[:concerns]) if options[:concerns]
      new do
        get :new
      end if parent_resource.actions.include?(:new)
      set_member_mappings_for_resource
      collection do
        post :create
      end if parent_resource.actions.include?(:create)
    end
  end
  self
end

def resource_method_scope?

def resource_method_scope?
  @scope.resource_method_scope?
end

def resource_scope(resource, &block)

def resource_scope(resource, &block)
  @scope = @scope.new(scope_level_resource: resource)
  controller(resource.resource_scope, &block)
ensure
  @scope = @scope.parent
end

def resource_scope?

def resource_scope?
  @scope.resource_scope?
end

def resources(*resources, &block)

resources :posts, path: "admin/posts"
# resource actions are at /admin/posts.

resources :posts, module: "admin"
# routes call Admin::PostsController

=== Examples

Allows you to override the default param name of +:id+ in the URL.
[:param]

segment or disable it by supplying +false+.
Allows you to specify the default value for optional +format+
[:format]

sekret_comment DELETE /comments/:id(.:format)
sekret_comment PATCH/PUT /comments/:id(.:format)
sekret_comment GET /comments/:id(.:format)
edit_sekret_comment GET /comments/:id/edit(.:format)
new_post_comment GET /posts/:post_id/comments/new(.:format)
post_comments POST /posts/:post_id/comments(.:format)
post_comments GET /posts/:post_id/comments(.:format)

The +comments+ resource here will have the following routes generated for it:

end
end
resources :comments, shallow: true
resources :posts do
scope shallow_prefix: "sekret" do

Prefixes nested shallow route names with specified prefix.
[:shallow_prefix]

comment DELETE /sekret/comments/:id(.:format)
comment PATCH/PUT /sekret/comments/:id(.:format)
comment GET /sekret/comments/:id(.:format)
edit_comment GET /sekret/comments/:id/edit(.:format)
new_post_comment GET /posts/:post_id/comments/new(.:format)
post_comments POST /posts/:post_id/comments(.:format)
post_comments GET /posts/:post_id/comments(.:format)

The +comments+ resource here will have the following routes generated for it:

end
end
resources :comments, shallow: true
resources :posts do
scope shallow_path: "sekret" do

Prefixes nested shallow routes with the specified path.
[:shallow_path]

Set shallow: false on a child resource to ignore a parent's shallow parameter.

to be shortened to just /comments/1234.
as a comment on a blog post like /posts/a-long-permalink/comments/1234
This allows URLs for resources that otherwise would be deeply nested such

resources :comments, only: [:show, :edit, :update, :destroy]
end
resources :comments, except: [:show, :edit, :update, :destroy]
resources :posts do

Is the same as:

end
resources :comments
resources :posts, shallow: true do

generates shallow routes for all nested resources.
Generates shallow routes for nested resource(s). When placed on a parent resource,
[:shallow]

resources :cows, except: [:show, :index]
resources :cows, except: :show

Generate all routes except for the given actions.
[:except]

resources :cows, only: [:show, :index]
resources :cows, only: :show

Only generate routes for the given actions.
[:only]

The resource and all segments will now route to /postings instead of /posts.

resources :posts, path: 'postings'

Allows you to change the path prefix for the resource.
[:path]

The above example will now change /posts/new to /posts/brand_new.

resources :posts, path_names: { new: "brand_new" }

Actions not specified are not changed.
Allows you to change the segment component of the +edit+ and +new+ actions.
[:path_names]

Takes same options as match[rdoc-ref:Base#match] as well as:
=== Options

DELETE /photos/:photo_id/comments/:id
PATCH/PUT /photos/:photo_id/comments/:id
GET /photos/:photo_id/comments/:id/edit
GET /photos/:photo_id/comments/:id
POST /photos/:photo_id/comments
GET /photos/:photo_id/comments/new
GET /photos/:photo_id/comments

This generates the following comments routes:

end
resources :comments
resources :photos do

Resources can also be nested infinitely by using this block syntax:

DELETE /photos/:id
PATCH/PUT /photos/:id
GET /photos/:id/edit
GET /photos/:id
POST /photos
GET /photos/new
GET /photos

the +Photos+ controller:
creates seven different routes in your application, all mapping to

resources :photos

routing file, such as
to particular CRUD operations in a database. A single entry in the
and URLs and controller actions. By convention, each action also maps
In Rails, a resourceful route provides a mapping between HTTP verbs
def resources(*resources, &block)
  options = resources.extract_options!.dup
  if apply_common_behavior_for(:resources, resources, options, &block)
    return self
  end
  with_scope_level(:resources) do
    options = apply_action_options options
    resource_scope(Resource.new(resources.pop, api_only?, @scope[:shallow], options)) do
      yield if block_given?
      concerns(options[:concerns]) if options[:concerns]
      collection do
        get  :index if parent_resource.actions.include?(:index)
        post :create if parent_resource.actions.include?(:create)
      end
      new do
        get :new
      end if parent_resource.actions.include?(:new)
      set_member_mappings_for_resource
    end
  end
  self
end

def resources_path_names(options)

def resources_path_names(options)
  @scope[:path_names].merge!(options)
end

def root(path, options = {})

of most Rails applications, this is beneficial.
because this means it will be matched first. As this is the most popular route
You should put the root route at the top of config/routes.rb,

root 'pages#main'

You can also pass a string which will expand

For options, see +match+, as +root+ uses it internally.

root to: 'pages#main'

You can specify what Rails should route "/" to with the root method:
def root(path, options = {})
  if path.is_a?(String)
    options[:to] = path
  elsif path.is_a?(Hash) && options.empty?
    options = path
  else
    raise ArgumentError, "must be called with a path and/or options"
  end
  if @scope.resources?
    with_scope_level(:root) do
      path_scope(parent_resource.path) do
        match_root_route(options)
      end
    end
  else
    match_root_route(options)
  end
end

def scope_action_options

def scope_action_options
  @scope[:action_options] || {}
end

def set_member_mappings_for_resource # :doc:

:doc:
def set_member_mappings_for_resource # :doc:
  member do
    get :edit if parent_resource.actions.include?(:edit)
    get :show if parent_resource.actions.include?(:show)
    if parent_resource.actions.include?(:update)
      patch :update
      put   :update
    end
    delete :destroy if parent_resource.actions.include?(:destroy)
  end
end

def shallow

def shallow
  @scope = @scope.new(shallow: true)
  yield
ensure
  @scope = @scope.parent
end

def shallow?

def shallow?
  !parent_resource.singleton? && @scope[:shallow]
end

def shallow_nesting_depth

def shallow_nesting_depth
  @scope.find_all { |node|
    node.frame[:scope_level_resource]
  }.count { |node| node.frame[:scope_level_resource].shallow? }
end

def shallow_scope

def shallow_scope
  scope = { as: @scope[:shallow_prefix],
            path: @scope[:shallow_path] }
  @scope = @scope.new scope
  yield
ensure
  @scope = @scope.parent
end

def using_match_shorthand?(path)

def using_match_shorthand?(path)
  %r{^/?[-\w]+/[-\w/]+$}.match?(path)
end

def with_scope_level(kind) # :doc:

:doc:
def with_scope_level(kind) # :doc:
  @scope = @scope.new_level(kind)
  yield
ensure
  @scope = @scope.parent
end