Rails routes: Nested, Member, Collection, namespace, scope and customizable - ruby-on-rails

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.

Related

need help on rails nesting routes

I want to get a route like
GET /example/notifications/status, to: example/notification#status
POST /example/notifications/disable to: example/notification#disable
Here is what I did
resources :example, only => %i[index] do
collection do
resources :notifications, :only => [] do
collection do
get :status
post :disable
end
end
end
end
it get the right path but it point to notification#status not example/notification#status
is there any way I can get both right path and controller expect code like
get "notifications/status", :to => "example/notifications#status"
You can use namespace for this purpose like below,
in you config/routes.rb file
namespace :example do
collection do
get :status
post :disable
end
end
It will hit the example/notification#status action.
for more information see here
Don't use resources since you don't want (aparently) any param on your routes. Go for namespace instead:
namespace :example do
namespace :notifications do
get 'status'
post 'disable'
end
end
Gives you:
$ rails routes -g example
Prefix Verb URI Pattern Controller#Action
example_notifications_status GET /example/notifications/status(.:format) example/notifications#status
example_notifications_disable POST /example/notifications/disable(.:format) example/notifications#disable

Customizing resource routes for every action

I have a resources :posts. How can I customize it's paths to the following and also ability to call it with the normal resource path names:
URL Controller action Helper function
'q' 'posts#index' posts_path
'q/(:id)' 'posts#show' post_path(:id)
'ask' 'posts#new' new_post_path
'q' 'posts#create' posts_path
Here is what I've tried and doesn't work as the expected result above...
get 'q' => 'posts#index', as: :posts
get 'q/(:id)' => "posts#show", as: :post
get 'ask' => 'posts#new'
You're presumably getting an error because you're trying to assign a route name that is already in use.
The resources posts call results in a definition for the posts and post routes. If you change your as: .. clause to reference different (unused) names, you will no longer get that error.
try resources :posts path => 'q'

Rails nesting scope block in resources

I'm trying to create a path like product/:id/monthly/revenue/ and product/:id/monthly/items_sold and the equivalent named routes product_monthly_revenue and product_monthly_items_sold, and these routes would simply show the charts. I tried
resources :products do
scope 'monthly' do
match 'revenue', to: "charts#monthly_revenue", via: 'get'
match 'items_sold', to: "charts#monthly_items_sold", via: 'get'
end
end
But this gives me the routes:
product_revenue GET /monthly/products/:product_id/revenue(.:format) charts#monthly_revenue
product_items_sold GET /monthly/products/:product_id/items_sold(.:format) charts#monthly_items_sold
where monthly gets appended in front instead, and the route naming is off. I know I could just do:
resources :products do
match 'monthly/revenue', to: "charts#monthly_revenue", via: 'get', as: :monthly_revenue
match 'monthly/items_sold', to: "charts#monthly_items_sold", via: 'get', as: :monthly_items_sold
end
but that isn't DRY, and it gets crazy when I try to add more categories like yearly. Using a namespace would force me to create a new controller for each namespace, when I want to consolidate all the charts into a single controller.
So I guess the summarised question would be: is it possible to namespace routes without namspacing controllers? Or is it possible to consolidate the creation of categories of named routes?
Edit: Using
resources :products do
scope "monthly", as: :monthly, path: "monthly" do
match 'revenue', to: "charts#monthly_revenue", via: 'get'
match 'items_sold', to: "charts#monthly_items_sold", via: 'get'
end
end
would give me the routes
monthly_product_revenue GET /monthly/products/:product_id/revenue(.:format) charts#monthly_revenue
monthly_product_items_sold GET /monthly/products/:product_id/items_sold(.:format) charts#monthly_items_sold
which similar to the first block, is unexpected because I expect that if a scope is nested in a resources block, only the routes in the scope block would affected by the the scope, not the resources block.
Edit 2: Forgot to include this information earlier, but I'm on Rails 4.0.0, with Ruby 2.0.0-p247
The real solution is to use nested:
resources :products do
nested do
scope 'monthly', as: :monthly do
get 'revenue', to: 'charts#monthly_revenue'
get 'items_sold', to: 'charts#monthly_items_sold'
end
end
end
Ref: https://github.com/rails/rails/issues/12626
Here's how I might approach:
periods = %w(monthly yearly)
period_sections = %w(revenue items_sold)
resources :products do
periods.each do |period|
period_sections.each do |section|
get "#{period}/#{section}", to: "charts##{period}_#{section}", as: "#{period}_#{section}"
end
end
end
It is also possible to use named routes and pass the values to your controller method via params (be sure to properly validate before using):
resources :products do
get ":period/:section", to: "charts#generate_report", as: :report
end
# report_path(period: 'monthly', section: 'revenue')

How do I rename a resources routes?

How do I rename some of these routes...for example, below i want to use signup_path in my controllers instead of signup_sessions_path...
resources :sessions, only: [] do
collection do
post :signup, :as => :signup
post :login
delete :logout
end
end
Try not to nest the routes under resources :sessions but rather use the to: option like so:
post :signup, to: 'sessions#signup', as: :signup, on: :collection
Not too sure about your collection there but I'm sure you get the gist of it
Update
According to your comment, as of today, I don't know of any way to remove the nested route resource name from the path name of a nested resource route. In other words, whatever is nested is purposely to use the scope of the resource and therefore there are no options to revert that behaviour other than taking it out of the resource's block.

RESTful route overriden by named route

When defining a resources in routes.rb in Rails, I have the following problem: My resource supports the standard CRUD operations and has a custom function/route, which allows filtering. Now this custom routes matches the edit route and jumps in before the actual RESTful route.
Is there a way to prioritize the RESTful routes so that they match first?
resources :items do
get ':category(/:level)', :action => :filter, :on => :collection, :as => 'filter'
end
You should just set a simple get route ( if it is a GET request )
get 'filter', :to => "items#filter"
If you have any problems there are always RoR Guides :)
http://guides.rubyonrails.org/routing.html

Resources