Consolidating your app’s constants
May 19, 2007 03:35 (Sydney Australia), updated 3 days later
Sprinkling constants doth maketh a mad-man
You’ve a bunch of configuration constants in your application (email addresses, host addresses, etc) and you’d like a nice, cozy place to keep them, and a method of overriding the defaults for each environment.
There’s a million ways to skin this cat, but here’s the simple approach I took in my latest app.
...and yes, a constant that changes is not a constant, but I’ll pretend you didn’t say that.
1st update: I originally had a typo: the variables should be declared with @@, not @.
2nd update: It’s active_support, not activesupport!
Creatin tha module
Firstly, we’ll set up a new module to store our config, let’s call it MyAppConfig and it can live in RAILS_ROOT/config/my_app_config.rb. In it, we’ll create a standard Ruby module:
module MyApp
module Config
end
end
Hookin it in
To allow each environment to customise the config we’ll want to set this up before Rails::Initializer#run is called within environment.rb (see my previous article Environments and the Rails Initialisation Process for more info).
We’ll simply require the file after boot in our environment.rb:
require File.join(File.dirname(__FILE__), 'boot')
require 'my_app_config'
Rails::Initializer.run do |config|
# ...
Next we’ll add a configurable variable, with a default, to our module:
module MyApp
module Config
@@paypal_host = "https://www.sandbox.paypal.com"
end
end
This works just fine, apart from the fact you can’t access the instance variable outside of the module:
irb> MyApp::Config.paypal_host
NoMethodError: undefined method `paypal_host' for MyApp::Config:Module
To access and modify the instance variable we need a set of good ‘ol accessors. If this were a class, not a module, we’d sprinkle an attr_accessor and be on our way, but as this is a module we have to define the methods ourself.
module MyApp
module Config
@@paypal_host = "https://www.sandbox.paypal.com"
def self.paypal_host
@@paypal_host
end
def self.paypal_host=(paypal_host)
@@paypal_host = paypal_host
end
end
end
irb> MyApp::Config.paypal_host
=> "https://www.sandbox.paypal.com"
Sprinklin the sexy
Ok, so far it’s not looking too pretty is it? Don’t worry… good ‘ol ActiveSupport ships with a nice little helper, mattr_accessor, which provides exactly the same functionality as Ruby’s attr_accessor but for modules. Unfortunately the author chose for it a life of seclusion, with a tattoo of :nodoc:, but that’s never stopped us brave venturers before.
ActiveSupport isn’t loaded until the Rails::Initializer#run method is executed, so we’ll also need to require 'activesupport' before we can use mattr_accessor:
require 'active_support'
module MyApp
module Config
mattr_accessor :paypal_host
@@paypal_host = "https://www.sandbox.paypal.com"
end
end
So now we have our environment.rb requiring this module and we can access it anywhere in our Rails application with a simple MyApp::Config. paypal_host.
Overidin the defaults
To override config vars in different environments you can simply call its mattr_writer. For example, we’ll want to use the live paypal server in production, so we add the following line to the bottom of RAILS_ROOT/config/environments/production.rb:
MyApp::Config.paypal_host = "https://www.paypal.com"
and voila! That’s all there is to it.
So you wanna get funky
...and here’s some exercises for the reader (all of which are a purely academic pursuit I’d most probably not bother using):
- Extending
Modulewithmattr_accessor_with_default - Extending
Rails::Configto provide amy_appmethod, so instead ofMyApp::Config.paypal_hostin your environments you could just usemy_app.paypal_host - Implement
MyApp.config(&block)for block-style configuration

Comments
Gabe da Silveira
I dig this approach. In order to get an app up and running quickly I recently had to do this:
ssl_required :checkout, :process_payment if RAILS _ENV == ‘production’
Mostly because I just didn’t have time to get Apache proxying to my development mongrel for local SSL testing.
But after seeing this I’m thinking making SSL a configurable option would be a reusable approach since any of the three environments may or may not support SSL in any given scenario.
Tim Lucas
Gabe: Exactly. Not to mention if you added more environments, such as staging. In this app I used the config to specify what protocol to use for
default_url_optionsin myActionMailer’s:maxm
And if you’re too lazy to repeat the attribute name, throw this in at the end:
Tim Lucas
mr max, dat, is sexy.
Dr J
I’m getting an error trying to start my mongrel process because it is choking on the activesupport include line:
require 'activesupport'/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require__': no such file to load -- activesupport (LoadError) from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require' from /home/cjolicoeur/rp_badge_signup/trunk/config/../config/my_app_config.rb:1 from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require__' from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require' from /home/cjolicoeur/rp_badge_signup/trunk/config/environment.rb:19 from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require__' from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require' from /usr/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/rails.rb:155:in `rails' ... 8 levels... from /usr/lib/ruby/gems/1.8/gems/mongrel-1.0.1/lib/mongrel/command.rb:211:in `run' from /usr/lib/ruby/gems/1.8/gems/mongrel-1.0.1/bin/mongrel_rails:243 from /usr/bin/mongrel_rails:18:in `load' from /usr/bin/mongrel_rails:18any thoughts?
Tim Lucas
Whoops sorry Dr J, that should read
require 'active_support'EP
Thanks for this. It was very helpful!
Greg Clarke
Very helpful post Tim. The info here plus your “Environments and the Rails initialisation process” helped me my solve my config problems and that has made my day. Thanks very much.
Alex Fetcher
Great site!
abc3fc27235e175b01a98fdd451ac4e0
To comment on this article you must have javascript enabled.