geekq / workflow

Ruby finite-state-machine-inspired API for modeling workflow
MIT License
1.75k stars 207 forks source link

Added safeguard to ensure proper method undefinition #185

Closed alexmobo closed 5 years ago

alexmobo commented 7 years ago

This code ensures that a class structure as:

class MessageParent
  include Workflow
  workflow do
    state :state1 do
      event :event1, transitions_to: :state2
      event :event2, transitions_to: :state3
    end
    state :state2 do
      event :event2, transitions_to: :state3
    end
    state :state3
  end
end

class MessageChild < MessageParent
  include Workflow
  workflow do
    state :state4 do
      event :event3, transitions_to: :state5
    end
    state :state5
  end
end

Doesen´t raise an exception like:

undefined method `event2!' for class `MessageChild'

This happens because .workflow method tries to undefine the same event more than once.

mherold commented 7 years ago

:+1:

Just ran into the same problem ;)

FYI, I came up with a slightly different solution: Undefine the methods based on the unique event names (see below). Not sure which is preferrable, yours might be more robust?

      if respond_to? :inherited_workflow_spec # undefine methods defined by the old workflow_spec
        state_values = inherited_workflow_spec.states.values
        state_names = state_values.collect(&:name)
        event_names = state_values.flat_map { |state| state.events.flat.collect(&:name) }.uniq

        state_names.each do |state_name|
          module_eval do
            undef_method "#{state_name}?"
          end
        end

        event_names.each do |event_name|
          module_eval do
            undef_method "#{event_name}!".to_sym
            undef_method "can_#{event_name}?"
          end
        end
      end