#85
Dec 24

YAML Configuration File

Application configuration shouldn't be spread throughout your code base. Instead a much better place to put it is an external YAML file. See how to do that in this episode.
Tags: tools
Download (13.7 MB, 7:03)
alternative download for iPod & Apple TV (9.2 MB, 7:03)
Resources
# config/initializers/load_config.rb
APP_CONFIG = YAML.load_file("#{RAILS_ROOT}/config/config.yml")[RAILS_ENV]

# application.rb
def authenticate
  if APP_CONFIG['perform_authentication']
    authenticate_or_request_with_http_basic do |username, password|
      username == APP_CONFIG['username'] && password == APP_CONFIG['password']
    end
  end
end
# config/config.yml
development:
  perform_authentication: false

test:
  perform_authentication: false

production:
  perform_authentication: true
  username: admin
  password: secret

25 comments:

Jim Dec 24, 2007 at 02:59

Thanks for this one, and happy holidays!


ash Dec 24, 2007 at 03:38

Thank you for sharing! Merry Christmas!


Mike Dec 24, 2007 at 05:20

Merry Christmas!

Good luck with the railscasts next year!


thom Dec 24, 2007 at 06:13

I'm using the following in `config/initializers/app_config.rb`:

require 'ostruct'
require 'yaml'

# Load application configuration
config = OpenStruct.new(YAML.load_file("#{RAILS_ROOT}/config/application.yml"))
::AppConfig = OpenStruct.new(config.send(RAILS_ENV))

Now I can use it in my application like this

AppConfig.perform_authentication


nelson jr Dec 24, 2007 at 06:13

Merry Christmas, guys! :-)

More success for all on next year!

Ryan Bates, thks for the casts on 2007, and share more on 2008. :-)

[again, srry my english]

[]s


firefly Dec 24, 2007 at 08:00

i do something very similar to this with my .yml files. if you don't want to commit your username/password in your repository like me, you can use a capistrano task like

task :create_config_config do
set :a_username, Capistrano::CLI.password_prompt('admin_username: ')
set :a_password, Capistrano::CLI.password_prompt('admin_password: ')
contents = render_erb_template(File.dirname(__FILE__) + "/templates/config.yml.erb")
put contents, "#{shared_path}/config/config.yml"
end


Jose Dec 24, 2007 at 08:08

Merry Christmass!


Henrik N Dec 24, 2007 at 13:42

I've been doing this for a while, too. This is what I do to get ERB in my YAML (like in fixtures). It also uses ostruct as described by thom above.

require 'ostruct'
raw_config = File.read("#{RAILS_ROOT}/config/config.yml")
erb_config = ERB.new(raw_config).result
config = YAML.load(erb_config)[RAILS_ENV]
Site = OpenStruct.new(config)

I can now do stuff like

foo_path: <%= RAILS_ROOT %>/tmp/foo


Bala Dec 24, 2007 at 15:50

Why do we need to use this method when Rails 2.0 has initializers? I can declare all my configuration under initializers directory, right?

I could have s3_config, mailer settings etc in its own initializer.


Michael Dec 25, 2007 at 08:30

if i add the first line:
APP_CONFIG = YAML.load_file("#{RAILS_ROOT}/config/config.yml")[RAILS_ENV]

then:

=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
=> Rails application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
** Starting Mongrel listening at 0.0.0.0:3000
** Starting Rails with development environment...
Exiting
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:133:in `load': syntax error on line 7, col 11: `' (ArgumentError)
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:133:in `load'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:144:in `load_file'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:143:in `open'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:143:in `load_file'
from /Users/michaelvoigt/Documents/private/projects/kraeftemessen/trunk/kraeftemessen/rails/kraeftemessen/config/environment.rb:84
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:27:in `require'
from /Library/Ruby/Gems/1.8/gems/activesupport-2.0.2/lib/active_support/dependencies.rb:496:in `require'
... 23 levels...
from /Library/Ruby/Gems/1.8/gems/rails-2.0.2/lib/commands/server.rb:39
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:27:in `require'
from script/server:3


Michael Dec 25, 2007 at 08:41

I have foregot the require 'yaml', SORRY!


noocx Dec 26, 2007 at 02:01

Thanks for the new cast and Merry Christmas!


Anonymous Dec 27, 2007 at 19:38

What happened to Convention over Configuration? So if you have a bunch of lil configuration options, whats the point from deviating away from what Rails gives us for configuration options? I'm lost...


Jaime Iniesta Dec 28, 2007 at 08:24

Thanks for this and the other 84 railscasts! You make me feel not so lonely on my freelance life :)


Paul Smith Dec 28, 2007 at 15:07

I love these RailsCasts. Every single on is amazing. Thanks


aleco Dec 28, 2007 at 15:26

I've always wanted to find a way to not have any passwords in my SVN. But, even with your suggestion, how would I do that when e.g. using deprec (the cap deply taks available at deprec.org)?

The only solution I can see is completely ignoring the config.yml in the SVN and keeping it far away from the app directory (as something like /var/www/apps/appname/current is replaced on each deploy). So, should one put it in /etc/ instead, or is there a nicer solution?


Ryan R. Dec 29, 2007 at 14:36

With HTTP Basic authentication, can you also use OpenID? A screencast on that would be tremendously helpful. Thanks for the great work!


August Lilleaas Jan 04, 2008 at 18:55

I love these kinds of railscasts - being recipes instead of re-iterations of stuff you can find in the API etc. Keep 'em coming!


Herb Jan 17, 2008 at 12:31

Well done. A very useful screencast and hard to find much info re: this online.


Dan Pickett Jan 30, 2008 at 10:27

What about putting configuration in the database? I know wordpress does something like this and I'd be interested in what the Rails community thinks about it.


Vivek Khokhar Jan 31, 2008 at 04:22

Since we are loading configs on server startups,
Is there anyway to change & reload config file/table without server restarts ?


Erik Petersen Apr 21, 2008 at 14:50

This is a pretty common app configuration pattern but for me it needs some rubification. I do pretty much the same thing but don't use a constant Hash, rather a class to manage the settings:

In lib/configuration.rb:

class Configuration
@@settings = YAML::load_file('config/myconfig.yml')[RAILS_ENV]
class MissingConfigOptionError < StandardError; end
def self.method_missing(key)
raise MissingConfigOptionError("#{key.to_s} is not in the config file") unless @@settings.include?(key.to_s)
@@settings[key.to_s]
end
end

That's it, there is nothing else to do. Create a config/myconfig.yml file with all the same stuff as in this railscast:

In config/myconfig.yml:

development:
foo: "bar"

production:
foo: "yummy"

To fetch and use the settings anywhere in your code, model, controller or view do:

x = Configuration.foo # => "bar"

Customize to suit. Maybe store the settings with mem_cache in a production environment and have a rake task to reload it or handle missing config options differently with perhaps a reasonable default (not a good idea IMHO) or add a test for the existence of a given key.

You could also make this a plugin without a problem by putting it in vendor/plugins/my_config_hotness/init.rb instead of lib/configuration.rb. Six and one half dozen.

Enjoy.

PS. This is not my idea. Someone a lot smarter than I put me on to it but went even further to allow for nested groups:

development:
foo: "bar"
bar:
foo: "yummy"

x = Configuration.bar.foo # => "yummy"

But I can't for the life of me find the page where I saw it done and I guess I failed to bookmark it :(


Erik Petersen Apr 21, 2008 at 14:58

Oh, I forgot to mention that the config file doesn't get loaded until the first time you try to access a setting (lazy loading). If you don't fetch a config setting value with Configuration.xxx it adds zero overhead. Ruby gives you this for free.


Chase Southard May 12, 2008 at 14:04

Can this be used with Helpers?

If so, what am I missing?

I'm getting a template error when using a method defined in application_helper.rb to return the key in that appears to be nil.

You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]

Thanks.


Chase Southard May 12, 2008 at 19:14

Yes it does work with helpers provided that you actually load the correct part of the yaml file.

Thank again, Ryan!

Add your comment:

(required)

(not displayed)

(SKIP THIS ONE)


(required)

subscribe:
sponsored by:
if you want to help:
required:
Get Quicktime Player