Make state machines customizable with modules / aka overridable
Opened this issue · 5 comments
It seems (to us here), that there is really no way to override / customize a state machine in order to for example remove a state and a transition.
I would not really want to open an issue, rather ask the question if there is an intended way, known tricks or what is the perspective on adding support.
I would now imagine, that adding things (states, transitions) will always work (right?) and removing not?
so maybe some dsl support for this would be possible? what would be the lower level api to be used?
thanks
to override / customize a state machine
That's trivial. You can add new states, events, transitions, before/after handlers in a subclass. If you implement the event handlers as methods you can also use inheritance to extend/override their behavior.
remove a state and a transition.
That on the other hand is a thorny issue due to all the inter-dependence of the various elements in the state-machine
StateMachines essentially are single-inheritance. Or more specifically: copy-from-parent-class-on-modification. So generally you want to implement common behavior in an abstract superclass and add what you need in subclasses.
If you really want multiple inheritence you could easily instance-eval a proc from in the state-machine object and essentially have a 2nd call to the DSL.
Always try to add, never delete. If you have to remove something from a subclass you need to split out more behavior.
class MachineExtender
attr_accessor :dsl
def initialize &block
self.dsl = block
end
def apply(clazz, machine = :state)
clazz.state_machines[machine].instance_exec(&dsl)
end
end
MyExtensionLibrary::SomeExtender = MachineExtender.new do
state :foo
event :bar do
transition ...
end
end
class MyClass
state_machine do
...
end
MyExtensionLibrary::SomeExtender.apply(self)
end
Always try to add, never delete. If you have to remove something from
a subclass you need to split out more behavior.
thanks,
I fully agree with you, in case i build the application.
In fact the problem we have here is in a spreecommerce environment. In a
newer version, than we use, the problem is probably solvable in a
different way.
The usecase:
Remove a certain state from the order model (deliver).
When i googled 'state_machine override remove state" or something like
this, i found nothing except exactly this spree commerce issue here:
https://groups.google.com/forum/#!topic/spree-user/nsvxY_ISRjc
so what we will do, is propose an upgrade of spreecommerce to the client
and the problem will be solved for us, but i think this example makes
clear, that you cant always fully rely on a workflow like you propose.
i would still propose to have 'remove_state' 'override_state_transition'
or similar
anyway thanks for the great answer! :)
chris
Remove a certain state from the order model (deliver).
Why remove a state? Shouldn't it be sufficient to make it unreachable through transitions? Personally i have extended transitions with a few custom configuration fields, one of them allows me to disable them/mark them for internal use only.
On 01/21/2014 11:44 AM, the8472 wrote:
Remove a certain state from the order model (deliver).
Why remove a state? Shouldn't it be sufficient to make it unreachable
through transitions? Personally i have extended transitions with a few
custom configuration fields, one of them allows me to disable
them/mark them for internal use only.
This might be enough, yes, probably depends on if someone queries them.
How would that be done? Making unreachable in transitions?
Like i said, it's custom code to do that. Alternatively you can also remove the branches from an Event object pointing to that state:
https://github.com/pluginaweek/state_machine/blob/master/lib/state_machine/event.rb#L41