I have resources that can optionally be accessed through another resources, like so
resources :projects do
resources :tasks
end
resources :tasks
If tasks are accessed through project, I want to render some project-related info, and that is pretty much all the difference.
However, I use cancan and need to authorize all the things so in TasksController I write
load_and_authorize_resource :project
load_and_authorize_resource :task, through: :project
but this breaks functionality when we don't nest tasks.
How can I solve this elegantly?
My first thought was to use two controllers instead of TasksController and share all the common things using concerns but that's kind of messy (at least I would have to explicitly specify views).
Another approach I can think of is to authorize things manually instead of using cancan helpers.
Are there some other ways?
I'd limit the routes that can be accesible without the project scope:
resources :projects do
resources :tasks
end
resources :tasks, only: :index
Then you might call the cancan helper method for the rest of actions and handle the custom loading and authorizing on the index method:
class TasksController < ActionController::Base
load_and_authorize_resource :project, except: :index
load_and_authorize_resource :task, through: :project, except: :index
def index
authorize! :index, Task
if params[:project_id].present?
@project = Project.find(params[:project])
authorize! :show, @project
@tasks = @project.tasks.accessible_by(current_ability)
else
@tasks = Task.accessible_by(current_ability)
end
end
Note: if you want to handle new/create/edit/update actions for both resources, it will require custom views and will be easier with two controllers.