mongoid yaml combines environment variables for hosts - ruby-on-rails

I am new to rails and mongoid, I have mongoid.yml file that contains the entries as follows:
development:
# Configure available database clients. (required)
clients:
# Defines the default client. (required)
default:
# Defines the name of the default database that Mongoid can connect to.
# (required).
database: mycollectionname
# Provides the hosts the default client can connect to. Must be an array
# of host:port pairs. (required)
hosts:
- localhost:27017
this works properly for development, however, in production, I'd like to specify the host from environment variables like ENV['OPENSHIFT_MONGODB_DB_HOST'] + ":" + ENV['OPENSHIFT_MONGODB_DB_PORT']
I've tried various ways such as this
hosts:
- <%= \"#{ENV['OPENSHIFT_MONGODB_HOST']}:#{ENV['OPENSHIFT_MONGODB_PORT']}\" %>
or
hosts:
- #{ENV['OPENSHIFT_MONGODB_HOST']:ENV['OPENSHIFT_MONGODB_PORT']}
etc, but none works

In yaml code, <%= %> is meant for you to insert ruby code, you can use Expression Substitution inside it to format your url
Expression substitution is a means of embedding the value of any Ruby expression into a string using #{ and }
Something like this will do:
<%= "#{ENV['OPENSHIFT_MONGODB_HOST']}:#{ENV['OPENSHIFT_MONGODB_PORT']}" %>
In my mongoid project with openshift, I am using its uri: field like this:
uri: <%= "#{ENV['OPENSHIFT_MONGODB_DB_URL']}#{ENV['OPENSHIFT_APP_NAME']}" %>
Please also pay attention to the indentation, it has to be accurate and it has to be space! Tab will cause problems too!

Related

Rails: configuring applications

I am reading and coding along with a tutorial.
I have an application.yml file with some constants created to hold data. To include those constants in ENV and initialize them at start, this code was given :
config_file = Rails.application.config_for(:application) #this is the bothersome part
config_file.each do |key,value|
ENV[key] = value
end unless config_file.nil?
I fail to fully understand this code. In particular, on the first line, where do the chained objects come from what do they mean and how do I create such on my own ?
It loads data from a config file into the app. The example from the docs about #config_for:
#config/app.yml:
production:
url: http://127.0.0.1:8080
namespace: my_app_production
development:
url: http://localhost:3001
namespace: my_app_development
If you do rails c and type Rails.application.config_for(:app) into the console, you'll get:
{"url"=>"http://localhost:3001", "namespace"=>"my_app_development"}
which is just a regular hash you can loop through using #each or access it's values through keys.

Issues using config/secrets.yml variable set from ENV variable on Rails 4.1.0

Have not played with Rails in ages so walking through the Learn Ruby on Rails tutorial which is excellent.
I'm having issues with google authentication, the example code calls the config/secrets.yml variables (which are read from ENV in the shell) from the app/models/contact.rb model update_spreadsheet method below
def update_spreadsheet
connection = GoogleDrive.login(Rails.application.secrets.gmail_username, Rails.application.secrets.gmail_password)
I have the ENV variable set via my ~/.bash_profile and have confirmed using the following code I can make things work, but it's not the example code so I'm just hacking.
def update_spreadsheet
connection = GoogleDrive.login(ENV["GMAIL_USERNAME"], ENV["GMAIL_PASSWORD"])
I can make it work but, I wanted to follow the tutorial and know how to troubleshoot better. if anyone has a pointer it would be appreciated.
Inside my config/secrets.yml file looks like:
development:
gmail_username: <%= ENV["GMAIL_USERNAME"] %>
gmail_password: <%= ENV["GMAIL_PASSWORD"] %>
Thank you
Rails reads secrets.yml but getting the value from secrets.yml is little different then how you had used it, check the code below:
secrets.yml:
development:
secret_key_base: 3b7cd727ee24e8444053437c36cc66c3
some_api_key: SOMEKEY
This is how you can access the value:
Rails.application.secrets.some_api_key returns SOMEKEY
I was having the same issue here and followed all the instructions here and was still experiencing difficulties. My gmail password does not have any special characters, but in my .bash profile I just tried putting single quotes around the contents of the double quotes for both GMAIL_USERNAME and GMAIL_PASSWORD, and it finally worked!
The above solution did not work for me. However, I found the solution on How do I use variables in a YAML file?
My .yml file contained something like:
development:
gmail_username: <%= ENV["GMAIL_USERNAME"] %>
gmail_password: <%= ENV["GMAIL_PASSWORD"] %>
In your .rb file,access the yml file as:
template = ERB.new File.new("path/to/config.yml.erb").read
processed = YAML.load template.result(binding)
So when you introduce a scriptlet tag in .yml file, it is more of erb template. So read it as a erb template first and then load the yml as shown above.

YAML configuration file: Why 'example' instead of :example?

I set up an environment YAML file for environment specific variables like username and password. To use these variables in my app, I need to use APP_CONFIG['username'] instead of APP_CONFIG[:username]. Why is this? How would I enable the latter instead? Not a major issue, but it bothers me to not know the cause of the difference.
config/initializers/load_app_config.rb
APP_CONFIG = YAML.load_file("#{Rails.root}/config/app_config.yml")[Rails.env]
config/app_config.yml
development:
username: development_name
password: secret
production:
username: production_name
password: super_secret
By default, a YAML key is rendered as String.
development:
username: development_name
password: secret
is accessible by
APP_CONFIG = YAML.load_file("#{Rails.root}/config/app_config.yml")[Rails.env]
APP_CONFIG['development']['username']
# => "development_name"
I you want a specific key to be a symbol, you should prefix it with : in the YAML file.
development:
:username: development_name
:password: secret
APP_CONFIG = YAML.load_file("#{Rails.root}/config/app_config.yml")[Rails.env]
APP_CONFIG['development'][:username]
# => "development_name"
APP_CONFIG['development']['username']
# => nil
Normally, this is not done because this is a specific Ruby behavior. Other languages might not be happy about the leading :.
If you specifically wants to access keys as symbol, you can either use symbolize_keys!
APP_CONFIG = YAML.load_file("#{Rails.root}/config/app_config.yml")[Rails.env].simbolize_keys!
but most of the time, the effort is not worth. Internally, the 90% of libraries converts Symbols to Strings during a comparison, especially when you deal with hashes with indifferent access. So, at the end of the story, you might want to keep strings in this case.
Last option would be to create a HashWithIndifferentAccess
APP_CONFIG = HashWithIndifferentAccess.new(YAML.load_file("#{Rails.root}/config/app_config.yml")[Rails.env])
This will allow you to access
APP_CONFIG[:development][:username]
APP_CONFIG['development'][:username]
APP_CONFIG['development']['username']
indifferently. It works by storing the hash keys to string internally and converting the request to [] to string, so that it always works. This is the class used by several Rails components, including the famous params[] hash in the controllers.
Use symbolize_keys on the hash returned by YAML.load_file

Can a YML file access another YML file?

We have a site wide config.yml file which contacts api keys etc...
Can another one of my YML files access a value in the config.yml?
config.yml:
development:
thing: 123123123123
plugin_config.yml:
development:
thing: config.yml.development.thing
is this possible?
You can always parse it with ERB:
development:
thing: 123123123123
development:
thing: <%= YAML.load(ERB.new(File.read(Rails.root.join('config','config.yml'))).result)['development'] %>
Then load the first one and also parse it with ERB (in an initializer or something):
CONFIG = YAML.load(ERB.new(File.read(Rails.root.join('config','plugin_config.yml'))).result)

rails: yml merging

Say I have a yml file for my rails configuration...
settings.yml
defaults: &defaults
interceptor_email: robot#wearemanalive.com
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults
and I want to have another yml file that is NOT included in version control that each developer maintains locally...
user_settings.yml
development:
interceptor_email: userfoo#domain.com
How can I merge these keys? I am processing my yml files with esb, so that is also an option. Just having trouble figuring out how to do it. I have it setup so keys fallback to the defaults if a key is missing for my environments.
Can't you read the two yml files separately?
settings = YAML.load(path_to_settings)[RAILS_ENV].symbolize_keys
user_settings = YAML.load(path_to_user_settings)[RAILS_ENV].symbolize_keys
settings.merge!(user_settings)
Now you should have the hash value of the settings, then you can merge the hash if you want. If the second hash has the same key as the first hash, the first one will be overwritten.
This is how I do it (disclaimer, I just wrote it, so it doesn't have unit tests yet etc...I'll update this as I improve on it):
require 'yaml'
# read config files (currently only yaml supported), merge user config files over
# defaults and make the parsed data available to the rest of your application.
#
module YourNamespace class Config
attr_reader :files, :get
# Accepts a string filename or an array of string filenames to parse.
# If an array is supplied, values from later files will override values
# of earlier files with the same name.
# Will choke if YAML.load_file returns false (invalid or empty file)
#
def initialize( files )
#files = files.respond_to?( 'map' ) ? files : [ files ]
#get = #files \
\
.map { | file | YAML.load_file file } \
.reduce( {}, :merge! )
;
end
end end
You can call it like this:
config = YourNamespace::Config.new 'config.yml'
# or have the second one override the first
#
config = YourNamespace::Config.new [ 'config-defaults.yml', 'config.yml' ]
And if you want to go fancy, there's a lot of room for improvement here. Ideally make ´Config´ an interface that does not deal with files, and implement in YamlConfig, IniConfig, CliConfig, DbConfig, CookieConfig. That way if you decide one day the that that new config format super seeding yaml is so cool, you can easily change it without breaking anything. And you can have the command line configuration options easily override the ones coming from the configuration files. And you can reuse the config module for any ruby project regardless of where the config values come from. Or maybe just stop inventing hot water. A quick browse makes me think there's some pretty hot water over there...
Next write some documentation, unit tests, input validation, error handling and create some fancy read/write accessors for the config values. Maybe you'd like to be able to ask for a config value like this instead of writing arrays and hashes all the time:
config.get 'app.component.section.setting'
# or this if you want to keep them separate:
#
config.get( 'app', 'component', 'section', 'setting' )

Resources