可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a need to use two different smtp servers in a Rails application. It appears that the way ActionMailer is constructed, it is not possible to have different smtp_settings for
a subclass. I could reload the smtp settings for each mailer class whenever a message is being sent, but that messes up the ExceptionNotifier plugin which is outside my control (unless I mess with it too). Does anyone have a solution/plugin for something like
this?
Ideally I would like to have
class UserMailer < ActionMailer::Base; end
and then set in environment.rb
ActionMailer::Base.smtp_settings = standard_smtp_settings
UserMailer.smtp_settings = user_smtp_settings
Thus, most of my mailers including ExceptionNotifier would pickup the default settings, but the UserMailer would use a paid relay service.
回答1:
Based on the Oreilly article, I came up with the solution I wrote about here:
http://transfs.com/devblog/2009/12/03/custom-smtp-settings-for-a-specific-actionmailer-subclass
Here's the relevant code:
class MailerWithCustomSmtp < ActionMailer::Base
SMTP_SETTINGS = {
:address => "smtp.gmail.com",
:port => 587,
:authentication => :plain,
:user_name => "custom_account@transfs.com",
:password => 'password',
}
def awesome_email(bidder, options={})
with_custom_smtp_settings do
subject 'Awesome Email D00D!'
recipients 'someone@test.com'
from 'custom_reply_to@transfs.com'
body 'Hope this works...'
end
end
# Override the deliver! method so that we can reset our custom smtp server settings
def deliver!(mail = @mail)
out = super
reset_smtp_settings if @_temp_smtp_settings
out
end
private
def with_custom_smtp_settings(&block)
@_temp_smtp_settings = @@smtp_settings
@@smtp_settings = SMTP_SETTINGS
yield
end
def reset_smtp_settings
@@smtp_settings = @_temp_smtp_settings
@_temp_smtp_settings = nil
end
end
回答2:
Solution for Rails 4.2+:
config/secrets.yml:
production:
gmail_smtp:
:authentication: "plain"
:address: "smtp.gmail.com"
:port: 587
:domain: "zzz.com"
:user_name: "zzz@zzz.com"
:password: "zzz"
:enable_starttls_auto: true
mandrill_smtp:
:authentication: "plain"
:address: "smtp.mandrillapp.com"
:port: 587
:domain: "zzz.com"
:user_name: "zzz@zzz.com"
:password: "zzz"
:enable_starttls_auto: true
mailgun_smtp:
:authentication: "plain"
:address: "smtp.mailgun.org"
:port: 587
:domain: "zzz.com"
:user_name: "zzz@zzz.com"
:password: "zzz"
:enable_starttls_auto: true
config/environments/production.rb:
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = Rails.application.secrets.gmail_smtp
app/mailers/application_mailer.rb:
class ApplicationMailer < ActionMailer::Base
default from: '"ZZZ" <zzz@zzz.com>'
private
def gmail_delivery
mail.delivery_method.settings = Rails.application.secrets.gmail_smtp
end
def mandrill_delivery
mail.delivery_method.settings = Rails.application.secrets.mandrill_smtp
end
def mailgun_delivery
mail.delivery_method.settings = Rails.application.secrets.mailgun_smtp
end
end
app/mailers/user_mailer.rb:
class UserMailer < ApplicationMailer
# after_action :gmail_delivery, only: [:notify_user]
after_action :mandrill_delivery, only: [:newsletter]
after_action :mailgun_delivery, only: [:newsletter2]
def newsletter(user_id); '...' end # this will be sent through mandrill smtp
def newsletter2(user_id); '...' end # this will be sent through mailgun smtp
def notify_user(user_id); '...' end # this will be sent through gmail smtp
end
回答3:
For anybody approaching this issue with later versions (3+) of Rails, try this
http://guides.rubyonrails.org/action_mailer_basics.html#sending-emails-with-dynamic-delivery-options
回答4:
class UserMailer < ActionMailer::Base
def welcome_email(user, company)
@user = user
@url = user_url(@user)
delivery_options = { user_name: company.smtp_user,
password: company.smtp_password,
address: company.smtp_host }
mail(to: @user.email,
subject: "Please see the Terms and Conditions attached",
delivery_method_options: delivery_options)
end
end
Rails 4 allows for dynamic delivery options. The above code is straight from the action mailer basics guide, which you can find here: http://guides.rubyonrails.org/v4.0/action_mailer_basics.html#sending-emails-with-dynamic-delivery-options
With this, it is possible to have different smtp settings for every email you send, or, like in your use case for different sub classes like UserMailer, OtherMailer etc.
回答5:
Tried to use jkrall's option with Rails 3.2.1 but for some reason it wouldn't override default configuration, but doing:
MyMailer.my_email.delivery_method.settings.merge!(SMTP_SETTINGS).deliver
Similar to http://www.scottw.com/multiple-smtp-servers-with-action-mailer, made it work.
回答6:
Rails-2.3.*
# app/models/user_mailer.rb
class UserMailer < ActionMailer::Base
def self.smtp_settings
USER_MAILER_SMTP_SETTINGS
end
def spam(user)
recipients user.mail
from 'spam@example.com'
subject 'Enlarge whatever!'
body :user => user
content_type 'text/html'
end
end
# config/environment.rb
ActionMailer::Base.smtp_settings = standard_smtp_settings
USER_MAILER_SMTP_SETTINGS = user_smtp_settings
# From console or whatever...
UserMailer.deliver_spam(user)
回答7:
I'm afraid it's not doable natively.
But you can trick it a bit by modifying the @@smtp_settings variable in the model.
There's an article on Oreilly which explains it pretty well even though they code is not clean at all.
http://broadcast.oreilly.com/2009/03/using-multiple-smtp-accounts-w.html
回答8:
https://github.com/AnthonyCaliendo/action_mailer_callbacks
I found this plugin helped solve the problem for me pretty easily (as in < 5 mins). I simply change the @@smtp_settings for a particular mailer in the before_deliver and then change it back to the defaults in the after_deliver. Using this approach, I only have to add the callbacks to mailers that need @@smtp_settings different than the default.
class CustomMailer < ActionMailer::Base
before_deliver do |mail|
self.smtp_settings = custom_settings
end
after_deliver do |mail|
self.smtp_settings = default_settings
end
def some_message
subject "blah"
recipients "blah@blah.com"
from "ruby.ninja@ninjaness.com"
body "You can haz Ninja rb skillz!"
attachment some_doc
end
end
回答9:
Here's another solution, which, while it looks ridiculous, I think is a little bit cleaner and easier to reuse in different AM::Base classes:
module FTTUtilities
module ActionMailer
module ClassMethods
def smtp_settings
dict = YAML.load_file(RAILS_ROOT + "/config/custom_mailers.yml")[self.name.underscore]
@custom_smtp_settings ||= HashWithIndifferentAccess.new(dict)
end
end
module InstanceMethods
def smtp_settings
self.class.smtp_settings
end
end
end
end
example Mailer:
class CustomMailer < ActionMailer::Base
extend FTTUtilites::ActionMailer::ClassMethods
include FTTUtilites::ActionMailer::InstanceMethods
end
回答10:
Solution for Rails 3.2:
class SomeMailer < ActionMailer::Base
include AbstractController::Callbacks
after_filter :set_delivery_options
private
def set_delivery_options
settings = {
:address => 'smtp.server',
:port => 587,
:domain => 'your_domain',
:user_name => 'smtp_username',
:password => 'smtp_password',
:authentication => 'PLAIN' # or something
}
message.delivery_method.settings.merge!(settings)
end
end
Solution inspired by How to send emails with multiple, dynamic smtp using Actionmailer/Ruby on Rails
回答11:
When I wanted a quick test in the console (Rails 5) I did the following:
settings = { username: "" } # etc
mailer = MyMailer.some_method
mailer.delivery_method.settings.merge!(settings)
mailer.deliver