edge-state-machine 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|