Ignoring the issue of non-linear state machines, I've found the following to work well for my needs on a few projects with simple state machines:
# Check if the stage is in or before the supplied stage (stage_to_check).
def in_or_before_stage?(stage_to_check)
if stage_to_check.present? && self.stage.present?
STAGES_IN_ORDER.reverse.lazy.drop_while { |stg| stg != stage_to_check }.include?(self.stage)
else
false
end
end
and the other check is sometimes desired as well:
# Check if the stage is in or after the supplied stage (stage_to_check).
def in_or_after_stage?(stage_to_check)
if stage_to_check.present? && self.stage.present?
# Get all the stages that are in and after the stage we want to check (stage_to_check),
# and then see if the stage is in that list (well, technically in a lazy enumerable).
STAGES_IN_ORDER.lazy.drop_while { |stg| stg != stage_to_check }.include?(self.stage)
else
false
end
end
Where "STAGES_IN_ORDER" is just an array with the stages in order from initial to final.
We're just removing items from the list and then checking if our object's current stage is in our resulting list. If we want to know if it's in or before some stage we remove the later stages until we reach our supplied test stage, if we want to know if it's after some given stage we remove items from the front of the list.
I realize you probably don't need this answer anymore, but hopefully it helps someone =]