Reputation: 48453
I want to send a summary of our new lists to our users every morning. What's the best approach to do that with Ruby On Rails 4, ActiveRecord (using SendGrid) and Delayed Job?
I am currently doing it this way:
In controller:
def yesterday_listings_for_users
yesterday_listings = Listings.where('status = "0" AND (DATE(created_at) = ?)', Date.today - 1)
if yesterday_listings.count > 0
NotificationMailer.delay.yesterday_listings_for_users_notification
end
render :nothing => true
end
And then in the mailer:
def yesterday_listings_for_users_notification
@listings = Listing.where('status = "0" AND (DATE(created_at) = ?)', Date.today-1)
mail(to: '[email protected]', subject: "Latest Listings", from: '[email protected]')
end
With using a CRON job, this sends me the report every morning on my email address. I have a few hundreds of users in the database and I would like to send them this email as well.
How to do that? I am wondering about something like this:
def yesterday_listings_for_users_notification
@listings = Listing.where('status = "0" AND (DATE(created_at) = ?)', Date.today-1)
Users.all.each do |user|
mail(to: user.email, subject: "Latest Listings", from: '[email protected]')
end
end
However, is looping through hundreds of records in database and sending hundreds of emails in a delayed mailer method recommened (or appropriate)?
Is there a better way to do that?
Thank you in advance!
Upvotes: 2
Views: 612
Reputation: 4322
I usually prefer to use Sidekiq
along with Sidetiq
but if you want to use delayed_job
I would advice you to use the whenever
gem for simplicity.
Whenever is a Ruby gem that provides a clear syntax for writing and deploying cron jobs.
gem 'whenever'
to your gemfilewheneverize .
which will generate a file config/schedule.rb
In your config/schedule.rb
do the following.
every 1.day, :at => '11:30 am' do
runner "User.delay.send_daily_newsletter"
end
In your user.rb
define the method send_daily_newsletter
and use find_each
instead of all.each
(batches)
def self.send_daily_newsletter
listings = Listing.where('status = "0" AND (DATE(created_at) = ?)', Date.today - 1).select(:title).to_json
User.select(:id, :email).find_each do |u|
NotificationMailer.delay.send_daily_newsletter(u.email, listings)
end
end
In your notification_mailer.rb
define send_daily_newletter
def send_daily_newsletter(user_email, listings)
@listings = listings
mail(to: user_email, subject: "Latest Listings", from: '[email protected]')
end
This way you will have one delayed job to get all users and send each email using a separate worker which is the most optimal way to do this task.
Note: Do not forget to change the methods for listings in your view from, for example,
listing.title
tolisting[:title]
since we are passing the listings asjson
.If you do not want to pass them as
json
every time to every delayed task just cache the listings inRails.cache
and clear it after you finish sending.
EDIT:
If you would like to use the cache method since you ran into a problem in the delayed_job
gem, edit your send_daily_newsletter
method in your mailer. (That's is why I would go to redis-based Sidekiq
rather than mysql-based delayed_job
.
def send_daily_newsletter(user_email)
@listings = Rails.cache.fetch('today_listings') { Listing.where('status = "0" AND (DATE(created_at) = ?)', Date.today - 1) }
mail(to: user_email, subject: "Latest Listings", from: '[email protected]')
end
and in your user.rb
def self.send_daily_newsletter
User.select(:id, :email).find_each do |u|
NotificationMailer.delay.send_daily_newsletter(u.email)
end
Rails.cache.clear('today_listings')
end
Good luck. I have been doing these email newsletters for a while now and they are truly pain :D
Upvotes: 4