Rails: How exactly does the "shallow" option benefit us? - ruby-on-rails

So i've been learning rails for a little bit. Sometimes routing I get confused on (Well not so much confused but just figuring out "best practices").
One thing i've never used is the "shallow" option, such as in this example in the docs:
resources :articles do
resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]
Which is what shallow would do if just appended onto the :comments portion.
So how exactly is this a benefit? does it just basically prevent the paths from being /articles/comments/:id for multiple things and instead just being /comments/:id for the methods? Unless im missing something?

Resources should never be nested more than 1 level deep.
Keeping this in mind we always try to avoid deep nesting.
shallow option is just used to avoid complicated(deeply) nested routes wherever possible. It should be used only for the resources which are bound to their parents.

Related

Rails not differentiating between two resources nested in the same namespaces

I have two types of products that are nested under the same categories. The routes I have setup are
resources :categories, path: '/', only: [:show] do
resources :subcategories, path: '/', only: [:show] do
resources :amazon_products, path: '/', only: [:show]
resources :other_products, path: '/', only: [:show]
end
end
which I was previously accessing using this link
<%= link_to "View Product Page", [product.collection, product.category, product.subcategory, product], class: 'product__link' %>
which that resulted in something like this url after friendly_id finished with it
/cleansers/face-wash-and-cleansers/blemish-remedy-acne-treatment-gelee-cleanser
The issue is that the link only resolves for amazon_products and I'm not sure how to make it differentiate between the two. I think the problem is in the way I am referencing the path since when I enter rails routes in the console, I can see the two different paths there like so
category_subcategory_amazon_product
GET :category_id/:subcategory_id/:id(.:format)
amazon_products#show
category_subcategory_other_product
GET /:collection_id/:category_id/:subcategory_id/:id(.:format)
other_products#show
I tried specifically referencing the other product path using the link
category_subcategory_other_product_path(product.category, product.subcategory, product)
but it is giving me an ActiveRecord::RecordNotFound since its still looking in the wrong controller
app/controllers/amazon_products_controller.rb:5:in `show'
How do I tell rails to differentiate between these two resources?
When you boil it down, Rails routes are relatively simple patterns that match URIs and dispatch requests to the proper controller/method.
If you had specified your nested routes in their basic form:
resources :categories, only: [:show] do
resources :subcategories, only: [:show] do
resources :amazon_products, only: [:show]
resources :other_products, only: [:show]
end
end
The resulting URI patterns for the product level would look like:
/categories/<id>/subcategories/<id>/amazon_products/<id>
/categories/<id>/subcategories/<id>/other_products/<id>
Admittedly verbose, but obviously distinct, patterns.
The issue is that you are using path: '/' on the resources to remove everything unique about the resource routes. So, your product-level route patterns are:
category_subcategory_amazon_product: /<id>/<id>/<id>
category_subcategory_other_product: /<id>/<id>/<id>
Since the two patterns are identical, Rails falls back to the time honoured practice of matching the first one defined. You can demonstrate that for yourself by swapping :other_products so it is first; its controller will then be the one always invoked.
NB: It doesn't matter that you are using friendly-id -- that just changes what the IDs look like to the user, not the basic route pattern.
The solution is to simply reintroduce some uniqueness, at least at the product level.
resources :categories, path: '/', only: [:show] do
resources :subcategories, path: '/', only: [:show] do
resources :amazon_products, path: 'amazon', only: [:show]
resources :other_products, path: 'other', only: [:show]
end
end
The result will be route patterns that Rails can actually distinguish:
category_subcategory_amazon_product: /<id>/<id>/amazon/<id>
category_subcategory_other_product: /<id>/<id>/other/<id>

rails - Best practice for creating similar routes?

In my application, a User can make a Post, and a User can make Correction (think of it as a comment) on another user's post. Each User can have many Posts, and each Post can have many Corrections.
On each show page for a Post, there is a form to create a new Correction. This uses the user_post_corrections path.
On the show page for each User, I would like to display each Correction they've submitted for any Post. This requires a user_corrections path.
In order to achieve this, I have the following in my routes.rb:
resources :users do
resources :posts do
resources :corrections
end
end
resources :users do
resources :corrections
end
This intuitively feels bad to me, as I've created two nested routes that are very similar to one another.
Is there a better way to do this? My code is working fine as it is but is there a best practice method for implementing this kind of model?
Routing concerns are an excellent but underused tool for DRYing out your routes:
concern :correctable do
resources :corrections
end
# just an example of multiple concerns
concern :commentable do
resources :comments
end
resources :users, concerns: :correctable
resources :posts, concerns: [:correctable, :commentable]
However you should take when creating nested routes so that you are not nesting needlessly.
Often you might want the collective actions [new, index, create] to be scoped by the parent:
GET|POST /posts/:post_id/corrections
GET /posts/:post_id/corrections/new
While you want the member actions to be unscoped since you can always access a record directly if it has a unique id.
GET /corrections/:id
GET /corrections/:id/edit
PATCH /corrections/:id
DELETE /corrections/:id
To do this you would declare the routes like so:
resources :corrections, only: [:show, :update, :edit]
concern :correctable do
resources :corrections, only: [:new, :index, :create]
end
resources :users, :posts, concerns: [:correctable]
The shallow: true option does something like this but does not work well when you declare the same resources several times as it adds unscoped routes for every call.

Nested pages with routing

I need to make nested pages (like when the one page(page1) is a parent for the other page(page2), and routes for these pages looks like: site.com/page1/page2. What gem should help me to do that ?
I don't know if there is a gem for this, but I would start with a route something like:
get 'pages/*parents/:page', to: 'pages#show'
The URL site.com/pages/page1/page2/page3 will be parsed to the params:
params[:parents] = 'page1/page2'
params[:page] = 'page3'
Now you can do something like:
parent_names = params[:parents].split('/')
Not sure if this is what you want but I hope it will get you started.
This is not common good practice. I'm not saying you should not do it as I don't know anything about your business logic but the usual practice is
/model_name/:id/model_name/:id
with at most one level of nesting. That means no such things as
/model_name/:id/model_name/:id/model_name/:id
In general, I favour the shortest possible path. Hence, if the resource can be identified with just its id, I go with /model_name/:id.
This often results in things such as
resources :post do
resources :comments, only: [:new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]
I hope that helped.

Mapping to the custom route

So far I have my routes like this: ( out of learning purposes I am curious to get it working with nested resources technique, but if you think this will get too complicated please feel free to suggest any other way of routing technique too)
resources :management, only: [:show] do
resources :report, only: [:show], controller: 'report' do
member do
# hmm what to write in here?!
end
end
end
my GOAL is to have a URL like this:
/managment/SOME_ID_WE_PASS_/report
/managment/1/report
But still can't figure out how exactly to write that route? Can you please take a look?
You don't need nested resources.
resources :crowd_management, only: [:show] do
get :exec_report, on: :member
end
That will produce:
/crowd_management/:id mapped to CrowdManagementController#show
/crowd_management/:id/exec_report mapped to CrowdManagementController#exec_report
The helper methods will be:
crowd_management_path
exec_report_crowd_management_path
You can run rake routes for a detailed list of all your routes.

Restricting resource routes and adding additional non-RESTful routes in Rails 3

I wasn't able to find anything here or elsewhere that covered both restricting a resource's routes and adding additional non-RESTful routes in Rails 3. It's probably very simple but every example or explanation I've come across addresses just one case not both at the same time.
Here's an example of what I've been doing in Rails 2:
map.resources :sessions, :only => [:new, :create, :destroy], :member => {:recovery => :get}
Pretty straightforward, we only want 3 of the 7 RESTful routes because the others don't make any sense for this resource, but we also want to add another route which is used in account recovery.
Now from what I gather doing either one of these things is very straightforward as well:
resources :sessions, :only => [:new, :create, :destroy]
Just like in Rails 2. And:
resources :sessions do
member do
get :recovery
end
end
So, how do I combine these two? Can I still use the old Rails 2 way of doing this? Is there a preferred way of doing this in Rails 3?
You can pass arguments and a block to resources:
resources :sessions, :only => [:new, :create, :destroy] do
get :recovery, :on => :member
end
And test it out with rake routes.
It should be working pretty much like this
resources :sessions, :only => [:new, :create, :destroy] do
member do
get :recovery
end
end
There is an even shorter way, as proposed by coreyward.
Check the rails guides, "Rails Routing from the Outside In".
I also can recommend "The Rails 3 Way" by Obie Fernandez, which got 2 pretty good chapters on Routing and RESt.
Cheers

Resources