Rails combine member & collection in routes.rb - ruby-on-rails

I am trying to combine collection and member in the conversations path. But I could not figure it out,
resources :conversations, only: [:index, :show, :destroy] do
member do
post :reply
post :restore
end
end
and;
resources :conversations, only: [:index, :show, :destroy] do
collection do
delete :empty_trash
end
end
When I combine them it does not work, and obviously this one is wrong too!.

Combine member and collection in resources block. Like this,
resources :conversations, only: [:index, :show, :destroy] do
member do
post :reply
post :restore
end
collection do
delete :empty_trash
end
end
Or you may combine it like this also,
resources :conversations, only: [:index, :show, :destroy] do
post :reply, on: :member
post :restore, on: :member
delete :empty_trash, on: :collection
end

try this
resources :conversations, only: [:index, :show, :destroy] do
member {
post :reply
post :restore
}
collection {
delete :empty_trash
}
end

Related

Ruby on Rails - Duplicate routes & impact on controllers

I am creating a Task Manager.
So, I have:
A dashboard where I can do CRUD for tasks (and then I will assign them a group)
A group section where I can go inside a group and do CRUD for tasks
Then my routes would look something like this:
Rails.application.routes.draw do
devise_for :users
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :groups, only: [ :index, :show, :create, :update, :destroy] do
member do
resources :tasks, only: [:index, :show, :create, :update, :destroy]
end
end
resources :tasks, only: [:index, :show, :create, :update, :destroy]
end
end
end
For this:
A dashboard where I can do CRUD for tasks (and then I will assign them a group)
resources :tasks, only: [:index, :show, :create, :update, :destroy]
And for this:
A group section where I can go inside a group and do CRUD for tasks
resources :groups, only: [ :index, :show, :create, :update, :destroy] do
member do
resources :tasks, only: [:index, :show, :create, :update, :destroy]
end
end
But I feel this is duplicating routes. I have 2 questions:
Is this the right approach or would there be a better way to solve it?
If I go with this, how would I approach the controller actions? Since in one situation (2) we would get the params[:group_id] but in the other I would need to add it as strong_params. Would an if-else condition work? (if there is no [:group_id] in strong_params then take the params[:group_id])
Thanks!
Is this the right approach or would there be a better way to solve it?
No. Not really.
Deeply nested routes quickly become cumbersome to work with so you should consider using shallow nesting. This only nests the collection routes (index, new, create) and not the member routes (show, edit, update, destroy).
If tasks have a unique id you should be able to show, edit, update and destroy them without the group being involved. The exception to this rule is if tasks are only unique per group (but why?).
In an API this is especially true. The backend shouldn't need to know that you're modifying the resource from page X or Y in your frontend application.
You can use the shallow: true option to generate shallow routes but I would probally just define it as:
# This respresents tasks not nested in a group
resources :tasks, only: [:index, :show, :create, :update, :destroy]
resources :groups, only: [:index, :show, :create, :update, :destroy] do
resources :tasks, only: [:index, :create]
end
If I go with this, how would I approach the controller actions?
There are really two different approaches here. One I call the "param sniffing method":
module API
module V1
class TasksController
# GET /api/v1/groups/1/tasks
# GET /api/v1/tasks
def index
if params[:group_id]
render json: Group.find(params[:group_id]).tasks
else
render json: Task.all
end
end
end
end
end
This is the most obvious solution but blatantly violates the Single Responsibity Principle and increases the cyclic complexity of all the methods.
The other solution is to route the nested representation of the resource to a separate controller:
# This respresents tasks not nested in a group
resources :tasks, only: [:index, :show, :create, :update, :destroy]
resources :groups, only: [ :index, :show, :create, :update, :destroy] do
resources :tasks, only: [:index, :create], module: :groups
end
This routes the /groups/:group_id/tasks routes into API::V1::Groups::TasksController.
module API
module V1
class TasksController
# GET /api/v1/tasks
def index
render json: Task.all
end
end
end
end
module API
module V1
module Groups
class TasksController
before_action :set_group
# GET /api/v1/groups/1/tasks
def index
render json: #group.tasks
end
private
def set_group
#group = Group.find(params[:group_id])
end
end
end
end
end
You could also just do resources :tasks, only: [:index, :create], controller: :group_tasks if you want to avoid nesting the contants one more step.

Renaming rails route parameter to id

In my routes.rb file, I have the following resources
resources :classrooms, only: [:index, :new, :create] do
resources :students, only: [:index, :edit, :update], controller: 'students' do
get :honor
end
end
My honor route becomes /classrooms/:classroom_id/students/:students_id/honor(.:format)
but I want to replace student_id to id so that I can easily use it in the students controllers.
Ideal route is /classrooms/:classroom_id/students/:id/honor(.:format)
You can do that specifying it's a member route. this way
get :honor, on: :member
or
member do
get :honor
end

Certain namespaced routes being ignored

I have the following routes
resources :eclubs, except: [:show]
namespace :eclubs do
resources :leaders, only: [:index, :show, :new, :create, :destroy]
resources :members, only: [:index, :show, :new, :create, :destroy]
end
However, /eclubs/members does not routes to the index action of the Eclubs::Members controller. Instead it routes to the show action of the Eclubs controller. How do I fix this?
Because it is declared first, the Eclubs controller is being given precedence over the Eclubs::Members. Instead declare the Eclubs controller's routes last e.g.
namespace :eclubs do
resources :leaders, only: [:index, :show, :new, :create, :destroy]
resources :members, only: [:index, :show, :new, :create, :destroy]
end
resources :eclubs, except: [:show]

Nesting APIs routes

Im trying to create API endpoints from my Post model . It should be able to read, update, delete and create posts.
But a post needs to be assosiated to a Topic in order for it to be created so i nested it under topics. Here are my routes
namespace :api do
namespace :v1 do
resources :users, only: [:index, :show, :create, :update]
resources :posts, only: [:show, :update, :destroy]
resources :topics, except: [:edit, :new] do
resources :posts, only: [:create]
end
end
end
api_v1_post GET /api/v1/posts/:id(.:format) api/v1/posts#show
PATCH /api/v1/posts/:id(.:format) api/v1/posts#update
PUT /api/v1/posts/:id(.:format) api/v1/posts#update
DELETE /api/v1/posts/:id(.:format) api/v1/posts#destroy
api_v1_topic_posts POST /api/v1/topics/:topic_id/posts(.:format) api/v1/posts#create
api_v1_topics GET /api/v1/topics(.:format) api/v1/topics#index
POST /api/v1/topics(.:format) api/v1/topics#create
api_v1_topic GET /api/v1/topics/:id(.:format) api/v1/topics#show
PATCH /api/v1/topics/:id(.:format) api/v1/topics#update
PUT /api/v1/topics/:id(.:format) api/v1/topics#update
DELETE /api/v1/topics/:id(.:format) api/v1/topics#destroy
my issue is im getting a "no routes match" error when i run my Rspec
I think the issue is with the relationship.
Please add the relationship between Topic and Post and then try.
class Topic < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :topic
end
If the route you are trying to create is POST api/v1/topics/:topic_id/create_post going to TopicsController than you would want the nesting to look like this instead:
resources :topics, except: [:edit, :new] do
post 'create_post' => 'topics#create_post'
end
Try this out for creating your routes if you haven't had success yet. It should provide the output that you want.
resources :topics, except: [:edit, :new] do
collection do
post 'create_post'
end
end

Add a common route for multiple resources

How do I add the /search endpoint to all these resources in one go?
MyCoolApp::Application.routes.draw do
resources :posts, only [:index, :show, :create] do
collection { get :search }
end
resources :authors, only [:index, :show] do
collection { get :search }
end
resources :comments, only: [:index, :show] do
collection { get :search }
end
resources :categories, only: [:index, :show] do
collection { get :search }
end
resources :tags, only: [:index] do
collection { get :search }
end
end
I saw this answer which is close, but I need to be able to specify the actions for reach resource. Is there a better way to inject the search route in a better way?
%w(one two three four etc).each do |r|
resources r do
collection do
post 'common_action'
end
end
end
Something like this?
resources :posts, only [:index, :show, :create, :search]
I know you've tagged this question ruby-on-rails-3, but I'm going to provide the Rails 4 solution for anyone who comes across this in the future.
Rails 4 introduces Routing Concerns
concern :searchable do
collection do
get :search
end
end
resources :posts, only [:index, :show, :create], concerns: [:searchable]
resources :authors, only [:index, :show], concerns: [:searchable]
resources :comments, only: [:index, :show], concerns: [:searchable]
resources :categories, only: [:index, :show], concerns: [:searchable]
resources :tags, only: [:index], concerns: [:searchable]

Resources