state_shifter 0.8.1 → 1.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.
- checksums.yaml +7 -0
- data/Gemfile +10 -12
- data/Gemfile.lock +94 -48
- data/README.md +163 -3
- data/VERSION +1 -1
- data/examples/advanced.rb +15 -3
- data/examples/review.rb +4 -1
- data/lib/state_shifter/definition/active_record_integration_methods.rb +15 -1
- data/lib/state_shifter/definition/class_methods.rb +70 -50
- data/lib/state_shifter/definition/contents.rb +26 -14
- data/lib/state_shifter/definition/instance_methods.rb +17 -18
- data/lib/state_shifter/definition.rb +1 -0
- data/lib/state_shifter/draw.rb +38 -0
- data/lib/state_shifter/event.rb +5 -0
- data/lib/state_shifter/railtie.rb +11 -0
- data/lib/state_shifter/state.rb +26 -4
- data/lib/state_shifter.rb +13 -7
- data/lib/tasks/state_shifter.rake +26 -0
- data/spec/state_shifter_spec.rb +82 -17
- data/state_shifter.gemspec +39 -26
- metadata +95 -75
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 560af59d82afed84d4b51b47e7c5f2487acc86f4
|
4
|
+
data.tar.gz: 0f6b9926727a1543f9c11a0f1b50ffd6dcb7bafb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 440824ce9be500d3ea26b8373916d020aad8c423a418fc6b491338e1681a8fcb11e46cffb2e8c27e85b2aef4b50624476131fb259392bb14ed2b2b7768e43e2d
|
7
|
+
data.tar.gz: dafc299c8726b62b3f55d832cec3eb9be8f38f475330727dd6ac960147a00ece293a7ef5f511282d96b1fff5135816fe5ba73c12fe4740701e894e3815650a06
|
data/Gemfile
CHANGED
@@ -1,19 +1,17 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
|
-
# Add dependencies required to use your gem here.
|
3
|
-
# Example:
|
4
|
-
# gem "activesupport", ">= 2.3.5"
|
5
2
|
|
6
|
-
# Add dependencies to develop your gem here.
|
7
|
-
# Include everything needed to run rake, tests, features, etc.
|
8
3
|
group :development do
|
9
|
-
gem 'activerecord', '~>
|
10
|
-
gem 'sqlite3'
|
11
|
-
gem
|
12
|
-
gem "rspec", "~> 2.9.0"
|
4
|
+
gem 'activerecord', '~> 4.0.9'
|
5
|
+
gem 'sqlite3', '~> 1.3.10'
|
6
|
+
gem "rspec"
|
13
7
|
gem "yard", "~> 0.7"
|
14
|
-
gem 'redcarpet'
|
15
|
-
gem "rdoc", "~>
|
16
|
-
gem "bundler"
|
8
|
+
gem 'redcarpet', '>= 3.2.3'
|
9
|
+
gem "rdoc", "~> 4.0"
|
10
|
+
gem "bundler"
|
17
11
|
gem "jeweler", "~> 1.8.4"
|
18
12
|
gem "simplecov", ">= 0"
|
13
|
+
gem 'pry'
|
14
|
+
gem 'ruby-graphviz'
|
15
|
+
gem 'json', ">= 1.8.2"
|
16
|
+
gem 'i18n', '>= 0.6.6'
|
19
17
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,64 +1,110 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
activemodel (
|
5
|
-
activesupport (=
|
6
|
-
builder (~> 3.
|
7
|
-
activerecord (
|
8
|
-
activemodel (=
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
4
|
+
activemodel (4.0.13)
|
5
|
+
activesupport (= 4.0.13)
|
6
|
+
builder (~> 3.1.0)
|
7
|
+
activerecord (4.0.13)
|
8
|
+
activemodel (= 4.0.13)
|
9
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
10
|
+
activesupport (= 4.0.13)
|
11
|
+
arel (~> 4.0.0)
|
12
|
+
activerecord-deprecated_finders (1.0.4)
|
13
|
+
activesupport (4.0.13)
|
14
|
+
i18n (~> 0.6, >= 0.6.9)
|
15
|
+
minitest (~> 4.2)
|
16
|
+
multi_json (~> 1.3)
|
17
|
+
thread_safe (~> 0.1)
|
18
|
+
tzinfo (~> 0.3.37)
|
19
|
+
addressable (2.3.8)
|
20
|
+
arel (4.0.2)
|
21
|
+
builder (3.1.4)
|
22
|
+
coderay (1.1.0)
|
23
|
+
diff-lcs (1.2.5)
|
24
|
+
docile (1.1.5)
|
25
|
+
faraday (0.8.9)
|
26
|
+
multipart-post (~> 1.2.0)
|
27
|
+
git (1.2.9.1)
|
28
|
+
github_api (0.10.1)
|
29
|
+
addressable
|
30
|
+
faraday (~> 0.8.1)
|
31
|
+
hashie (>= 1.2)
|
32
|
+
multi_json (~> 1.4)
|
33
|
+
nokogiri (~> 1.5.2)
|
34
|
+
oauth2
|
35
|
+
hashie (3.4.1)
|
36
|
+
highline (1.7.1)
|
37
|
+
i18n (0.7.0)
|
38
|
+
jeweler (1.8.8)
|
39
|
+
builder
|
21
40
|
bundler (~> 1.0)
|
22
41
|
git (>= 1.2.5)
|
42
|
+
github_api (= 0.10.1)
|
43
|
+
highline (>= 1.6.15)
|
44
|
+
nokogiri (= 1.5.10)
|
23
45
|
rake
|
24
46
|
rdoc
|
25
|
-
json (1.
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
47
|
+
json (1.8.2)
|
48
|
+
jwt (1.4.1)
|
49
|
+
method_source (0.8.2)
|
50
|
+
minitest (4.7.5)
|
51
|
+
multi_json (1.11.0)
|
52
|
+
multi_xml (0.5.5)
|
53
|
+
multipart-post (1.2.0)
|
54
|
+
nokogiri (1.5.10)
|
55
|
+
oauth2 (1.0.0)
|
56
|
+
faraday (>= 0.8, < 0.10)
|
57
|
+
jwt (~> 1.0)
|
58
|
+
multi_json (~> 1.3)
|
59
|
+
multi_xml (~> 0.5)
|
60
|
+
rack (~> 1.2)
|
61
|
+
pry (0.10.1)
|
62
|
+
coderay (~> 1.1.0)
|
63
|
+
method_source (~> 0.8.1)
|
64
|
+
slop (~> 3.4)
|
65
|
+
rack (1.6.0)
|
66
|
+
rake (10.4.2)
|
67
|
+
rdoc (4.2.0)
|
68
|
+
redcarpet (3.2.3)
|
69
|
+
rspec (3.2.0)
|
70
|
+
rspec-core (~> 3.2.0)
|
71
|
+
rspec-expectations (~> 3.2.0)
|
72
|
+
rspec-mocks (~> 3.2.0)
|
73
|
+
rspec-core (3.2.3)
|
74
|
+
rspec-support (~> 3.2.0)
|
75
|
+
rspec-expectations (3.2.1)
|
76
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
77
|
+
rspec-support (~> 3.2.0)
|
78
|
+
rspec-mocks (3.2.1)
|
79
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
80
|
+
rspec-support (~> 3.2.0)
|
81
|
+
rspec-support (3.2.2)
|
82
|
+
ruby-graphviz (1.2.1)
|
83
|
+
simplecov (0.9.2)
|
84
|
+
docile (~> 1.1.0)
|
43
85
|
multi_json (~> 1.0)
|
44
|
-
simplecov-html (~> 0.
|
45
|
-
simplecov-html (0.
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
86
|
+
simplecov-html (~> 0.9.0)
|
87
|
+
simplecov-html (0.9.0)
|
88
|
+
slop (3.6.0)
|
89
|
+
sqlite3 (1.3.10)
|
90
|
+
thread_safe (0.3.5)
|
91
|
+
tzinfo (0.3.43)
|
92
|
+
yard (0.8.7.6)
|
50
93
|
|
51
94
|
PLATFORMS
|
52
95
|
ruby
|
53
96
|
|
54
97
|
DEPENDENCIES
|
55
|
-
activerecord (~>
|
56
|
-
bundler
|
98
|
+
activerecord (~> 4.0.9)
|
99
|
+
bundler
|
100
|
+
i18n (>= 0.6.6)
|
57
101
|
jeweler (~> 1.8.4)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
102
|
+
json (>= 1.8.2)
|
103
|
+
pry
|
104
|
+
rdoc (~> 4.0)
|
105
|
+
redcarpet (>= 3.2.3)
|
106
|
+
rspec
|
107
|
+
ruby-graphviz
|
62
108
|
simplecov
|
63
|
-
sqlite3
|
109
|
+
sqlite3 (~> 1.3.10)
|
64
110
|
yard (~> 0.7)
|
data/README.md
CHANGED
@@ -1,11 +1,172 @@
|
|
1
1
|
state\_shifter
|
2
2
|
==============
|
3
3
|
|
4
|
-
|
4
|
+
This gem makes it easy to incorporate state machine behavior in a Ruby class.
|
5
|
+
|
6
|
+
Features include:
|
7
|
+
|
8
|
+
* on\_entry and on\_transition handlees
|
9
|
+
* ActiveRecord integration
|
10
|
+
* event guards and easy event handlers
|
11
|
+
* graphViz visualization creator
|
12
|
+
* flexible machine syntax
|
13
|
+
|
14
|
+
Usage
|
15
|
+
-----
|
16
|
+
|
17
|
+
An example of state machine definition possible with this gem:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
class Simple
|
21
|
+
include StateShifter::Definition
|
22
|
+
|
23
|
+
state_machine do
|
24
|
+
|
25
|
+
# first state to be defined is the initial one
|
26
|
+
state :new do
|
27
|
+
event :submit => :awaiting_review
|
28
|
+
end
|
29
|
+
|
30
|
+
state :awaiting_review do
|
31
|
+
event :review => :being_reviewed
|
32
|
+
end
|
33
|
+
|
34
|
+
state :being_reviewed do
|
35
|
+
event :accept => :accepted, :if => :cool_article?
|
36
|
+
event :reject => :rejected, :if => :bad_article?
|
37
|
+
end
|
38
|
+
|
39
|
+
state :accepted
|
40
|
+
state :rejected
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
def cool_article?
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def bad_article?
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
Basically, you need to have a ```state_machine``` block with a collection of states and events. The initial state is the first one on the definition, and events are in the form of ```event :event_name => :next_state_name```. Events can have guards, and also refer back to the same state, in which case you simple omit the ```next_state_name``` - mostly to have "touch-and-go" events that just execute a method specified in the ```:call``` option passed to it, and remain in the same state. The next example shows relevant usage of it.
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class Advanced
|
59
|
+
include StateShifter::Definition
|
60
|
+
|
61
|
+
###
|
62
|
+
|
63
|
+
state_machine do
|
64
|
+
|
65
|
+
state :initialized do
|
66
|
+
|
67
|
+
event :start_date_changed, :call => :handle_start_date_changed
|
68
|
+
event :forced_start => :running
|
69
|
+
event :start_date_reached => :running, :if => :start_date_reached?
|
70
|
+
event :abort_initialized_contest => :finalized
|
71
|
+
end
|
72
|
+
|
73
|
+
state :running do
|
74
|
+
|
75
|
+
on_entry do |previous_state, trigger_event|
|
76
|
+
running_entry previous_state, trigger_event
|
77
|
+
end
|
78
|
+
|
79
|
+
event :abort_running_contest => :notify_stakeholders
|
80
|
+
event :deadline_reached => :notify_organizers, :if => :entries_deadline_reached?
|
81
|
+
event :spots_filled => :notify_organizers, :if => :spots_filled?
|
82
|
+
event :deadline_reached_without_approvals => :notify_pending_users, :if => :entries_deadline_reached_without_approvals?
|
83
|
+
event :deadline_reached_without_entries => :finalized, :if => :entries_deadline_reached_without_entries?
|
84
|
+
end
|
85
|
+
|
86
|
+
state :notify_organizers do
|
87
|
+
on_entry :send_notification_to_organizers
|
88
|
+
event :organizers_notified => :awaiting_organizer_reply
|
89
|
+
end
|
90
|
+
|
91
|
+
state :awaiting_organizer_reply do
|
92
|
+
event :organizer_confirmation_missing => :notify_stakeholders, :if => :organizer_confirmation_deadline_reached?
|
93
|
+
event :organizer_confirmation_received => :notify_approved_users
|
94
|
+
event :organizer_has_more_tickets => :running
|
95
|
+
end
|
96
|
+
|
97
|
+
state :notify_stakeholders do
|
98
|
+
on_entry :send_notification, :stakeholders, :organizers
|
99
|
+
event :stakeholders_notified => :cancelled
|
100
|
+
end
|
101
|
+
|
102
|
+
state :cancelled
|
103
|
+
|
104
|
+
state :notify_pending_users do
|
105
|
+
on_entry :send_notification, :pending_users
|
106
|
+
event :pending_users_notified => :finalized
|
107
|
+
end
|
108
|
+
|
109
|
+
state :notify_approved_users do
|
110
|
+
on_entry :send_notification_to_approved_users
|
111
|
+
event :approved_users_notified => :send_list_to_organizers
|
112
|
+
end
|
113
|
+
|
114
|
+
state :send_list_to_organizers do
|
115
|
+
on_entry :send_guestlist_to_organizers
|
116
|
+
event :list_sent_to_organizers => :awaiting_attendance
|
117
|
+
end
|
118
|
+
|
119
|
+
state :awaiting_attendance do
|
120
|
+
event :remind_to_fill_in_report => :create_report_filling_requests
|
121
|
+
end
|
122
|
+
|
123
|
+
state :create_report_filling_requests do
|
124
|
+
on_entry :send_report_filling_requests
|
125
|
+
event :finalize => :finalized
|
126
|
+
end
|
127
|
+
|
128
|
+
state :finalized
|
129
|
+
|
130
|
+
on_transition do |from,to,trigger_event, duration|
|
131
|
+
benchmark from, to, trigger_event, duration
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
###
|
136
|
+
|
137
|
+
def send_notification to
|
138
|
+
#
|
139
|
+
end
|
140
|
+
|
141
|
+
def entries_deadline_reached?
|
142
|
+
true
|
143
|
+
end
|
144
|
+
|
145
|
+
def running_entry previous_state, trigger_event
|
146
|
+
#
|
147
|
+
end
|
148
|
+
|
149
|
+
def benchmark from, to, trigger_event, duration
|
150
|
+
#
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
```
|
155
|
+
|
156
|
+
Plagiarism alert
|
157
|
+
----------------
|
158
|
+
|
159
|
+
This gem draws _heavy_ inspiration from both [pluginaweek's state_machine](https://github.com/pluginaweek/state_machine) and [mdh's ssm](https://github.com/mdh/ssm) gems. I liked both of them, but the DSL syntax was not 100% to my liking. Kudos to them.
|
160
|
+
|
161
|
+
Future
|
162
|
+
------
|
163
|
+
|
164
|
+
I want to add "mountable" state machines as per ssm's gem, to have a clear separation which would ease testing, but haven't had the chance yet.
|
165
|
+
|
5
166
|
|
6
167
|
Contributing to state\_shifter
|
7
168
|
------------------------------
|
8
|
-
|
169
|
+
|
9
170
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
10
171
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
11
172
|
* Fork the project.
|
@@ -19,4 +180,3 @@ Copyright
|
|
19
180
|
|
20
181
|
Copyright (c) 2012 Bruno Antunes. See LICENSE.txt for
|
21
182
|
further details.
|
22
|
-
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.3
|
data/examples/advanced.rb
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
class Advanced
|
2
2
|
include StateShifter::Definition
|
3
3
|
|
4
|
-
###
|
4
|
+
###
|
5
5
|
|
6
6
|
state_machine do
|
7
7
|
|
8
8
|
state :initialized do
|
9
|
-
|
10
9
|
event :start_date_changed, :call => :handle_start_date_changed
|
11
10
|
event :forced_start => :running
|
12
11
|
event :start_date_reached => :running, :if => :start_date_reached?
|
13
12
|
event :abort_initialized_contest => :finalized
|
13
|
+
event :event_associated => :preparing
|
14
|
+
end
|
15
|
+
|
16
|
+
state :preparing do
|
17
|
+
on_entry :prepare
|
18
|
+
|
19
|
+
event :all_done => :running
|
14
20
|
end
|
15
21
|
|
16
22
|
state :running do
|
@@ -19,7 +25,7 @@ class Advanced
|
|
19
25
|
running_entry previous_state, trigger_event
|
20
26
|
end
|
21
27
|
|
22
|
-
event :abort_running_contest => :notify_stakeholders
|
28
|
+
event :abort_running_contest => :notify_stakeholders
|
23
29
|
event :changed_properties
|
24
30
|
event :keep_users_engaged
|
25
31
|
event :deadline_reached => :notify_organizers, :if => :entries_deadline_reached?
|
@@ -30,6 +36,7 @@ class Advanced
|
|
30
36
|
|
31
37
|
state :notify_organizers do
|
32
38
|
on_entry :send_notification_to_organizers
|
39
|
+
|
33
40
|
event :organizers_notified => :awaiting_organizer_reply
|
34
41
|
end
|
35
42
|
|
@@ -42,6 +49,7 @@ class Advanced
|
|
42
49
|
|
43
50
|
state :notify_stakeholders do
|
44
51
|
on_entry :send_notification, :stakeholders, :organizers
|
52
|
+
|
45
53
|
event :stakeholders_notified => :cancelled
|
46
54
|
end
|
47
55
|
|
@@ -80,6 +88,10 @@ class Advanced
|
|
80
88
|
|
81
89
|
###
|
82
90
|
|
91
|
+
def prepare
|
92
|
+
all_done!
|
93
|
+
end
|
94
|
+
|
83
95
|
def send_notification to
|
84
96
|
#
|
85
97
|
end
|
data/examples/review.rb
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
class Review < ActiveRecord::Base
|
2
2
|
include StateShifter::Definition
|
3
3
|
|
4
|
-
state_machine do
|
4
|
+
state_machine do
|
5
5
|
|
6
6
|
# first state to be defined is the initial one
|
7
7
|
state :new do
|
8
|
+
tags :reviewable
|
8
9
|
event :submit => :awaiting_review
|
9
10
|
end
|
10
11
|
|
11
12
|
state :awaiting_review do
|
13
|
+
tags :reviewable, :processing
|
12
14
|
event :review => :being_reviewed
|
13
15
|
end
|
14
16
|
|
15
17
|
state :being_reviewed do
|
18
|
+
tags :reviewable, :processing
|
16
19
|
event :accept => :accepted, :if => :cool_article?
|
17
20
|
event :reject => :rejected, :if => :bad_article?
|
18
21
|
end
|
@@ -2,7 +2,21 @@ module StateShifter
|
|
2
2
|
module Definition
|
3
3
|
module ActiveRecordIntegrationMethods
|
4
4
|
|
5
|
-
class ::StateShifter::Definition::StatePersistenceAttributeNotPresent < RuntimeError; end
|
5
|
+
class ::StateShifter::Definition::StatePersistenceAttributeNotPresent < RuntimeError; end
|
6
|
+
|
7
|
+
def self.include_state_scopes(base)
|
8
|
+
base.state_machine_definition.states.each do |name, definition|
|
9
|
+
base.class_eval do
|
10
|
+
scope name, -> { where(persist_attr_name => name) } unless respond_to?(name)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
base.state_machine_definition.state_tags.each do |name, states|
|
15
|
+
base.class_eval do
|
16
|
+
scope name, -> { where(persist_attr_name => states) } unless respond_to?(name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
6
20
|
|
7
21
|
def check_attr_presence
|
8
22
|
raise StatePersistenceAttributeNotPresent unless self.attribute_names.include? self.class.persist_attr_name.to_s
|
@@ -2,86 +2,106 @@ module StateShifter
|
|
2
2
|
module Definition
|
3
3
|
module ClassMethods
|
4
4
|
|
5
|
-
attr_accessor :state_machine_definition, :persist_attr_name
|
5
|
+
attr_accessor :state_machine_definition, :persist_attr_name, :_include_state_scopes
|
6
6
|
|
7
7
|
def persist_attribute attr_name
|
8
|
-
raise
|
8
|
+
raise PersistenceAttributeAlreadyDefined if @persist_attr_name
|
9
9
|
@persist_attr_name = attr_name.to_sym
|
10
10
|
end
|
11
11
|
|
12
12
|
def state_machine &definition
|
13
13
|
@persist_attr_name ||= :current_state
|
14
14
|
@state_machine_definition = Contents.new(&definition)
|
15
|
-
|
16
|
-
@state_machine_definition.states.each do |state_name, state_definition|
|
17
|
-
|
18
|
-
module_eval do
|
19
15
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
@state_machine_definition.states.each { |name, definition| _load_state(name, definition) }
|
17
|
+
@state_machine_definition.state_tags.each { |tag, states| _load_tag(tag, states) }
|
18
|
+
ActiveRecordIntegrationMethods.include_state_scopes(self) if _include_state_scopes
|
19
|
+
end
|
24
20
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
private
|
22
|
+
|
23
|
+
def _load_state(state_name, state_definition)
|
24
|
+
module_eval do
|
25
|
+
|
26
|
+
define_method "_next_states" do |from_state, *options|
|
27
|
+
options = options.first || {}
|
28
|
+
next_states_hash = {}
|
29
|
+
check_guards = options.has_key?(:check_guards)
|
29
30
|
|
30
|
-
|
31
|
+
state_machine_definition.get(:state, from_state).events.each do |event_name, event_def|
|
32
|
+
if event_def.has_guards? && check_guards
|
33
|
+
next if self.send(:check_guards, event_name).is_a?(Array)
|
31
34
|
end
|
32
35
|
|
33
|
-
next_states_hash.
|
36
|
+
next_states_hash.merge!( event_def.to.nil? ? { from_state.to_sym => event_name } : { event_def.to => event_def.name } )
|
34
37
|
end
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
end
|
39
|
+
next_states_hash.keys.uniq.sort
|
40
|
+
end
|
39
41
|
|
40
|
-
|
42
|
+
define_method "#{state_name}?" do
|
43
|
+
current_state.to_sym == state_name
|
44
|
+
end
|
41
45
|
|
42
|
-
|
43
|
-
|
44
|
-
this_event = state_machine_definition.get(:event, event_name)
|
45
|
-
|
46
|
-
current_state.to_sym == this_event.from.to_sym && !check_guards(event_name).is_a?(Array)
|
47
|
-
|
48
|
-
end
|
46
|
+
state_machine_definition.events.each do |event_name, event_definition|
|
49
47
|
|
50
|
-
|
48
|
+
define_method "can_#{event_name}?" do
|
51
49
|
|
52
|
-
|
53
|
-
|
54
|
-
end
|
50
|
+
this_event = state_machine_definition.get(:event, event_name)
|
55
51
|
|
56
|
-
|
52
|
+
current_state.to_sym == this_event.from.to_sym && !check_guards(event_name).is_a?(Array)
|
57
53
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
54
|
+
end
|
55
|
+
|
56
|
+
define_method "#{event_name}!" do
|
57
|
+
|
58
|
+
self.send event_name.to_sym, true
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
define_method "#{event_name}" do |bang=false|
|
63
|
+
|
64
|
+
if current_state.to_sym != event_definition.from.to_sym
|
65
|
+
if bang
|
66
|
+
halt("you cannot transition from #{current_state} via #{event_name}")
|
67
|
+
else
|
68
|
+
return false
|
73
69
|
end
|
70
|
+
end
|
74
71
|
|
75
|
-
|
76
|
-
|
72
|
+
if (failed_guards = check_guards(event_name)).is_a?(Array)
|
73
|
+
if bang
|
74
|
+
failed_guards.delete_at(0)
|
75
|
+
raise GuardNotSatisfied, "#{failed_guards.join}"
|
76
|
+
else
|
77
|
+
return false
|
78
|
+
end
|
77
79
|
end
|
78
80
|
|
81
|
+
transition :to => ( event_definition.to.nil? ? current_state : event_definition.to ), :trigger => ( bang ? "#{event_name}!" : event_name )
|
82
|
+
|
79
83
|
end
|
80
84
|
|
81
85
|
end
|
86
|
+
|
82
87
|
end
|
83
|
-
end
|
84
88
|
|
89
|
+
def _load_tag(tag, states)
|
90
|
+
module_eval do
|
91
|
+
|
92
|
+
define_singleton_method "#{tag}_states" do
|
93
|
+
states
|
94
|
+
end
|
95
|
+
|
96
|
+
define_method "#{tag}?" do
|
97
|
+
states.map(&:to_s).include? current_state.to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
85
105
|
end
|
86
106
|
end
|
87
107
|
end
|