Tuesday, December 28, 2010

Harness the Power of an Object's Singleton Class

There are a few blog's discussing what the Singleton Class is in ruby.  For a pretty good explanation see this one.  However, I always like to discuss a practical example that demonstrates how you might use it to improve your code when the opportunity comes up like yesterday.

I was writing a small script to produce some reports on log files that were all text files with some having been zipped and others not depending on their current status.  For the zipped files I wanted to be able to open the files and examine their contents just like the existing files that weren't zipped.  An example glob would be:

=> ["2010-09-13.access.log", "2010-09-14.access.log", "2010-09-15.access.log", "2010-09-16.access.log.gz"]

The main focus of the script should be to examine the files in a directory and then loop through each line of the file. The gist below gets us started:


This feels like a clunky way to solve the problem.  Because a gzip file is temporarily opened, the name of the file is changed.  The second part of the problem requires us to keep track of whether to gzip the file when we are done or not.  We have created 2 separate variables (gzip and use_file) that we have to maintain now to handle the logic.

Since the reference to the file in this case is just a String, we could monkey patch it and add methods that would help us clean up the code in the previous gist.  This would be an improvement for sure but a better way to solve the problem would be to create a mixin to encapsulate the necessary logic and then create a singleton class like so:


Using this technique we have cleaned up the block focusing on the file looping considerably.  We have also created a more extensible solution should we encounter other files types in the future.  This method also avoids the problematic alternative solution of opening up a Ruby standard class (in this case String) to achieve the same result.

This is just another example of how flexible Ruby is compared to other languages so embrace the fact you can create a Singleton class and harness its power.

7 comments:

  1. This is a cool way to add some functionality to an instance, though extending single objects can be slow (or so I've read/heard). If you do this on something where performance is really important, you might want to benchmark it.

    ReplyDelete
  2. This isn't an example of a singleton class. You are using a module to create a mixin.

    Interestingly, instead of mixing in an instance methods into the Dir class though with include you are extending an instance. This means you have to do it for EVERY single directory instance.

    I guess the advantage of doing it on each instance individually is that you are not polluting the directory class with your methods. However, you could easily use inheritance and then mixin your module to your new class to achieve this functionality.

    ReplyDelete
  3. @Adam - I'll explore performance in the next blog post.

    @Theo - I encourage you to read the blog suggesting in mine regarding the Singleton Class. When you extend an object with a mixin a Singleton Class is created to manage the Singleton Methods that are being created.

    ReplyDelete
  4. Wes, I've had a look through that article, and I must say I'm now very confused.

    My understanding of a singleton is a class that can only be instantiated once. This allows for global access to state. The book "Design Patterns in Ruby" which is based on the Gang of Four book is a good read. I don't understand how the examples used in either article fulfill this criteria.

    ReplyDelete
  5. Wes. I think the confusion is between the distinction of a singleton class - a class which can only be instantiated once, and singleton methods - methods which are only available to a certain instance, rather than all instances of the same class.

    The example used is inappropriate because all of the Dir objects are being extended with the same methods. It would be better to create a new class which inherits from Dir with the extra methods that you want. This saves extending each and every object with exactly the same logic.

    ReplyDelete
  6. I think the confusion here is because the use of Singleton here isn't the same as the "Gang of Four" version. In a Ruby context it usually refers to the "class" of an instance, with Singleton here roughly meaning the class of a single object rather than a class for which there can only be one instance (Gang of Four definition).

    There has been some feeling in general that this Ruby-style use of Singleton is confusing, with which I agree. You will also see this class called the "eigenclass", which I think is better if only because it avoids this confusion.

    ReplyDelete
  7. The problem here is that Theo and I are talking about two different things. What I am discussing is Ruby's Singleton Class sometimes called the Eigen class or Meta-class. What you are discussing is the Singleton Pattern which in limits the instantiation of an object to a single instance. The ruby singleton class is an internal construction that allows you to dynamically modify the properties of the class that the object was created from which I take advantage of in my example code.

    ReplyDelete