How to set ActionMailer SMTP settings in a YML file? - ruby-on-rails

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?

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.

You can use config_for for this:
Portfolio::Application.config.action_mailer.smtp_settings = config_for(:smtp)

Portfolio::Application.config.action_mailer.smtp_settings = config_for(:smtp).symbolize_keys

Related

undefined method `each' for #<String:0x00000003a27a58>

First, I do really apologize, since I'm still newbie on this.
I was trying to install Fat Free CRM by following the instruction on this following sites:
http://www.blogdugeek.fr/crm-installation-fat-free-crm-debian-squeeze/
http://guides.fatfreecrm.com/Setup-Linux-or-Mac-OS.html
As I follow the instructions, I've encounter some errors and resolved some. However, upon executing this command:
RAILS_ENV=production rake db:create db:migrate crm:settings:load
I was stuck in this command line and here are the following errors that I've been stuck with:
rake aborted!
NoMethodError: undefined method `each' for #<String:0x00000003a27a58>
/usr/local/rvm/gems/ruby-2.2.4/gems/activerecord-4.2.6/lib/active_record/connection_adapters/connection_specification.rb:150:in `resolve_all'
/usr/local/rvm/gems/ruby-2.2.4/gems/activerecord-4.2.6/lib/active_record/connection_handling.rb:69:in `resolve'
/usr/local/rvm/gems/ruby-2.2.4/gems/activerecord-4.2.6/lib/active_record/core.rb:46:in `configurations='
/usr/local/rvm/gems/ruby-2.2.4/gems/activerecord-4.2.6/lib/active_record/railties/databases.rake:5:in `block (2 levels) in <top (required)>'
/usr/local/rvm/gems/ruby-2.2.4/bin/ruby_executable_hooks:15:in `eval'
/usr/local/rvm/gems/ruby-2.2.4/bin/ruby_executable_hooks:15:in `<main>'
Tasks: TOP => db:create => db:load_config
(See full trace by running task with --trace)
As I've search for more related issue, I found some, but it's still no use.
Also, here are some data that might be needed:
Ruby Version
ruby 2.2.4p230 (2015-12-16 revision 53155) [x86_64-linux]
Rails Version
Rails 4.2.6
Here are the error lines
connection_specification.rb
def resolve(config)
if config
resolve_connection config
elsif env = ActiveRecord::ConnectionHandling::RAILS_ENV.call
resolve_symbol_connection env.to_sym
else
raise AdapterNotSpecified
end
end
# Expands each key in #configurations hash into fully resolved hash
def resolve_all
config = configurations.dup
config.each do |key, value| <---- Error line
config[key] = resolve(value) if value
end
config
end
connection_handling.rb
class MergeAndResolveDefaultUrlConfig # :nodoc:
def initialize(raw_configurations)
#raw_config = raw_configurations.dup
#env = DEFAULT_ENV.call.to_s
end
# Returns fully resolved connection hashes.
# Merges connection information from `ENV['DATABASE_URL']` if available.
def resolve
Error line ----> ConnectionAdapters::ConnectionSpecification::Resolver.new(config).resolve_all
end
private
def config
#raw_config.dup.tap do |cfg|
if url = ENV['DATABASE_URL']
cfg[#env] ||= {}
cfg[#env]["url"] ||= url
end
end
end
core.rb
def self.configurations=(config)
Error line ---> ##configurations = ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(config).resolve
end
self.configurations = {}
# Returns fully resolved configurations hash
def self.configurations
##configurations
end
databases.rake
db_namespace = namespace :db do task :load_config do
Error line ----> ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration || {}
ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
Here's the config/database.yml file.
# MySQL. Versions 4.1 and 5.0 are recommended.
#
# Install the MySQL driver:
# gem install mysql2
#
# And be sure to use new-style password hashing:
# http://dev.mysql.com/doc/refman/5.0/en/old-client.html
#------------------------------------------------------------------------------
development:&development
adapter:mysql2
encoding:utf8
database:fat_free_crm_development
pool:5
username:root
# password:
socket:/var/run/mysqld/mysqld.sock
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *development
database: fat_free_crm_test
production:
adapter: mysql
encoding: utf8
database: fat_free_crm_production
pool: 5
username: root
password:
socket: /var/run/mysqld/mysqld.sock
socket: /tmp/mysql.sock
staging:
<<: *development
database: fat_free_crm_staging
Hope to hear and seek some advises and learning.
If there's need some more information, let me know.
Thanks,
Your database.yml is the problem. YAML requires a separator between the key and data.
So not like this:
production:
adapter:mysql
encoding:utf8
...
But like this:
production:
adapter: mysql
encoding: utf8
...
You need to correct all the lines in the file, because you have this error everywhere.
Check the database.yml file again. Don't add anything
ie the file format must be correct.
had unkowngly comment a line on to that was causing the error.

Rake task create not getting Rails.env in db:create for ActiveRecord>=4.05

For ActiveRecord 3.2.18, in /lib/active_record/railties/databases.rake:
task :load_config do
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH)
if engine.paths['db/migrate'].existent
ActiveRecord::Migrator.migrations_paths += engine.paths['db/migrate'].to_a
end
end
end
desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)'
task :create => [:load_config, :rails_env] do
if ENV['DATABASE_URL']
create_database(database_url_config)
else
configs_for_environment.each { |config| create_database(config) }
ActiveRecord::Base.establish_connection(configs_for_environment.first)
end
end
For ActiveRecord 4.0.5+, in /lib/active_record/railties/databases.rake:
task :load_config do
ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration || {}
ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
end
desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)'
task :create => [:load_config] do
if ENV['DATABASE_URL']
ActiveRecord::Tasks::DatabaseTasks.create_database_url
else
ActiveRecord::Tasks::DatabaseTasks.create_current
end
end
When I call bundle exec rake db:create from a Rakefile (for a gem I'm testing), ActiveRecord 3.2.18, ActiveRecord::Base.configurations gets the information it needs from my test/config/database.yml file via Rails.application.config.database_configuration. But when calling db:create using ActiveRecord 4.0.5+, ActiveRecord::AdapterNotSpecified: database configuration does not specify adapter error. It doesn't matter whether I call it with RAILS_ENV=some_environment. How do I give ActiveRecord 4.0.5+ the database configuration it needs without monkey patching it? The ideal solution is to somehow do it my Rakefile.
I had the same problem, and I "fixed" with a monkey patch:
in your Rakefile, after you imported the active_record, I did the following:
# hack to make it works with sqlite3
module Rails
def self.root
File.dirname(__FILE__)
end
def self.env
"development"
end
end
for sure you can do whatever you want inside your "self.env" method.

Figaro environment variables won't set for Heroku

I generated a skeleton application using Rails Composer and included Figaro. It runs successfully locally. Before I modify it, I am pushing it down to Heroku. However, the heroku run rake db:seed failed. I've come to find out that the app/config/application.yml is .gitignored. So, I need to use rake figaro:heroku to set the environment variables before I run heroku run rake db:seed. But, the rake Figaro:heroku is failing as follows:
D:\BitNami\rubystack-2.0.0-11\projects\myapp>rake figaro:heroku
! Usage: heroku config:set KEY1=VALUE1 [KEY2=VALUE2 ...]
! Must specify KEY and VALUE to set.
This looks like it is just ignoring my app/config/application.yml and asking for line directed input to me, but I don't know. Again, the application runs successfully locally, so that application.yml should be correct. Here it is:
MANDRILL_USERNAME: valid.address#gmail.com
MANDRILL_APIKEY: a.valid.apikey
ADMIN_NAME: Admin Name
ADMIN_EMAIL: valid.address#gmail.com
ADMIN_PASSWORD: validpassword
ROLES: [admin, user, VIP]
The failure occurs in seeds when I issue heroku run rake db:seed. The file is:
puts 'ROLES'
YAML.load(ENV['ROLES']).each do |role|
Role.find_or_create_by_name(role)
puts 'role: ' << role
end
puts 'DEFAULT USERS'
user = User.find_or_create_by_email :name => ENV['ADMIN_NAME'].dup, :email => ENV['ADMIN_EMAIL'].dup, :password => ENV['ADMIN_PASSWORD'].dup, :password_confirmation => ENV['ADMIN_PASSWORD'].dup
puts 'user: ' << user.name
user.confirm!
user.add_role :admin
It fails on the first access to variable role because ENV['ROLES'] is uninitialized. It would be initialized by application.yml, and is locally, but it is .gitignored. Thus, the need for rake Figaro:heroku to succeed.
This seems so simple, especially since it runs smoothly locally. OBTW, I have tried application.yml as shown and with the strings double-quoted but it doesn't seem to make a difference in any case so...
Ideas? Thanks...
I understand from the path you're mentioning that this is a Windows question. Problem is that the arrays are not correctly dealt with on Windows. Workaround I once made is to override the "vars" method of Heroku in a rake file in lib/tasks, like
module Figaro
module Tasks
class Heroku # < Struct.new(:app)
def vars
Figaro.env(environment).map { |key, value|
if value.start_with? "["
value = "'#{value.gsub('"', '')}'"
elsif value.include? " "
value = "'#{value}'"
end
"#{key}=#{value}"
}.sort.join(" ")
end
end
end
end
I'd surmise the problem will likely be with Figaro's processing of your different variable types:
MANDRILL_USERNAME: "valid.address#gmail.com"
MANDRILL_APIKEY: "a.valid.apikey"
ADMIN_NAME: "Admin Name"
ADMIN_EMAIL: "valid.address#gmail.com"
ADMIN_PASSWORD: "validpassword"
ROLES: ["admin", "user", "VIP"]
Try removing any spaces & ensuring you only send KEY: "VALUE" to Figaro. Your spaces are basically going to cause the system to misinterpret it

Using ENV files in production on Heroku

I've got the following configuration line in my production.rb environment file, as instructed in this article:
config.cache_store = :mem_cache_store, ENV["MEMCACHEDCLOUD_SERVERS"].split(','), { :username => ENV["MEMCACHEDCLOUD_USERNAME"], :password => ENV["MEMCACHEDCLOUD_PASSWORD"] }
But when I try to deploy, I'll get an error:
Running: rake assets:precompile rake aborted!
undefined method split' for nil:NilClass
/tmp/build_abdc.../config/environments/production.rb:107:inblock in '
This is because config vars are not available during compilation. There's a Heroku labs add-on that you can supposedly use to remedy this, but it comes with a warning from Heroku that "Using this labs feature is considered counter to Heroku best practices."
So then what is the best practice when it comes to using ENV vars in production config? Should they just all be wrapped in rescue handlers so that Heroku ignores them during compilation?
We ended up just checking for the ENV var before assigment. It looks like that's the pattern you need whenever you use ENV vars in config/initializers on Heroku:
# NOTE: ENV vars aren't available during slug compilation, so we must check if they exist:
if ENV["MEMCACHEDCLOUD_SERVERS"]
config.cache_store = :mem_cache_store, ENV["MEMCACHEDCLOUD_SERVERS"].split(','), { :username => ENV["MEMCACHEDCLOUD_USERNAME"], :password => ENV["MEMCACHEDCLOUD_PASSWORD"] }
end
See also:
https://devcenter.heroku.com/articles/rails-asset-pipeline#failures-in-the-assets-precompile-task

Correct Ruby on Rails 3 replacement for ENV["RAILS_ENV"] ||= 'production'?

We're doing an upgrade to Ruby on Rails 3 (like half the world right now), and I've been diligently replacing usages of RAILS_ENV, for example
RAILS_ENV == 'wibble'
# becomes
Rails.env.wibble?
But I'm not as certain of what to do with:
ENV["RAILS_ENV"] ||= 'production'
We've got it at the top of a whole bunch of Rake tasks and daemons, and the idea is that you can pass RAILS_ENV on the command-line, but it defaults to 'production' if it's not passed.
I'm not sure of the new Rails3-appropriate way of doing this. So for now my rails:upgrade:check is complaining mightily of this intrusion of Rails2-ishness...
I don't know if:
::Rails.env ||= 'production'
will work.
Does Rails.env exist in a daemon?
Does it automagickally get pre-populated with the value of RAILS_ENV passed on the command-line or do we need a new way of invoking the daemons?
What is the correct mantra for this?
Update:
Looking into the source-code for Rails.env,
def env
#_env ||= ActiveSupport::StringInquirer.new(RAILS_ENV)
end
we can deduce a number of things.
Firstly, it looks like RAILS_ENV does actually still exist - which means it can be set and Rails.env will find it...
If Rails is valid in the context of a daemon, then nothing more needs to be done. If not - then I could just not care much and use the old RAILS_ENV as before.
Rails.env is actually of type ActiveSupport::StringInquirer, which overrides method_missing in order to provide that nice equality syntax. Check: http://api.rubyonrails.org/classes/ActiveSupport/StringInquirer.html
So, if you want to override it to be "production" by defaut, you should write:
Rails.env ||= ActiveSupport::StringInquirer.new('production')
However, you'll have to check which is the uninitialized value of Rails.env, I'm not sure it's really nil.
The best course of action, IMO, is to just prepend env RAILS_ENV=production to all your scripts.
Edit lib/tasks/environments.rake
# Sets environments as needed for rake tasks
%w[development production staging].each do |env|
desc "Runs the following task in the #{env} environment"
task env do
Rails.env = env
end
end
task :testing do
Rake::Task["test"].invoke
end
task :dev do
Rake::Task["development"].invoke
end
task :prod do
Rake::Task["production"].invoke
end
Source
UPDATE
pass RAILS_ENV=production via command line, something like this:
RAILS_ENV=production rake db:setup
Does this help:
# before
if RAILS_ENV == 'production'
...
# Rails 3
if Rails.env.production?
if Rails.env.production?
puts '...'

Resources