I am using an Observer to log outgoing emails; it fires correctly but when I attempt to extract the body of the email I get an empty string. The emails are definitely not blank and the logging record is definitely created. Breakpointing and inspecting message.body confirms that it is an empty string.
class MailObserver
def self.delivered_email(message)
for address in message.to
user = User.find_by_email(address)
if user
UserMailerLogging.create!(user_id: user.id, email_type: message.subject,
contents: message.body, sent_at: Time.now)
end
end
end
end
ActionMailer::Base.register_observer(MailObserver)
In Rails 3, emails are built and sent using the Mail gem. According to their docs
message.text_part # plaintext version
message.html_part # html version
You can also go a bit further depending on whether your emails are multipart or not.
message.text_part.body.decoded
This question here on SO may also be hefpful.
Related
Good day everyone,
I created a mailer to send an email to my client. As of right now im still testing it, but I couldn't make it to work. I've read redis, sidekiq, rails_mailer and still nothing. I can see that the mail is in the queue of sidekiq UI but I cant receive the email.
Here's the flow of my code.
User will check the text box on the view if they wanted to send an email to a client.
I a method will be triggered on the controller. Heres my code.
def send_workorder_message
if params.has_key?(:to_send_email)
WorkorderMessage::WorkorderMessageJob.perform_in(10.seconds, #curr_user, params[:message])
end
endv
then a workorder job is created. heres the code.
class WorkorderMessage::WorkorderMessageJob
# include SuckerPunch::Job
include Sidekiq::Worker
sidekiq_options queue: 'mailers'
def perform(user, message)
Spree::WorkorderMailer.workorder_send_to_email(user, message).deliver_now
# ActiveRecord::Base.connection_pool.with_connection do
# end
end
end
after that it will trigger the WorkorderMailer heres the code.
class WorkorderMailer < BaseMailer
def workorder_send_to_email(to_user, message)
ActiveRecord::Base.connection_pool.with_connection do
subject = "sample message mailer"
#message = message
#user = to_user
mail(
to: #user.email,
# 'reply-to': Spree::Store.current.support_address,
from: Spree::Store.current.support_address,
subject: subject
)
end
end
end
when I use the preview mailer I can see the UI working fine.
Also I've noticed that on sidekiq view I see this User Obj. I that normal?
According to the Sidekiq documentation, the arguments you pass must be primitives that cleanly serialize to JSON, and not full Ruby objects, like the user you are passing here:
Complex Ruby objects do not convert to JSON, by default it will
convert with to_s and look like #<Quote:0x0000000006e57288>. Even if
they did serialize correctly, what happens if your queue backs up and
that quote object changes in the meantime? Don't save state to
Sidekiq, save simple identifiers. Look up the objects once you
actually need them in your perform method.
The arguments you pass to perform_async must be composed of simple
JSON datatypes: string, integer, float, boolean, null(nil), array and
hash. This means you must not use ruby symbols as arguments. The
Sidekiq client API uses JSON.dump to send the data to Redis. The
Sidekiq server pulls that JSON data from Redis and uses JSON.load to
convert the data back into Ruby types to pass to your perform method.
Don't pass symbols, named parameters or complex Ruby objects (like
Date or Time!) as those will not survive the dump/load round trip
correctly.
I would suggest you change it to lookup the User by ID within the job, and only pass the ID instead of the entire user object.
# pass #curr_user.id instead of #curr_user
WorkorderMessage::WorkorderMessageJob.perform_in(10.seconds, #curr_user.id, params[:message])
# accept the ID instead of user here
def perform(user_id, message)
# get the user object here
user = User.find(user_id)
# send the mail
mail(
to: user.email,
#...
end
I have an ActionMailer Observer that gets triggered on each email and writes some information to a log database table to keep track of who sends emails. I want to add some metadata to this like logged in user, type of email, etc.
class MailObserver
def self.delivered_email(message)
if message.header[:client_id]
EmailLog.create!(:client_id => message.header[:client_id].to_s,
:to => message.to ? message.to.join(',') : nil,
:cc => message.cc ? message.cc.join(',') : nil,
:bcc => message.bcc ? message.bcc.join(',') : nil,
:subject => message.subject.to_s,
:content => message.multipart? ? message.text_part.body.decoded : message.body.decoded,
:reference_type => message.header[:reference_type].to_s,
:reference => message.header[:reference].to_s,
:user_id => message.header[:user_id].to_s)
end
end
end
ActionMailer::Base.register_observer(MailObserver)
All this information is available the moment the mail is created.
I currently pass this data via the message.header, but then the values show up in the actual email that is delivered
Is there a better way to pass information from the ActionMailers to the Observers while preventing this data from actually being sent?
Looking through the mail gem, there isn't a lot you can do with meta data on the Mail object besides the headers.
One approach I considered was persisting the message_id along with user details, etc and then retrieving it again in the observer and perform logging after that.
Another I was toying with was to set the message id myself and load it with the user id, etc but that was much the same as setting headers.
Possibly, you could set the header then unset it again in an interceptor.
You can consider creating new key-value pair inside mail method in the specific mailer class itself.
Example:
class UserMailer
.....
mail(to: email, subject: 'Your subject', user_id: user.id, client_id: client.id)
end
I tried this and it worked. But it appends extra key-values to the response header.
Have you considered using a callback on the mailer itself?
class MyMailer < ApplicationMailer
after_action :log_email!
...
private
def log_email!
EmailLog.create! # should be able to access instance variables from your mailer method here to reference what you need
end
end
Hope that helps!
Background: in a Rails 3.2 app, I have an ActionMailer purchase confirmation email that is manipulated in a "stage" environment so that email destined for addresses associated with payment processor sandbox accounts will actually be sent to the email addresses of the people who manage the sandbox accounts. This is currently done inside the mailer class:
# app/mailers/purchase_mailer.rb
class PurchaseMailer < ActionMailer::Base
default :from => "\"#{SiteConfig.name}\" <#{SiteConfig.support_email}>"
def purchase_notification(purchase)
#purchase = purchase
mail :to => "\"#{purchase.customer_name}\" <#{address_filter(purchase.customer_email)}>",
:subject => "[#{SiteConfig.name}] Purchase Confirmation"
end
private
def address_filter(email_address)
# Check for and remove sandbox identifiers
if Rails.env.stage?
email_address.sub(/_\d+_p(er|re)#/, '#')
else
email_address
end
end
end
But, hey, that looks like a great use case for an interceptor, no? So I pulled out the address_filter method above and added this to the Rails app.
# config/initializers/mail.rb
Mail.register_interceptor(StageMailInterceptor) if Rails.env.stage?
# lib/stage_mail_interceptor.rb
class StageMailInterceptor
def self.delivering_email(message)
receivers = []
message.to.each do |to|
receivers << to.sub(/_\d+_p(er|re)#/, '#')
end
message.to = receivers
end
end
At first glance, this appears to work great. In the stage environment, the email is intercepted and the "to" address becomes the email address I want the email to go to. The person managing the sandbox account used to make the purchase receives the email. Perfect... except the name of the sandbox account is gone. What once was "Joe Example" <joe_1338142567_per#example.com> changed to "Joe Example" <joe#example.com> is now changed to joe#example.com
...the name is now gone.
Looking at the Mail message interface, I see that message.to= can be set with a name, but calling message.to gets me an array of just email addresses without names, whether the name was provided or not.
Question: what is the correct way to alter an email address without altering the name associated with the email address in a mail interceptor?
This doesn't seem like it's the "right" way to do this, but grabbing the "To" header of the message, doing the replacement, and setting with message.to= allows me to preserve the names while altering the email address. So my interceptor became:
# lib/stage_mail_interceptor.rb
class StageMailInterceptor
def self.delivering_email(message)
message.to = message.header["To"].to_s.gsub(/_\d+_p(er|re)#/, '#')
end
end
We have an application built in rails 3.2.8. We are sending emails to customers. I want to block certain emails addressses. Basically mailer just ignore those particular email addresses.
For example: My company name is abc and I dont want to send emails to all my employees ie john#abc.com or rayn#abc.com ie *#abc.com
How can I do this?
PS: I am using sendgrid, they are not providing anything like this.
EDIT:
Placing this code in initializers directory:
class EmailAddressFilter
def self.delivering_email(message)
message.perform_deliveries = false
end
end
ActionMailer::Base.register_interceptor(EmailAddressFilter)
Should block all the emails. But still I can see emails in my development log.
PS: I have restarted my server.
In a file in config/initializers you can add something like this
class EmailAddressFilter
def self.delivering_email(message)
# permit or deny the message using its "to", "body" etc properties
# note message.to is an array (multiple emails)
message.perform_deliveries = Email.whitelisted?(message.to)
end
end
ActionMailer::Base.register_interceptor(EmailAddressFilter)
I have a table of emails. And i need that Each user received email.
SO i made:
script/generate mailer Notifier
Next.
class Notifier < ActionMailer::Base
def newgrants_notification(respondent)
recipients user.email
from "lala#lala.com"
subject "Hi!"
body (:respondent => respondent)
end
end
In app/views/notifier/newgrants_notification.erb
wrote : Hello!
and this my controller where i create question
#question = Question.create(:text => params[:question][:text], :security => rand(888).to_i)
if success = #question.save
respondents = Respondent.find(:all)
respondents.each do |res|
Inquiry.create(:question_id=>#question.id.to_i, :respondent_id=>res.id.to_i)
Notifier.newgrants_notification(respondents).deliver #this is right??
end
what mistakes i did? messages aren't coming ;(
HI
respondents.each do |res|
Inquiry.create(:question_id=>#question.id.to_i, :respondent_id=>res.id.to_i)
Notifier.newgrants_notification(res).deliver
end
When you are sending mail,the mail id is passed as a parameter so respondents is replaced with res.
You are passing through your array of respondents, when you use each, the variable in the pipes (|res|) is the one to use to refer to the singular object in the loop.
Notifier.newgrants_notification(res).deliver
In development mode all email are not send. It's all log only in your log file. So if you test on this environment is normal. Check on your log if you see it :)