Rails 5 shallow routing on parent, but not shallow for child - ruby-on-rails

I have an issue regarding shallow routing in rails. I have a set of routes nested 3 levels users have many portfolios have many displays. What i want to do is have a shallow route for my portfolios, but have verbose routing for displays. I've tried passing shallow: false but that doesnt seem to do anything
# config/routes.rb
resources :users do
resources :portfolios, shallow: true do
resources :displays #shallow: false
end
end
for my users and portfolios, this works the way i want
#users routes
/users #index
/users/:id #show
#portfolios routes
/users/:user_id/portfolios #index
/portfolios/:id #show
however, from here i want verbose displays keyed off the shallow portfolio
#desired display routes
/portfolios/:portfolio_id/displays #index
/portfolios/:portfolio_id/displays/:id #show
#actual display routes
/portfolios/:portfolio_id/displays #index
/displays/:id #show
Is there a way to utilize the shallow configuration in this way?

Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
resources :users do
resources :portfolios, shallow: true
end
resources :users, only: [] do
resources :portfolios, only: [] do
resources :displays
end
end
end
Here are the routes
Prefix Verb URI Pattern Controller#Action
user_portfolios GET /users/:user_id/portfolios(.:format) portfolios#index
POST /users/:user_id/portfolios(.:format) portfolios#create
new_user_portfolio GET /users/:user_id/portfolios/new(.:format) portfolios#new
edit_portfolio GET /portfolios/:id/edit(.:format) portfolios#edit
portfolio GET /portfolios/:id(.:format) portfolios#show
PATCH /portfolios/:id(.:format) portfolios#update
PUT /portfolios/:id(.:format) portfolios#update
DELETE /portfolios/:id(.:format) portfolios#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
user_portfolio_displays GET /users/:user_id/portfolios/:portfolio_id/displays(.:format) displays#index
POST /users/:user_id/portfolios/:portfolio_id/displays(.:format) displays#create
new_user_portfolio_display GET /users/:user_id/portfolios/:portfolio_id/displays/new(.:format) displays#new
edit_user_portfolio_display GET /users/:user_id/portfolios/:portfolio_id/displays/:id/edit(.:format) displays#edit
user_portfolio_display GET /users/:user_id/portfolios/:portfolio_id/displays/:id(.:format) displays#show
PATCH /users/:user_id/portfolios/:portfolio_id/displays/:id(.:format) displays#update
PUT /users/:user_id/portfolios/:portfolio_id/displays/:id(.:format) displays#update
DELETE /users/:user_id/portfolios/:portfolio_id/displays/:id(.:format) displays#destroy

Related

Rails routing: add a custom route to the standard list of actions

Rails routes create the 7 CRUD actions by default following REST.
resources :users
However, I have a confirm_destroy action that I use in almost every resource, because I have a lot of logic that goes on the confirmation page; it's not just a simple yes/no alert dialog.
resources :users do
get :confirm_destroy, on: :member
end
With 50+ resources, it gets tedious to write this out for each resource and my routes file is literally 3x longer because of this.
Is there any way to add an action to the standard 7 for the resources block such that
resources :users
would be the same as
resources :users do
get :confirm_destroy, on: :member
end
and I can use it in the routes as a standard action, ie:
resources :users, only: [:show, :confirm_destroy, :destroy]
resources :users, except: [:confirm_destroy]
While not quite as elegant as you might like, the Rails way would be to use a routing concern, as #dbugger suggested.
For example:
concern :confirmable do
get 'confirm_destroy', on: :member
end
resources :users, :widgets, concerns: :confirmable
$ rails routes
Prefix Verb URI Pattern Controller#Action
confirm_destroy_user GET /users/:id/confirm_destroy(.:format) users#confirm_destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
confirm_destroy_widget GET /widgets/:id/confirm_destroy(.:format) widgets#confirm_destroy
widgets GET /widgets(.:format) widgets#index
POST /widgets(.:format) widgets#create
new_widget GET /widgets/new(.:format) widgets#new
edit_widget GET /widgets/:id/edit(.:format) widgets#edit
widget GET /widgets/:id(.:format) widgets#show
PATCH /widgets/:id(.:format) widgets#update
PUT /widgets/:id(.:format) widgets#update
DELETE /widgets/:id(.:format) widgets#destroy

Scoped routes creating a routing issue

Im trying to learn to dry up my code a bit but have come across a issue,
Im scoping my routes with controller and path options
scope path: '/administrators', controller: :administrators do
get 'unverified' => :unverified
patch 'verify/:id' => :verify
get 'reported' => :reported
get 'ban_user' => :ban_user
patch 'execute_ban/:id' => :execute_ban
end
So this is what ive done so far, all the get links are working correctly...
but this is making the patch 'execute_ban/:id' => :execute_ban become a extension of ban user like this: (also the same with verify)
verified GET /administrators/unverified(.:format) administrators#unverifie
PATCH /administrators/verify/:id(.:format) administrators#verify
reported GET /administrators/reported(.:format) administrators#reported
ban_user GET /administrators/ban_user(.:format) administrators#ban_user
PATCH /administrators/execute_ban/:id(.:format) administrators#execute_ban
now ive changed my link_to route = link_to 'ban', ban_user_path(x.id), method: :patch
but its throwing an routing error saying no path matches.
Is there something im missing, any insight would aooreciated as always.
Thanks
Why not go for a more restful design that centers around the resource being modified?
Rails.application.routes.draw do
resources :users do
get :unverified, on: :collection
get :reported, on: :collection
get :ban
patch :ban, action: 'execute_ban'
end
end
You don't need different paths for the form and acting on a resource - use the HTTP verb instead.
Prefix Verb URI Pattern Controller#Action
unverified_users GET /users/unverified(.:format) users#unverified
reported_users GET /users/reported(.:format) users#reported
user_ban GET /users/:user_id/ban(.:format) users#ban
PATCH /users/:user_id/ban(.:format) users#execute_ban
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
If you are adding a separate controller just for authorization purposes it's an anti-pattern of sorts since you are just adding more complexity for something that should be handled by Pundit/CanCanCan.

How to nest a namespaced ruby on rails controller

I have a controller in a name spaced controller/user directory, so its first line is
class User::BookingsController < ApplicationController
I have set the routes as follows
resources :users do
namespace :user do
resources :bookings
end
end
The path to index action is
user_user_bookings_path or
/users/:user_id/user/bookings(.:format)
which works fine, but the double user_user sounds like a Catch 22 joke. Is there a more elegant way to do this?
What would you like to achieve? You are namespacing under "user" after all. You can try options like as: nil, but I think this will be even funnier (user__bookings_path).
What you can do is write every route by hand, something like:
resources :users do
post 'bookings', to: 'user/bookings#create'
end
# => user_bookings_path, POST /users/:user_id/bookings
or if you want to preserve the URL
resources :users do
post '/user/bookings', to: 'user/bookings#create', as: 'bookings'
end
# => user_bookings_path, POST /users/:user_id/user/bookings
Try this
resources :users do
resources :bookings
end
According to the docs you can namespace your resource
by using the namespace block
namespace :api do
resources :users
end
this would give you those routes:
/api/users [GET, POST]
/api/users/:id [GET, PUT, DELETE]
However you want to nest the resource within another resource (docs) you would do this:
resources :users do
resources :bookings
end
Which would result in these routes:
/users/:user_id/bookings [GET, POST]
/users/:user_id/bookings/:id [GET, PUT, DELETE]
Because you have your controller scoped under User you have to set the user scope like this:
resources :users do
resources :bookings, module: :user
end
Which results in those routes:
➜ playground rake routes
Prefix Verb URI Pattern Controller#Action
user_bookings GET /users/:user_id/bookings(.:format) user/bookings#index
POST /users/:user_id/bookings(.:format) user/bookings#create
new_user_booking GET /users/:user_id/bookings/new(.:format) user/bookings#new
edit_user_booking GET /users/:user_id/bookings/:id/edit(.:format) user/bookings#edit
user_booking GET /users/:user_id/bookings/:id(.:format) user/bookings#show
PATCH /users/:user_id/bookings/:id(.:format) user/bookings#update
PUT /users/:user_id/bookings/:id(.:format) user/bookings#update
DELETE /users/:user_id/bookings/:id(.:format) user/bookings#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy

Unable to route to a method in my controller

I’m using Rails 4.2.5. I’m trying to set up my controller so that when a logged in user visits /users/edit, they see my form where they can edit some of their profiles. So in config/routes.rb I have
resources :users
…
get "users/edit" => "users#edit"
then in “app/controllers/users_controller.rb” I have
def edit
#user = User.find(session["user_id"])
render 'edit'
end
but when I visit “http://localhost:3000/users/edit” in a browser, I get the error
The action 'show' could not be found for UsersController
It is true I have no “show” method in my controller, but that is not where I want the user to go. I want them going to the edit method.
You are trying to go to the show action with this link:
http://localhost:3000/users/edit
You have this route for the show action:
GET /users/:id(.:format) users#show
(:id) is (edit)
Because you have defined first RESTful route:
resources :users
Which includes all routes listed below:
users_path GET /users(.:format) users#index
POST /users(.:format) users#create
new_user_path GET /users/new(.:format) users#new
edit_user_path GET /users/:id/edit(.:format) users#edit
user_path GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
and then
get "users/edit" => "users#edit"
Rails always finds first match. In this case show action of RESTFul routes will be applied:
GET /users/:id(.:format) users#show
and the other route will be ignored.
Solution: Change the order of the routes. That way edit route will be applied first.
The problem is that you're mixing a resource route with your own edit method, and "Rails routes are matched in the order they are specified" so it's matching the resources show route users/:id and stopping there.
You need to move your edit route above the resource.
Alternatively, read the linked guide and see if you can add edit as a collection route to the resource, and except the resources edit method. You may also need to except the show method, but it's worth having a play and seeing what you come up with. Routing is an important aspect and worth time to understand.
remove get "users/edit" => "users#edit" from your routes, change to resources :users, only: [:edit] (or more if actions you need them), remove the render 'edit' from your controller (default action).
Visit http://localhost:3000/users/1/edit to see the edit page for user_id 1 (there can't be an edit page for all users, you have to specify the id)

Is there a better way to define these routes?

I'm working on supporting users in my Rails app. There's no need for a user to even be aware that there are other users out there. I don't want to just use resources :users, because the routes that generates are these:
users GET /users users#index
POST /users users#create
new_user GET /users/new users#new
edit_user GET /users/:id/edit users#edit
user GET /users/:id users#show
PATCH /users/:id users#update
PUT /users/:id users#update
DELETE /users/:id users#destroy
(.:format) removed for improved readability.
You'd have to put the user id number in the URL, and that offers a chance for users to be aware that other users exist. I want these routes:
users GET /users users#index
POST /users users#create
new_user GET /users/new users#new
edit_user GET /users/me/edit users#edit
user GET /users/me users#show
PATCH /users/me users#update
PUT /users/me users#update
DELETE /users/me users#destroy
Yes. /users/me is the path of your user, and that's the only user path you can get at.
But the problem is defining these routes. Here's one idea:
resources :users, constraints: { id: 'me' }
And in the User model:
def to_param
'me'
end
But that seems too kludgy for me. Any better ideas?
With Singular Resources your are good to go ;-)
Define your routes like this:
# config/routes.rb
resources :users, only: [:index, :create, :new]
resource :user, path: '/users/me', only: [:show, :edit, :update, :destroy]
Your routes will be singular (/user) if you leave the path: option. Play around with the options ;-)
And your rake routes result should look like this:
Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/me/edit(.:format) users#edit
user GET /users/me(.:format) users#show
PATCH /users/me(.:format) users#update
PUT /users/me(.:format) users#update
DELETE /users/me(.:format) users#destroy
You can also test the routes on the Rails console (rails c)
2.1.3 :001 > Rails.application.routes.url_helpers.user_path
=> "/users/me"
2.1.3 :002 > Rails.application.routes.url_helpers.edit_user_path
=> "/users/me/edit"
If you want, you can pluralize :user on the Singular Resources, but don't forget to set the as: option, her an example:
# config/routes.rb
resources :users, only: [:index, :create, :new]
resource :users, path: '/users/me', as: 'user', only: [:show, :edit, :update, :destroy]
Please take a look to the notes and warnings in the Rails guide! Here is an excerpt:
A long-standing bug prevents form_for from working automatically with singular resources. ...

Resources