Access a mailer method without sending the email - ruby-on-rails-3

I have a working mailer in Rails 3.
I want to have some logic inside the mailer methods which will decide if to email it or not.
I cannot have this logic outside the mailer, since I am using Delayed_job with a delay. I delay the email for an hour. If a condition hasn't been met, I send the email.
1. Is there a way to enter the mailer method and not complete the sending (gracefully)
2. Is there a better way of achieve what I am trying?
Code Sample
This is the mailer
class MessagesMailer < ActionMailer::Base
def message_notification(user, message)
#user = user
#msg = message
mail(to: #user.email, from: "#{#msg.sender.name} <me#myapp.com>", subject: "You have recieved a new message from #{#msg.sender.name}")
end
end
MessagesMailer.delay(:run_at => 5.minutes.from_now).message_notification(#user, #message) is being called throughout the app, particularly from MessagesController
I want to check if #user has already read that message. If so, not send the email.
Delay_job would perform message_notification in 5 minutes, but once that method is complete the email is sent regardless of what happens inside of it.
Is there a way to cancel delivery upon a condition (#user read the message)?

Related

Send email to all users in rails

I am trying to create a method that, when called send a email to all users.
My ultimate goal is to call this method via a scheduler (i already got the scheduler working) And the method will go thought all users and send emails to some of then if some pre-requisites are met.
Right now i just want to learn how i make a simplest stuff that is to send a custom email to every user in the table.
My first issue is:
def send_digest
#users = User.all
#users.each do |user|
#time = Time.now
mail(to: user.email, subject: user.name)
end
end
This method (is inside app/mailer/user_mailer.rb) only is sending one e-mail to the guy with biggest ID in the table. Why that?
Also, what i need to do to access the variable "user.name" inside the email?
EDIT:
There a better way for accessing user variable inside the mail body than doing #user = user?
def send_digest(user)
#time = Time.now
#user = user
mail(to: user.email, subject: 'mail message')
end
For each call to the mailer method one email is sent
in scheduled worker
def calling_method
#users.each do |user|
send_digest(user.email, user.name)
end
end
in user mailer
def send_digest(user_email, user_name)
mail(to: user_email, subject: user_name)
end

Delayed_job ActionMailer caching?

I currently call a delayed_job (gem delayed_job_mongoid) to send a confirmation email. However, it doesn't seem to use the latest data I'm passing, and instead uses a cached version. See below:
My controller:
...
_user.calculate_orders
_user.save
_user.reload
Mailer.delay.order_reported(_user)
...
The Mailer
class Mailer < Devise::Mailer
def order_reported(to_user)
#to_user = to_user
email_with_name = "#{#to_user.name} <#{#to_user.email}>"
mail(:to => email_with_name, :subject => "Test email")
end
end
For example, if an attribute _user.total_orders = 3 gets updated to 5 and saved. It's correctly reflected in the database, the delayed job DB record contains the updated info of 5, but when email is sent, it uses cached info of 3.
I've also tried calling the method via rails console:
This works and uses the parameter that's passed and updated info
Mailer.order_reported(u).deliver
This doesn't work and uses cached data
Mailer.delay.order_reported(u)
I had a very similar problem with Sidekiq recently, related to passing in a serialized version of my User object instead of just an ID.
I would refactor slightly to only pass simple objects as parameters in the delayed method. So, you could do this instead:
Mailer.delay.order_reported(_user.id)
and then update the method to read as follows:
class Mailer < Devise::Mailer
def order_reported(to_user_id)
#to_user = User.find(to_user_id)
email_with_name = "#{#to_user.name} <#{#to_user.email}>"
mail(:to => email_with_name, :subject => "Test email")
end
end
That would ensure you always get a fresh user object when your mailer is actually delivered.

pass extra instance vars to devise_invitable email template

I'm overriding the devise_invitable controller and in my create method I'd like to pass extra values to the invitations_instructions email template. For example group name, has anyone been successful at this, if so please give me some clues here.
what I've tried...
my #group in my Users::InvitesController < Devise::InvitationsController create method is undefined in the email template.
tried to add :skip_invitation => true in my create and then send the email manually like...
self.resource = resource_class.invite!(params[resource_name], current_inviter, :skip_invitation => true)
::Devise.mailer.invitation_instructions(self.resource).deliver
but this gives the wrong number of arguments so there is something I'm not reading correctly from the documentation.
UPDATE - possible solution
The only way appears to be this, but I'm curious if there is a better way that uses the templates provided and devise mailer
in my /app/controller/users/InvitesController#create
(inherits from InvitationsController)
self.resource = resource_class.invite!(params[resource_name], current_inviter) do |u|
u.skip_invitation = true
end
UserMailer.invitation_instructions(self.resource, current_inviter, #object).deliver
where UserMailer is my general (standard) action mailer and goes something like...
def invitation_instructions(resource, inviter, object)
#resource = resource
#object = object
#inviter = inviter
mail(:to => resource.email, :subject => 'New invitation from ' + inviter.first_name)
end
There is a cleaner way to achieve the solution that you're looking for, and that is to use Devise's own procedures for overriding mailer templates.
First create a custom mailer that extends from Devise::Mailer:
class MyMailer < Devise::Mailer
helper :application # gives access to all helpers defined within `application_helper`.
include Devise::Controllers::UrlHelpers # Optional. eg. `confirmation_url`
default template_path: 'devise/mailer' # to make sure that your mailer uses the devise views
end
Then, in config/initializers/devise.rb, set config.mailer to "MyMailer". This is going to allow you to override ANY email that devise sends out and customize to your liking.
Then for you, you can override invitable_instructions like this:
def invitation_instructions(record, token, opts={})
# Determine a way to set object -- likely through a query of some type
#object = get_object_for(record)
opts[:subject] = 'New invitation from ' + inviter.first_name
super
end
The main sticking point from your example was passing in extra data to set #group/#object. To do that, I would personally go with some type of query within the mailer (not clean, but it is encapsulated and therefore less "magical") to retrieve those objects.
Additionally, if you wanted to use custom email templates instead of devise's, you can simply add them to the app/views/my_mailer/ directory and devise will prefer emails in that directory over emails from the gem.

Log emails sent by devise in rails

I am using devise for user authentication in rails. How can I log the emails sent by devise. I have a model for storing the emails. How can I hook in so that before devise sends emails for new registration, change password, forgot password etc, I can just store the emails in the db?
Create a file called config/initializers/devise_mail_logger.rb and re-open the Devise::Mailer class
devise_mail_logger.rb:
Devise::Mailer.class_eval do
def devise_mail_with_logger(record, action)
email = devise_mail_without_logger(record, action)
#code to log this email to DB goes here
end
alias_method_chain :devise_mail, :logger
end
The email object will have the message body, subject, recipient details. You can pass this object to the model you have to store emails.

Rails 3 app with Devise loading existing users

We've built a new app which involves a lengthly registration process. We're using Rails 3 and devise for authentication using :confirmable.
Now we've got to import a large set of existing users from an old system. We want this to be done manually to test the process.
So the problem is we don't want to send confirmation emails to these users.
Any suggestions how we can either suppress emails and still manually complete the registration process?
Dom
Looking at the confirmable source code, it seems as if it won't send out any emails if the user is set to confirmed on create.
First off, you need to determine whether you want the newly created user to get an email or not. I'd suggest adding a checkbox to the form, or alternatively cross-referencing the email address against the old users table:
def create
# Form style
skip_email? = params[:user].delete(:skip_email)
# Old users style
skip_email? = !!OldUser.find_by_email(params[:user][:email])
#user = User.new(params[:user])
...
end
Assuming you've done one of the two, you'll have a boolean value of skip_email?. Now you can do:
def create
skip_email? = true # See above
#user = User.new(params[:user])
#user.skip_confirmation!
if #user.save
...
end
end
skip_confirmation! is a method Devise adds to the User model. You can find the Devise source code here.