Skip to content

Doesn't work with spring #26

@TylerRick

Description

@TylerRick

Steps to reproduce

  1. Check out a Rails app that has spring inserted in the binstubs — for example, this example app:
    https://github.com/TylerRick/exception_notification-rake_example_with_spring/tree/workaround_using_load_in_bin_rake

  2. Run bin/rake check:exception

  3. Observe that no email is sent

How it's supposed to work

Normally (without spring), what happens is:

  • the bin/rake binstub gets loaded first, and
  • that is what triggers rake.rb and rake/rake_module.rb (which defines Rake.application) to get loaded.
  • Then Bundler.require loads exception_notification-rake gem, which checks that Rake.application is defined and patches Rake::Application#display_error_message

This is evident from the following backtraces taken at those 2 points:

"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/rake_module.rb:2:in `<top (required)>'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake.rb:44:in `require'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake.rb:44:in `<top (required)>'",
  require "rake/rake_module"
"bin/rake:8:in `require'",
"bin/rake:8:in `<main>'"]
  require 'rake'

(or if using global rake instead of bin/rake binstub:

"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/rake_module.rb:4:in `<top (required)>'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake.rb:44:in `<top (required)>'",
  require "rake/rake_module"
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/exe/rake:25:in `<top (required)>'",
"~/.gem/ruby/2.6.0/bin/rake:23:in `load'",
"~/.gem/ruby/2.6.0/bin/rake:23:in `<main>'"]
  load Gem.activate_bin_path('rake', 'rake', version)

)

"exception_notification-rake/lib/exception_notifier/rake/rake_patch.rb:5:in `<top (required)>'",
"exception_notification-rake/lib/exception_notifier/rake.rb:2:in `require'",
"exception_notification-rake/lib/exception_notifier/rake.rb:2:in `<top (required)>'",
"exception_notification-rake/lib/exception_notification/rake.rb:3:in `require'",
"exception_notification-rake/lib/exception_notification/rake.rb:3:in `<top (required)>'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler/runtime.rb:95:in `require'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler/runtime.rb:95:in `rescue in block in require'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler/runtime.rb:72:in `block in require'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler/runtime.rb:65:in `each'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler/runtime.rb:65:in `require'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler.rb:114:in `require'",
"example_app/config/application.rb:7:in `<top (required)>'",
  Bundler.require(*Rails.groups)
"example_app/Rakefile:4:in `require_relative'",
"example_app/Rakefile:4:in `<top (required)>'",
  require_relative 'config/application'
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/rake_module.rb:31:in `load'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/rake_module.rb:31:in `load_rakefile'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/application.rb:703:in `raw_load_rakefile'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/application.rb:104:in `block in load_rakefile'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/application.rb:186:in `standard_exception_handling'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/application.rb:103:in `load_rakefile'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/application.rb:82:in `block in run'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/application.rb:186:in `standard_exception_handling'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/application.rb:80:in `run'",
"bin/rake:10:in `<main>'"]

How it works instead when using spring

But when using spring:

  • spring's preload loads exception_notifier-rake gem before bin/rake is loaded
  • exception_notification-rake checks if Rake.application is defined; since it is not, it doesn't patch Rake::Application#display_error_message and so this gem has no effect
  • Then the bin/rake binstub gets loaded,
  • which triggers rake.rb and rake/rake_module.rb (which defines Rake.application) to get loaded (too late).

Again, you can see this from the backtraces if you wish:

"exception_notification-rake/lib/exception_notifier/rake/rake_patch.rb:5:in `<top (required)>'",
"exception_notification-rake/lib/exception_notifier/rake.rb:2:in `require'",
"exception_notification-rake/lib/exception_notifier/rake.rb:2:in `<top (required)>'",
"exception_notification-rake/lib/exception_notification/rake.rb:3:in `require'",
"exception_notification-rake/lib/exception_notification/rake.rb:3:in `<top (required)>'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler/runtime.rb:95:in `require'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler/runtime.rb:95:in `rescue in block in require'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler/runtime.rb:72:in `block in require'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler/runtime.rb:65:in `each'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler/runtime.rb:65:in `require'",
"~/.rubies/ruby-2.6.0/lib/ruby/2.6.0/bundler.rb:114:in `require'",
"example_app/config/application.rb:7:in `<top (required)>'",
  Bundler.require(*Rails.groups)
"ruby/2.6.0/gems/spring-2.0.2/lib/spring/application.rb:92:in `require'",
"ruby/2.6.0/gems/spring-2.0.2/lib/spring/application.rb:92:in `preload'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake/rake_module.rb:2:in `<top (required)>'",
"~/.rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/rake-12.3.2/lib/rake.rb:44:in `<top (required)>'",
"example_app/bin/rake:8:in `<top (required)>'",
  require 'rake'
"~/.gem/ruby/2.6.0/gems/spring-2.0.2/lib/spring/command_wrapper.rb:40:in `call'",
"~/.gem/ruby/2.6.0/gems/spring-2.0.2/lib/spring/application.rb:201:in `block in serve'",

Workaround

One workaround is to add this line to bin/rake:

 require 'rake'
+load 'exception_notifier/rake/rake_patch.rb'
 Rake.application.run

But a better solution is needed that doesn't require modifying the binstubs that spring automatically generated...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions