Rails - Clean way to mix nested+non-nested or member routes?

397 Views Asked by At

New to programming in general and Rails specifically.

TLDR: Is there a clean way of using a variety of member actions or controlling flow in controllers for nested resources? Is there a third option? I have read through Rails Routing from the Outside In and related stackoverflow posts, and haven't reached a satisfactory conclusion, so any help is appreciated.

Situation: I would like for my users to be able to see all locations, and to see all locations in a specified group. (Locations also have groups, users have groups, etc).

Initially, I defined the routes and actions:

resources :groups do
    member do
      get :locations
    end
  end
resources :locations do
   member do
    get :groups
  end
end

This worked fine, but I also need users to be able to create location in a specific group, etc. I could define more member routes, e.g.

resources :groups do
  member do
    get :locations_index
    get :location_new
  end
end

resources :locations do
...
end

I have considered replacing the above with mixed nested and non-nested resources:

resources :groups do
  resources :locations
end
resources :locations
etc.

The problem is that I am concerned the controllers will need a lot of flow control to ensure users see what I want them to see, can create locations in groups or not in groups, and so on.

Using before filters would help, but my controllers would still be much fatter than I'd like. So, as above, is there a clean way of doing this?

2

There are 2 best solutions below

1
Jeanro On

Try to avoid non REST routes. You can have a look to this blog post http://jeromedalbert.com/how-dhh-organizes-his-rails-controllers/

0
max On

You can use the module option to route the nested resources to a separate controller:

resources :locations, only: [:index]
resources :groups do
  resources :locations,
    only: [:index],
    module: :groups
end

class LocationsController  < ApplicationController
  # Displays all locations
  # GET /locations
  def index
    @locations = Location.all
  end
end

module Groups
  class LocationsController < ApplicationController
    before_action :set_group

    # Displays the locations of a group
    # GET /groups/1/locations
    def index
      @locations = @group.locations
    end

    private 

    def set_group
      @group = Group.find(params[:group_id])
    end
  end
end

It can be passed to most of the routing macros such as resource/resources, scope, namespace, member and collection.

This keeps the controllers lean since they are only handling a single representation of a resource.

The problem is that I am concerned the controllers will need a lot of flow control to ensure users see what I want them to see, can create locations in groups or not in groups, and so on.

Thats really the job of a separate authentication layer such as Pundit.