I've been trying rails and I found myself with a very simple problem: I have two models, survivors and locations, and they're nested resources in routes.rb
Rails.application.routes.draw do
namespace 'api' do
namespace 'v1' do
resources :survivors do
resources :locations
end
resources :abduction_reports
end
end
end
which gives me almost all the routes I need. The problem is that survivors and locations is one-to-one association, so if I want to update the survivor location, it seems more interesting if the end-point for that is
PUT /survivors/:survivor_id/location
To solve it, I did this:
Rails.application.routes.draw do
namespace 'api' do
namespace 'v1' do
[...]
put 'survivors/:survivor_id/location', to: 'locations#update'
end
end
end
but it doesn't seem the correct way to do it... I mean, can it be defined inside the 'resources :survivors do ... end' scope?
I probably have already seen it in the rails documentation, but I think I just haven't realized how to adapt the documentation examples to my problem yet.
For a singular resource use the resource macro instead:
Rails.application.routes.draw do
namespace 'api' do
namespace 'v1' do
resources :survivors do
resource :location
end
resources :abduction_reports
end
end
end
Prefix Verb URI Pattern Controller#Action
api_v1_survivor_location POST /api/v1/survivors/:survivor_id/location(.:format) api/v1/locations#create
new_api_v1_survivor_location GET /api/v1/survivors/:survivor_id/location/new(.:format) api/v1/locations#new
edit_api_v1_survivor_location GET /api/v1/survivors/:survivor_id/location/edit(.:format) api/v1/locations#edit
GET /api/v1/survivors/:survivor_id/location(.:format) api/v1/locations#show
PATCH /api/v1/survivors/:survivor_id/location(.:format) api/v1/locations#update
PUT /api/v1/survivors/:survivor_id/location(.:format) api/v1/locations#update
DELETE /api/v1/survivors/:survivor_id/location(.:format) api/v1/locations#destroy
api_v1_survivors GET /api/v1/survivors(.:format) api/v1/survivors#index
POST /api/v1/survivors(.:format) api/v1/survivors#create
new_api_v1_survivor GET /api/v1/survivors/new(.:format) api/v1/survivors#new
edit_api_v1_survivor GET /api/v1/survivors/:id/edit(.:format) api/v1/survivors#edit
api_v1_survivor GET /api/v1/survivors/:id(.:format) api/v1/survivors#show
PATCH /api/v1/survivors/:id(.:format) api/v1/survivors#update
PUT /api/v1/survivors/:id(.:format) api/v1/survivors#update
DELETE /api/v1/survivors/:id(.:format) api/v1/survivors#destroy
See:
https://guides.rubyonrails.org/routing.html#singular-resources
Related
I'm using Rails 4.2 with Rails Engine to handle my admin panel. I want to move the engine code to the main rails app and part of doing that is moving the routes.
For now, my routes in the Engine my-web-app/lib/admin/config/routes.rb is something like:
Admin::Engine.routes.draw do
resources :countries, only: [:index]
end
And at my app's routes config/routes.rb:
Rails.application.routes.draw do
.. some routes ...
mount GlobalAdmin::Engine => "/admin"
end
Now the engine's route for countries is countries_url NOT admin_countries_url but for URL you'll need to access it with something like admin/countries (this is the default behavior for the engine and I want to keep it this way).
What I did is that I moved my-web-app/lib/admin/config/routes.rb to my-web-app//config/routes.admin.rb and made it look something like:
Rails.application.routes.draw do
resources :countries, only: [:index]
end
And then in my config/application.rb I added something like this:
config.paths["config/routes.rb"] = [
Rails.root.join("config/routes.admin.rb"),
Rails.root.join("config/routes.rb")
]
The problem with this approach is that I have countries_url, however, I can't access it with admin namespace URL like admin/countries. If I add namespace admin something like:
Rails.application.routes.draw do
namespace :admin do
resources :countries, only: [:index]
end
end
Then I'll need to refer to countries url with admin_countries_url which is not the behavior I need.
Any help of how to move the routes from the engine without affecting the previous engine routes?
I'm aware that routes inside an engine are isolated from the application by default. The application and its engines can have routes with the same names, so integrating both can cause some issues, however I want to preserve the exact routes if possible and not to add a namespace and stick with the exact routes names.
Use scope instead of namespace:
Rails.application.routes.draw do
scope path: "/admin" do
resources :countries
end
end
max#MaxBook ~/p/sandbox> rails routes
Prefix Verb URI Pattern Controller#Action
countries GET /admin/countries(.:format) countries#index
POST /admin/countries(.:format) countries#create
new_country GET /admin/countries/new(.:format) countries#new
edit_country GET /admin/countries/:id/edit(.:format) countries#edit
country GET /admin/countries/:id(.:format) countries#show
PATCH /admin/countries/:id(.:format) countries#update
PUT /admin/countries/:id(.:format) countries#update
DELETE /admin/countries/:id(.:format) countries#destroy
I have a blog with root
root 'posts#index'
And works best with example.com/ to example.com/posts
But what I want is something like this:
example.com/blog/posts/1.
I've tried creating blog Controller and add
resources :blog do
resources :posts
end
But this is making my routes to blog/:id/posts/:id
If you don't have the relationship between the post and the blog as you mentioned, rails gives you the freedom to declare routes as our own.
so, to make the route example.com/posts/1 to, example.com/blog/posts/1, just add a custom route at the last.
get '/blog/posts/:id', to: :show, controller: 'posts'
what this does is over rides the previous route and make this route final.
Now type rake routes and it will give the last route for you as,
GET /blog/posts/:id(.:format) posts#show
Now you can access using,
example.com/blog/posts/1
Reference for rails routing
Just to expand upon #Sravan answer. If you have multiple routes that will start with /blog/ you might want to check Rails guide on routing.
You can add something along the lines of
scope '/blog' do
resources :posts
resources :users
resources :images
end
Which will create corresponding routes under /blog/.
namespace :blog do
resources :posts
resources :users
resources :images
end
And your controller with namespace will look like this: Blog::PostsController
Currently in my routes I have:
# USER RESOURCES
resources :users do
resources :repositories
patch 'change_password'
get 'account_setting'
end
Which generates this path for the account_setting action:
user_account_setting GET /users/:user_id/account_setting(.:format) users#account_setting
What I want is to have:
user_account_setting GET /users/:id/account_setting(.:format) users#account_setting
The two are essentially the same thing, but the first has a user_ prefix for the id which rails adds because it is in users resource block.
SIDE NOTE
I know I can simply remove the account_setting action from the users resource block and write:
get 'users/:id/account_setting', to: 'users#account_setting'
But I don't want to.
You can do it as follows:
resources :users do
member do
get 'account_setting'
end
end
To add a member route, add a member block into the resource block.
For documentation, you can check http://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html
I'm searching a reason why rake routes doesn't match the index path of my nested resource.
Here is my code:
namespace :api do
resources :photos do
resource :comments
end
end
Here is the result of the command: rake routes | grep comment
batch_action_admin_user_comments POST /admin/user_comments/batch_action(.:format) admin/user_comments#batch_action
admin_user_comments GET /admin/user_comments(.:format) admin/user_comments#index
POST /admin/user_comments(.:format) admin/user_comments#create
new_admin_user_comment GET /admin/user_comments/new(.:format) admin/user_comments#new
edit_admin_user_comment GET /admin/user_comments/:id/edit(.:format) admin/user_comments#edit
admin_user_comment GET /admin/user_comments/:id(.:format) admin/user_comments#show
PATCH /admin/user_comments/:id(.:format) admin/user_comments#update
PUT /admin/user_comments/:id(.:format) admin/user_comments#update
DELETE /admin/user_comments/:id(.:format) admin/user_comments#destroy
admin_comments GET /admin/comments(.:format) admin/comments#index
POST /admin/comments(.:format) admin/comments#create
admin_comment GET /admin/comments/:id(.:format) admin/comments#show
api_photo_comments POST /api/photos/:photo_id/comments(.:format) api/comments#create
new_api_photo_comments GET /api/photos/:photo_id/comments/new(.:format) api/comments#new
edit_api_photo_comments GET /api/photos/:photo_id/comments/edit(.:format) api/comments#edit
GET /api/photos/:photo_id/comments(.:format) api/comments#show
PATCH /api/photos/:photo_id/comments(.:format) api/comments#update
PUT /api/photos/:photo_id/comments(.:format) api/comments#update
DELETE /api/photos/:photo_id/comments(.:format) api/comments#destroy
I tried to add only: [:create, :index] to my comments resource but only the create route is visible.
According to the documentation about nested-resources I don't understand what's happening.
Thank you for your help.
It's because you are using a singular resource (resource :comments)
From the docs:
Sometimes, you have a resource that clients always look up without
referencing an ID. For example, you would like /profile to always show
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
You'll need to use the standard resources method to get this working (resource omits the index action):
#config/routes.rb
namespace :api do
resources :photos do
resources :comments
end
end
My mistake. A "S" was missing on my resource.
namespace :api do
resources :photos do
resources :comments
end
end
Now it works.
I would like to create a resourceful route on a resource member, but I can't seem to find the syntax to create the named route that I want.
namespace :admin
resources :foobars do
get :attribute, on: :member, as: :attribute
end
end
This will provide a route method called:
attribute_admin_foobar_path
I would like it to say:
admin_foobar_attribute_path
The only other way I can think of would be to reject the resources block and create a single route:
namespace :admin
resources :foobars
get 'foobars/:id/attribute', as: :foobar_attribute
end
However, I don't like this approach because it forces me to duplicate the routing structure of already existing routes...not very DRY.
Is there a way that I can create the route name that I want while still using the resources routing block?
If you do it like this:
namespace :admin do
resources :foobars do
get :attribute
end
end
You will get:
admin_foobar_attribute GET /admin/foobars/:foobar_id/attribute(.:format) admin/foobars#attribute
That is admin_foobar_attribute_path.