trailblazer-rails: breaking bug in ApplicationController extending when alias_method is used

We decided to try out Trailblazer in earnest, and immediately hit a blocker issue.

Background

We have an alias in ApplicationController that is redefining current_user to add extra functionality:

alias devise_current_user current_user
def current_user
  # ...
end

Issue

When adding trailblazer-rails to the Gemfile and trying to boot, we get this error:

project_dir/app/controllers/application_controller.rb:27:in `<class:ApplicationController>': undefined method `current_user' for class `ApplicationController' (NameError)
  from project_dir/app/controllers/application_controller.rb:26:in `<class:ApplicationController>'
  from project_dir/app/controllers/application_controller.rb:1:in `<top (required)>'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:477:in `load'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:477:in `block in load_file'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:662:in `new_constants_in'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:476:in `load_file'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:375:in `block in require_or_load'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:37:in `block in load_interlock'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies/interlock.rb:12:in `block in loading'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/concurrency/share_lock.rb:150:in `exclusive'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies/interlock.rb:11:in `loading'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:37:in `load_interlock'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:358:in `require_or_load'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:511:in `load_missing_constant'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:203:in `const_missing'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/inflector/methods.rb:268:in `const_get'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/inflector/methods.rb:268:in `block in constantize'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/inflector/methods.rb:266:in `each'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/inflector/methods.rb:266:in `inject'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/inflector/methods.rb:266:in `constantize'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/core_ext/string/inflections.rb:66:in `constantize'
  from ~/.rvm/gems/ruby-2.2.4/gems/trailblazer-rails-1.0.4/lib/trailblazer/rails/railtie.rb:75:in `extend_application_controller!'
  from ~/.rvm/gems/ruby-2.2.4/gems/trailblazer-rails-1.0.4/lib/trailblazer/rails/railtie.rb:44:in `block (2 levels) in <class:Railtie>'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/lazy_load_hooks.rb:43:in `instance_eval'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/lazy_load_hooks.rb:43:in `execute_hook'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/lazy_load_hooks.rb:33:in `block in on_load'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/lazy_load_hooks.rb:32:in `each'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/lazy_load_hooks.rb:32:in `on_load'
  from ~/.rvm/gems/ruby-2.2.4/gems/trailblazer-rails-1.0.4/lib/trailblazer/rails/railtie.rb:43:in `block in <class:Railtie>'
  from ~/.rvm/gems/ruby-2.2.4/gems/railties-5.0.1/lib/rails/initializable.rb:30:in `instance_exec'
  from ~/.rvm/gems/ruby-2.2.4/gems/railties-5.0.1/lib/rails/initializable.rb:30:in `run'
  from ~/.rvm/gems/ruby-2.2.4/gems/railties-5.0.1/lib/rails/initializable.rb:55:in `block in run_initializers'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/2.2.0/tsort.rb:226:in `block in tsort_each'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/2.2.0/tsort.rb:348:in `block (2 levels) in each_strongly_connected_component'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/2.2.0/tsort.rb:429:in `each_strongly_connected_component_from'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/2.2.0/tsort.rb:347:in `block in each_strongly_connected_component'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/2.2.0/tsort.rb:345:in `each'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/2.2.0/tsort.rb:345:in `call'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/2.2.0/tsort.rb:345:in `each_strongly_connected_component'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/2.2.0/tsort.rb:224:in `tsort_each'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/2.2.0/tsort.rb:203:in `tsort_each'
  from ~/.rvm/gems/ruby-2.2.4/gems/railties-5.0.1/lib/rails/initializable.rb:54:in `run_initializers'
  from ~/.rvm/gems/ruby-2.2.4/gems/railties-5.0.1/lib/rails/application.rb:352:in `initialize!'
  from project_dir/config/environment.rb:5:in `<top (required)>'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `require'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `block in require'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:259:in `load_dependency'
  from ~/.rvm/gems/ruby-2.2.4/gems/activesupport-5.0.1/lib/active_support/dependencies.rb:293:in `require'
  from ~/.rvm/gems/ruby-2.2.4/gems/spring-2.0.2/lib/spring/application.rb:102:in `preload'
  from ~/.rvm/gems/ruby-2.2.4/gems/spring-2.0.2/lib/spring/application.rb:153:in `serve'
  from ~/.rvm/gems/ruby-2.2.4/gems/spring-2.0.2/lib/spring/application.rb:141:in `block in run'
  from ~/.rvm/gems/ruby-2.2.4/gems/spring-2.0.2/lib/spring/application.rb:135:in `loop'
  from ~/.rvm/gems/ruby-2.2.4/gems/spring-2.0.2/lib/spring/application.rb:135:in `run'
  from ~/.rvm/gems/ruby-2.2.4/gems/spring-2.0.2/lib/spring/application/boot.rb:19:in `<top (required)>'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:55:in `require'
  from ~/.rvm/rubies/ruby-2.2.4/lib/ruby/site_ruby/2.2.0/rubygems/core_ext/kernel_require.rb:55:in `require'
  from -e:1:in `<main>'

Possible fix?

It appears that the loading system in lib/trailblazer/rails/railtie.rb isn’t properly extending ApplicationController. It looks like #11 may have been related, but it is quite out of date.

Attempted fix

If we comment out:

# lib/trailblazer/rails/railtie.rb:41-45
initializer 'trailblazer.application_controller' do |_app|
  reloader_class.to_prepare do
    # Trailblazer::Railtie.extend_application_controller!(app)
  end
end

And manually load the modules:

class ApplicationController < ActionController::Base
  include Trailblazer::Rails::Controller
  include Trailblazer::Rails::Controller::Cell if defined?(::Cell)

It works fine.

Summary

It looks like when the ApplicationController class is forced to constantize in extend_application_controller!, Rails hasn’t fully loaded the class, so the devise current_user method hasn’t been defined yet.

I would PR this, but I couldn’t find consistent answer online on how this should be properly loaded.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 18 (9 by maintainers)

Commits related to this issue

Most upvoted comments

@sadjow Really? That happens without spring? Then we should investigate. We don’t have this problem here, are you using anything “crazy”?

And dropped the temperature of the Earth! Move over Al Gore, we’re cloning Genghis Khan to solve climate change.

Rubocop stubbornly complains about alias_method, but I did managed to break the law and try alias_method instead. I also tried putting it into a module and include-ing, which also didn’t work.

I expect that a prepend might work, but prepend sucks when you need to call the original method outside of the prepended method (method(:current_user).super_method.call()), and at that point we’re writing weird code to work around what seems like a library bug (sorry 🤞).

Great bug report, I wish everyone could formulate that well and concise.

Maybe a fix could be to add one of the million :after options to initializer 'trailblazer.application_controller' so this gets executed at the right time? I’m not very savvy on Rails initialization and honestly have no interest in learning more about it, but maybe @seuros knows a solution?

By the way, be prepared to hit more immediate blockers with TRB, spring doesn’t play with modern gems like Dry.rb and TRB, and the autoloading mixed with ours can lead to having to require things manually sometimes, etc etc.