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
jeebies.
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,
:draft,
:just_published, :published,
:publication_pending,
:just_withdrawn, :withdrawn]
:handles => [:before_save, :after_save,
:withdraw, :published=]
module States
class New < State
def before_save
content.state = :draft
end
def published=(boolean)
return if boolean.nil?
content.state = boolean ? :just_published : :draft
end
end
class JustPublished < State
def enter_hook
class << content; self; end.after_save {|content|
content.send_pings}
content.state = :published
end
end
class Published < State
def enter_hook
content[:published] = true
content[:published_at] ||= Time.now
end
def withdraw
content.state = :just_withdrawn
end
end
...
end
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.