Most of the times I use a config.yml
to store application settings, that I want to be able to change quickly between environments, servers, deployments. But what with settings that need to be changed on the fly, by a user?
I create a small model, with three fields.
rails g model Setting name:string description:string value:string
I use a seed file to define the different settings. Settings are checked in code, but I can not preload them, since a user might change them. So first I added a method as_hash
that loads all settings, and I can then directly use a hash, but that gets wordy quickly.
What if ... I could provide a method on the class Setting
for each setting in the database? That would be really nice. This seems like a job for ... method-missing-man Special superpower: seeing places where method_missing
could be used :)
class Setting < ActiveRecord::Base
validates_presence_of :name
def self.as_hash
settings = {}
Setting.all.each do |setting|
settings[setting.name.to_sym] = setting.value
end
settings
end
# offer all settings as methods on the class
def self.method_missing(meth, *args, &block) #:nodoc:
@all_settings ||= Setting.as_hash
if @all_settings.keys.include?(meth)
@all_settings[meth]
else
super
end
end
end
For documentation, the test:
context "convenience: define methods for all settings" do
before do
Setting.instance_variable_set('@all_settings', nil)
Setting.create(name: 'my_other_test_setting', value: '123ZZZ')
end
it "Setting.my_other_test_setting returns the correct result" do
Setting.my_other_test_setting.should == '123ZZZ'
end
it "an unexisting setting behaves as a normal missing method" do
expect {
Setting.this_setting_does_not_exist
}.to raise_exception(NoMethodError)
end
end
I love ruby :) :)