This post originated from an RSS feed registered with Ruby Buzz
by Christopher Williams.
Original Post: Patterns in Ruby: Singleton Pattern
Feed Title: Late to the Party
Feed URL: http://cwilliams.textdriven.com/xml/rss20/feed.xml
Feed Description: Ruby. Rails. Stuff.
The Singleton pattern is the black sheep of the pattern family.
It was easy to grasp, developers everywhere applied it liberally, and an inevitable backlash came against its overuse.
I won't make any judgments or reccomendations on when to use it - but I will show you just how easy it is to apply in Ruby.
The literal translation of the pattern is to create a class level instance method and to hide the new method.
class Exampledef initialize# do something?enddef self.instancereturn@@instanceifdefined?@@instance@@instance=newendprivate_class_method:newendputsExample.instance.object_id#=> 21783380putsExample.instance.object_id#=> 21783380
This example gives you the basic idea, but it doesn't cover many cases you'd like to handle, like cloning or duping the singleton. It also doesn't hide the class level allocate method, which means a sneaky coder could still create another instance through some hacking.
Lastly, it's not thread safe.
Luckily, Ruby already provides a module for making classes singletons. It's in the standard library, inside 'singleton.rb'.
Here's how you use it:
This module will do the same thing as my example above but will also handle hiding allocate, overriding the clone and dup methods, and is thread safe. The library file itself contains a bunch of examples of its usage, and those interested should definitely read through it.
One thing to note about these implementations is that the instance method takes no arguments, so none are passed on to the object's constructor. This makes sense because the first time instance is called those will be the arguments used for this global instance. Setters are typically more appropriate for most singletons.
Since singletons are global in nature setters should be at the class level. As an extra bonus here's the implementation of the class level attr_ methods to generate the vanilla getter/setter methods (stolen from Rails).
class Class# :nodoc:def cattr_reader(*syms)syms.flatten.eachdo|sym|class_eval(<<-EOS,__FILE__,__LINE__)
unless defined? @@#{sym}
@@#{sym} = nil
end
def self.#{sym}
@@#{sym}
end
def #{sym}
@@#{sym}
end
EOSendenddef cattr_writer(*syms)syms.flatten.eachdo|sym|class_eval(<<-EOS,__FILE__,__LINE__)
unless defined? @@#{sym}
@@#{sym} = nil
end
def self.#{sym}=(obj)
@@#{sym} = obj
end
def #{sym}=(obj)
@@#{sym} = obj
end
EOSendenddef cattr_accessor(*syms)cattr_reader(*syms)cattr_writer(*syms)endend