I have been playing around with Rails again for the last couple of days.
I'm unsure if I should nest a resource or not, and how to determine it.
Let's pretend I have an application where users can sign up and make a todo list. They can choose to make these lists public or private.
Where I'm running into trouble is if I should nest the lists under the user resource:
If I do that, I end up with something like:
/users/:id/lists
/users/:id/lists/:list_id
But what if I want to say, have a view with all public lists under /lists
? That would interfere with the restful-ness of the routes, no?
I could not nest the lists and have separate routes for users and lists, but then I run into trouble again, in my mind at least.
/users/:id # some profile page
/lists # should this link to all lists or all the current user's lists?
But then, if I want to show a specific user's lists, I don't know what my route would be.
Is there a way to combine this somehow? Nest the lists under the user resource, but still have the /lists
lists_path? Or would that just be a custom named route?
I'm interested in how people who are more proficient than me in Rails would tackle this.
You can have both a nested and non-nested lists.
resources :lists
will create all of the restful routes.
resources :users do
resources :lists
end
will then create all of the nested routes and associated helpers. I wouldn't use nested routes in this scenario, because your controller is going to be a shitshow.
Nested routes (let's say users/7/lists) by default routes to ListsController#index
, but you also have /lists that is going to ListsController#index
. So you can add some logic to your controller:
ListsController < ApplicationController
def index
if params[:user_id].present?
# I'm in a user_list
else
# I'm in a public list
end
end
end
Ugh.
Okay how about a before_filter?
ListsController < ApplicationController
before_filter :unjankify_nested_routes
private
def unjankify_nested_routes
if params[:user_id].present?
# I'm in a user_list
render "users_#{@action}"
else
# I'm in a public list
end
end
end
Now we can basically scope the controller methods by saying users_show, users_index etc. But what is that @action variable? We need to capture that.
class ApplicationController
def process_action(name, *args)
@action_name = name
super rescue false #no super in tests
end
end
We just overrode an internal Rails method to capture the action name. That's fun, but kind of seems like we're doing something wrong.
There is no 'right way' but scopes are a good option
scope 'privates' do
resources :lists
end
Now we can have a Privates::ListsController
in app/controllers/privates/lists_controller
. you can add the user_id in the query params, or nest routes under the scope:
scope 'privates' do
resources :users do
resources :lists
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With