[typo] States!

Piers Cawley pdcawley at bofh.org.uk
Sun May 6 05:44:07 EDT 2007

I have, in my local directory a rewrite of the whole ContentState
debacle. The State pattern is definitely the way to go for handling
publication state; dealing with the kind of tangled conditional logic
that we'd have to replace the state objects with gives me the heebie

However, as I'm sure anyone who's looked at the existing code will
agree, what we have good be a good deal better. The problem is that I
got seduced by the (oh so handy but not quite what I need) existence
of ActiveRecord::Base.composed_of and instead of writing my own class
method to handle marshalling state objects to and from the database, I
used what Rails provided.

When you find yourself overriding 'new', it's probably a good time to
take a good hard think about what you're trying to achieve.

Anyhoo. A few days ago I was taking part in a discussion about
eliminating conditional code on a blog[1] and I found myself sketching
a rather nicer way of setting up state objects.

So, I've implemented it in typo and I must say I'm rather pleased with
it - I expect to be committing it soon, once I've given it a more
comprehensive test. Here's a taster of the new style:

  class Article
    self.has_state :state,
      :valid_states => [:new,
                        :just_published, :published, 
                        :just_withdrawn, :withdrawn]
      :handles      => [:before_save, :after_save,
                        :withdraw, :published=]

    module States
      class New < State
        def before_save
          content.state = :draft

        def published=(boolean)
          return if boolean.nil?
          content.state = boolean ? :just_published : :draft

      class JustPublished < State
        def enter_hook
          class << content; self; end.after_save {|content| content.send_pings}
          content.state = :published

      class Published < State
        def enter_hook
          content[:published] = true
          content[:published_at] ||= Time.now

        def withdraw
          content.state = :just_withdrawn

    include States

The has_state method deals with all the composed_of type ugliness and
setting up instantiation of state objects and method delgation, and we
can get rid of nasties like ContentState::Factory, which can only be a
good thing.

1. http://giantrobots.thoughtbot.com/2007/5/1/coding-without-ifs

Piers Cawley <pdcawley at bofh.org.uk>

More information about the Typo-list mailing list