Stupid Ruby Serialization Tricks

This post is for the small fraction of readers of this blog that dig programming in Ruby… apologies to anyone else….

So, I was fooling around with Ruby when I came up with the following trick.

Let’s say you have an object that you want to be persistent across invocations of your application. A set of configuration settings, history, who knows what. You don’t want to go nuts and worry about a whole database though. There are a couple good super-light-weight transactional persistence libraries built in to Ruby, PStore and YAML::Store, which do the job. You could them like this:

o = MyCoolObject.new

# to store it first
YAML::Store.new(".storage_for_my_app").transaction do | store |
  store['MyCoolObject'] = o
end

# .. and to retrieve it from storage.
YAML::Store.new(".storage_for_my_app").transaction do | store |
  o = store['MyCoolObject']
end

You’ve got to remember to store it again when you’re done with it of course.

I came up with a variation on this:

class MyCoolObject
  def MyCoolObject.stored_in(filename, *newargs)  # class method
    YAML::Store.new(filename).transaction do | store |
      store['self'] ||= MyCoolObject.new(*newargs)
      yield store['self']
   end
  end
end

Now I can make sure everything I do with my cool object “o” is stored persistently. All I have to do is wrap my actions on “o” in a stored_in block:

MyCoolObject.stored_in(".storage_for_my_app", 'a', 'b') do | o |
  # o is either recreated from .storage_for_my_app, or
  # created anew with args 'a' and 'b' passed to its constructor

  # .. here we do stuff with o
end
# and here o is re-serialized, with any changes intact.

When the block opens, ‘o’ is either resurrected from the storage file, or created anew from *new_args if there’s nothing in the file.

When the block ends, any changes to ‘o’ are stored there.

As long as you keep all your interaction with the object inside stored_in blocks, you’re golden. You get persistence!

Note that ‘o’ can hold other objects in its instance variables, which can hold other objects, and so on — anything in there that’s serializable can be stored this way. So you can use this to persist a whole pile of objects in one file as long as they all live in a single object which has a stored_in class method.

I thought this would be cool functionality to include in a module, whereupon I learned that in Ruby, a module’s “class methods” are not added to a class when you include a module in it. (I guess because a module isn’t a class and so it doesn’t really technically have “class methods”…) but you can get the same effect using a tiny bit of trickery with the “included” method of the Module class.

Here’s a module you can use to give any class these kind of storage abilities:

require 'yaml/store'

module StoredInFile
  def self.included(base)
   def base.stored_in(path, *args)
     YAML::Store.new(path).transaction do | store |
       store['self'] ||= self.new(*args)
       yield store['self']
     end
    end
  end
end

You use it like this:

class MyCoolObject
include StoredInFile

# other class stuff goes here

end

And that’s it.

UPDATE:

Stupid WordPress makes including code in a page really hard…  keeps eating my formatting.  I think I’ve got it…