Class: Newflow::Workflow
- Inherits:
-
Object
- Object
- Newflow::Workflow
- Defined in:
- lib/newflow/workflow.rb
Instance Method Summary collapse
- #construct_workflow!(definition) ⇒ Object
- #current_state ⇒ Object
- #current_state=(state) ⇒ Object
- #define_state_query_methods ⇒ Object
-
#initialize(extendee, definition) ⇒ Workflow
constructor
A new instance of Workflow.
- #state(name, opts = {}, &block) ⇒ Object
- #states ⇒ Object
- #to_dotty ⇒ Object
- #transition!(do_trigger = Newflow::WITH_SIDE_EFFECTS) ⇒ Object
- #transition_once!(do_trigger = Newflow::WITH_SIDE_EFFECTS) ⇒ Object
- #validate_workflow! ⇒ Object
- #would_transition_to ⇒ Object
Constructor Details
#initialize(extendee, definition) ⇒ Workflow
Returns a new instance of Workflow.
3 4 5 6 |
# File 'lib/newflow/workflow.rb', line 3 def initialize(extendee, definition) @extendee = extendee construct_workflow!(definition) end |
Instance Method Details
#construct_workflow!(definition) ⇒ Object
24 25 26 27 28 29 30 31 |
# File 'lib/newflow/workflow.rb', line 24 def construct_workflow!(definition) instance_eval &definition start_state = states.values.detect { |s| s.start? } @extendee.workflow_state ||= start_state.name.to_s if start_state validate_workflow! define_state_query_methods raise InvalidWorkflowStateError.new(current_state) unless states[current_state] end |
#current_state ⇒ Object
74 75 76 |
# File 'lib/newflow/workflow.rb', line 74 def current_state @extendee.workflow_state.to_sym end |
#current_state=(state) ⇒ Object
78 79 80 |
# File 'lib/newflow/workflow.rb', line 78 def current_state=(state) @extendee.workflow_state = state.to_s end |
#define_state_query_methods ⇒ Object
33 34 35 36 37 38 39 |
# File 'lib/newflow/workflow.rb', line 33 def define_state_query_methods states.keys.each do |key| instance_eval " def \#{key}?; current_state == :\#{key}; end\n EOS\n end\nend\n" |
#state(name, opts = {}, &block) ⇒ Object
19 20 21 22 |
# File 'lib/newflow/workflow.rb', line 19 def state(name, opts={}, &block) # TODO: Assert we're not overriding a state states[name] = State.new(name, opts, &block) end |
#states ⇒ Object
15 16 17 |
# File 'lib/newflow/workflow.rb', line 15 def states @states ||= {} end |
#to_dotty ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/newflow/workflow.rb', line 82 def to_dotty dot = "" dot << "digraph {\n" states.keys.each { |state_name| state = states[state_name] # it'd be nice to have the current state somehow shown visually shape = "circle" if state_name == current_state puts "setting current shape to doublecircle #{state_name} vs #{current_state}" shape = "doublecircle" end dot << %Q[ "#{state_name}" [ shape = #{shape} ]; \n] state.transitions.each { |transition| dot << " \"#{state_name}\" -> \"#{transition.target_state}\" [ label = \"#{transition.predicate_name}\" ];\n" } } dot << "}\n" return dot end |
#transition!(do_trigger = Newflow::WITH_SIDE_EFFECTS) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/newflow/workflow.rb', line 52 def transition!(do_trigger=Newflow::WITH_SIDE_EFFECTS) # TODO: watch out for max # of transits previous_state = current_state previous_states = {} num_transitions = 0 begin if previous_states[current_state] raise "Error: possible [infinite] loop in workflow, started in: #{previous_state}, currently in #{current_state}, been through all of (#{previous_states.keys.map(&:to_s).sort.join(", ")})" # TODO: TEST end previous_states[current_state] = true the_state = current_state transition_once!(do_trigger) end while the_state != current_state && states[current_state] previous_state == current_state ? nil : current_state ensure @extendee.workflow_state = previous_state unless do_trigger end |
#transition_once!(do_trigger = Newflow::WITH_SIDE_EFFECTS) ⇒ Object
41 42 43 44 45 46 47 48 49 50 |
# File 'lib/newflow/workflow.rb', line 41 def transition_once!(do_trigger=Newflow::WITH_SIDE_EFFECTS) state = states[current_state] raise InvalidWorkflowStateError.new(current_state) unless state # TODO: TEST target_state = states[state.run(@extendee, do_trigger)] if state != target_state @extendee.workflow_state = target_state.to_s target_state.run_on_entry(@extendee, do_trigger) end target_state end |
#validate_workflow! ⇒ Object
8 9 10 11 12 13 |
# File 'lib/newflow/workflow.rb', line 8 def validate_workflow! # TODO: Validate that all transitions reach a valid state # TODO: Validate that there is at least one stop state raise InvalidStateDefinitionError.new("#{@extendee.class} needs at least two states") if states.size < 2 raise InvalidStateDefinitionError.new("#{@extendee.class} needs a start of the workflow") unless current_state end |
#would_transition_to ⇒ Object
70 71 72 |
# File 'lib/newflow/workflow.rb', line 70 def would_transition_to transition!(Newflow::WITHOUT_SIDE_EFFECTS) end |