What is the difference between collection routes and member routes in Rails?
For example,
resources :photos do
member do
get :preview
end
end
versus
resources :photos do
collection do
get :search
end
end
I don't understand.
A member route will require an ID, because it acts on a member. A collection route doesn't because it acts on a collection of objects. Preview is an example of a member route, because it acts on (and displays) a single object. Search is an example of a collection route, because it acts on (and displays) a collection of objects.
URL Helper Description
----------------------------------------------------------------------------------------------------------------------------------
member /photos/1/preview preview_photo_path(photo) Acts on a specific resource so required id (preview specific photo)
collection /photos/search search_photos_path Acts on collection of resources(display all photos)
Theo's answer is correct. For documentation's sake, I'd like to also note that the two will generate different path helpers.
member {get 'preview'} will generate:
preview_photo_path(#photo) # /photos/1/preview
collection {get 'search'} will generate:
search_photos_path # /photos/search
Note plurality!
1) :collection - Add named routes for other actions that operate on the collection. Takes a hash of #{action} => #{method}, where method is :get/:post/:put/:delete, an array of any of the previous, or :any if the method does not matter. These routes map to a URL like /users/customers_list, with a route of customers_list_users_url.
map.resources :users, :collection => { :customers_list=> :get }
2) :member - Same as :collection, but for actions that operate on a
specific member.
map.resources :users, :member => { :inactive=> :post }
it treated as /users/1;inactive=> [:action => 'inactive', :id => 1]
Short Answer:
Both the member and collection blocks allow you to define additional routes for your resources than the seven standard routes that Rails will generate for you.
A member block creates new routes on a single member of the resource.
A collection block creates new routes for a collection of that resource.
Long Answer
Rails provides the member and collection blocks so you can define custom routes for both the resource collection and the individual resource.
Here's how you'd typically define routes for the article resource.
resources :articles
This creates the following routes.
➜ bin/rails routes -g article
Prefix Verb URI Pattern Controller#Action
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
But let's say you are writing your articles in markdown, and need to see a preview of the article as you write it.
You could create a PreviewController and display the article's preview using its show action, but it's convenient to add a preview action on the ArticlesController itself.
Custom Member Routes
Here's how you define the preview route on the ArticlesController using the member block.
resources :articles do
member do
get 'preview'
end
end
It adds a new route that directs the request to ArticlesController#preview action. The remaining routes remain unchanged. It also passes the article id in params[:id] and creates the preview_article_path and preview_article_url helpers.
➜ bin/rails routes -g article
Prefix Verb URI Pattern Controller#Action
preview_article GET /articles/:id/preview(.:format) articles#preview
... remaining routes
If you have a single member route, use the short-hand version by passing the :on option to the route, eliminating the block.
resources :articles do
get 'preview', on: :member
end
You can go one step further and leave out the :on option.
resources :articles do
get 'preview'
end
It generates the following route.
➜ bin/rails routes -g preview
Prefix Verb URI Pattern Controller#Action
article_preview GET /articles/:article_id/preview(.:format) articles#preview
There are two important differences here:
The article's id is available as params[:article_id] instead of params[:id].
The route helpers changes from preview_article_path to article_preview_path and preview_article_url to article_preview_url.
Custom Collection Routes
To add a new route for the collection of a resource, use the collection block.
resources :articles do
collection do
get 'search'
end
end
This adds the following new route. It will also add a search_articles_path and search_articles_url helper.
search_articles GET /articles/search(.:format) articles#search
If you don't need multiple collection routes, just pass :on option to the route.
resources :articles do
get 'search', on: :collection
end
This will add the same route as above.
Conclusion
Rails allows you to break out of its convention of using seven resourceful routes using the member and collection blocks. Both allow you to define additional routes for your resources than the standard seven routes.
A member block acts on a single member of the resource, whereas a collection operates on a collection of that resource.
Source: Define New Routes Using the Member and Collection Blocks
Related
In a rails controller, GET /objects/ routes to index, POST /objects routes to create, GET /objects/<id> routes to show. What do PUT /objects/<id> and PATCH /objects/<id> route to?
By convention (for example if you use the resources helper in config/routes.rb), PUT and PATCH will both route to the .update method on the controller. So:
namespace :api do
resources :posts
end
Will give you:
% r routes
...
PUT /api/posts/:id(.:format) api/posts#update
PATCH /api/posts/:id(.:format) api/posts#update
I am trying to understand more about Rails routes.
Member and Collection
# Example resource route with options:
resources :products do
member do
get 'short'
post 'toggle'
end
collection do
get 'sold'
end
end
Namespace and Scope
# Example resource route within a namespace:
namespace :admin do
resources :products
end
scope :admin do
resources :products
end
Constraints, Redirect_to
# Example resource route with options:
get "/questions", to: redirect {|params, req|
begin
id = req.params[:category_id]
cat = Category.find(id)
"/abc/#{cat.slug}"
rescue
"/questions"
end
}
Customization:
resources :profiles
original url from resource profiles for edit.
http://localhost:3000/profiles/1/edit
I want to make it for users available only through click edit profile and see url like in below.
http://localhost:3000/profile/edit
Also, is there advanced routing, How most big companies design their routes in rails ? I would be really glad to see new kind of routes if there exist.
Thank You !
**Collection & Member routes**
A member route requires an ID, because it acts on a member.
A collection route doesn't require an ID because it acts on a
collection of objects
:member creates path with pattern /:controller/:id/:your_method
:collection creates path with the pattern /:controller/:your_method
For example :
map.resources :users, :collection => { :abc => :get } => /users/abc
map.resources :users, :member => { :abc => :get } => /users/1/abc
**Scopes & Namespaces routes**
namespace and scope in the Rails routes affect the controller
names, URIs, and named routes.
The scope method gives you fine-grained control:
scope 'url_path_prefix', module: 'module_prefix', as: 'named_route_prefix' do
resources :model_name
end
For Example :
scope 'foo', module: 'bar', as: 'baz' do
resources :posts
end
produces routes as :
Prefix Verb URI Pattern Controller#Action
baz_posts GET /foo/posts(.:format) bar/posts#index
POST /foo/posts(.:format) bar/posts#create
new_baz_post GET /foo/posts/new(.:format) bar/posts#new
edit_baz_post GET /foo/posts/:id/edit(.:format) bar/posts#edit
baz_post GET /foo/posts/:id(.:format) bar/posts#show
PATCH /foo/posts/:id(.:format) bar/posts#update
PUT /foo/posts/:id(.:format) bar/posts#update
DELETE /foo/posts/:id(.:format) bar/posts#destroy
The namespace method is the simple case — it prefixes everything.
namespace :foo do
resources :posts
end
produces routes as :
Prefix Verb URI Pattern Controller#Action
foo_posts GET /foo/posts(.:format) foo/posts#index
POST /foo/posts(.:format) foo/posts#create
new_foo_post GET /foo/posts/new(.:format) foo/posts#new
edit_foo_post GET /foo/posts/:id/edit(.:format) foo/posts#edit
foo_post GET /foo/posts/:id(.:format) foo/posts#show
PATCH /foo/posts/:id(.:format) foo/posts#update
PUT /foo/posts/:id(.:format) foo/posts#update
DELETE /foo/posts/:id(.:format) foo/posts#destroy
**Constraints & Redirect**
Rails routes are executed sequentially, you can mimic conditional
login in the following manner:
match '/route' => 'controller#action', :constraints => Model.new
match '/route' => 'user#action'
The first line checks whether the conditions of the constraint are met (i.e., if the request is emanating from a Model domain). If the constraint is satisfied, the request is routed to controller#action.
We can add constraints to routes for multiple uses like for ip-matching, params matching, restrict format parameter, request-based restrictions etc as :
- ip-matching
=> resources :model, constraints: { ip: /172\.124\.\d+\.\d+/ }
- filtering id params
=> match 'model/:id', to: 'model#show' ,constraints: { id: /\d+/}, via: :get
- restrict format params
=> match 'model/:id', to: 'model#show' ,constraints: { format: 'json' }, via: :get
- request-based constraints
=> get 'admin/', to: 'admin#show', constraints: { subdomain: 'admin' }
Use a singular resource for it:
resource :profile
and in controller manipulate the profile of current user.
As for complex routes - usually namespaces, nested resources with shallow routes and custom actions are all that is needed.
You can go through this answer which answers you first part of the question.
To answer second part of your question. You can treat "profile" as your singular resource (the singularity of the noun itself represents a singular resource). For a detailed description you can refer to this link.
I have an article resource that's defined as a flat resource, but also as one scoped to a variable user:
resources :articles
scope ':user' do
resources :articles
end
In this case, article_path 123 will generate /articles/123. How can I generate a scoped path, e.g. /mary/articles/123? For example, article_path 123, user: 'mary' doesn't work; it just adds ?user=mary.
rails routes shows there's no special name for the scoped path:
article_path GET /articles/:id(.:format) articles#show
...
GET /:user/articles(.:format) articles#index
Try adding, as to create route helpers
scope ':user', as: 'user' do
resources :articles
end
You will get
user_articles_path(user, article)
Check also routing
What I'm wondering, in this case where I want to create a few web service type controllers, is if there is a way to "export" what method is allowed to be called from the controller. I'm still very new to RoR, and its routes feature, but in the end, is it expected that a fully functional RoR application just has hundreds of routes? Not every controller method I'm creating falls into a "resource" category.
Rails routes come in several varieties – RESTful routes are merely the ones that happen to provide native support for Rails resources. Remember that event resource routes can be modified to have member and collection routes:
# routes.rb
resources :products do
member do
get 'short' #=> products/:product_id/short/:id
post 'toggle' #=> products/:product_id/toggle/:id
end
collection do
get 'sold' #=> products/sold
end
end
You can also nest resource routes within other resource routes:
# routes.rb
resources :products do
resources :comments #=> RESTful routes patterned as products/:product_id/comments/:id/:action
resources :sales do
get 'recent', :on => :collection
end
end
Another handy feature is named routing. The following route is not resourceful:
# routes.rb
match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase # Creates route path akin to purchase_path(:id)
Namespaced routes can be very helpful for organization and readability:
# routes.rb
namespace :admin do
resources :products #=> RESTful routes patterned as admin/products/:product_id/:action
end
So, basically, there's a route for everything you want to do, whether it's RESTful/resourceful or not. But yes, you need to write a route for every action you want exposed to your app.
Using the routes.rb file, you can create routes or pattern matching for routes, as well as parameterized routes, and nested routes. You should read more about this here.
You can also give routes their own method name such as my_new_route_path. If you really wanted to, you could just hardcode routes into your HTML. Please don't do this.
Every controller action needs routes that map to it.
When you type
rake routes
a bunch of routes come out, but where are they defined???
I know some are default, and how about the others?
For example, this is a script from a controller, I tried to take off the 's' from do_something, but can't make it work.... are they defined somewhere else too?
Also, when do they take parameters and when not, how I know it ? Thanks!
def hello
redirect_to do_things_shop_path(shop)
end
def do_things
end
Rails routing configurations are kept in config/routes.rb file.
Taking parameters depends on many things. rake routes will show with routes take parameters. Member actions will take parameters.
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
edit_post GET /posts/:id/edit(.:format) posts#edit
In the last line, you will url like posts/:id/edit. This path requires :id parameter. You can call this route many ways. One of them is like:
edit_post_path(#post)
If you want to create a custom action, (say under posts controller), you can declare it as follow:
match `/posts/:id/things_with_id`, :to => 'posts#do_things_with_id', :as => 'do_things_with_id
match `/posts/things_without_id`, :to => 'posts#do_things_without_id', :as => 'do_things_without_id
First one requires an ID while the second one does not. Call them accordingly:
do_things_with_id_path(#post)
do_things_without_id()
For a resource, you can create these easily using member & collection action. Member action needs id while collection action does not.
resources :posts do
member { get 'do_thing' }
collection { get do_things' }
end
hope you got it.
By the way, you must read the following guide if you want to understand these clearly.
http://guides.rubyonrails.org/routing.html