Configuration With a Singleton Instance

I was considering how I might want to further implement my gem and incorporate configuration settings for it. I wanted only one instance of my configuration and it needed to be part of my module. So I’ve come up with a solution I like that allows me to do this and even have a true “sense” of untouchable constants.

I’ve found OpenStruct to be for my liking in this situation as it allows for any configuration (variable) to be set on it. I’ve also found I can set “unalterable” variables… at least through standard assignment options.

Here’s the basics of it:

require 'ostruct' class MyProject::MyConfig < OpenStruct end module MyProject class << self def config @config ||= MyConfig.new end end end 1 2 3 4 5 6 7 8 9 10 11 12 13 require 'ostruct' class MyProject :: MyConfig < OpenStruct end module MyProject class << self def config @config || = MyConfig . new end end end

The reason I created the class MyProject::MyConfig just to inherit OpenStruct is that I want my configuration to be identified by a class name that indicates it is a config object that belongs to my project. In module MyProject I use attr_reader for config because we are getting the OpenStruct instance handed back to us and we can still invoke method/value assignment on it. I use the single class variable @config for the internal singleton value because it’s not going anywhere (No need to use @@config). The method config is defined on the singleton instance of the module MyProject via the class << self. So to set any value all I need to do is MyProject.config.new_value = 42.

MyProject.config.new_value = 42 MyProject.config.new_value # => 42 MyProject.config.hair = :awesome MyProject.config.hair # => :awesome MyProject.config.hair = :bad_hair_day MyProject.config.hair # => :bad_hair_day 1 2 3 4 5 6 7 8 9 10 11 12 MyProject . config . new_value = 42 MyProject . config . new_value # => 42 MyProject . config . hair = : awesome MyProject . config . hair # => :awesome MyProject . config . hair = : bad_hair _ day MyProject . config . hair # => :bad_hair_day

If I want to set a constant value that I don’t want to change then all I have to do is define the method within the OpenStruct instance. You can do it in the class, or open it up with the singleton opener.

class MyProject::MyConfig < OpenStruct def no_touchy :always_this end end x = MyProject::MyConfig.new x.no_touchy # => :always_this x.no_touchy = 493872 x.no_touchy # => :always_this class << x def also_no_touchy :cant_touch_this end end x.also_no_touchy # => :cant_touch_this x.also_no_touchy = :batman x.also_no_touchy # => :cant_touch_this 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class MyProject :: MyConfig < OpenStruct def no_touchy : always _ this end end x = MyProject :: MyConfig . new x . no_touchy # => :always_this x . no_touchy = 493872 x . no_touchy # => :always_this class << x def also_no_touchy : cant_touch _ this end end x . also_no_touchy # => :cant_touch_this x . also_no_touchy = : batman x . also_no_touchy # => :cant_touch_this

If you want a default value that can be updated; then you may implement that method with a method_missing(:method_name) || :value . As soon as you assign a new value it will choose the updated value.

class Example < OpenStruct def max_limit method_missing(:max_limit) || 42 end end x = Example.new x.max_limit # => 42 x.max_limit = 493872 x.max_limit # => 493872 1 2 3 4 5 6 7 8 9 10 11 12 class Example < OpenStruct def max _ limit method_missing ( : max_limit ) || 42 end end x = Example . new x . max_limit # => 42 x . max_limit = 493872 x . max_limit # => 493872

Summary

So now you can have a single instance configuration that’s easily updatable and you can set values that you don’t want to change. I do realize that the “constants” in this case aren’t defined the way the language normally defines constants. But in this situation it’s what works… and it works better than constants that don’t truly stay constant when reassigned ;-).

I hope this was informative and enjoyable for you! Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!

God Bless!

-Daniel P. Clark

Image by murray 9000 via the Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Generic License