I'm having trouble sending an email blast to only certain users who have a boolean set to true and to not send the email to those users who have it set to false.
In my app I have Fans following Artists through Artists Relationships. Inside my ArtistRelationship model I have a boolean that fans can set to true or false based on if they want email blasts from Artists or not when the Artist makes a post.
So far, I have this:
class Artist < ApplicationRecord
Artist.inlcudes(:fans).find_each do |fan|
fan.includes(:artist_relationships).where(:post_email => true).find_each do |fan|
class Artists::PostsController < ApplicationController
@artist = current_artist
@post = @artist.artist_posts.build(post_params)
flash.now[:alert] = "You've failed!"
You want to send mails to fans of a particular artist. Therefore you call
That is you call a method on an instance of the Artist class. Instance methods are not defined with a
self.[METHOD_NAME]. Doing so defines class methods (if you where to call e.g.
First part then is to remove the
self. part, second is adapting the scope. The complete method should look like this:
def fan_post_email artists_relationships .includes(:fan) .where(post_email: true) .find_each do |relationship| FanMailer.post_email(relationship.fan).deliver_now end end end
Let's walk through this method.
We need to get all fans in order to send mails to them. This can be done by using the
artist_relationships association. But as we only want to have those fans having checked the e-mail flag, we limit those by the
The resulting SQL condition will give us all such relationships. But we do it in batches (
find_each) in order to not have to load all of the records into memory upfront.
The block provided to
find_each is yielded with an artists_relationships instance. But we need the fan instances and not the artists_relationships instances to send the mail in our block and thus call
post_email with the fan instance associated with the relationship. In order to avoid N+1 queries (a query for the fan record of every artists_relationships record one by one) there, we eager load the fan association on the artists_relationships.
Unrelated to the question
The usage of that method within the normal request/response cycle of a user's request will probably slow down the application quite a lot. If an artists has many fans, the application will send an e-mail to every one of them before rendering the response for the user. If it is a popular artist, I can easily imagine this taking minutes.
There is a counterpart to
deliver_now which is
deliver_later (documentation. Jobs, like sending an e-mail, can be queued and resolved independent from the request/response cycle. It will require setting up a worker like Sidekiq or delayed_job but the increase in performance is definitely worth it.
If the queueing mechanism is set up, it probably makes sense to move the call to
fan_post_email there as well as the method itself might also take some time.
Additionally, it might make sense to send e-mail as BCC which would allow you to send one e-mail to multiple fans at the same time.