Search code examples
ruby-on-railsconfigurationactionmailer

How to set ActionMailer SMTP settings in a YML file?


For my deployment, I prefer to have all Rails configuration options in YML files under config/. I am having particular problems with action_mailer.smtp_settings.

I have a file, config/smtp.yml :

development:
  address: localhost
  port: 1025

test:

production:
  address: smtp.xs4all.nl
  port: 465
  authentication: plain
  user_name: xxxxx
  password: xxxxx
  tls: true

I then try to include these values via an initializer. config/initializers/smtp.rb:

options = YAML.load_file(Rails.root.join('config', 'smtp.yml'))[Rails.env]

options.each do |name, value|
  Portfolio::Application.config.action_mailer.smtp_settings[name.to_sym] = value
end unless options.nil?

This loads the smtp.yml file, parses that and returns the hash for the current environment, e.g.

 {"address"=>"smtp.xs4all.nl", "port"=>465, "authentication"=>"plain", "user_name"=>"xxxxx", "password"=>"xxxxx", "tls"=>true}

Converts the keys to symbols and adds that to the smtp-settings, resuling in e.g:

  irb(main):002:0> Portfolio::Application.config.action_mailer.smtp_settings
  => {:port=>465, :address=>"smtp.xs4all.nl", :authentication=>"plain", :user_name=>"xxxxx", :password=>"xxxx", :tls=>true}

But when deploying to production, this causes some conflict, probably because some part has not been booted yet. Capistrano fails with:

   * executing "cd -- /var/www/ANT_cms/releases/20131218170336 && bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile"
servers: ["li153-5.members.linode.com"]
[li153-5.members.linode.com] executing command
 ** [out :: li153-5.members.linode.com] rake aborted!
 ** [out :: li153-5.members.linode.com] undefined method `[]=' for nil:NilClass

Why is my Portfolio::Application.config.action_mailer.smtp_settings nil here? How can I make sure that actionMailers config options are already loaded?


Solution

  • Digging deeper, I found the issue is that bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile" loads only a small subset, and not e.g. ActionMailer.

    Adding an early exit to my loader fixes this in config/initializers/smtp.rb:

    unless Portfolio::Application.config.action_mailer.nil?
    
      options = YAML.load_file(Rails.root.join('config', 'smtp.yml'))[Rails.env]
    
      options.each do |name, value|
        Portfolio::Application.config.action_mailer.smtp_settings[name.to_sym] = value
      end unless options.nil?
    
    end
    

    Then, the next issue is that action_mailer in production is set, but that smtp_settings does not have to be defined and filled with defaults, yet. So prefilling this with an empty hash solves the entire issue:

    unless Portfolio::Application.config.action_mailer.nil?
    
      Portfolio::Application.config.action_mailer.smtp_settings = {}
    
      options = YAML.load_file(Rails.root.join('config', 'smtp.yml'))[Rails.env]
    
      options.each do |name, value|
        Portfolio::Application.config.action_mailer.smtp_settings[name.to_sym] = value
      end unless options.nil?
    end
    

    Still, somewhat ugly, but it does the job.