[Rails 2.3] Adding a "phase" to the ActiveRecord Lifecycle

I’ve run into a situation where it seems to me that if I could
“extend” the typical AR lifecycle to add another “phase” or step to
it, it would simplify things. My problem is that I just don’t know
how to do that, and I haven’t been able to find documentation anywhere
on it!

I’m working on an e-commerce application that’s somewhat complex. Due
to constraints of the organization, I can’t have accounts or
authentication. My theoretical approach is to store an “order ID” in
a user’s session, and of course all selects/updates etc. will be only
for that ID from their session, thus keeping the current status of the
order in the database at all times. If it were something simple I’d
just store an array of object IDs in the user’s session, but there are
several different kinds of products/services to buy, each with vastly
different models and various requirements (“if you have this you must
have that but MUST NOT have this”, etc.).

The AR lifecycle question comes into play because I’d like to add some
form of validation on the Order model, but ONLY before and after the
user’s card is charged.

So here’s a simple example -

Order < ActiveRecord::Base
belongs_to :object
has_many :item_joins
has_many :items, :through => :item_joins
has_many :other_item_joins
has_many :other_items, :through => :other_item_joins

… and so on …

end

Because the user can add multiple items and other items to their
order, as well as set the “object” on their order at any point in the
ordering process, I can’t call the validations I want after save. I’d
much rather have a before/after “card_charged” step in the lifecycle,
and run a validation before the card is charged, then one after it’s
charged.

So, for example, I’d like to be able to do something like:

Order < ActiveRecord::Base

…associations as above…

before_charged :ensure_not_empty

private
def ensure_not_empty
if self.items.count < 1 and self.other_items.count < 1
errors.add_to_base(“Your order is empty!”)
return false
end
end

And this would need to fire when Order#charge_card (or similarly named
method) is called, canceling the call to Order#charge_card if false is
returned.

I took a quick look at state machines (aasm), and I have to confess
that I don’t know much about a state machine or how one works other
than very basic theory. It seems like it might be overkill to me,
when all I want to do is add two methods to the lifecycle as above.
However, I’m fully willing to consider the possibility that what I’m
suggesting here is outside the realm of best practices.

Any ideas as to how I could add these steps to the lifecycle of just
this object?

On Tuesday 13 April 2010, Phoenix R. wrote:

Because the user can add multiple items and other items to their
order, as well as set the “object” on their order at any point in the
ordering process, I can’t call the validations I want after
save. I’d much rather have a before/after “card_charged” step in
the lifecycle, and run a validation before the card is charged, then
one after it’s charged.

I think the best way would be to use a state machine, as you indicated
yourself in a paragraph I snipped. Second best would be to use
conditional validations, see the :if and :unless options on all the
validates_something methods.

Michael


Michael S.
mailto:[email protected]
http://www.schuerig.de/michael/