Class: StateManager::Base
- Inherits:
-
State
- Object
- State
- StateManager::Base
show all
- Extended by:
- DSL::Base
- Defined in:
- lib/state_manager/base.rb,
lib/state_manager/dsl.rb,
lib/state_manager/serialization.rb,
lib/state_manager/serialization.rb
Overview
The base StateManager class is responsible for tracking the current state of an object as well as managing the transitions between states.
Instance Attribute Summary collapse
Attributes inherited from State
#name, #parent_state, #states
Class Method Summary
collapse
Instance Method Summary
collapse
-
#around_event(event, *args, &block) ⇒ Object
-
#available_events ⇒ Object
All events the current state will respond to.
-
#current_state ⇒ Object
-
#current_state=(value) ⇒ Object
-
#did_transition(from, to, event) ⇒ Object
-
#encode_with(coder) ⇒ Object
-
#find_state_for_event(name) ⇒ Object
-
#has_state? ⇒ Boolean
Will not have a state if the state is invalid or nil.
-
#in_state?(path) ⇒ Boolean
Returns true if the underlying object is in the state specified by the given path.
-
#init_with(coder) ⇒ Object
-
#initialize(resource, context = {}) ⇒ Base
constructor
-
#perform_initial_transition? ⇒ Boolean
In the case of a new model, we wan’t to transition into the initial state and fire the appropriate callbacks.
-
#persist_state ⇒ Object
-
#read_state ⇒ Object
-
#respond_to_event?(name) ⇒ Boolean
-
#send_event(name, *args) ⇒ Object
-
#send_event!(name, *args) ⇒ Object
-
#state_manager ⇒ Object
-
#to_s ⇒ Object
-
#to_yaml_properties ⇒ Object
-
#transition_error(state) ⇒ Object
-
#transition_to(path, current_state = self.current_state) ⇒ Object
Transitions to the state at the specified path.
-
#will_transition(from, to, event) ⇒ Object
-
#write_state(value) ⇒ Object
These methods can be overriden by an adapter.
Methods included from DSL::Base
resource_class, resource_name, state_property
Methods inherited from State
create_resource_accessor!, #enter, #entered, #exit, #exited, #find_state, #find_states, #has_event?, #initial_state, #leaf?, #path, #perform_event, #to_sym
Methods included from DSL::State
#event, #initial_state, #state
#delayed_events, #entered, #exited
Constructor Details
#initialize(resource, context = {}) ⇒ Base
Returns a new instance of Base.
18
19
20
21
22
23
24
25
26
27
|
# File 'lib/state_manager/base.rb', line 18
def initialize(resource, context={})
super(nil, nil)
self.resource = resource
self.context = context
if perform_initial_transition?
initial_path = current_state && current_state.path || initial_state.path
transition_to initial_path, nil
end
end
|
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
in the class StateManager::State
Instance Attribute Details
#context ⇒ Object
Returns the value of attribute context.
16
17
18
|
# File 'lib/state_manager/base.rb', line 16
def context
@context
end
|
#resource ⇒ Object
Returns the value of attribute resource.
16
17
18
|
# File 'lib/state_manager/base.rb', line 16
def resource
@resource
end
|
Class Method Details
.added_to_resource(klass, property, options) ⇒ Object
188
189
|
# File 'lib/state_manager/base.rb', line 188
def self.added_to_resource(klass, property, options)
end
|
.infer_resource_name! ⇒ Object
175
176
177
178
179
180
181
|
# File 'lib/state_manager/base.rb', line 175
def self.infer_resource_name!
return if _resource_name
if name =~ /States/
self._resource_name = name.demodulize.gsub(/States/, '').underscore
create_resource_accessor!(_resource_name)
end
end
|
.inherited(base) ⇒ Object
183
184
185
186
|
# File 'lib/state_manager/base.rb', line 183
def self.inherited(base)
super(base)
base.infer_resource_name!
end
|
.yaml_new(klass, tag, val) ⇒ Object
7
8
9
|
# File 'lib/state_manager/serialization.rb', line 7
def self.yaml_new(klass, tag, val)
klass.new(val['resource'], val['context'])
end
|
Instance Method Details
#around_event(event, *args, &block) ⇒ Object
160
161
162
|
# File 'lib/state_manager/base.rb', line 160
def around_event(event, *args, &block)
yield
end
|
#available_events ⇒ Object
All events the current state will respond to
165
166
167
168
169
170
171
172
173
|
# File 'lib/state_manager/base.rb', line 165
def available_events
state = current_state
ret = {}
while(state) do
ret = state.class.specification.events.merge(ret)
state = state.parent_state
end
ret
end
|
#current_state ⇒ Object
76
77
78
79
|
# File 'lib/state_manager/base.rb', line 76
def current_state
path = read_state
find_state(path) if path && !path.empty?
end
|
#current_state=(value) ⇒ Object
81
82
83
|
# File 'lib/state_manager/base.rb', line 81
def current_state=(value)
write_state(value)
end
|
#did_transition(from, to, event) ⇒ Object
157
158
|
# File 'lib/state_manager/base.rb', line 157
def did_transition(from, to, event)
end
|
#encode_with(coder) ⇒ Object
19
20
21
22
23
24
|
# File 'lib/state_manager/serialization.rb', line 19
def encode_with(coder)
coder.map = {
"resource" => resource,
"context" => context
}
end
|
#find_state_for_event(name) ⇒ Object
107
108
109
110
111
112
113
|
# File 'lib/state_manager/base.rb', line 107
def find_state_for_event(name)
state = current_state
while(state) do
return state if state.has_event?(name)
state = state.parent_state
end
end
|
#has_state? ⇒ Boolean
Will not have a state if the state is invalid or nil
138
139
140
|
# File 'lib/state_manager/base.rb', line 138
def has_state?
!!current_state
end
|
#in_state?(path) ⇒ Boolean
Returns true if the underlying object is in the state specified by the given path. An object is ‘in’ a state if the state lies at any point of the current state’s path. E.g:
state_manager.current_state.path state_manager.in_state? 'outer' state_manager.in_state? 'outer.inner' state_manager.in_state? 'inner'
133
134
135
|
# File 'lib/state_manager/base.rb', line 133
def in_state?(path)
self.find_states(current_state.path).include? find_state(path)
end
|
#init_with(coder) ⇒ Object
26
27
28
|
# File 'lib/state_manager/serialization.rb', line 26
def init_with(coder)
initialize(coder["resource"], coder["context"])
end
|
In the case of a new model, we wan’t to transition into the initial state and fire the appropriate callbacks. The default behavior is to just check if the state field is nil.
32
33
34
|
# File 'lib/state_manager/base.rb', line 32
def perform_initial_transition?
!current_state
end
|
#persist_state ⇒ Object
151
152
|
# File 'lib/state_manager/base.rb', line 151
def persist_state
end
|
#read_state ⇒ Object
147
148
149
|
# File 'lib/state_manager/base.rb', line 147
def read_state
resource.send self.class._state_property
end
|
#respond_to_event?(name) ⇒ Boolean
103
104
105
|
# File 'lib/state_manager/base.rb', line 103
def respond_to_event?(name)
!!find_state_for_event(name)
end
|
#send_event(name, *args) ⇒ Object
93
94
95
96
97
98
99
100
101
|
# File 'lib/state_manager/base.rb', line 93
def send_event(name, *args)
self.current_event = name
state = find_state_for_event(name)
raise(InvalidEvent, transition_error(name)) unless state
result = state.perform_event name, *args
self.current_event = nil
result
end
|
#send_event!(name, *args) ⇒ Object
85
86
87
88
89
90
91
|
# File 'lib/state_manager/base.rb', line 85
def send_event!(name, *args)
around_event(name, *args) do
result = send_event(name, *args)
persist_state
result
end
end
|
#state_manager ⇒ Object
115
116
117
|
# File 'lib/state_manager/base.rb', line 115
def state_manager
self
end
|
#to_s ⇒ Object
119
120
121
122
|
# File 'lib/state_manager/base.rb', line 119
def to_s
path = "#{current_state.path}" if current_state
"#<%s:0x%x:%s>" % [self.class, object_id, path]
end
|
#to_yaml_properties ⇒ Object
11
12
13
|
# File 'lib/state_manager/serialization.rb', line 11
def to_yaml_properties
['@resource', '@context']
end
|
#transition_error(state) ⇒ Object
191
192
193
|
# File 'lib/state_manager/base.rb', line 191
def transition_error(state)
"Unable to transition from #{current_state} to #{state}"
end
|
#transition_to(path, current_state = self.current_state) ⇒ Object
Transitions to the state at the specified path. The path can be relative to any state along the current state’s path.
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
# File 'lib/state_manager/base.rb', line 38
def transition_to(path, current_state=self.current_state)
path = path.to_s
state = current_state || self
exit_states = []
new_states = state.find_states(path)
while(!new_states) do
exit_states << state
state = state.parent_state
raise(StateNotFound, transition_error(path)) unless state
new_states = state.find_states(path)
end
new_states.unshift(self) unless has_state?
raise(InvalidTransition, transition_error(path)) unless new_states.last.leaf?
enter_states = new_states - exit_states
exit_states = exit_states - new_states
from_state = current_state
to_state = enter_states.last || from_state
run_before_callbacks(from_state, to_state, current_event, enter_states, exit_states)
self.current_state = to_state
run_after_callbacks(from_state, to_state, current_event, enter_states, exit_states)
end
|
#will_transition(from, to, event) ⇒ Object
154
155
|
# File 'lib/state_manager/base.rb', line 154
def will_transition(from, to, event)
end
|
#write_state(value) ⇒ Object
These methods can be overriden by an adapter
143
144
145
|
# File 'lib/state_manager/base.rb', line 143
def write_state(value)
resource.send "#{self.class._state_property.to_s}=", value.path
end
|