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!
Related
I can't seem to find the shoppe tag in stackoverflow.
I'm using Shoppe gem for rails.
I want to know if there is a way to edit the views for the emails that are being sent when an order is placed in shoppe.
I would like to add an attachment to the email when you accept the order.
Thanks!
It seems that you can just re-define Shoppe's mailer method:
module Shoppe
class OrderMailer < ActionMailer::Base
def received(order)
#order = order
attachment(content_type: 'image/jpeg', body: File.read('image.jpg'))
mail :from => Shoppe.settings.outbound_email_address, :to => order.email_address, :subject => I18n.t('shoppe.order_mailer.received.subject', :default => "Order Confirmation")
end
end
end
Puts that somewhere into your app/initializers. Remember to set your content-type properly.
This is the best and fastest solution for anyone who just wanting to change the actual text like i did.
You can just go directly to the file of the email and edit it. all i did was visit The exact File Here and recreate that file and its path into my own application and changed the words to my pleasing.
https://github.com/tryshoppe/shoppe/blob/master/app/views/shoppe/order_mailer/accepted.text.erb
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.
I worked through some basic tutorials on Rails 3. The goal is a community-website on abilities and activities. I am using Devise for authentication. The creation of user profiles with avatars worked well (thanks to paperclip).
As a next step, I want to enable registered users to send an e-mail to a user from his (or her) profile page. I found a great tutorial on creating a contact form using Google Apps:
http://matharvard.ca/posts/2011/aug/22/contact-form-in-rails-3/
The mailer class in this tutorial looks like:
class NotificationsMailer < ActionMailer::Base
default :from => "noreply#youdomain.dev"
default :to => "you#youremail.dev"
def new_message(message)
#message = message
mail(:subject => "[YourWebsite.tld] #{message.subject}")
end
end
My question: What is the best way to replace you#youremail.dev with the receivers E-Mail-Address? (from the User-Model)
Thanks in advance!
You can modify the new_message to accept the user (or list of users) to whom you want to send the email. Or an array of email addresses if you want to. Then pass the receiver's email address to the mail method as the :to option.
def new_message(receiver, message)
#message = message
mail(:subject => "[YourWebsite.tld] #{message.subject}",
:to => receiver.email_address) # or something similar
end
Then you can invoke your mailer like this
NotificationEmail.new_message(a_user, a_message).deliver
To read the API see here or here (I prefer APIdock).
Also a more comprehensive guide on ActionMailer is available here. If you are new to Rails, you can find more guides here.
I'm trying to implement an ActionMailer function that will send out a newsletter to a specific user. I want to make sure that the newsletter is only sent to subscribed users. I tried implementing it like so:
class UserMailer < ActionMailer::Base
def newsletter(user)
return unless user.subscribed # This still renders my mailer view
mail(:to => user.email, :subject => "Newsletter")
end
end
The problem is that the return unless user.subscribed line still appears to be rendering the mailer view and is still sent by the calling code (from a cron job):
task :cron => :environment do
User.where(:subscribed => true).each do |user|
UserMailer.newsletter(user).deliver
end
end
Note that I do have that subscription logic in my cron job as well for performance reasons (shouldn't have to iterate over ALL users, only those that are subscribed). However, it feels like the UserMailer class is the right place for this logic to exist (otherwise any other location that calls the newsletter method will need to check the subscribed flag as well.
The Mailer, IMHO, is the wrong place for this logic. The mailer should do nothing but format and send messages. The logic to decide whether or not to send should be within the calling block of code. It's not the right way, but something as simple as:
UserMailer.newsletter(user).deliver if user.subscribed?
Alternately, as you mentioned, you shouldn't have to iterate over all users, just the subscribed. So with a scope in the User model called subscribed:
User.subscribed.each do |user|
UserMailer.newsletter(user).deliver
end
This way you don't need to test on a per-user basis; only the subscribed users are included, and the logic is in the calling block, not in the mailer.
I want send newsletter to all registered users, but only the last user (in database) receive the mail.
def letter(nletter)
#nletter = nletter
#users=Newsletter.all
#users.each do |users|
mail(:to => users.email, :subject => #nletter.subject)
end
end
Whats wrong?
If the code you showed us is in your Mailer class, it's because the mail method just sets various attributes on a Mail object. When you loop through all your users and call mail for each one, you're just resetting the subject and (more importantly) the recipient on a single Mail object. So when the function finishes and the Mail is sent out, the current recipient is the last user you set it to - the last user in your database.
You can get around this in two different ways. If it's a generic e-mail (everybody gets the same exact message), you can just pass an array of e-mail addresses as :to:
Note: As Josh points out in the comments, you'll pretty much always want to use :bcc instead of :to, so you don't end up broadcasting your entire mailing list.
def letter(nletter)
#nletter = nletter
#users=Newsletter.all
mail(:to => #users.map(&:email), :subject => #nletter.subject)
end
But if each user gets a custom e-mail ("Hi, #{username}!" or somesuch), you'll have to create a new Mail object for each user. You can do this by calling Mailer.deliver_letter(nletter, user) for each user:
# Somewhere outside of your Mailer:
nletter = "...whatever..."
Newsletter.all.each do |user|
Mailer.deliver_letter(nletter, user)
end
# And your mailer function would look like:
def letter(nletter, user)
#nletter = nletter
mail(:to => user.email, :subject => #nletter.subject)
end
Hope this helps!