Module: ActionDispatch::Routing::Mapper::Resources
- Included in:
- ActionDispatch::Routing::Mapper
- Defined in:
- actionpack/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_or_actions, as: DEFAULT, via: nil, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &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(name, deprecated_options = nil, as: DEFAULT, path: DEFAULT, shallow_path: DEFAULT, shallow_prefix: DEFAULT, **options, &block) ⇒ Object
See ActionDispatch::Routing::Mapper::Scoping#namespace.
- #nested(&block) ⇒ Object
- #new(&block) ⇒ Object
-
#resource(*resources, concerns: nil, **options, &block) ⇒ Object
Sometimes, you have a resource that clients always look up without referencing an ID.
-
#resources(*resources, concerns: nil, **options, &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.
1707 1708 1709 1710 1711 1712 1713 1714 1715 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1707 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.
1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1816 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_or_actions, as: DEFAULT, via: nil, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &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
1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1837 def match(*path_or_actions, as: DEFAULT, via: nil, to: nil, controller: nil, action: nil, on: nil, defaults: nil, constraints: nil, anchor: nil, format: nil, path: nil, internal: nil, **mapping, &block) if path_or_actions.grep(Hash).any? && ( = path_or_actions.) as = assign_deprecated_option(, :as, :match) if .key?(:as) via ||= assign_deprecated_option(, :via, :match) to ||= assign_deprecated_option(, :to, :match) controller ||= assign_deprecated_option(, :controller, :match) action ||= assign_deprecated_option(, :action, :match) on ||= assign_deprecated_option(, :on, :match) defaults ||= assign_deprecated_option(, :defaults, :match) constraints ||= assign_deprecated_option(, :constraints, :match) anchor = assign_deprecated_option(, :anchor, :match) if .key?(:anchor) format = assign_deprecated_option(, :format, :match) if .key?(:format) path ||= assign_deprecated_option(, :path, :match) internal ||= assign_deprecated_option(, :internal, :match) (, mapping, :match) end ActionDispatch.deprecator.warn(<<-MSG.squish) if path_or_actions.count > 1 Mapping a route with multiple paths is deprecated and will be removed in Rails 8.1. Please use multiple method calls instead. MSG if path_or_actions.none? && mapping.any? hash_path, hash_to = mapping.find { |key, _| key.is_a?(String) } if hash_path.nil? raise ArgumentError, "Route path not specified" else mapping.delete(hash_path) end if hash_path path_or_actions.push hash_path case hash_to when Symbol action ||= hash_to when String if hash_to.include?("#") to ||= hash_to else controller ||= hash_to end else to ||= hash_to end end end path_or_actions.each do |path_or_action| if defaults defaults(defaults) { map_match(path_or_action, as:, via:, to:, controller:, action:, on:, constraints:, anchor:, format:, path:, internal:, mapping:, &block) } else map_match(path_or_action, as:, via:, to:, controller:, action:, on:, constraints:, anchor:, format:, path:, internal:, mapping:, &block) end 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.
1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1728 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(name, deprecated_options = nil, as: DEFAULT, path: DEFAULT, shallow_path: DEFAULT, shallow_prefix: DEFAULT, **options, &block) ⇒ Object
See ActionDispatch::Routing::Mapper::Scoping#namespace.
1775 1776 1777 1778 1779 1780 1781 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1775 def namespace(name, = nil, as: DEFAULT, path: DEFAULT, shallow_path: DEFAULT, shallow_prefix: DEFAULT, **, &block) if resource_scope? nested { super } else super end end |
#nested(&block) ⇒ Object
1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1754 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
1744 1745 1746 1747 1748 1749 1750 1751 1752 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1744 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, concerns: nil, **options, &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)
1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1490 def resource(*resources, concerns: nil, **, &block) if resources.grep(Hash).any? && ( = resources.) concerns = assign_deprecated_option(, :concerns, :resource) if .key?(:concerns) (, , :resource) end if apply_common_behavior_for(:resource, resources, concerns:, **, &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, concerns: nil, **options, &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"
1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1663 def resources(*resources, concerns: nil, **, &block) if resources.grep(Hash).any? && ( = resources.) concerns = assign_deprecated_option(, :concerns, :resources) if .key?(:concerns) (, , :resources) end if apply_common_behavior_for(:resources, resources, concerns:, **, &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
1457 1458 1459 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1457 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.
1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1906 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
1783 1784 1785 1786 1787 1788 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1783 def shallow @scope = @scope.new(shallow: true) yield ensure @scope = @scope.parent end |
#shallow? ⇒ Boolean
1790 1791 1792 |
# File 'actionpack/lib/action_dispatch/routing/mapper.rb', line 1790 def shallow? !parent_resource.singleton? && @scope[:shallow] end |