piotrmurach / loaf

Manages and displays breadcrumb trails in Rails app - lean & mean.
MIT License
407 stars 24 forks source link

Loaf does not work correctly with cancancan gem #32

Closed tomkr4l closed 6 years ago

tomkr4l commented 6 years ago

Describe the problem

When working with cancancan gem, loaf loading breadcrumbs and their paths before load_resource method is called.

Steps to reproduce the problem

class TopicsController < ApplicationController
  load_resource :project #cancacan method
  load_and_authorize_resource through: :project

  breadcrumb @project.name, [@project], only: :index
end

Actual behaviour

@project should be loaded before breadcrumb call, but instead I get NoMethodError undefined method `name' for nil:NilClass and @project is not loaded.

Expected behaviour

@project and @topics should be loaded before breadcrumb method call.

Notes

One method how to solve this problem is to move breadcrumb right inside method e.g.

def index
  breadcrumb @project.name, [@project], only: :index
end

This works fine, but It now allows dry code because I have to repeat same breadcrumbs in all methods where I need to use them.

Describe your environment

piotrmurach commented 6 years ago

Hey Tomáš,

Thanks for using loaf!

I don't belive this is an issue with loaf or even connected to cancancan gem. Let me try to explain. First of all, let's forget about cancancan gem for a minute because it is not relevant to this discussion per say.

The breadcrumb at the controller level is a simple wrapper around before_action. Therfore, for the sake of simplicty, let's assume it means the same. Let's also assume that we have implemented our own load_resource method:

def load_resource
  @project = Project.find(params[:id])
end

Knowing the above, let's simplify your issue to the following:

class TopicsController < ApplicationController
  before_action :load_resource # loads @project similar to what cancancan gem would do
  before_action @project.name  # here @project is nil, because we're at class level!

  def index
     @project.name # this is fine since we're referencing an instance which was created
                   # during load_resource call
  end
end

Please read loaf documentation, in particular 2.1.1 controller section. In there you will see that you can evaluted breadcrumb name in the context of controller instance.

jrochkind commented 5 years ago

So would this work as:

class TopicsController < ApplicationController
  load_resource :project #cancacan method
  load_and_authorize_resource through: :project

  breadcrumb -> { @project.name }, -> { [@project] }, only: :index
end
piotrmurach commented 5 years ago

@jrochkind Provided that the proc is evaluated in the context of controller class instance then this would work.

jrochkind commented 5 years ago

which... it is, right? like, that's how loaf works?

piotrmurach commented 5 years ago

It boils down to Rails and before_actionsince breadcrumb call piggybacks on it. I don't remember off top of my head if that's the case or not.