Flexible application configuration in Ruby on Rails

Posted by Dmytro Shteflyuk on under Ruby & Rails · Русский (54,609 views)

In my current project based on Ruby on Rails framework, I need to store application specific configuration somehow. There are several approaches I’ve found in Internet: AppConfig plugin, several methods described on the HowtoAddYourOwnConfigInfo wiki page, but neither one looks “config-like”. Me with my friend, Alexey Kovyrin, discovered all of them and decided to use YAML-file as a configuration format. In my opinion, the ideal configuration file looks like this:

1
2
3
4
5
6
7
8
9
10
11
common:
  support_email
: admin@myhost.com
  root_url
: myhost.com
  photos_max_number
: 6

production
:
  email_exceptions
: true

development
:
  root_url
: localhost:3000
  photos_max_number
: 10

In this example you can see three sections: common will be used as a base configuration for all environments, production and development — environment specific options. Optional sections are production, development, and testing, or any other custom environment name. I’ve placed this file in config/config.yml and created lib/app_config.rb which looks like this:

1
2
3
4
5
6
7
8
# Load application configuration
require 'ostruct'
require 'yaml'
 
config = YAML.load_file("#{Rails.root}/config/config.yml") || {}
app_config = config['common'] || {}
app_config.update(config[Rails.env] || {})
AppConfig = OpenStruct.new(app_config)

Now I’m able to use constructions like AppConfig.support_email and AppConfig.root_url. Looks like I’ve kept all my configs as DRY as possible :-)

16 Responses to this entry

Subscribe to comments with RSS

said on October 17, 2006 at 11:53 am · Permalink

Во-первых, лично мне не нравится, как выглядит

1
2
3
4
5
6
7
8
9
10
11
12
13
common: &common
  support_email
: admin@myhost.com
  root_url
: myhost.com
  photos_max_number
: 6

production
:
  <<
: *common
  email_exceptions
: true

development
:
  <<
: *common
  root_url
: localhost:3000
  photos_max_number
: 10

Во-вторых, код все равно останется в виде

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

Имхо, уродство конфига не оправдание программисту, который сэкономил две строчки кода.

К тому же у себя я их вынес в плагин, чтоб не мешалось под ногами и можно было юзать в разных проектах.

said on October 17, 2006 at 11:54 am · Permalink

Кстати, спасибо за ссылки. Очень полезно :-)

said on January 25, 2007 at 2:05 am · Permalink

Небольшая модификация кода на случай, если секция common не содержит данных или отсутсвует как таковая:

1
2
3
4
5
config = OpenStruct.new(YAML.load_file("#{RAILS_ROOT}/config/config.yml"))
env_config = config.send(RAILS_ENV)
common = config.common || {}
common.update(env_config) unless env_config.nil?
::AppConfig = OpenStruct.new(common)
said on January 25, 2007 at 7:08 am · Permalink

Спасибо! В некоторых проектах может быть полезно :-)

said on January 25, 2007 at 12:31 pm · Permalink

Да не за что, мне уже помогло :)

said on April 25, 2007 at 4:34 am · Permalink

[...] solutions have been posted for handling application level config file but this solution is by far the best. It’s simple, DRY and works. Try it. You will love [...]

Eugene @
said on July 26, 2007 at 11:29 am · Permalink

Many thanks for the great idea. I’ve decided to go further and now config file is parsed with ERB, one can have another file to override the values in the main file (when you place config.yml to svn and need specific values in your working copy) and config values can not be overwritten in the application (by mistake). Please check it out.

Alex
said on February 21, 2008 at 10:36 pm · Permalink

This code does’t work in ‘test’ environment (Rails 2.0.2).

Problem with:

1
env_config = config.send(RAILS_ENV)

It’s strange that config.test() work, but confg.send('test') not.
I don’t know why. Can anyone explain this behaivor?

said on February 29, 2008 at 6:32 am · Permalink

It works for my Rails 2.0.2 project:

1
2
3
4
5
# app-specific config
require 'ostruct'
require 'yaml'
config = OpenStruct.new(YAML.load_file("#{RAILS_ROOT}/config/chef.yml"))
::AppConfig = OpenStruct.new(config.send(RAILS_ENV))

Test:

1
2
3
  def test_test_base
    assert_not_nil AppConfig.test_base
  end

Passes. When I change it to assert_nil it fails, and I see that it was actually set.

Steve C
said on July 12, 2008 at 1:48 am · Permalink

If you are getting errors when using this in your test environment you forgot to add “test:” to your .yml file

The example in this article had production and dev, but for some reason omits test

Comments are closed

Comments for this entry are closed for a while. If you have anything to say – use a contact form. Thank you for your patience.