Ruby’s module – Categories from ObjC

Ruby

Today I did quite a bit of work on refactoring the existing code which represents a simple buffering storage container for the output of the pipeline in our project. The original version had a lot of things in it that had no real need - the output was ordered (why?), there were a lot of methods and nested classes that really didn't need to be there. So Jeff and I started peeling the layers back and cleaning up a lot of this code.

What we came up with was a very nice design. The store had two basic methods, and two optional ones, if it happened to maintain a state:

  class Store
    attr_accessor :backend
 
    def store(block)
      self.backend.store(block)
    end
 
    def bulk_store(blocks)
      self.backend.bulk_store(blocks)
    end
 
    def clear
      self.backend.clear if self.backend.respond_to?(:clear)
    end
 
    def flush
      self.backend.flush if self.backend.respond_to?(:flush)
    end
  end

With this, we can have a very simple store class and then use it as the "backend" to the main store that just happens to be a Singleton (not shown):

  class StreamStore
    attr_accessor :stream
 
    def initialize(stream = $stdout)
      self.stream = stream
    end
 
    def store(block)
      write_one(block)
    end
 
    def bulk_store(blocks)
      blocks.each do |b|
        write_one(b)
      end
    end
 
    private
 
    def write_one(b)
      stream.puts b.to_s
    end
  end

All this is nice, but it doesn't address the buffering, and that's where I've come to find one of Ruby's really nice features: Modules. You can write a module - like a class, and then you can augment an existing instance with this behavior without having to include this in the class inheritance.

Methods will be added, ivars as well - it's just like the categories in ObjC - but maybe even one step better: in Ruby you can add this to a single instance, and in ObjC you have to add it to the class everywhere in the runtime.

I'm really impressed. This is really powerful, but at the same time, it's possible to have one instance of an object have entirely different methods and ivars than another. One might think this is really cool, but from a production stand-point, it's also very dangerous. This kind of power can be abused so easily.