edge-state-machine 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +12 -9
- data/lib/active_record/edge-state-machine.rb +54 -9
- data/lib/edge-state-machine/event.rb +20 -91
- data/lib/edge-state-machine/exception.rb +8 -0
- data/lib/edge-state-machine/machine.rb +23 -68
- data/lib/edge-state-machine/state.rb +25 -23
- data/lib/edge-state-machine/transition.rb +32 -25
- data/lib/edge-state-machine/version.rb +1 -1
- data/lib/edge-state-machine.rb +51 -36
- data/lib/mongoid/edge-state-machine.rb +54 -9
- data/spec/active_record/active_record_helper.rb +2 -0
- data/spec/active_record/active_record_spec.rb +36 -144
- data/spec/active_record/samples/order.rb +47 -0
- data/spec/active_record/samples/traffic_light.rb +46 -0
- data/spec/event_spec.rb +13 -21
- data/spec/machine_spec.rb +11 -14
- data/spec/mongoid/mongoid_helper.rb +4 -2
- data/spec/mongoid/mongoid_spec.rb +35 -153
- data/spec/mongoid/samples/order.rb +54 -0
- data/spec/mongoid/samples/traffic_light.rb +46 -0
- data/spec/non_persistent/non_persistent_helper.rb +9 -0
- data/spec/non_persistent/non_persistent_spec.rb +80 -0
- data/spec/non_persistent/samples/dice.rb +31 -0
- data/spec/non_persistent/samples/microwave.rb +54 -0
- data/spec/non_persistent/samples/user.rb +23 -0
- data/spec/state_spec.rb +19 -14
- data/spec/transition_spec.rb +69 -31
- metadata +24 -14
data/README.rdoc
CHANGED
@@ -1,14 +1,9 @@
|
|
1
|
-
= Travis Build Status
|
2
|
-
|
3
|
-
{<img src="https://secure.travis-ci.org/danpersa/edge-state-machine.png"/>}[http://travis-ci.org/danpersa/edge-state-machine]
|
4
|
-
|
5
1
|
= Edge State Machine
|
6
2
|
|
7
|
-
|
8
|
-
|
9
|
-
commit}[http://github.com/rails/rails/commit/db49c706b62e7ea2ab93f05399dbfddf5087ee0c].
|
3
|
+
Edge state machine wants to be a complete state machine solution.
|
4
|
+
It offers support for ActiveRecord and Mongoid
|
10
5
|
|
11
|
-
|
6
|
+
{<img src="https://secure.travis-ci.org/danpersa/edge-state-machine.png"/>}[http://travis-ci.org/danpersa/edge-state-machine]
|
12
7
|
|
13
8
|
== Installation
|
14
9
|
|
@@ -60,4 +55,12 @@ If you're using Rails + Mongoid + Bundler
|
|
60
55
|
event :available do
|
61
56
|
transitions :to => :available, :from => [:out_of_stock], :on_transition => :send_alerts
|
62
57
|
end
|
63
|
-
end
|
58
|
+
end
|
59
|
+
|
60
|
+
= Credits
|
61
|
+
|
62
|
+
The gem is based on Rick Olson's code of ActiveModel::StateMachine,
|
63
|
+
axed from ActiveModel in {this
|
64
|
+
commit}[http://github.com/rails/rails/commit/db49c706b62e7ea2ab93f05399dbfddf5087ee0c].
|
65
|
+
|
66
|
+
And on Krzysiek Heród's gem, {Transitions}[https://github.com/netizer/transitions], which added Mongoid support.
|
@@ -5,28 +5,73 @@ module ActiveRecord
|
|
5
5
|
included do
|
6
6
|
include ::EdgeStateMachine
|
7
7
|
after_initialize :set_initial_state
|
8
|
-
|
8
|
+
validate :state_variables_presence
|
9
9
|
validate :state_inclusion
|
10
10
|
end
|
11
11
|
|
12
|
+
# The optional options argument is passed to find when reloading so you may
|
13
|
+
# do e.g. record.reload(:lock => true) to reload the same record with an
|
14
|
+
# exclusive row lock.
|
15
|
+
def reload(options = nil)
|
16
|
+
super.tap do
|
17
|
+
@current_states = {}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
12
21
|
protected
|
13
22
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
23
|
+
def load_from_persistence(machine_name)
|
24
|
+
machine = self.class.state_machines[machine_name]
|
25
|
+
send machine.persisted_variable_name.to_s
|
17
26
|
end
|
18
27
|
|
19
|
-
def
|
20
|
-
self.
|
28
|
+
def save_to_persistence(new_state, machine_name, options = {})
|
29
|
+
machine = self.class.state_machines[machine_name]
|
30
|
+
send("#{machine.persisted_variable_name}=".to_sym, new_state)
|
31
|
+
save! if options[:save]
|
21
32
|
end
|
22
33
|
|
23
34
|
def set_initial_state
|
24
|
-
|
35
|
+
# set the initial state for each state machine in this class
|
36
|
+
self.class.state_machines.keys.each do |machine_name|
|
37
|
+
machine = self.class.state_machines[machine_name]
|
38
|
+
|
39
|
+
if persisted_variable_value(machine.persisted_variable_name).blank?
|
40
|
+
if load_from_persistence(machine_name)
|
41
|
+
send("#{machine.persisted_variable_name}=".to_sym, load_from_persistence(machine_name))
|
42
|
+
else
|
43
|
+
send("#{machine.persisted_variable_name}=".to_sym, machine.initial_state_name)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def persisted_variable_value(name)
|
50
|
+
send(name.to_s)
|
51
|
+
end
|
52
|
+
|
53
|
+
def state_variables_presence
|
54
|
+
# validate that state is in the right set of values
|
55
|
+
self.class.state_machines.keys.each do |machine_name|
|
56
|
+
machine = self.class.state_machines[machine_name]
|
57
|
+
validates_presence_of machine.persisted_variable_name.to_sym
|
58
|
+
end
|
25
59
|
end
|
26
60
|
|
27
61
|
def state_inclusion
|
28
|
-
|
29
|
-
|
62
|
+
# validate that state is in the right set of values
|
63
|
+
self.class.state_machines.keys.each do |machine_name|
|
64
|
+
machine = self.class.state_machines[machine_name]
|
65
|
+
unless machine.states.keys.include?(persisted_variable_value(machine.persisted_variable_name).to_sym)
|
66
|
+
self.errors.add(machine.persisted_variable_name.to_sym, :inclusion, :value => persisted_variable_value(machine.persisted_variable_name))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module ClassMethods
|
72
|
+
def add_scope(state, machine_name)
|
73
|
+
machine = state_machines[machine_name]
|
74
|
+
scope state.name, where(machine.persisted_variable_name.to_sym => state.name.to_s)
|
30
75
|
end
|
31
76
|
end
|
32
77
|
end
|
@@ -2,106 +2,35 @@ module EdgeStateMachine
|
|
2
2
|
class Event
|
3
3
|
attr_reader :name, :success, :timestamp
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@machine
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
|
12
|
-
machine.klass.send(:define_method, name.to_s) do |*args|
|
13
|
-
machine.fire_event(name, self, false, *args)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
update(options, &block)
|
5
|
+
def initialize(name, machine, &transitions)
|
6
|
+
@machine = machine
|
7
|
+
@name = name
|
8
|
+
@transitions = []
|
9
|
+
instance_eval(&transitions) if block_given?
|
17
10
|
end
|
18
11
|
|
19
|
-
def fire(obj,
|
20
|
-
|
21
|
-
|
12
|
+
def fire(obj, options = {})
|
13
|
+
current_state = obj.current_state(@machine.name)
|
14
|
+
transition = @transitions.select{ |t| t.from.include? current_state.name }.first
|
22
15
|
|
23
|
-
|
24
|
-
|
25
|
-
next if to_state && !Array(transition.to).include?(to_state)
|
26
|
-
if transition.perform(obj)
|
27
|
-
next_state = to_state || Array(transition.to).first
|
28
|
-
transition.execute(obj, *args)
|
29
|
-
break
|
30
|
-
end
|
31
|
-
end
|
32
|
-
# Update timestamps on obj if a timestamp has been defined
|
33
|
-
update_event_timestamp(obj, next_state) if timestamp_defined?
|
34
|
-
next_state
|
35
|
-
end
|
16
|
+
raise NoTransitionFound.new("No transition found for event #{@name}") if transition.nil?
|
17
|
+
return false unless transition.possible?(obj)
|
36
18
|
|
37
|
-
|
38
|
-
|
39
|
-
|
19
|
+
next_state = @machine.states[transition.find_next_state(obj)]
|
20
|
+
raise NoStateFound.new("Invalid state #{transition.to.to_s} for transition.") if next_state.nil?
|
21
|
+
transition.execute(obj)
|
40
22
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
else
|
45
|
-
name == event.name
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Has the timestamp option been specified for this event?
|
50
|
-
def timestamp_defined?
|
51
|
-
!@timestamp.nil?
|
52
|
-
end
|
23
|
+
current_state.execute_action(:exit, obj)
|
24
|
+
#klass._previous_state = current_state.name.to_s
|
25
|
+
next_state.execute_action(:enter, obj)
|
53
26
|
|
54
|
-
|
55
|
-
|
56
|
-
self.timestamp = options[:timestamp] if options[:timestamp]
|
57
|
-
instance_eval(&block) if block
|
58
|
-
self
|
27
|
+
obj.set_current_state(next_state, @machine.name, options)
|
28
|
+
true
|
59
29
|
end
|
60
|
-
|
61
|
-
# update the timestamp attribute on obj
|
62
|
-
def update_event_timestamp(obj, next_state)
|
63
|
-
obj.send "#{timestamp_attribute_name(obj, next_state)}=", Time.now
|
64
|
-
end
|
65
|
-
|
66
|
-
# Set the timestamp attribute.
|
67
|
-
# @raise [ArgumentError] timestamp should be either a String, Symbol or true
|
68
|
-
def timestamp=(value)
|
69
|
-
case value
|
70
|
-
when String, Symbol, TrueClass
|
71
|
-
@timestamp = value
|
72
|
-
else
|
73
|
-
raise ArgumentError, "timestamp must be either: true, a String or a Symbol"
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
30
|
|
78
31
|
private
|
79
|
-
|
80
|
-
|
81
|
-
# If the timestamp was simply true it returns the default_timestamp_name
|
82
|
-
# otherwise, returns the user-specified timestamp name
|
83
|
-
def timestamp_attribute_name(obj, next_state)
|
84
|
-
timestamp == true ? default_timestamp_name(obj, next_state) : @timestamp
|
85
|
-
end
|
86
|
-
|
87
|
-
# If @timestamp is true, try a default timestamp name
|
88
|
-
def default_timestamp_name(obj, next_state)
|
89
|
-
at_name = "%s_at" % next_state
|
90
|
-
on_name = "%s_on" % next_state
|
91
|
-
case
|
92
|
-
when obj.respond_to?(at_name) then at_name
|
93
|
-
when obj.respond_to?(on_name) then on_name
|
94
|
-
else
|
95
|
-
raise NoMethodError, "Couldn't find a suitable timestamp field for event: #{@name}.
|
96
|
-
Please define #{at_name} or #{on_name} in #{obj.class}"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
|
101
|
-
def transitions(trans_opts)
|
102
|
-
Array(trans_opts[:from]).each do |s|
|
103
|
-
@transitions << Transition.new(trans_opts.merge({:from => s.to_sym}))
|
104
|
-
end
|
32
|
+
def transition(trans_opts)
|
33
|
+
@transitions << EdgeStateMachine::Transition.new(trans_opts)
|
105
34
|
end
|
106
35
|
end
|
107
36
|
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module EdgeStateMachine
|
2
|
+
class InvalidTransition < StandardError; end
|
3
|
+
class InvalidMethodOverride < StandardError; end
|
4
|
+
class NoTransitionFound < Exception; end
|
5
|
+
class NoStateFound < Exception; end
|
6
|
+
class NoEventFound < Exception; end
|
7
|
+
class NoGuardFound < Exception; end
|
8
|
+
end
|
@@ -1,85 +1,40 @@
|
|
1
1
|
module EdgeStateMachine
|
2
2
|
class Machine
|
3
|
-
|
4
|
-
|
5
|
-
attr_reader :klass, :name, :auto_scopes
|
3
|
+
attr_accessor :states, :events, :klass, :persisted_variable_name
|
4
|
+
attr_reader :name, :initial_state_name
|
6
5
|
|
7
|
-
def initialize(klass, name,
|
8
|
-
@klass
|
9
|
-
|
6
|
+
def initialize(klass, name, &block)
|
7
|
+
@klass = klass
|
8
|
+
@name = name
|
9
|
+
@states = Hash.new
|
10
|
+
@events = Hash.new
|
11
|
+
instance_eval(&block) if block_given?
|
10
12
|
end
|
11
13
|
|
12
|
-
def initial_state
|
13
|
-
@
|
14
|
+
def initial_state(name)
|
15
|
+
@initial_state_name = name
|
14
16
|
end
|
15
17
|
|
16
|
-
def
|
17
|
-
@
|
18
|
-
@auto_scopes = options[:auto_scopes]
|
19
|
-
instance_eval(&block) if block
|
20
|
-
include_scopes if @auto_scopes && defined?(ActiveRecord::Base) && @klass < ActiveRecord::Base
|
21
|
-
self
|
18
|
+
def persisted_to(name)
|
19
|
+
@persisted_variable_name = name
|
22
20
|
end
|
23
21
|
|
24
|
-
def
|
25
|
-
|
26
|
-
if new_state = @events[event].fire(record, nil, *args)
|
27
|
-
state_index[new_state].call_action(:enter, record)
|
28
|
-
|
29
|
-
if record.respond_to?(event_fired_callback)
|
30
|
-
record.send(event_fired_callback, record.current_state, new_state, event)
|
31
|
-
end
|
32
|
-
|
33
|
-
record.current_state(@name, new_state, persist)
|
34
|
-
record.send(@events[event].success) if @events[event].success
|
35
|
-
true
|
36
|
-
else
|
37
|
-
if record.respond_to?(event_failed_callback)
|
38
|
-
record.send(event_failed_callback, event)
|
39
|
-
end
|
40
|
-
|
41
|
-
false
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def states_for_select
|
46
|
-
states.map { |st| [st.display_name, st.name.to_s] }
|
47
|
-
end
|
48
|
-
|
49
|
-
def events_for(state)
|
50
|
-
events = @events.values.select { |event| event.transitions_from_state?(state) }
|
51
|
-
events.map! { |event| event.name }
|
22
|
+
def create_scopes(bool = false)
|
23
|
+
@create_scopes = bool
|
52
24
|
end
|
53
25
|
|
54
|
-
def
|
55
|
-
|
26
|
+
def create_scopes?
|
27
|
+
@create_scopes
|
56
28
|
end
|
57
29
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@states
|
30
|
+
def state(name, &state)
|
31
|
+
state = EdgeStateMachine::State.new(name, &state)
|
32
|
+
@initial_state_name ||= state.name
|
33
|
+
@states[name.to_sym] = state
|
62
34
|
end
|
63
35
|
|
64
|
-
def event(name,
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
def event_fired_callback
|
69
|
-
@event_fired_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_fired'
|
70
|
-
end
|
71
|
-
|
72
|
-
def event_failed_callback
|
73
|
-
@event_failed_callback ||= (@name == :default ? '' : "#{@name}_") + 'event_failed'
|
74
|
-
end
|
75
|
-
|
76
|
-
def include_scopes
|
77
|
-
@states.each do |state|
|
78
|
-
state_name = state.name.to_s
|
79
|
-
raise InvalidMethodOverride if @klass.respond_to?(state_name)
|
80
|
-
@klass.scope state_name, @klass.where(:state => state_name)
|
81
|
-
end
|
36
|
+
def event(name, &transitions)
|
37
|
+
@events[name.to_sym] ||= EdgeStateMachine::Event.new(name, self, &transitions)
|
82
38
|
end
|
83
39
|
end
|
84
|
-
end
|
85
|
-
|
40
|
+
end
|
@@ -2,44 +2,46 @@ module EdgeStateMachine
|
|
2
2
|
class State
|
3
3
|
attr_reader :name, :options
|
4
4
|
|
5
|
-
def initialize(name,
|
5
|
+
def initialize(name, &block)
|
6
6
|
@name = name
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
update(options)
|
7
|
+
@options = Hash.new
|
8
|
+
instance_eval(&block) if block_given?
|
11
9
|
end
|
12
10
|
|
13
|
-
def
|
14
|
-
|
15
|
-
name == state
|
16
|
-
else
|
17
|
-
name == state.name
|
18
|
-
end
|
11
|
+
def enter(method = nil, &block)
|
12
|
+
@options[:enter] = method.nil? ? block : method
|
19
13
|
end
|
20
14
|
|
21
|
-
def
|
22
|
-
|
15
|
+
def exit(method = nil, &block)
|
16
|
+
@options[:exit] = method.nil? ? block : method
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute_action(action, base)
|
20
|
+
action = @options[action.to_sym]
|
23
21
|
case action
|
24
22
|
when Symbol, String
|
25
|
-
|
23
|
+
base.send(action)
|
26
24
|
when Proc
|
27
|
-
action.call(
|
25
|
+
action.call(base)
|
28
26
|
end
|
29
27
|
end
|
30
28
|
|
31
|
-
def display_name
|
32
|
-
@display_name
|
29
|
+
def use_display_name(display_name)
|
30
|
+
@display_name = display_name
|
33
31
|
end
|
34
32
|
|
35
|
-
def
|
36
|
-
|
33
|
+
def display_name
|
34
|
+
@display_name ||= name.to_s.gsub(/_/, ' ').capitalize
|
37
35
|
end
|
38
36
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
37
|
+
def ==(state)
|
38
|
+
if state.is_a? Symbol
|
39
|
+
name == state
|
40
|
+
elsif state.is_a? String
|
41
|
+
name == state
|
42
|
+
else
|
43
|
+
name == state.name
|
44
|
+
end
|
43
45
|
end
|
44
46
|
end
|
45
47
|
end
|
@@ -3,36 +3,37 @@ module EdgeStateMachine
|
|
3
3
|
attr_reader :from, :to, :options
|
4
4
|
|
5
5
|
def initialize(opts)
|
6
|
-
@from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
|
7
|
-
@options = opts
|
6
|
+
@from, @to, @guard, @on_transition = [opts[:from]].flatten, [opts[:to]].flatten, opts[:guard], [opts[:on_transition]].flatten
|
8
7
|
end
|
9
8
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
def find_next_state(obj)
|
10
|
+
# if we have many states we can go but no guard
|
11
|
+
if @guard.nil? && @to.size > 1
|
12
|
+
raise NoGuardFound.new("There are many possible 'to' states but there is no 'guard' to decide which state to go")
|
13
|
+
end
|
14
|
+
if @guard
|
15
|
+
return execute_action(@guard, obj)
|
16
16
|
else
|
17
|
-
|
17
|
+
return @to.first
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
obj.send(
|
21
|
+
def possible?(obj)
|
22
|
+
next_state = find_next_state(obj)
|
23
|
+
return true if @to.include? next_state
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute(obj)
|
28
|
+
@on_transition.each do |transition|
|
29
|
+
case transition
|
30
|
+
when Symbol, String
|
31
|
+
obj.send(transition)
|
32
|
+
when Proc
|
33
|
+
transition.call(obj)
|
34
|
+
else
|
35
|
+
raise ArgumentError, "You can only pass a Symbol, a String or a Proc to 'on_transition' - got #{transition.class}." unless transition.nil?
|
32
36
|
end
|
33
|
-
else
|
34
|
-
# TODO We probably should check for this in the constructor and not that late.
|
35
|
-
raise ArgumentError, "You can only pass a Symbol, a String, a Proc or an Array to 'on_transition' - got #{@on_transition.class}." unless @on_transition.nil?
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
@@ -40,8 +41,14 @@ module EdgeStateMachine
|
|
40
41
|
@from == obj.from && @to == obj.to
|
41
42
|
end
|
42
43
|
|
43
|
-
|
44
|
-
|
44
|
+
private
|
45
|
+
def execute_action(action, base)
|
46
|
+
case action
|
47
|
+
when Symbol, String
|
48
|
+
base.send(action)
|
49
|
+
when Proc
|
50
|
+
action.call(base)
|
51
|
+
end
|
45
52
|
end
|
46
53
|
end
|
47
54
|
end
|
data/lib/edge-state-machine.rb
CHANGED
@@ -2,11 +2,15 @@ require "edge-state-machine/event"
|
|
2
2
|
require "edge-state-machine/machine"
|
3
3
|
require "edge-state-machine/state"
|
4
4
|
require "edge-state-machine/transition"
|
5
|
+
require "edge-state-machine/exception"
|
5
6
|
require "edge-state-machine/version"
|
6
7
|
|
7
8
|
module EdgeStateMachine
|
8
|
-
|
9
|
-
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.extend(ClassMethods)
|
12
|
+
base.send :include, InstanceMethods
|
13
|
+
end
|
10
14
|
|
11
15
|
module ClassMethods
|
12
16
|
def inherited(klass)
|
@@ -22,52 +26,63 @@ module EdgeStateMachine
|
|
22
26
|
@state_machines = value ? value.dup : nil
|
23
27
|
end
|
24
28
|
|
25
|
-
def state_machine(name =
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
+
def state_machine(name = :default, &block)
|
30
|
+
machine = Machine.new(self, name, &block)
|
31
|
+
state_machines[name] ||= machine
|
32
|
+
|
33
|
+
machine.persisted_variable_name ||= :state
|
34
|
+
|
35
|
+
machine.states.values.each do |state|
|
36
|
+
state_name = state.name
|
37
|
+
define_method "#{state_name}?" do
|
38
|
+
state_name == current_state(name).name
|
39
|
+
end
|
40
|
+
add_scope(state, name) if machine.create_scopes?
|
29
41
|
end
|
30
|
-
name ||= :default
|
31
|
-
state_machines[name] ||= Machine.new(self, name)
|
32
|
-
block ? state_machines[name].update(options, &block) : state_machines[name]
|
33
|
-
end
|
34
42
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
machine.events.keys.each do |key|
|
44
|
+
define_method "#{key}" do
|
45
|
+
fire_event(machine.name, {:save => false}, key)
|
46
|
+
end
|
47
|
+
|
48
|
+
define_method "#{key}!" do
|
49
|
+
fire_event(machine.name, {:save => true}, key)
|
50
|
+
end
|
40
51
|
end
|
41
52
|
end
|
42
53
|
end
|
43
54
|
|
44
|
-
|
45
|
-
|
46
|
-
end
|
55
|
+
module InstanceMethods
|
56
|
+
attr_writer :current_state
|
47
57
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
if persist && respond_to?(:write_state)
|
53
|
-
write_state(sm, new_state)
|
54
|
-
end
|
58
|
+
def initial_state_name(name = :default)
|
59
|
+
machine = self.class.state_machines[name]
|
60
|
+
return machine.initial_state_name
|
61
|
+
end
|
55
62
|
|
56
|
-
|
57
|
-
|
63
|
+
def current_state(name = :default)
|
64
|
+
@current_states ||= {}
|
65
|
+
machine = self.class.state_machines[name]
|
66
|
+
if (respond_to? :load_from_persistence)
|
67
|
+
@current_states[name] ||= self.class.state_machines[name].states[load_from_persistence(name).to_sym]
|
58
68
|
end
|
69
|
+
@current_states[name] ||= machine.states[machine.initial_state_name]
|
70
|
+
end
|
59
71
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
value = instance_variable_get(ivar)
|
64
|
-
return value if value
|
72
|
+
def current_state_name(name = :default)
|
73
|
+
current_state(name).name
|
74
|
+
end
|
65
75
|
|
66
|
-
|
67
|
-
|
68
|
-
|
76
|
+
def fire_event(name = :default, options = {}, event_name)
|
77
|
+
machine = self.class.state_machines[name]
|
78
|
+
event = machine.events[event_name]
|
79
|
+
raise Stateflow::NoEventFound.new("No event matches #{event_name}") if event.nil?
|
80
|
+
event.fire(self, options)
|
81
|
+
end
|
69
82
|
|
70
|
-
|
83
|
+
def set_current_state(new_state, machine_name = :default, options = {})
|
84
|
+
save_to_persistence(new_state.name.to_s, machine_name, options) if self.respond_to? :save_to_persistence
|
85
|
+
@current_states[machine_name] = new_state
|
71
86
|
end
|
72
87
|
end
|
73
88
|
end
|