In my development environment, I use a copy of the production database when testing locally. For reasons both for testing and simply for protection against sending out test/dev emails to real users, what's the best way to override the mail-to address when in development mode?
I know I can write logic in each mailer, but I have several and it would be nice to put it all in once place. Can I override the mail() method somehow to make the :to parameter always point at an email address I specify?
I use an ActionMailer interceptor so all mails sent while in development or test environments are sent to the same address. Like this:
# config/initializers/override_mail_recipient.rb
if Rails.env.development? or Rails.env.test?
class OverrideMailRecipient
def self.delivering_email(mail)
mail.to = 'to#example.com'
end
ActionMailer::Base.register_interceptor(OverrideMailRecipient)
end
end
I think the best option is to use a service like mailtrap.io: http://mailtrap.io or mailcatcher gem: https://rubygems.org/gems/mailcatcher
What I like to do is configure action mailer in the development environment to use mailtrap.
You could do a default of
class UserMailer < ActionMailer::Base
default :to=> "to#example.com"
end
And then make the address an option in the methods. That way it would default to the :to you set. Another idea I had was a bit more:
class UserMailer < ActionMailer::Base
attr_accessor :email_address
def initialize
if RAILS_ENV == "development"
#email_address = "to#example.com"
end
end
end
That would require you to set a new address in your code but it would be overwritten each time in Development.
Related
I use this class for Action Mailer:
class UserMailer < ActionMailer::Base
default from: "something#example.com",
reply_to: 'whatever#example.com'
def mail_method
mail(to: 'email#example.com', subject: "SUBJECT")
end
end
So like this I got many classes and methods which send emails like this from smtp delivery method.
But now I want to perform_deliveries , i.e. send emails only on production environment not in development or test environment.
So for that I want to use my email only, which is why I need to override mail method.
Things I have tried.
-> Making a function to return email, where function name is get_right_email
class UserMailer < ActionMailer::Base
default from: "something#example.com",
reply_to: 'whatever#example.com'
def mail_method
mail(to: get_right_email('email#example.com'), subject: "SUBJECT")
end
end
And definition of get_right_email is as follows:
def get_right_email(email)
if(Rails.env=='production')
return email
else
return 'myPersonalEmail#example.com'
end
end
It would need some refactoring but it is still manageable. Will take a few hours and I can do, but is there a quicker way where I can just override mail function.
In your config > enviroments folder you should have a file for production, development and test. Here you can specify your settings for each one.
Settings in these folders overide those in config > application.rb
For example when testing I don't usually actually send the emails but I do want to be able to test the emails so I use
config.action_mailer.delivery_method = :test
This makes the emails accessible by calling ActionMailer::Base.deliveries
You can set the default from email address in these files as well using:
config.action_mailer.default_options = {
:from => "foo#bar.com"
}
Theres a gem called letter_opener managed by the fantastic Ryan Bates which instead of sending an email in development opens the email in a new tab. This makes testing out emails in development a breeze.
Letter Opener
UPDATE BELOW ------
Apologies, I didn't quite follow what you were looking for.
Rails has webhooks you can use to intercept emails and redirect them. You'll want to use an environment different than production.
The test environment is typically used for automated testing, to keep things clear you might want to consider setting up a new environment (eg: staging).
To create a new environment just create a new file in config/environments/ and give it a suitable name - eg: staging.rb
You can then call Rails.env.staging? where ever you like.
Anyway back to the main event...
To intercept the emails first create an intercept class:
class StagingEmailInterceptor
def self.delivering_email(message)
message.to = ['my#email.com']
end
end
and then create an initializer file, eg:
config/initializers/staging_email_interceptor.rb
and inside do this:
if Rails.env.staging?
ActionMailer::Base.register_interceptor(StagingEmailInterceptor)
end
That way all emails sent in the staging environment will be sent to your email.
I'm using Rails 4.2 want to override the to field for all ActionMailer mailers for a certain environment. In this case I want to override the to field for all mailers used in Staging. My goal is for the staging environment to deliver mail exactly the same way as production, but to dump it all into a testing inbox.
I know there are services that assist with this, but my goal is to use my production API for staging delivery as a thorough test.
I'm hoping I can use a mixin or something to reset the to field before the mailer fires off.
Not sure what version of Rails you are using, but you might consider using the new mail interceptors to accomplish this.
Main advantage is that it doesn't clutter your ActionMailer classes directly.
http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
Copying their example:
class SandboxEmailInterceptor
def self.delivering_email(message)
message.to = ['sandbox#example.com']
end
end
config/initializers/sandbox_email_interceptor.rb:
ActionMailer::Base.register_interceptor(SandboxEmailInterceptor) if Rails.env.staging?
The simplest way would be to check which environment is running and set the to field accordingly. For example, a simple password reset mailer might look something like:
class UserMailer < ActionMailer::Base
default from: "support#example.com"
def reset_password(user_id)
#user = User.find(user_id)
#url = reset_password_users_url(token: #user.password_reset_token)
mail(to: #user.email, subject: '[Example] Please reset your password')
end
end
Now to check for the staging environment and route all of these emails to admin#example.com:
class UserMailer < ActionMailer::Base
default from: "support#example.com"
def reset_password(user_id)
#user = User.find(user_id)
#url = reset_password_users_url(token: #user.password_reset_token)
to = Rails.env.staging? ? 'admin#example.com' : #user.email
mail(to: to, subject: '[Example] Please reset your password')
end
end
I need to be able to generate links to sites that all run off my app but which have different domains (I'm running a whitelabel service).
The email being sent on behalf of these domains to set a different host depending on the mailing.
Normally I'd setup the host value application.rb:
config.action_mailer.default_url_options[:host] = 'myhost.com'
However, because my host varies according to the link I'm trying to do this at runtime instead.
user_mailer.rb:
Rails.configuration.action_mailer.default_url_options[:host] = new_host
mail(...)
The problem is that every time I run this it continues to use whatever's defined in application.rb. I can't seem to get the application to respect the newly defined value of default_url_optiions[:host]. What am I doing wrong?
The default_url_options method is set on ActionMailer using class_attribute which is defined in ActiveSupport core extensions for Class. According to the documentation it also provides an instance level accessor that can be overridden on a per instance basis without affecting the class level method. So you should be able to override the host setting directly for each email
class UserMailer < ActionMailer::Base
def welcome(user)
#user = user
# don't need this if you override #mail method.
self.default_url_options = default_url_options.merge(host: #user.host)
mail(to: user.email, subject: "Welcome")
end
# might be better to override mail method if you need it for all emails in
# a particular mailer
private
def mail(headers, &block)
self.default_url_options = default_url_options.merge(host: #user.host)
super
end
end
This should allow you to modify the setting at runtime; please ignore the #user.host call and replace it with however you would determine the host.
If there aren't a whole lot of views you could simply define the host on the url_for helper, and if there are too many views I'd suggest you write your own helper that wraps the url_for helper with the :host => 'mysite.com'.
for non-production rails 2.x environments i want to block/filter any outgoing emails that aren't addressed to people in my organization (e.g. "*#where-i-work.com").
please note, i don't want to block email entirely - i know i can just write them to the logs in test mode - i need emails to internal employees to be delivered.
thanks.
You could try extending the Mail::Message.deliver function in your environment.rb file - something like (not tested - just demo code!):
class Mail::Message
def deliver_with_recipient_filter
self.to = self.to.to_a.delete_if {|to| !(to =~ /.*#where-i-work.com\Z/)} if RAILS_ENV != production
self.deliver_without_recipient_filter unless self.to.blank?
end
alias_method_chain :deliver, :recipient_filter
end
Note that this id for Rails 3 - I think all versions of Rails 2 use TMail instead of Mail, so you'll need to override something else if you're not using Rails 3.
Hope this helps!
based on #Xavier's rails 3 proposal i was able to get it working in rails 2:
class ActionMailer::Base
def deliver_with_recipient_filter!(mail = #mail)
unless 'production' == Rails.env
mail.to = mail.to.to_a.delete_if do |to|
!to.ends_with?('where-i-work.com')
end
end
unless mail.to.blank?
deliver_without_recipient_filter!(mail)
end
end
alias_method_chain 'deliver!'.to_sym, :recipient_filter
end
I'm wondering if it's possible to configure a Rails email derived from ActionMailer to send to a different recipient based on the environment. For example, for development I'd like it to send mail to my personal email so I don't clog up our company email account with "Testing" emails; for production however I want it to use the real address.
How can I achieve this?
The mail_safe plugin might be a little over kill.
A simple initializer will do
Rails 2.x
if Rails.env == 'development'
class ActionMailer::Base
def create_mail_with_overriding_recipients
mail = create_mail_without_overriding_recipients
mail.to = "mail#example.com"
mail
end
alias_method_chain :create_mail, :overriding_recipients
end
end
Rails 3.x
if Rails.env == 'development'
class OverrideMailReciptient
def self.delivering_email(mail)
mail.to = "mail#example.com"
end
end
ActionMailer::Base.register_interceptor(OverrideMailReciptient)
end
By default the development environment isn't setup to actually send emails (it just logs them).
Setting up alternate accounts can be done in many different ways. You can either use some logic in your mailer like so...
recipients (Rails.env.production? ? "email#company.com" : "test#non-company.org")
Or you can define the recipient as a constant in the environment files like so:
/config/environment/production.rb
EMAIL_RECIPIENT = "email#company.com"
/config/environment/development.rb
EMAIL_RECIPIENT = "test#non-company.org"
and then use the constant in your mailer. example:
recipients EMAIL_RECIPIENT
Also, there are several plugins that do this. The best one that I've found of the three I looked at was mail_safe.