Testing Mails queued with Sidekiq - ruby-on-rails

Because we need to have the ability to schedule email delivery, we have migrated to using Sidekiq to send emails with the deliver_later method. Under the old regime that used deliver_now, our testing could use
ActionMailer::Base.deliveries[index]
to inspect the recipient, subject, body, attachments, etc...
For testing purposes, is there an equivalent mechanism to inspect the contents of queued email when using Sidekiq and deliver_later?

For testing contents of the email, you have to render the email template for which the job needs to be executed. I suggest you should separate unit tests:
Job spec to check if email is getting enqueued with correct parameters.
Email spec to check the email contents.
If you want to go the end-to-end style, use Sidekiq::Testing.inline! to execute the job and then use ActionMailer::Base.deliveries[index] like before.

The solution turned out to be to execute perform_enqueued_jobs after all emails were queued. After this, all of the existing testing mechanisms worked.
See https://api.rubyonrails.org/v7.0.4/classes/ActiveJob/TestHelper.html for additional information.

Related

Rails Action Mailer with deliver_later on AWS

I have my production app running on AWS but am having issues with the active job - deliver_later on certain mailers. I am successfully sending all emails with deliver_later in development but there is something different in production. Certain mailers work with deliver_later but not my welcome mailer (welcomes new users). So I have to set deliver_now on this welcome mailer to have it actually send the email.
//doesn't work, email is not sent
UserMailer.welcome_email(self).deliver_later
//works
UserMailer.welcome_email(self).deliver_now
The log file from the server show this and nothing more when I use deliver_later:
[ActiveJob] Enqueued ActionMailer::DeliveryJob (Job ID:
d7114-464e-4a90-9721-126650) to Async(mailers) with arguments:
"UserMailer", "welcome_email", "deliver_now", #>
Any help would be appreciated. Thanks.
deliver_now Delivers an email at a time.
If you use deliver_later you have to use ActiveJob runner like Sidekiq you can see this gist Sending emails with ActionMailer and Sidekiq also see this for basic Sidekiq email sent.
Enqueues the email to be delivered through Active Job. When the job runs it will send the email using deliver_now.
UserMailer.welcome_email(self).deliver_later(wait: 1.hour)
method-i-deliver_later
Basically, deliver_later is asynchronous. When you use this method, the email is not sent at the moment, but rather is pushed into a job's queue. If the job is not running, the email will not be sent. deliver_now will send the email at the moment, no matter what is the job's state. Here you can see the documentation for delivery methods.
What ActiveJob backend are you using?
ActiveJob to provide asynchronous execution but does not provide asynchronicity itself.
You need to use some asynchronous backend like sidekiq, delayed_job or something else.
This is because you haven't set the time and rails picking up default time. and in log file also it shows that the deliver_later method is not being called yet.
Try using:
UserMailer.welcome_email(self).deliver_later(wait: 1.minute)
Then check the log after one min.
If you are using delayed_job, you have to run rake jobs:work
https://www.youtube.com/watch?v=AoGPaWEasxs

External web service for sending background emails in rails

I've used this instructions and sent "Welcome" mail to my signed up user. But this makes the user wait for 5-8 seconds because the server is trying to complete this mail thing.
I don't want the user to wait until the mail is sent but immediately see the "Mail has sent" message. So this brings me background jobs in Rails.
There are many options like delayed_job, Resque etc for background jobs in Rails. But to use these kind of solutions, as I understand:
1- Create a background job
2- Run this job
Let's say I used one of the background job solutions, so then I need something else to run also this job, like cron job...
I think just for sending sign-up and password reminder emails, another easier solution should be possible. I mean like another external service that 1- I'll create a template for each kind of mail I'll send, 2- I'll pass some arguments like receiver_email, template_id, receiver_username, password_link etc... With that way, I won't need any background job, and the user will not wait.
I came across some other gem called "sucher_punch" but as I understand from the people's messages and posted problems, with using heroku, this gem can fail for some reasons of heroku dynos and the mail may not be sent, and you don't know it.
Anyway, what is the general way that rails developers handle this email issue? Maybe I can also use sendgrid like the way I explained above, can I ?
Sending emails in the background is such a common use case, Rails 4.2 introduced a #deliver_later method in ActionMailer to provide seamless ActiveJob integration.
You don't need to set up a cron job to check if there are any jobs in the background queue. Sidekiq, Resque or DelayedJob will take care of that for you.
It seems Sendgrid does allow creating templates and sending variable content to fill them up, but that feature doesn't undermine the benefits of making that call asynchronously. In fact, deferring it to the background also has the added benefit of not disrupting user experience if the external resource(Sendgrid) is unavailable.
You should try installing one of the background processing solutions you mentioned(I recommend sidekiq) and take advantage of the ActionMailer + ActiveJob integration.

Rails 4: How to send mail in a separate thread?

I have an application that needs to occasionally send an email blast to the entire user base when an admin does something. This was working fine, but when there are a lot of users, the page for the admin will wait until all the mail is sent, which is undesirable.
To mitigate this, I tried sending email in a new thread:
t = Thread.new do
User.all.each do |user|
Mailer.email(user).deliver
end
end
at_exit{ t.join }
This worked fine, but then in my test suite, I can't test to ensure the email sending works:
# This test now fails with the new Thread above
test "admin action should send email blast" do
assert_difference("ActionMailer::Base.deliveries.count", User.count) do
post :action
end
end
So my questions are:
Is this method the best way to send email in a new Thread? Or is there a gem available that handles this kind of interaction?
How can I test that the emails are sent in my test suite if the sending is done in a new thread? Is there a way to check to wait for all threads to finish?
In rails 4.2 there's a special class that handles jobs called ActiveJob, active job allows you to queue long tasks for another process to handle them in the background, also you can queue tasks for a certain time, like for example "send this email tomorrow at 8 am".
For these queues to get handled you need to choose a backend to handle them, here's a list of backends that support ActiveJob
Each has it's pros and cons, sidekiq for example is a multithread handler, so it uses lower memory, while for example sucker punch uses the same thread as the main server, so it uses a lot less memory, suitable if you have a low memory server that can't handle a second ruby thread.
As for the testing part, rails guide already explains how to test your emails and test things like if emails has been queued or not, and test that the right template was rendered, and if it contains the right text.
I think you want to send mail asynchronously, For that you may use many gems like - delayed jobs, sidekiq etc. I would personally recommend to use sidekiq as its faster and used Redis in memory db behind the scene.
With Rails 4.2, Active jobs are introduced, so using it has advantage that you can switch from one queuing system to other without having any worry at any time and you can specify which queing machinery yiu want like
module YourApp
class Application < Rails::Application
# Be sure to have the adapter's gem in your Gemfile
# and follow the adapter's specific installation
# and deployment instructions.
config.active_job.queue_adapter = :sidekiq
end
end
I prefer using a queue based approach for async tasks. [Delayed job] (https://github.com/collectiveidea/delayed_job) is one option, but I prefer using [sidekiq] (http://sidekiq.org).
[Here] (http://blog.remarkablelabs.com/2013/01/using-sidekiq-to-send-emails-asynchronously) is an example of sending emails asynchronously using sidekiq.
Best way to send emails in separate threads is to use delayed jobs or similar gem.
https://github.com/collectiveidea/delayed_job
With delayed jobs gem, you can also send emails or do something else in different threads at scheduled time.
Another option is to use sidekiq, install sidekiq gem and add this line to application.rb file
config.active_job.queue_adapter = :sidekiq

Kinda-mass emailing from Rails, but with own mail server

I've read most of the other answers on this topic, but a lot of them related to either third-party services like MailChimp (which I'm not necessarily opposed to) or how not to upset the host's email server.
I believe this case is unique so that it'll contribute...
I have my own DigitalOcean droplet running a rails app. I need to send out 100-1000 emails every so often, each with a unique message (a link I'm using for tracking clicks originating from the email).
I'm also operating my own iRedMail server.
Can someone recommend how to best-handle this task? I was going to simply cycle through the list of emails and use the template.html.erb to drop in my link, but what types of problems might I run into?
Thank you!
You should decouple your Rails App from the mail sending so that you don't have to wait in your view for the mails to be sent (assuming that you click on something that triggers the start of your mail sending). Use something like delayed_job or another queueing mechanism that Rails offers and only queue up the sending job of the e-mails. Then when the queue comes to execute the particular job you can customize the message with an HTML part and a text part or whatever else you need and pass them on individually to your MTA.

View emails sent

I have config.action_mailer.delivery_method = :test and use delayed_job. I run some code that places an email to be sent in a queue, then run rake jobs:work, but nowhere do I see the email that is sent out, and ActionMailer::Base.deliveries is nil. I'm just looking to debug and view the content of htese emails, how can I do so?
When config.action_mailer.delivery_method is set to :test, emails are not actually sent but instead merely added to a list of "sent" messages. That list exists only in memory. That means only the process that "sent" the email can see the list and verify that it was actually "sent".
Since the code that actually sends your mail is being executed in an external process (through a system() or backtick call), your calling script won't be able to see the in-memory queue of that external process and thus won't be able to verify that the emails were actually "sent".
This shouldn't really be a big deal unless something has gone wrong. By default outgoing emails will be written to the log file, so you can verify that they're actually sending by checking there. If you want to view/manipulate the queue in-memory, you'll have to add code to your job to do so, as that is the only code that will have access to it.

Resources