Logic for sending emails to particular User in Rails - ruby-on-rails

This is more like a conceptual question because I have the feeling that I am doing this wrong:
I have to send emails to some Users that satisfy some conditions. What I have right now is the following:
1) A task that basically does this:
users = User.includes(:aptitudes).where({:role => ['Boy','boy','kid'], :aptitudes => {:name =>'Good'} })
users.each do |user|
MyMailer.report(user).deliver
end
2) In the report method of MyMailer, I have several things:
def report(user)
#user = user
#value = #user.value
#travels = #user.travels.where(:end_at)
#rewards = #user.rewards
# More logic depending of values of User.
...
end
What happens is that in the Mailer itself sometimes a particular property or attribute of the object #user does not satisfy for the email to be sent. I am wondering where am I supposed to make sure that all the Users that I pass to the Mailers will be sent an email ? Shall I create a new method in the rake to do those checkings? Or how would you do that?

Generally speaking, you can do as you wish as long as the name is revealing enough of the intentions.
If you gave me this MyMailer object to use, without knowing the implementation, I would expect it to send a report when requested and not filter out of its own whim... eventually deal with other problems (fail to send, delays, retries etc.) but when I give it a user I want that user to be emailed.
So, to answer your question, yes I would either filter out all the users before calling the Mailer, or define a method in the Mailer called something like report_only_those_users that will take care of the filtering.
By the way, you can pass a list of emails to ActionMailer (section 2.3.4), without having to loop and send one at a time.

Related

Rails 5 - Best way to prevent emails from being sent to unsubscribed users

I am using Rails 5.
I have an Affiliate model, with a boolean attribute email_notifications_on.
I am building a quite robust email drip system for affiliates and can't figure out where the best place is to check if the affiliate has email notifications on before delivering the email.
Most of my emails are being sent from Resque BG jobs, a few others from controllers.
Here is an example of how I am checking the subscribe status from a BG job:
class NewAffiliateLinkEmailer
#queue = :email_queue
def self.perform(aff_id)
affiliate = Affiliate.find(aff_id)
if affiliate.email_notifications_on?
AffiliateMailer.send_links(affiliate).deliver_now
end
end
end
It seems like writing if affiliate.email_notifications_on? in 10+ areas is not the right way to do this, especially if I need another condition to be met in the future. Or is this fine?
I thought maybe some sort of callback in the AffiliteMailer would work, but saw many people advising against business logic in the Mailer.
Any thoughts/advice would be appreciated.
To be honest, I don't think any better way than creating a method in Affiliate model as follows,
def should_send_email?
# all business logic come here
# to start with you will just have following
# email_notifications_on?
# later you can add `&&` or any business logic for more conditions
end
You can use this method instead of the attribute. It is more re-usable and extendable. You will still have to use the method in every call. If you like single liners then you can use lambda.

Issue and clarification needed with attr_accessible

There is so much written about the security threat of attr_accessible that I am beginning to wonder if I should even have any attributes in it. Here is the issue. I have a Message model which has the following:
attr_accessible :body,:sender_id,:recipient_id
I do not have the update or edit action in my messages_controller. With the new and create action I am able to create a new message and send it to a recipient. Only users who have logged in and meet certain conditions can message each other. I do that with the help of a before_filter and the conditions work fine. The message is stored and can be viewed by the sender and the recipient. Perfect!
The question I have is that since :body,:sender_id,:recipient_id are included in attr_accessible, can a malicious user somehow change the :body,:sender_id,:recipient_id of the original message? Should I just add these attributes to attr_readonly as well so they cannot be modified once saved?
This question has been haunting me for practically all my models.
can a malicious user somehow change the :body,:sender_id,:recipient_id
of the original message?
This would depend on other things rather than attr_accesible. attr_accesible will only filter which fields are allowed to be updated using mass assignment. Since you say you don't have any update action, then no, there is now way a user can edit a message since you always create a new Message through you create action.
But there is something you need to care about. What is sender_id? If you do have users in your app and they send messages to each others, then sender_id should not be an accessible field, since this will allow users to send messages on behalf of other users. You probably want to keep that field off the attr_accessible list and do something like this:
m = Message.new params[:message] # body and recipient_id
m.sender_id = current_user.id # this is not mass assignment
m.save
.....
Well, it depends on how your are creating your model's instance. If you use:
FooModel.create(params[:foo])
then yes, your are not secure because a logged in user may pass additional parameters to the request even if you don't provide explicitly form fields for those attributes.
So, for your case, anyone posting to your "create" action with sender_id, recipient_id (values in the request) will be able to change them unless you take care about this assignments in your action.

rails 3.1: Where to filter out email addresses that have opted out

Trying to figure out the cleanest way prevent sending email to users who have opted out from receiving them in rails 3.1.
I was thinking about overriding Mail.deliver to check the db and determine if the recipients are unsubscribed or not, then conditionally delivering the email.
That seems like the least intrusive way to go about it, but requires creating the Mail objects that are never going to be sent.
Seems like the most resource conscious way would be to do the check in the controller, thus preventing the Mail objects that are never going to be sent from the burden of existence.
This though seems more intrusive and prone to developers forgetting to make the check when creating new mailers.
Is there a standard practice for this situation?
** edit **
This is for managing a collection of users who have opted out of receiving notifications, rather than something like managing subscriptions to a news letter.
If the attribute that determines whether or not to get email notifications is just a field on a model in the DB, you could create a named scope called something like 'want_email_notifications' to get all the users that have subscribed.
So, if you have a User class, and that class has an attribute called opt_out, then you could do something like:
class User < ActiveRecord::Base
named_scope :want_email_notifications, :conditions => ['opt_out = ?', false]
...
end
Then, to call it, you do User.want_email_notifications, which gives you an array of all User objects that want email notifications.
Then, when you're checking whether or not a given user should receive an email notification, write a condition similar to:
send_email_notification(user_in_question) if User.want_email_notifications.include?(user_in_question)
In this example, send_email_notification is the method where you would call the associated delivery method, which actually sends the email.

Global before_sending_mail callback in Rails

When a user earns 10 points in my application, he gets an email saying "You just earned 10 points!"
If someone earns 10 points per day over a week, I don't mind sending him an email a day – but if someone earns 50 points in a single day, I don't want to send him 5 emails.
So I'd like a way to intercept each outgoing email before it gets sent, examine it, and determine whether I should send it or not. What's the best way to do this?
I have a similar application, where I have a sent_previous_email_at timestamp field for the customer. I just check:
send_email if send_previous_email_at < DateTime.today - 1.days
I would attach some sort of method to either the User model or the Point model (if you have something like that).
class User
def add_points(count)
# Add points
send_email if criteria_is_met
end
def send_email
# Send email
end
end
Not exactly "global", but instead of calling the mailer directly, just use something like this so you can easily add conditional behavior.

ActionMailer- access Mail::Message content, or manually initialize view in controller?

I've got a form where an internal user can request that informational materials be sent to a client. When the form is submitted, it sends an email to the person in charge of physically mailing the materials to the client.
Now, I want to capture the content of the email so I can add a note to the client's show page, and I'm unsure how to go about it.
One option I've looked at is to use an after_filter in the MaterialsRequestMailer, but calling message.body returns a large string with way more text than I need and I want to avoid adding a bunch of parsing logic to get the message content.
Basically, I want what is generated by the views/materials_request_mailer/send_request_notification.text.erb template. I've looked through http://www.rubydoc.info/github/mikel/mail/Mail/Message and can't find a method to return just the rendered template content. Is there a way to do that?
If not, is there a way to manually initialize a View in the controller, where I already have the instance variables I'm passing to the mailer? That doesn't seem to be an ideal solution, because I'm using DelayedJob, and the code for adding the note would be run before the email is actually sent. Also, due to DelayedJob, it appears that I can't directly access the mail object from within the controller (if I do mail = MaterialsRequestMailer.delay.send_request_notification(...) it assigns an instance of Delayed::Backend::ActiveRecord::Job to mail).
Found a solution that works- message.text_part.body.raw_source is what I was looking for. (credit to this answer: https://stackoverflow.com/a/15257098/2599738)
class MaterialsRequestMailer < ActionMailer::Base
include AbstractController::Callbacks
after_filter :add_note_to_client
def send_request_notification(client, ...)
#client = client
...
end
def add_note_to_client
mail_text = message.text_part.body.raw_source
#client.add_account_note(mail_text)
end
end

Resources