ActionMailer -- NoMethodError: undefined method X for MyMailer:Class - ruby-on-rails

Question: Why is the method undefined if it's just there?
Details:
I have a very simple mailer class:
class ProductMailer < ApplicationMailer
def sample_email
mail(to: "me#example.com") # I hardcode my own email just to test
end
end
And a very simple call from ProductsController:
def sample_email
ProductMailer.sample_email().deliver_later
redirect_to #product, notice: 'Email was queued.'
end
The email fails to be sent. I am using Sidekiq to process emails in background. The Sidekiq Web UI shows failed jobs in the Tries page and I can see why it failed:
NoMethodError: undefined method `sample_email' for ProductMailer:Class
I tried to rename the method and restart the server with rails server but none of that removes the error. I am not using any namespaces.
Question: Why is the method undefined if it's just there?
Note: I found out by chance that the method is found if I name it notifybut maybe that's because I'm overwriting some method from ActionMailer base class, I don't know.

Answer: Restart Sidekiq
I created the mailer class before starting Sidekiq, but I renamed the sample_email method while Sidekiq was already running, so it seems that Sidekiq doesn't recognize new methods on-the-fly.
I renamed the method because I am used to development environment, where you can change almost anything on the fly...

It's because you've defined an instance method, and then you try to call it on a class. Change it to
def self.sample_email
....

Related

ActionMailer rescue_from failing when called from ActiveJob dispatched to Sidekiq

I use Postmark for sending email and was looking to capture Postmark::InactiveRecipientError which currently just ends up in my dead jobs in my Sidekiq queue. I found on the postmark-rails wiki, what I thought would be the correct course: https://github.com/wildbit/postmark-rails/wiki/Error-Handling
class ApplicationMailer < ActionMailer::Base
default from: "user#stackoverflow.com"
layout 'mailer'
rescue_from Postmark::InactiveRecipientError, with: :reactivate_and_retry
private
def postmark_client
::Postmark::ApiClient.new(Rails.application.credentials.postmark_api_token)
end
def reactivate_and_retry(error)
error.recipients.each do |recipient|
bounce = postmark_client.bounces(emailFilter: recipient).first
next unless bounce
postmark_client.activate_bounce(bounce[:id])
end
# Try again immediately
message.deliver
end
end
When I test the above in development, everything works. My UserMailer goes to Sidekiq which kicks off and correctly activates a suppressed email which I can see the change made via the Postmark interface. However, in production, my jobs are failing with:
undefined method 'reactivate_and_retry' for class '#<Class:UserMailer>'
I haven't been able to find any resources -- this GitHub Issue seems to be a similar problem but doesn't have much for information -- https://github.com/mperham/sidekiq/issues/4161
I am using Rails 6.1.4.4 and Sidekiq 6.4.0.
I am having the same problem. It seems like nobody has this problem.
The problem is that the job calls handle_exception_with_mailer_class and this doesn't have an instance of mailer so it tries to call a class level method. If you define it as a class method it should work though, or use a lambda/block.
https://github.com/rails/rails/blob/cf82c9d7826aa36f2481114961af02dbf39896dd/actionmailer/lib/action_mailer/delivery_job.rb#L37

ActionMailer not working after upgrade Rails & Ruby version. Only works in terminal

I just upgraded my app from Rails 4.0 to 4.2.6 and our Ruby version to 2.3.7.
As I am testing my app, I am noticing my reset password emails are NOT being sent out. Take a look.
password_resets_controller.rb
def create
#user = User.find_by_email(params[:email])
if #user
#user.deliver_password_reset_instructions!
end
flash[:notice] = "Instructions to reset your password have been emailed to you"
render :action => :new
end
This deliver_password_reset_instructions! exists in my User model file.
user.rb (hasn't been changed in over a year)
def deliver_password_reset_instructions!
reset_perishable_token! #this method updates the user's perishable_token
NotificationMailer.send_reset_instructions(self)
end
But... when I call my send_reset_instructions method in the NotificationMailer model, nothing happens anymore. However... when I put a debugger in my code, as such:
def deliver_password_reset_instructions!
reset_perishable_token! #this method updates the user's perishable_token
binding.pry # If I call `NotificationMailer.send_reset_instructions(self)` it works
NotificationMailer.send_reset_instructions(self)
end
and I run the NotificationMailer.send_reset_instructions(self), it works, but not without me typing it.
It just seems realyl wierd to me that... it works... but only if it I manually type it in the debugger. So, I can't put my finger on WHERE it is broken.
ActionMailer changed in Rails 4.2, quoting the upgrade guide:
Previously, calling a mailer method on a mailer class will result in the corresponding instance method being executed directly. With the introduction of Active Job and #deliver_later, this is no longer true. In Rails 4.2, the invocation of the instance methods are deferred until either deliver_now or deliver_later is called.
So in your case, simply appending .deliver_now to your call should work:
NotificationMailer.send_reset_instructions(self).deliver_now

Rails, RSpec: How to test, that a specific mailer action gets triggered

I need to ensure that running an importer results in sending out an email.
This is what I got so far:
describe '#import' do
it 'triggers the correct mailer and action', :vcr do
expect(OrderMailer).to receive(:delivery_confirmation).with(order)
Importer.new(#file).import
remove_backed_up_file
end
end
It fails with:
pry(#<ActiveRecord::ConnectionAdapters::TransactionManager>)> error
=> #<NoMethodError: undefined method `deliver_now' for nil:NilClass>
Which obviously can't work out as I am expecting the Mailer class to receive the (instance) method call. But how can I get a hold of the mailer instance that will receive the call? How would you test that a unit's method triggers a certain mailer?
I assume the delivery_confirmation method in reality returns a Mail object. The problem is that ActionMailer will call the deliver method of the mail object. You've set an expectation stubbing out the delivery_confirmation method but you haven't specified what should be the return value. Try this
mail_mock = double(deliver: true)
# or mail_mock = double(deliver_now: true)
expect(mail_mock).to receive(:deliver)
# or expect(mail_mock).to receive(:deliver_now)
allow(OrderMailer).to receive(:delivery_confirmation).with(order).and_return(mail_mock)
# the rest of your test code
If I got you right,
expect_any_instance_of(OrderMailer).to receive(:delivery_confirmation).with(order)
will test the mailer instance that will receive the call.
For more precision you may want to set up your test with the particular instance of OrderMailer (let's say order_mailer) and write your expectation the following way
expect(order_mailer).to receive(:delivery_confirmation).with(order)

Why can't I have a mailer method named message?

I've created a new mail on rails 5 using the mailer generator:
$ rails g mailer mymailer message
Rails created the application_mailer, mymailer_mailer, views and tests. Ok.
This is the mailer generated by rails:
class MymailerMailer < ApplicationMailer
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
# en.mymailer_mailer.message.subject
#
def message
#greeting = "Hi"
mail to: "to#example.org"
end
end
But whenever I've tried to send the mail I've got the following error:
NoMethodError: undefined method `reject!' for nil:NilClass
After spent about two hours double-checking every config file I've decided to change method to bla...
VoilĂ : It worked, Ok! But why?
BTW: The message method I've found is from ActionMailer::MessageDelivery but there's no mention on Rails Guides of that.
If you look at the docs for MessageDelivery, there appears to be a method already provided named message which
Returns the resulting Mail::Message
My assumption is that your definition is overriding this provided method, but you are not returning the expected Mail::Message type object.
As another answer stated, there's already a method in the class named message. This shouldn't be a problem if you use the class as intended, since the mailer shouldn't have a single message named "message", it should have a more descriptive name.
The intent of a Mailer object is to define a context for messages that may be sent.
So for example, a UserMailer would be used to build messages to a user. Then each different type of message has a method, such as forgotten_password or welcome.
The documentation includes a more thorough example that follows this.

Delayed_job not sending Rails 3 emails

Note: Using Rails 3.1 and current delayed_job gem.
I have a User model that calls after_create :mail_confirmation.
The mail_confirmation method looks like the following, per the delayed_job instructions:
def mail_confirmation
UserMailer.delay.registration_confirmation(self)
end
The UserMailer is:
class UserMailer < ActionMailer::Base
default from: "test#mysite.com"
def registration_confirmation(user)
#user = user
mail(:to => "#{user.full_name} <#{user.email}>", :subject => "Test registration email")
end
end
The job is queued, and the output from rake jobs:work makes it seem as if it completed successfully:
[Worker(host:mymac.local pid:73694)] Starting job worker
[Worker(host:mymac.local pid:73694)] Class#registration_confirmation completed after 1.3659
[Worker(host:mymac.local pid:73694)] 1 jobs processed at 0.7288 j/s, 0 failed ...
There is no error but the email is never sent. It works fine if I remove delayed from the method call in the User model and go with the standard deliver method:
def mail_confirmation
UserMailer.registration_confirmation(self).deliver
end
How can I find out what is happening when the job is processed? Any idea how to fix it?
Update It appears that it is related to this:
NoMethodError with delayed_job (collectiveidea gem)
Yeah i had this same issue. #Clay is correct, there is an issue at the moment: https://github.com/collectiveidea/delayed_job/issues/323
I resolved this problem by reverting back to the previous version of delayed_job.
gem 'delayed_job', '2.1.2'
I'm having the same issues here. I discovered that for some reason the delay method called on Mailer classes is being handled by the method Delayed::MessageSending#delay instead of Delayed::DelayMail#delay which instantiates the right performable (which is PerformableMailer instead of PerformableMethod). It doesn't crash the job because PerformableMethod just calls the method without the deliver.
Take a look at:
delayted_job/lib/delayed/message_sending.rb
delayted_job/lib/delayed/performable_mailer.rb

Resources