Module: ActionDispatch::Routing::Mapper::Resources
- Included in:
- ActionDispatch::Routing::Mapper
- Defined in:
- lib/action_dispatch/routing/mapper.rb
Overview
Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your ‘index`, `show`, `new`, `edit`, `create`, `update`, and `destroy` actions, a resourceful route declares them in a single line of code:
resources :photos
Sometimes, you have a resource that clients always look up without referencing an ID. A common example, /profile always shows the profile of the currently logged in user. In this case, you can use a singular resource to map /profile (rather than /profile/:id) to the show action.
resource :profile
It’s common to have resources that are logically children of other resources:
resources :magazines do
resources :ads
end
You may wish to organize groups of controllers under a namespace. Most commonly, you might group a number of administrative controllers under an ‘admin` namespace. You would place these controllers under the `app/controllers/admin` directory, and you can group them together in your router:
namespace "admin" do
resources :posts, :comments
end
By default the ‘:id` parameter doesn’t accept dots. If you need to use dots as part of the ‘:id` parameter add a constraint which overrides this restriction, e.g:
resources :articles, id: /[^\/]+/
This allows any character other than a slash as part of your ‘:id`.
Defined Under Namespace
Classes: Resource, SingletonResource
Constant Summary collapse
- VALID_ON_OPTIONS =
CANONICAL_ACTIONS holds all actions that does not need a prefix or a path appended since they fit properly in their scope level.
[:new, :collection, :member]
- RESOURCE_OPTIONS =
[:as, :controller, :path, :only, :except, :param, :concerns]
- CANONICAL_ACTIONS =
%w(index create new show update destroy)
Instance Method Summary collapse
-
#collection(&block) ⇒ Object
To add a route to the collection:.
-
#draw(name) ⇒ Object
Loads another routes file with the given ‘name` located inside the `config/routes` directory.
-
#match(path, *rest, &block) ⇒ Object
Matches a URL pattern to one or more routes.
-
#member(&block) ⇒ Object
To add a member route, add a member block into the resource block:.
-
#namespace(path, options = {}) ⇒ Object
See ActionDispatch::Routing::Mapper::Scoping#namespace.
- #nested(&block) ⇒ Object
- #new(&block) ⇒ Object
-
#resource(*resources, &block) ⇒ Object
Sometimes, you have a resource that clients always look up without referencing an ID.
-
#resources(*resources, &block) ⇒ Object
In Rails, a resourceful route provides a mapping between HTTP verbs and URLs and controller actions.
- #resources_path_names(options) ⇒ Object
-
#root(path, options = {}) ⇒ Object
You can specify what Rails should route “/” to with the root method:.
- #shallow ⇒ Object
- #shallow? ⇒ Boolean
Instance Method Details
#collection(&block) ⇒ Object
To add a route to the collection:
resources :photos do
collection do
get 'search'
end
end
This will enable Rails to recognize paths such as ‘/photos/search` with GET, and route to the search action of `PhotosController`. It will also create the `search_photos_url` and `search_photos_path` route helpers.
1558 1559 1560 1561 1562 1563 1564 1565 1566 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1558 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 |
#draw(name) ⇒ Object
Loads another routes file with the given ‘name` located inside the `config/routes` directory. In that file, you can use the normal routing DSL, but *do not* surround it with a `Rails.application.routes.draw` block.
# config/routes.rb
Rails.application.routes.draw do
draw :admin # Loads `config/routes/admin.rb`
draw "third_party/some_gem" # Loads `config/routes/third_party/some_gem.rb`
end
# config/routes/admin.rb
namespace :admin do
resources :accounts
end
# config/routes/third_party/some_gem.rb
mount SomeGem::Engine, at: "/some_gem"
CAUTION: Use this feature with care. Having multiple routes files can negatively impact discoverability and readability. For most applications — even those with a few hundred routes — it’s easier for developers to have a single routes file.
1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1667 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 |
#match(path, *rest, &block) ⇒ Object
Matches a URL pattern to one or more routes. For more information, see [match](Base#match).
match 'path', to: 'controller#action', via: :post
match 'path', 'otherpath', on: :member, via: :get
1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1688 def match(path, *rest, &block) if rest.empty? && Hash === path = path path, to = .find { |name, _value| name.is_a?(String) } raise ArgumentError, "Route path not specified" if path.nil? case to when Symbol [:action] = to when String if to.include?("#") [:to] = to else [:controller] = to end else [:to] = to end .delete(path) paths = [path] else = rest.pop || {} paths = [path] + rest end if .key?(:defaults) defaults(.delete(:defaults)) { map_match(paths, , &block) } else map_match(paths, , &block) end end |
#member(&block) ⇒ Object
To add a member route, add a member block into the resource block:
resources :photos do
member do
get 'preview'
end
end
This will recognize ‘/photos/1/preview` with GET, and route to the preview action of `PhotosController`. It will also create the `preview_photo_url` and `preview_photo_path` helpers.
1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1579 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 |
#namespace(path, options = {}) ⇒ Object
See ActionDispatch::Routing::Mapper::Scoping#namespace.
1626 1627 1628 1629 1630 1631 1632 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1626 def namespace(path, = {}) if resource_scope? nested { super } else super end end |
#nested(&block) ⇒ Object
1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1605 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(, &block) end end else path_scope(parent_resource.nested_scope) do scope(, &block) end end end end |
#new(&block) ⇒ Object
1595 1596 1597 1598 1599 1600 1601 1602 1603 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1595 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 |
#resource(*resources, &block) ⇒ Object
Sometimes, you have a resource that clients always look up without referencing an ID. A common example, /profile always shows the profile of the currently logged in user. In this case, you can use a singular resource to map /profile (rather than /profile/:id) to the show action:
resource :profile
This creates six different routes in your application, all mapping to the ‘Profiles` controller (note that the controller is named after the plural):
GET /profile/new
GET /profile
GET /profile/edit
PATCH/PUT /profile
DELETE /profile
POST /profile
If you want instances of a model to work with this resource via record identification (e.g. in ‘form_with` or `redirect_to`), you will need to call [resolve](CustomUrls#resolve):
resource :profile
resolve('Profile') { [:profile] }
# Enables this to work with singular routes:
form_with(model: @profile) {}
### Options Takes same options as [resources](#resources)
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1347 def resource(*resources, &block) = resources..dup if apply_common_behavior_for(:resource, resources, , &block) return self end with_scope_level(:resource) do = :resource, resource_scope(SingletonResource.new(resources.pop, api_only?, @scope[:shallow], )) do yield if block_given? concerns([:concerns]) if [: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 |
#resources(*resources, &block) ⇒ Object
In Rails, a resourceful route provides a mapping between HTTP verbs and URLs and controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as
resources :photos
creates seven different routes in your application, all mapping to the ‘Photos` controller:
GET /photos
GET /photos/new
POST /photos
GET /photos/:id
GET /photos/:id/edit
PATCH/PUT /photos/:id
DELETE /photos/:id
Resources can also be nested infinitely by using this block syntax:
resources :photos do
resources :comments
end
This generates the following comments routes:
GET /photos/:photo_id/comments
GET /photos/:photo_id/comments/new
POST /photos/:photo_id/comments
GET /photos/:photo_id/comments/:id
GET /photos/:photo_id/comments/:id/edit
PATCH/PUT /photos/:photo_id/comments/:id
DELETE /photos/:photo_id/comments/:id
### Options Takes same options as [match](Base#match) as well as:
:path_names : Allows you to change the segment component of the ‘edit` and `new`
actions. Actions not specified are not changed.
resources :posts, path_names: { new: "brand_new" }
The above example will now change /posts/new to /posts/brand_new.
:path : Allows you to change the path prefix for the resource.
resources :posts, path: 'postings'
The resource and all segments will now route to /postings instead of
/posts.
:only : Only generate routes for the given actions.
resources :cows, only: :show
resources :cows, only: [:show, :index]
:except : Generate all routes except for the given actions.
resources :cows, except: :show
resources :cows, except: [:show, :index]
:shallow : Generates shallow routes for nested resource(s). When placed on a parent
resource, generates shallow routes for all nested resources.
resources :posts, shallow: true do
resources :comments
end
Is the same as:
resources :posts do
resources :comments, except: [:show, :edit, :update, :destroy]
end
resources :comments, only: [:show, :edit, :update, :destroy]
This allows URLs for resources that otherwise would be deeply nested such
as a comment on a blog post like `/posts/a-long-permalink/comments/1234`
to be shortened to just `/comments/1234`.
Set `shallow: false` on a child resource to ignore a parent's shallow
parameter.
:shallow_path : Prefixes nested shallow routes with the specified path.
scope shallow_path: "sekret" do
resources :posts do
resources :comments, shallow: true
end
end
The `comments` resource here will have the following routes generated for
it:
post_comments GET /posts/:post_id/comments(.:format)
post_comments POST /posts/:post_id/comments(.:format)
new_post_comment GET /posts/:post_id/comments/new(.:format)
edit_comment GET /sekret/comments/:id/edit(.:format)
comment GET /sekret/comments/:id(.:format)
comment PATCH/PUT /sekret/comments/:id(.:format)
comment DELETE /sekret/comments/:id(.:format)
:shallow_prefix : Prefixes nested shallow route names with specified prefix.
scope shallow_prefix: "sekret" do
resources :posts do
resources :comments, shallow: true
end
end
The `comments` resource here will have the following routes generated for
it:
post_comments GET /posts/:post_id/comments(.:format)
post_comments POST /posts/:post_id/comments(.:format)
new_post_comment GET /posts/:post_id/comments/new(.:format)
edit_sekret_comment GET /comments/:id/edit(.:format)
sekret_comment GET /comments/:id(.:format)
sekret_comment PATCH/PUT /comments/:id(.:format)
sekret_comment DELETE /comments/:id(.:format)
:format : Allows you to specify the default value for optional ‘format` segment or
disable it by supplying `false`.
:param : Allows you to override the default param name of ‘:id` in the URL.
### Examples
# routes call +Admin::PostsController+
resources :posts, module: "admin"
# resource actions are at /admin/posts.
resources :posts, path: "admin/posts"
1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1517 def resources(*resources, &block) = resources..dup if apply_common_behavior_for(:resources, resources, , &block) return self end with_scope_level(:resources) do = :resources, resource_scope(Resource.new(resources.pop, api_only?, @scope[:shallow], )) do yield if block_given? concerns([:concerns]) if [: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 |
#resources_path_names(options) ⇒ Object
1314 1315 1316 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1314 def resources_path_names() @scope[:path_names].merge!() end |
#root(path, options = {}) ⇒ Object
You can specify what Rails should route “/” to with the root method:
root to: 'pages#main'
For options, see ‘match`, as `root` uses it internally.
You can also pass a string which will expand
root 'pages#main'
You should put the root route at the top of ‘config/routes.rb`, because this means it will be matched first. As this is the most popular route of most Rails applications, this is beneficial.
1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1735 def root(path, = {}) if path.is_a?(String) [:to] = path elsif path.is_a?(Hash) && .empty? = 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() end end else match_root_route() end end |
#shallow ⇒ Object
1634 1635 1636 1637 1638 1639 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1634 def shallow @scope = @scope.new(shallow: true) yield ensure @scope = @scope.parent end |
#shallow? ⇒ Boolean
1641 1642 1643 |
# File 'lib/action_dispatch/routing/mapper.rb', line 1641 def shallow? !parent_resource.singleton? && @scope[:shallow] end |