https://gist.github.com/vidarh/04b7d54c94f1f03b4cf084081166d...
It's very limited compared to this gem, some on purpose, some because it's a toy.
On purpose: Only a single state machine per class; I might be inclined to offer a tiny helper to let you delegate to a (sub-)state machine, but there's little to be gained by not using extra classes as extra namespaces to contain this.
The biggest gap is the lack of a lot of little helpers that'd be easy to add if you want (e.g. "state_name?") but that I feel creates little benefit over "state == :state_name", though trivially added if you eg. define an "event(state, &block)" class method that does "define_method(state, block)" and then add whatever extra "define_method"'s you want for introspection.
The other big gap is introspection. I think if you need to be able to introspect the state machine, then chances are it's too big to start with and you might want to decompose it.
But if you absolutely need that, you can still maintain most of the simplicity of the example I gave. E.g. you can either define a helper to define events (so you'd do e.g. event(:event_name) do ... end instead of def event_name = ...), but you can also use "method_added" and a flag to e.g. wrape the event methods in an "events" block so you can do "events do ... def some_event" and still obtain a list of events.
If you do the former, you can return or instance_eval a builder object (personally, and I've done this myself, I think Ruby devs are way too quick to resort to builder/instance_eval DSLs in cases where regular methods work just fine; builders/instance_eval risks creating all kinds of unintended issues).
If you do the latter, you can have the helpers honor a "dry run" flag that records the transitions instead.
The caveat in both cases is that unlike the example in the gist you can't use the regular Ruby flow control or some transitions and conditions might be "invisible", so you might end up with something like this:
events do
def repair = if_state(:stalled) do
unless_action(:auto_shop_busy) { transition_to(:parked) }
end
# other events
end
The change to the helpers to support building a state graph from this is trivial, or if you want you can even conditionally include the "dryrun" version of the helpers as needed.