Reputation: 5892
Here's an example of my database.yml
file:
default: &default
adapter: postgresql
encoding: unicode
pool: 200
production:
<<: *default
host: <%= ENV['DBHOST'] %>
username: <%= ENV['DBUSER'] %>
password: <%= ENV['DBPASS'] %>
database: <%= ENV['DBNAME'] %>
development:
<<: *default
host: <%= Rails.application.credentials.development[:host] %>
username: <%= Rails.application.credentials.development[:username] %>
password: <%= Rails.application.credentials.development[:password] %>
database: <%= Rails.application.credentials.development[:database] %>
However, when trying to run rails c -e production
, it fails with the following error:
$ rails c -e production
(erb):15:in `<main>': Cannot load database configuration:
undefined method `[]' for nil:NilClass (NoMethodError)
If I take the development
section out of my database.yml
file, then everything runs perfectly fine. For awhile, I've had to either have the credentials for development or production in this file; however, I thought the file was made to have multiple credentials.
What am I doing wrong? This should be pretty simple.
Upvotes: 1
Views: 416
Reputation: 102423
Your expectation is completely wrong.
In order to parse the file Rails has to pass the entire file through ERB before its sent through YAML. Rails can just cherry pick the production:
section of your YAML file since variable expansion takes place before it is parsed - at that point its just a string buffer.
You can fix the issue by using the safe navigation operator to avoid nil errors:
default: &default
adapter: postgresql
encoding: unicode
pool: 15
production:
<<: *default
host: <%= ENV.fetch('DBHOST') %>
username: <%= ENV.fetch('DBUSER') %>
password: <%= ENV.fetch('PASS') %>
database: <%= ENV.fetch('DBNAME') %>
development:
<<: *default
<% creds = Rails.application.credentials.development) %>
host: <%= creds&.dig(:host) %>
username: <%= creds&.dig(:username) %>
password: <%= creds&.dig(:password) %>
database: <%= creds&.dig(:database) %>
Using Hash#fetch
can save you tons of debugging time as it will raise a KeyError instead of letting your app boot up with nil values.
There also really is no need to use 4 different env vars to configure your database. Just set DATABASE_URL
postgresql://hostname/database?username=bob&password=abcd12345
This is how its done on Heroku for example. ENV['DATABASE_URL']
takes preceedence over any settings in database.yml
.
This lets you simplefy the whole thing down to:
default: &default
adapter: postgresql
encoding: unicode
pool: 15
# Hacky per env workaround for Rails 5.2
<% creds = Rails.application.credentials.try(Rails.env) %>
host: <%= creds&.dig(:host) %>
username: <%= creds&.dig(:username) %>
password: <%= creds&.dig(:password) %>
database: <%= creds&.dig(:database) %>
production:
<<: *default
development:
<<: *default
test:
<<: *default
Or even:
default: &default
adapter: postgresql
encoding: unicode
pool: 15
production:
<<: *default
development:
<<: *default
test:
<<: *default
Which is really all you need for Postgres.app
, the Homebrew/Linuxbrew pg and other common Postgres presets. Developers then can customize the settings by setting ENV['DATABASE_URL']
with DirEnv/ShellEnv or through the container if you are using docker. This avoids developer wars and config clashes.
See Configuring Rails Applications.
Upvotes: 1
Reputation: 5892
Solved this problem using information in this post: https://stackoverflow.com/a/43747708/1493116
Updated my database.yml
file to look more like this instead:
default: &default
adapter: postgresql
encoding: unicode
pool: 15
production:
<<: *default
host: <%= ENV['DBHOST'] %>
username: <%= ENV['DBUSER']%>
password: <%= ENV['PASS'] %>
database: <%= ENV['DBNAME'] %>
development:
<<: *default
<% if Rails.env.development? %>
host: <%= Rails.application.credentials.development[:host] %>
username: <%= Rails.application.credentials.development[:username] %>
password: <%= Rails.application.credentials.development[:password] %>
database: <%= Rails.application.credentials.development[:database] %>
<% end %>
Seems a bit sloppy but it solved my problem.
Upvotes: 0