I was looking through a new default Rails application, and in config/database.yml I noticed a snippet of ERB:
...
default: &default
adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> # <-- ERB
timeout: 5000
...
Now, I know what ERB is and how it works. What I'm confused by is the fact that the fact a file contains ERB is usually indicated in the file name. For example, a view file might be called index.html.erb.
It's a sort of trivial point, and it might be that the answer is "they just didn't feel like it, it's their package", but why isn't this file called (say) database.yml.erb? The name as it stands suggests it should just contain plain YAML, and it doesn't.
This is indeed a special case. At some point the strategy outlined http://urgetopunt.com/2009/09/12/yaml-config-with-erb.html became the default. I tried to find where in GitHub, but didn't find the exact date of when this happened.
Related
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.
For the first time I have a question to an answer I already solved, this is more about WHY my little fix solved it in the first place.
I'm following the Learn Rails book tutorial by Daniel Kehoe, creating an application that is supposed to subscribe people to a MailChimp list. I'm using the Gibbon gem, and in the book this is (part of) the code in the model used to set the variables:
def subscribe
mailchimp = Gibbon::API.new(Rails.application.secrets.mailchimp_api_key)
result = mailchimp.lists.subscribe({
:id => Rails.application.secrets.mailchimp_list_id,
<more code here for email and stuff>
end
My secrets.yml file for the variables in question looks like this:
mailchimp_api_key: <%= ENV["MAILCHIMP_API_KEY"] %>
mailchimp_list_id: <%= ENV["MAILCHIMP_LIST_ID"] %>
And, since I'm on Windows, I set the actual value of them in application.yml: [Edit: As people have pointed out, this comes from the Figaro gem and may or may not be part of the tutorial]
MAILCHIMP_API_KEY: mysecretkey
MAILCHIMP_LIST_ID: mysecretid
This, I should note, works perfectly fine locally. I can run my server and subscribe. However, pushing it to Heroku and setting the ENV variables via heroku config:set MAILCHIMP_API_KEY=mysecretkey and the same thing for MAILCHIMP_LIST_ID, it doesn't work. More specifically, it returns the error Invalid Mailchimp List ID:.
So, despite setting it and checking that it is actually set, it has no value at all for the List ID.
I fixed the problem by searching through Heroku's own help text on Config Vars, where it recommends the following: "Set up your code to read the vars at runtime. For example, in Ruby you access the environment variables using the ENV['KEY'] pattern"
I changed my code in the model, so instead of using Rails.application.secrets.mailchimp_list_id it's now this:
def subscribe
mailchimp = Gibbon::API.new(Rails.application.secrets.mailchimp_api_key)
result = mailchimp.lists.subscribe({
:id => ENV['MAILCHIMP_LIST_ID'],
<more code here for email and stuff>
end
And, what do you know, it works in Heroku (and locally)! Why, though? I mean, it did understand the same kind of code for the API Key, but not for the list ID, which seems strange to me. Also, how come the book in question doesn't reflect on this? The author seems willing to update it very regularly, and I see him answer questions on it with an almost religious dedication. Am I the only one to run into this problem?
Edit: Solved below. I feel a bit silly now, and as usual things have a very easy solution. I will note that the book did in fact have the keys duplicated into production in the secrets.yml file, I just missed that part, probably because I had to find an alternative solution to the whole problem of environmental variables and using the Figaro gem. If I have any criticism of the book, it's that it doesn't cater to us poor Windows users. :)
Figaro creates the config/application.yml file and according to its documentation, adds it to your .gitignore:
This creates a commented config/application.yml file and adds it to your .gitignore. Add your own configuration to this file and you're done!
This is done to prevent sensitive secrets (like passwords and salts) from being committed into your Git repository, where those secrets would be compromised.
Figaro does have a feature which you can use to transfer all the settings in application.yml to Heroku at once by running
figaro heroku:set -e production
As it turns out, the config/secrets.yml did not repeat the Mailchimp keys in the production section. To fix this, it should look like this:
development:
mailchimp_api_key: <%= ENV["MAILCHIMP_API_KEY"] %>
mailchimp_list_id: <%= ENV["MAILCHIMP_LIST_ID"] %>
production:
mailchimp_api_key: <%= ENV["MAILCHIMP_API_KEY"] %>
mailchimp_list_id: <%= ENV["MAILCHIMP_LIST_ID"] %>
Had almost the exact same error, but a slightly different cause.
Double check to make sure your secrets.yml is properly configured. Mine looked like the following:
...
production:
mailchimp_api_key: <%= ENV["MAILCHIMP_API_KEY"] %>
mailchimp_list_id: ENV["MAILCHIMP_LIST_ID"]
...
I didn't see the missing <%= %> after scanning the file MANY times, even after I ended up on this page. I'll leave this answer up in case Google sends others with the same typo here.
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.
My question is seemingly simple but has been pretty elusive. I want one value that can be called anywhere in the application - it's a minimum value we are using for validations in the model, we want to inform the user of that value, etc.
So far, I have done the following:
In config/initializers, I created a new .rb file (under suggestion I found on another thread):
minimum = 15
and only got that it couldn't be found after starting rails server.
I found a strange suggestion to put the value in en.yml as minimum: 15, same issue as above.
Obviously, application helper and controller won't help as I need the value in my models. I did however find one solution, which was to put it in the class Application in config/application.rb:
config.after_initialize do
::Minimum = 15
end
and afterwards call it like this:
MyApp::Application::Minimum
Obviously, this solution, while it does work, isn't ideal. If I could find a way to alias MyApp::Application::Minimum as just "minimum", I'll be completely satisfied, but getting it done in the initializers is, from what I've heard, a better solution.
You can stick something in a model if you want and call it from anywhere:
Couriers = %w[TNT UPS RoyalMail Bike Hand Pigeon Other]
But I prefer putting them in a yaml file.
Create a config.yml file in your configs directory and put your vars in there:
defaults: &defaults
node_1: '10.8'
node_2: '10.9'
node_3: '10.0'
whatever: 1
thereis: 2
development:
<<: *defaults
playground:
<<: *defaults
production:
<<: *defaults
poststaging:
<<: *defaults
test:
<<: *defaults
Load the config file using something like this in your initializers directory. Create a file in /config/initializers called 'load_vars.rb' and put the following in there:
SETTINGS = YAML.load_file("#{Rails.root}/config/config.yml")[Rails.env]
You'll have to restart your server after changing these though.
-- EDIT --
To use these variables, you can load with something like this from your model, controller, views:
SETTINGS['var_1']
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)