I am using Ruby on Rails 3.0.7 and in my application I have an Users::Article class (note that it is namespaced). In the routes.rb file it is stated as follow:
resources :users do
resources :articles
end
I would like to set the route related to the Users::Article class so that I can access that at the URL /articles, /articles/new, /articles/1 and /articles/1/edit but I would like to still use the RoR Convention over Configuration "system". That is, I would like to use:
Users::ArticlesHelper;
views/users/articles/<file_name>.html.erb view files;
named routes (<name>_path and <name>_url);
and others "a là Ruby on Rails Way"...
How can I do that?
In few words, for example, I would like to refer to the /articles/1 path but making the application to work exactly as it is considering the users/<user_id>/articles/1 path.
it's not an estetic matter, but it's related to parameters for routing.
you can use /articles/:id and then refer to the owner (User) inside controller.
in the second case /users/:user_id/articles/:id will pass 2 params to controller.
From the ActionDispatch::Routing::Mapper::Resources docs:
:shallow
# Generates shallow routes for nested resource(s). When placed on a parent
# resource, generates shallow routes for all nested resources.
resources :posts, :shallow => true do
resources :comments
end
# Is the same as:
resources :posts do
resources :comments, :except => [:show, :edit, :update, :destroy]
end
resources :comments, :only => [:show, :edit, :update, :destroy]
# This allows URLs for resources that otherwise would be deeply nested such as a
# comment on a blog post like /posts/a-long-permalink/comments/1234 to be
# shortened to just /comments/1234.
To get this from >rake routes
users_articles GET /articles(.:format) {:action=>"index", :controller=>"users/articles"}
POST /articles(.:format) {:action=>"create", :controller=>"users/articles"}
new_users_article GET /articles/new(.:format) {:action=>"new", :controller=>"users/articles"}
edit_users_article GET /articles/:id/edit(.:format) {:action=>"edit", :controller=>"users/articles"}
users_article GET /articles/:id(.:format) {:action=>"show", :controller=>"users/articles"}
PUT /articles/:id(.:format) {:action=>"update", :controller=>"users/articles"}
DELETE /articles/:id(.:format) {:action=>"destroy", :controller=>"users/articles"}
We need this in our routes.rb
namespace :users, :path => '' do
resources :articles
end
Note: I am using :namespace as that is what you stated.
Hope this helps.
Related
In my routes.rb file, I have the following code:
Rails.application.routes.draw do
get 'getTodos', to: 'todos#get'
get 'getUsers', to: 'users#get'
get 'getStates', to: 'states#get'
post 'addTodo', to: 'todos#add'
post 'addUser', to: 'users#add'
delete 'deleteTodo/*id', to: 'todos#delete'
delete 'deleteUsers/*IDs', to: 'users#delete'
delete 'deleteAllTodos', to: 'todos#delete_all'
put 'updateTodo', to: 'todos#update'
end
How can I modify this code to make it more beautiful and correct?
The biggest issue with this code is that its completely unidiomatic. In Rails you create, read, update and destroy (CRUD) resources through the following routes:
HTTP Method Path Controller#Action
GET /todos(.:format) todos#index
POST /todos(.:format) todos#create
GET /todos/new(.:format) todos#new
GET /todos/:id/edit(.:format) todos#edit GET /todos/:id(.:format) todos#show
PATCH /todos/:id(.:format) todos#update
PUT /todos/:id(.:format) todos#update
DELETE /todos/:id(.:format) todos#destroy
The key here is the combination of the HTTP method and path.
GET /todos gets you all the todos while GET /todos/:id shows you a specific resource.
GET /todos/new displays the form to create a new todo. POST /todos actually creates the resource from a form submission.
GET /todos/:id/edit displays the form to edit a todo. PATCH /todos/:id actually updates the resource from a form submission.
DELETE /todos/:id - You should be able to guess what this does.
You can generate these routes with:
Rails.application.routes.draw do
resources :todos
end
If you want to define a routes that deletes all the todos RESTfully it should be defined as DELETE /todos (without an id).
Rails.application.routes.draw do
resources :todos do
delete '/', on: :collection, action: :destroy_all
end
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
See:
Rails Routing from the Outside In
I would do something like this. Try to use default Rails REST actions instead of overwriting them
Rails.application.routes.draw do
resources :todos, only: [:index, :create, :update, :destroy] do
collection do
delete :delete_all, to: 'todos#delete_all'
end
end
resources :users, only: [:index, :create, :destroy]
resources :states, only: :index
I am using the rails_best_practices gem which tells me i have an error:
overuse route customizations (customize_count > 8)
resources :stores do
collection do
get :api
end
member do
get :printer
get :delete
get :inventory
delete :inventory
get :daysheet
get :detailed_daysheet
get :labels
patch :restore
patch :print_labels
post :daysheet
end
end
Resulting in these paths:
api_stores_path GET /stores/api(.:format) stores#api
printer_store_path GET /stores/:id/printer(.:format) stores#printer
delete_store_path GET /stores/:id/delete(.:format) stores#delete
inventory_store_path GET /stores/:id/inventory(.:format) stores#inventory
daysheet_store_path GET /stores/:id/daysheet(.:format) stores#daysheet
detailed_daysheet_store_path GET /stores/:id/detailed_daysheet(.:format) stores#detailed_daysheet
labels_store_path GET /stores/:id/labels(.:format) stores#labels
DELETE /stores/:id/inventory(.:format) stores#inventory
restore_store_path PATCH /stores/:id/restore(.:format) stores#restore
print_labels_store_path PATCH /stores/:id/print_labels(.:format) stores#print_labels
POST /stores/:id/daysheet(.:format) stores#daysheet
After refactoring, I need it to still function as it does now with get routes such as /stores/7/inventory and /stores/18/printer
How can i compress these get routes to accomplish the same routing goals?
One approach would be to do:
resources :stores do
scope module: :stores do
resource :printer, only: [:show]
resource :daysheet, only: [:show, :create]
resource :detailed_daysheet, only: [:show]
resource :inventory, only: [:show, :destroy]
resources :labels, only: [:index]
resources :print_labels, only: [:update]
resource :restore, only: [:update]
end
collection do
get :api
end
member do
get :delete
end
end
Which gives you:
store_printer GET /stores/:store_id/printer(.:format) stores/printers#show
store_daysheet GET /stores/:store_id/daysheet(.:format) stores/daysheets#show
POST /stores/:store_id/daysheet(.:format) stores/daysheets#create
store_detailed_daysheet GET /stores/:store_id/detailed_daysheet(.:format) stores/detailed_daysheets#show
store_inventory GET /stores/:store_id/inventory(.:format) stores/inventories#show
DELETE /stores/:store_id/inventory(.:format) stores/inventories#destroy
store_labels GET /stores/:store_id/labels(.:format) stores/labels#index
store_print_label PATCH /stores/:store_id/print_labels/:id(.:format) stores/print_labels#update
PUT /stores/:store_id/print_labels/:id(.:format) stores/print_labels#update
store_restore PATCH /stores/:store_id/restore(.:format) stores/restores#update
PUT /stores/:store_id/restore(.:format) stores/restores#update
api_stores GET /stores/api(.:format) stores#api
delete_store GET /stores/:id/delete(.:format) stores#delete
stores GET /stores(.:format) stores#index
POST /stores(.:format) stores#create
new_store GET /stores/new(.:format) stores#new
edit_store GET /stores/:id/edit(.:format) stores#edit
store GET /stores/:id(.:format) stores#show
PATCH /stores/:id(.:format) stores#update
PUT /stores/:id(.:format) stores#update
DELETE /stores/:id(.:format) stores#destroy
Naturally, this requires that you create a number of new, nested controllers, such as Stores::Printers which will reside in app/controllers/stores/printers_controller.rb. But, you're now using standard RESTful routes, which I guess some people thing is a good thing.
Also, for your nested routes, you'll have :store_id in the params instead of id.
That collection api and member delete still seem odd, but I'm not sure what the intention is there.
I have a rails api application where I am using Devise gem for user management. I created a user model off the devise gem. After that, I noticed that I have two same routes listed in the rake routescommand. I want POST (/users) to call api/v1/users#create action first and then call devise/registrations#create.
user_registration POST /users(.:format) devise/registrations#create
api_users POST /users(.:format) api/v1/users#create {:format=>:json}
When I test POST (/users) using users_controller_spec file, api/v1/users#create action is called. However, when I do a POST (/users) using POSTMAN, the logs indicates that devise/registrations#createaction is called instead.
How do I correct this so that the POST (/users) I do using POSTMAN or curl calls api/v1/users#create first to create the user model and then calls devise/registrations#create to register the user?
I am not 100% sure how devise works so any help here would be helpful.
This is my config/routes.rb
Rails.application.routes.draw do
devise_for :users
# Api definition
namespace :api, defaults: { format: :json }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
# We are going to list our resources here
resources :users, only: [:show, :create, :update, :destroy]
resources :sessions, only: [:create, :destroy]
end
end
end
So, the thing with Rails Routes is, when you make a request, routes are checked as they are defined in the routes.rb from top to bottom.
Now, when you make a request via POSTMAN, the /users path matches with a path generated via devise_for, as it is the first line in the file.
Now, when you are writing tests for the controller, you are not really accessing /users, you are just telling the api/v1/users_controller to invoke the create method, which is bound to hit the api/v1/users#create
Now, a way you can resolve this conflict is by changing what devise names its routes. If you do something like this:
Rails.application.routes.draw do
devise_for :users, path: 'customer'
# Api definition
namespace :api, defaults: { format: :json }, path: '/' do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: true) do
# We are going to list our resources here
resources :users, only: [:show, :create, :update, :destroy]
resources :sessions, only: [:create, :destroy]
end
end
end
This is what the devise routes will be:
new_user_session GET /customer/sign_in(.:format) devise/sessions#new
user_session POST /customer/sign_in(.:format) devise/sessions#create
destroy_user_session DELETE /customer/sign_out(.:format) devise/sessions#destroy
user_password POST /customer/password(.:format) devise/passwords#create
new_user_password GET /customer/password/new(.:format) devise/passwords#new
edit_user_password GET /customer/password/edit(.:format) devise/passwords#edit
...
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 have the following statement in routes:
resource :users, :only => [:index, :show, :update, :destroy] do
get "users/dashboard"
end
However, when I do rake routes, I got:
dashboard_users GET /users/users/dashboard(.:format) users/users#dashboard
users GET /users(.:format) users#show
PUT /users(.:format) users#update
DELETE /users(.:format) users#destroy
Note that users_path was incorrectly mapped to users#show. I was expecting:
users GET /users/:id/(.:format) users#show
What might cause this issue and how I can fix it?
you should use resources instead of resource. e.g.
resources 'users'
the difference between resource and resources is:
resource is for operations for a single model. when using resource model, there's no "index" action.
see: http://guides.rubyonrails.org/routing.html
further more, I notice that your route is a bit strange. if you just want to add an action named 'dashboard' with GET accessing method, just declare like this:
resources 'users' do
get :dashboard
end
Instead of doing this
resource :users, :only => [:index, :show, :update, :destroy] do
get "users/dashboard"
end
Do this
namespace :dashboard do
resource :users, :only=>[:index,:show,:udpate,:destroy]
end
I think this will work for you.