Git Product home page Git Product logo

Comments (21)

ckhall avatar ckhall commented on June 21, 2024 2

experiencing the same problem after upgrading from 2.2.0 to 2.2.1

test_helper.rb

# other requires
require "minitest-matchers"
require "email_spec"
# other requires

class ActiveSupport::TestCase
  # other includes
  include EmailSpec::Helpers
  include EmailSpec::Matchers
  # other includes
end

tests using reset_mailer fail with the error:

NameError: undefined local variable or method `reset_mailer' for #<#<Class:0x00007f7aa5aeee90>:0x00007f7aa2d4eb20>

attempted to move includes to top/bottom of include list in class ActiveSupport::TestCase as well as adding includes within the tests themselves but error still raised. reverting to 2.2.0 fixes the issue.

require "test_helper"

class SomeTest < ActiveSupport::TestCase
  describe "a test" do
    it "does a thing" do
      reset_mailer
    end
  end
end

from email-spec.

cgunther avatar cgunther commented on June 21, 2024 2

Thanks, I can reproduce it. Looks like my projects have some other gems that also eager load actionmailer (what #219 corrected in this gem), hiding this problem for me.

I understand why it's happening, though I'm not sure what the fix is at the moment. Here's a summary incase anyone else has any ideas in the meantime.

At the point you require "email_spec/rspec", actionmailer is not yet loaded, so when the helpers are included into RSpec, this gem hasn't yet included the MailerDeliveries module that defines reset_mailer into the EmailSpec::Helpers module.

Later, actionmailer is loaded, which now includes the EmailSpec::MailerDeliveries module into the EmailSpec::Helpers module.

However, Ruby doesn't seem to apply future includes into classes/modules the parent was previously included. Therefore, it's as if RSpec is frozen with the EmailSpec::Helpers module WITHOUT EmailSpec::MailerDeliveries, since that was the state of the module at the time it was included into RSpec.

This seems to be a quirk of Ruby, nothing specific to RSpec, as demonstrated by this script. Even though we've included B into A, which itself is included in Foo, since B was included in A AFTER A was included in Foo, Foo doesn't seem to treat B as being an ancestor, hence the error when we call a method defined in B. If you move the A.include(B) line above the Foo class definition, then it works.

I'll give some thought to what a fix could be. #219 corrected a real issue, so I don't think simply reverting it is the right fix. If actionmailer is loaded too early, then when you try to enable actionmailer configs in a new_framework_defaults file of Rails, they don't actually take effect, as actionmailer was loaded before your initializer, and therefore it already copied all the configuration internally, and any other changes to configuration in an initializer via Rails.application.config have no effect.

from email-spec.

ckhall avatar ckhall commented on June 21, 2024

fails too:

gem "email_spec", git: "https://github.com/email-spec/email-spec"

works:

gem "email_spec", "2.2.0"

from email-spec.

ut avatar ut commented on June 21, 2024

I can confirm this issue (Rails 5)

from email-spec.

gravitystorm avatar gravitystorm commented on June 21, 2024

Other than pinning to 2.2.0, does anyone have any workarounds, or any insight as to what has caused this and what might be the solution?

from email-spec.

alec-c4 avatar alec-c4 commented on June 21, 2024

@etagwerker and @cgunther may i ask you to check this issue?

from email-spec.

alec-c4 avatar alec-c4 commented on June 21, 2024

it looks like this commit 58f94e2 is the source of the problem

from email-spec.

cgunther avatar cgunther commented on June 21, 2024

What config.action_mailer.delivery_method is set in your test environments? Mine is :test and I've had no issues running with that across multiple projects.

There's a separate codepath for :activerecord, so maybe the issue is there if you all coincidentally are using that delivery method? The test suite doesn't seem to cover that path, which could hide an issue. I'm not even seeing that Rails/ActionMailer supports :activerecord out of the box, so is that coming from an external gem?

from email-spec.

gravitystorm avatar gravitystorm commented on June 21, 2024

What config.action_mailer.delivery_method is set in your test environments? Mine is :test and I've had no issues running with that across multiple projects.

I have the delivery_method as :test on both my affected apps.

If there's anything I can do to help debug this, please let me know. One of my apps is an open-source prototype, and it's possible to use that to recreate the error - https://gitlab.com/gravitystorm/coppermill/ - if you release the pin in the Gemfile, run bundle update, then bundle exec rspec will fail.

from email-spec.

RobertGauld avatar RobertGauld commented on June 21, 2024

However, Ruby doesn't seem to apply future includes into classes/modules the parent was previously included. Therefore, it's as if RSpec is frozen with the EmailSpec::Helpers module WITHOUT EmailSpec::MailerDeliveries, since that was the state of the module at the time it was included into RSpec.

This seems to be a quirk of Ruby, nothing specific to RSpec, as demonstrated by this script. Even though we've included B into A, which itself is included in Foo, since B was included in A AFTER A was included in Foo, Foo doesn't seem to treat B as being an ancestor, hence the error when we call a method defined in B. If you move the A.include(B) line above the Foo class definition, then it works.

This works so it's not completely stupid, but it does feel a bit yucky and I'm not sure why.

module A
end

module B
  def bar
    'bar'
  end
end

class Foo
  include A

  def method_missing(method_name, *args, **kwargs, &block)
    puts "method_missing(#{method_name.inspect}, #{args.inspect}, #{kwargs.inspect})"
    if method_name == :bar
      Foo.include A
      send method_name, *args, **kwargs, &block
    else
      super
    end
  end
end

A.include(B)

puts Foo.new.bar
puts Foo.new.bar
# undefined method `bar' for #<Foo:0x00007fc43e0d4940> (NoMethodError)
method_missing(:bar, [], {})
bar
bar
11a12,21
> 
>   def method_missing(method_name, *args, **kwargs, &block)
>     puts "method_missing(#{method_name.inspect}, #{args.inspect}, #{kwargs.inspect})"
>     if method_name == :bar
>       Foo.include A
>       send method_name, *args, **kwargs, &block
>     else
>       super
>     end
>   end
15a26
> puts Foo.new.bar

from email-spec.

cgunther avatar cgunther commented on June 21, 2024

Unfortunately I don't think that would work, that was an oversimplified example, but thanks for the idea, seemed like it had some promise as I went down that path.

In our case, when method_missing gets called, we'd have to include the appropriate deliveries module (and re-call the method), but wrapped in the ActiveSupport.on_load(:action_mailer) block to avoid prematurely loading ActionMailer, which means method_missing could return before that block gets called to re-call the method (ie. reset_mailer).

Spending some more time on this, I'm still stumped. ActionMailer is lazily loaded in tests, so at the point reset_mailer is called in the before hook (using @gravitystorm's app) ActionMailer may not even be loaded yet (it could load later in the process of running the test). I'm starting to wonder if calling methods like reset_mailer need to trigger loading ActionMailer itself, rather than waiting on Rails to load it, but still not simply upon requiring this library (as was the case prior to #219).

from email-spec.

axlekb avatar axlekb commented on June 21, 2024

I just started encountering this issue but I have 2.2.1 installed since September working.
I found that it started happening when I upgraded premailer-rails from 1.11.1 to 1.12.0. Hopefully that's a useful data point to someone.

from email-spec.

cgunther avatar cgunther commented on June 21, 2024

premailer-rails v1.12.0 fixed eagerloading ActionMailer (fphilipe/premailer-rails#260). Very similar in concept to what my PR introducing this issue here intended to do. Eager loading ActionMailer can cause headaches in the future, namely around trying to configure it via initializers.

In premailer-rails v1.11.1, ActionMailer was eagerly loaded, so email-spec could hook in properly, even with email-spec v2.2.1. With premailer-rails v1.12.0, ActionMailer is no longer eager loaded, and now email-spec seems to try hooking in too late.

I'm still rather stumped on the proper fix here. Before my PR, email-spec caused ActionMailer to be loaded too early, causing problems, and now ActionMailer is loaded too late, causing different problems.

from email-spec.

theycallmeswift avatar theycallmeswift commented on June 21, 2024

Did this ever get resolved? We are seeing the same intermittent error where a test gets run before ActionMailer is loaded, resulting in the error below:

NameError: undefined local variable or method `reset_mailer' 

Would be great to have a recommended official fix.

from email-spec.

alec-c4 avatar alec-c4 commented on June 21, 2024

Hey! Any news with this issue? @etagwerker @bmabey @cgunther

from email-spec.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.