4

Trailblazer recommends injecting current_user into the cell, like this

<%= concept(Appointment::Cell::UserStatus,user,current_user: current_user) %>

One could then make it accessible within the cell using a method

  def current_user
    options[:current_user]
  end

However, this means adding this injection on every call to a cell, when in fact current_user is a special global var.

I've managed to get around having to inject current_user by creating a superclass cell, like this:

class Template::Cell < Cell::Concept
  include Pundit
  include Devise::Controllers::Helpers

  Devise::Controllers::Helpers.define_helpers(Devise::Mapping.new(:user, {}))
  include Escaped
  include ActionView::Helpers::JavaScriptHelper
end

This works well, except I cannot work out to sign in a user during RSpec testing. The usual controller-based method doesn't work as there is no access to requests.

By the way, I tried the "Devise mocking" solution, but it didn't work:

RSpec.describe Friendship::Cell, type: :cell do
  include Devise::TestHelpers
  include ControllerHelpers
  let!(:user) { create :user }

  describe '#methods' do
    before do
      sign_in(user)
    end
    subject { concept(described_class, user, current_user: user) }
    it { expect(current_user).to eq(user) }
  end
end

module ControllerHelpers
  def sign_in(user = double('user'))
    if user.nil?
      allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, {:scope => :user})
      allow(controller).to receive(:current_user).and_return(nil)
    else
      allow(request.env['warden']).to receive(:authenticate!).and_return(user)
      allow(controller).to receive(:current_user).and_return(user)
    end
  end
end

Error

1) Friendship::Cell cell can be instantiated
     Failure/Error: @request.env['action_controller.instance'] = @controller

     NoMethodError:
       undefined method `env' for nil:NilClass
     # /Users/sean/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/devise-4.3.0/lib/devise/test/controller_helpers.rb:40:in `setup_controller_for_warden'
     # /Users/sean/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/rspec-rails-3.6.0/lib/rspec/rails/adapters.rb:165:in `block (2 levels) in setup'
port5432
  • 5,889
  • 10
  • 60
  • 97

1 Answers1

0

Documentation https://trailblazer.to/2.0/gems/cells/getting-started.html#discussion-navigation says:

It is important to understand that the cell has no access to global state. You as the cell author have to define the interface and the dependencies necessary to render the cell.

In contrast to your words:

However, this means adding this injection on every call to a cell, when in fact current_user is a special global var.

I think you misunderstand the goal of Trailblazer cells. They are not supposed to have access to the global state of the app. They are meant as separate entities, that rely on dependency injection.

If you want cells to have access to the global state, you might actually be looking for a different tool for that. Maybe https://github.com/github/view_component will fit you more, they are somewhat similar to cells, but more accessible for rails integration with rails, while trailblazer treats rails as optional.

https://trailblazer.zulipchat.com/ if anyone stumbles upon this topic and wants to ask the core team directly about this approach, they are pretty active on zulipchat but knowing them, they will simply advise against doing so, since it is against what the tool was designed for, as specified by the documentation.

kkp
  • 436
  • 4
  • 11