state_shifter 0.8.1 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|