Advanced YAML: Tricking Out Your database.yml
I’ve been cultishly carrying this snippet around for years:
login: &login
adapter: mysql
host: localhost
username: myuser
password: mypass
encoding: utf8
For a couple of reasons around running DB migrations in differently configured environments, I wanted to factor this into bits common to all environments and bits particular to the config on the local machine. I came up with something like:
# Provide default local block
local: &local
socket: /var/run/mysqld/mysqld.sock
common: &common
adapter: mysql
encoding: utf8
reconnect: false
pool: 5
username: myuser
password: mypass
<<: *local
development:
database: myproj_test
<<: *common
This works swimmingly, but I was tired of ritually mimicking and extending the original snippet without actually understanding it. I decided to dig deeper into what was actually going on and found, unsurprisingly, that I stand on the shoulders of giants like Doug Alcorn, James Duncan Davidson, and Ben Bleything. To render proper credit, Ben’s post from June 2006 builds on a missing post from James’s blog, both of which were preceded by Doug’s observations in March 2006, and all of which draw on tricks originally found in the Typo database config. None of them seems to have detailed what’s going on with the &login/*login construct, so I thought I’d dive into that a bit for posterity.
YAML is nothing more than a human-readable way to serialize basic data structures like lists and associative arrays (aka hashes), which makes it ideal to represent the configuration values needed by Rails. It turns out that YAML provides anchors (&), references (*), and associative array merges («), all of which allow you to include by reference either by assignment or by merging a referenced array into another array.
Here’s an example of each:
# Generate a reference
mammal: &mammal_ref
warm_blooded: true
lays_eggs: false
# Define via reference assignment
beaver: *mammal_ref
# Define including a hash merge
otter:
cute: true
<<: *mammal_ref
# Define including a hash merge, overriding a value in the reference
platypus:
<<: *mammal_ref
lays_eggs: true
I found that the Wikipedia entry on YAML was a good, quick overview; the official YAML spec has all that and more, but it’s not a quick read. Meanwhile, both Doug and Ben observed that bringing ERB into the mix let’s you seriously customize things. I took a bit from both of them; here’s my database.yml now:
# Provide default local block
local: &local
username: myuser
password: mypass
<% if File.exist? "/opt/local/var/run/mysql5/mysqld.sock" %>
socket: /opt/local/var/run/mysql5/mysqld.sock
<% elsif File.exist? "/var/run/mysqld/mysqld.sock" %>
socket: /var/run/mysqld/mysqld.sock
<% elsif File.exist? "/tmp/mysql.sock" %>
socket: /tmp/mysql.sock
<% end %>
# Allow for local DB configuration
<%= File.read(File.join(File.dirname( __FILE__ ), 'dblocal.yml')) if File.exist?(File.join(File.dirname( __FILE__ ), 'dblocal.yml')) %>
common: &common
adapter: mysql
encoding: utf8
reconnect: false
pool: 5
<<: *local
development:
database: myproj_development
<<: *common
test:
database: myproj_test
<<: *common
production:
database: myproj_production
<<: *common
Works a treat. Thanks, Doug, James, and Ben.
blog comments powered by Disqus