state_pattern 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +276 -0
- data/examples/rails_2_3_8_button_example/config/environment.rb +1 -1
- data/examples/rails_2_3_8_button_example/db/development.sqlite3 +0 -0
- data/examples/rails_3_button_example/Gemfile +1 -1
- data/examples/rails_3_button_example/Gemfile.lock +2 -2
- data/examples/rails_3_button_example/db/development.sqlite3 +0 -0
- data/examples/rails_3_button_example/log/development.log +95 -1600
- data/lib/state_pattern/active_record.rb +1 -1
- data/test/state_pattern/active_record/test_helper.rb +0 -2
- metadata +10 -25
- data/README.rdoc +0 -264
- data/examples/rails_3_button_example/db/test.sqlite3 +0 -0
metadata
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: state_pattern
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
segments:
|
7
|
-
- 2
|
8
|
-
- 0
|
9
|
-
- 0
|
10
|
-
version: 2.0.0
|
4
|
+
prerelease:
|
5
|
+
version: 2.0.1
|
11
6
|
platform: ruby
|
12
7
|
authors:
|
13
8
|
- Daniel Cadenas
|
@@ -15,8 +10,7 @@ autorequire:
|
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
12
|
|
18
|
-
date: 2010-10-09 00:00:00
|
19
|
-
default_executable:
|
13
|
+
date: 2010-10-09 00:00:00 Z
|
20
14
|
dependencies: []
|
21
15
|
|
22
16
|
description:
|
@@ -25,14 +19,13 @@ executables: []
|
|
25
19
|
|
26
20
|
extensions: []
|
27
21
|
|
28
|
-
extra_rdoc_files:
|
29
|
-
|
30
|
-
- README.rdoc
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
31
24
|
files:
|
32
25
|
- .document
|
33
26
|
- .gitignore
|
34
27
|
- LICENSE
|
35
|
-
- README.
|
28
|
+
- README.md
|
36
29
|
- Rakefile
|
37
30
|
- rails/init.rb
|
38
31
|
- ./lib/state_pattern/active_record.rb
|
@@ -114,7 +107,6 @@ files:
|
|
114
107
|
- ./examples/rails_3_button_example/db/development.sqlite3
|
115
108
|
- ./examples/rails_3_button_example/db/migrate/20101009052900_create_buttons.rb
|
116
109
|
- ./examples/rails_3_button_example/db/schema.rb
|
117
|
-
- ./examples/rails_3_button_example/db/test.sqlite3
|
118
110
|
- ./examples/rails_3_button_example/Gemfile
|
119
111
|
- ./examples/rails_3_button_example/Gemfile.lock
|
120
112
|
- ./examples/rails_3_button_example/log/development.log
|
@@ -137,13 +129,12 @@ files:
|
|
137
129
|
- ./test/state_pattern_test.rb
|
138
130
|
- ./test/test_class_creation_helper.rb
|
139
131
|
- ./test/test_helper.rb
|
140
|
-
has_rdoc: true
|
141
132
|
homepage: http://github.com/dcadenas/state_pattern
|
142
133
|
licenses: []
|
143
134
|
|
144
135
|
post_install_message:
|
145
|
-
rdoc_options:
|
146
|
-
|
136
|
+
rdoc_options: []
|
137
|
+
|
147
138
|
require_paths:
|
148
139
|
- lib
|
149
140
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -151,23 +142,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
151
142
|
requirements:
|
152
143
|
- - ">="
|
153
144
|
- !ruby/object:Gem::Version
|
154
|
-
hash: 3
|
155
|
-
segments:
|
156
|
-
- 0
|
157
145
|
version: "0"
|
158
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
147
|
none: false
|
160
148
|
requirements:
|
161
149
|
- - ">="
|
162
150
|
- !ruby/object:Gem::Version
|
163
|
-
hash: 3
|
164
|
-
segments:
|
165
|
-
- 0
|
166
151
|
version: "0"
|
167
152
|
requirements: []
|
168
153
|
|
169
154
|
rubyforge_project: statepattern
|
170
|
-
rubygems_version: 1.
|
155
|
+
rubygems_version: 1.8.16
|
171
156
|
signing_key:
|
172
157
|
specification_version: 3
|
173
158
|
summary: A Ruby state pattern implementation
|
@@ -248,7 +233,6 @@ test_files:
|
|
248
233
|
- ./examples/rails_3_button_example/db/development.sqlite3
|
249
234
|
- ./examples/rails_3_button_example/db/migrate/20101009052900_create_buttons.rb
|
250
235
|
- ./examples/rails_3_button_example/db/schema.rb
|
251
|
-
- ./examples/rails_3_button_example/db/test.sqlite3
|
252
236
|
- ./examples/rails_3_button_example/Gemfile
|
253
237
|
- ./examples/rails_3_button_example/Gemfile.lock
|
254
238
|
- ./examples/rails_3_button_example/log/development.log
|
@@ -271,3 +255,4 @@ test_files:
|
|
271
255
|
- ./test/state_pattern_test.rb
|
272
256
|
- ./test/test_class_creation_helper.rb
|
273
257
|
- ./test/test_helper.rb
|
258
|
+
has_rdoc:
|
data/README.rdoc
DELETED
@@ -1,264 +0,0 @@
|
|
1
|
-
= state_pattern
|
2
|
-
|
3
|
-
A Ruby state pattern implementation.
|
4
|
-
|
5
|
-
This library intentionally follows the classic state pattern implementation (no mixins, classical delegation to simple state classes, etc.) believing that it increases flexibility (internal DSL constraints vs plain object oriented Ruby power), simplicity and clarity.
|
6
|
-
|
7
|
-
The gem is ready for Rails active record integration (see below and the examples folder).
|
8
|
-
|
9
|
-
== Usage and functionality summary
|
10
|
-
|
11
|
-
* Define the set of states you want your stateful object to have by creating a class for each state and inheriting from +StatePattern:State+.
|
12
|
-
* All public methods defined in this state classes, except +enter+ and +exit+ (see below), are then available to the stateful object and their behaviour will depend on the current state .
|
13
|
-
* If this automatic delegation to the current state public methods is not enough for your stateful object then you can just reopen the method and use super whenever you want to call the state implementation.
|
14
|
-
* Inside each state instance you can access the stateable object through the +stateful+ method.
|
15
|
-
* Inside each state instance you can access the previous state through the +previous_state+ method.
|
16
|
-
* Define +enter+ or +exit+ methods to hook any behaviour you want to execute whenever the stateful object enters or exits the state.
|
17
|
-
* An event is just a method that calls +transition_to+ at some point.
|
18
|
-
* If you want guards for some event just use plain old ifs before your +transition_to+.
|
19
|
-
* In the stateful object you must +set_initial_state+.
|
20
|
-
|
21
|
-
== Examples
|
22
|
-
|
23
|
-
So here's a simple example that mimics a traffic semaphore
|
24
|
-
|
25
|
-
require 'rubygems'
|
26
|
-
require 'state_pattern'
|
27
|
-
|
28
|
-
class Stop < StatePattern::State
|
29
|
-
def next
|
30
|
-
sleep 3
|
31
|
-
transition_to(Go)
|
32
|
-
end
|
33
|
-
|
34
|
-
def color
|
35
|
-
"Red"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class Go < StatePattern::State
|
40
|
-
def next
|
41
|
-
sleep 2
|
42
|
-
transition_to(Caution)
|
43
|
-
end
|
44
|
-
|
45
|
-
def color
|
46
|
-
"Green"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
class Caution < StatePattern::State
|
51
|
-
def next
|
52
|
-
sleep 1
|
53
|
-
transition_to(Stop)
|
54
|
-
end
|
55
|
-
|
56
|
-
def color
|
57
|
-
"Amber"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class TrafficSemaphore
|
62
|
-
include StatePattern
|
63
|
-
set_initial_state Stop
|
64
|
-
end
|
65
|
-
|
66
|
-
semaphore = TrafficSemaphore.new
|
67
|
-
|
68
|
-
loop do
|
69
|
-
puts semaphore.color
|
70
|
-
semaphore.next
|
71
|
-
end
|
72
|
-
|
73
|
-
Let's now use one nice example from the AASM documentation and translate it to state_pattern.
|
74
|
-
|
75
|
-
require 'rubygems'
|
76
|
-
require 'state_pattern'
|
77
|
-
|
78
|
-
class Dating < StatePattern::State
|
79
|
-
def get_intimate
|
80
|
-
transition_to(Intimate) if stateable.drunk?
|
81
|
-
end
|
82
|
-
|
83
|
-
def get_married
|
84
|
-
transition_to(Married) if stateable.willing_to_give_up_manhood?
|
85
|
-
end
|
86
|
-
|
87
|
-
def enter
|
88
|
-
stateable.make_happy
|
89
|
-
end
|
90
|
-
|
91
|
-
def exit
|
92
|
-
stateable.make_depressed
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
class Intimate < StatePattern::State
|
97
|
-
def get_married
|
98
|
-
transition_to(Married) if stateable.willing_to_give_up_manhood?
|
99
|
-
end
|
100
|
-
|
101
|
-
def enter
|
102
|
-
stateable.make_very_happy
|
103
|
-
end
|
104
|
-
|
105
|
-
def exit
|
106
|
-
stateable.never_speak_again
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
class Married < StatePattern::State
|
111
|
-
def enter
|
112
|
-
stateable.give_up_intimacy
|
113
|
-
end
|
114
|
-
|
115
|
-
def exit
|
116
|
-
stateable.buy_exotic_car_and_wear_a_combover
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
class Relationship
|
121
|
-
include StatePattern
|
122
|
-
set_initial_state Dating
|
123
|
-
|
124
|
-
def drunk?; @drunk; end
|
125
|
-
def willing_to_give_up_manhood?; @give_up_manhood; end
|
126
|
-
def make_happy; end
|
127
|
-
def make_depressed; end
|
128
|
-
def make_very_happy; end
|
129
|
-
def never_speak_again; end
|
130
|
-
def give_up_intimacy; end
|
131
|
-
def buy_exotic_car_and_wear_a_combover; end
|
132
|
-
end
|
133
|
-
|
134
|
-
== Enter and exit hooks
|
135
|
-
|
136
|
-
Inside your state classes, any code that you put inside the enter method will be executed when the state is instantiated.
|
137
|
-
You can also use the exit hook which is triggered when a successful transition to another state takes place.
|
138
|
-
|
139
|
-
== Overriding automatic delegation
|
140
|
-
|
141
|
-
If the automatic delegation to the current state public methods is not enough for your stateful object then you can just reopen the method and use super whenever you want to call the state implementation.
|
142
|
-
|
143
|
-
class TrafficSemaphore
|
144
|
-
include StatePattern
|
145
|
-
set_initial_state Stop
|
146
|
-
|
147
|
-
def color
|
148
|
-
# some great code here
|
149
|
-
#this calls the current state implementation
|
150
|
-
super
|
151
|
-
# more cool hacking here
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
== Rails
|
156
|
-
|
157
|
-
To use the state pattern in your Rails models you need to:
|
158
|
-
|
159
|
-
* Add a state column for your model table of type string
|
160
|
-
* Include StatePattern::ActiveRecord in your model file
|
161
|
-
* Use the state pattern as you would do in a plain Ruby class as shown above
|
162
|
-
|
163
|
-
Please see the examples folder for a Rails 3 example.
|
164
|
-
|
165
|
-
=== Example
|
166
|
-
|
167
|
-
Note this is not the best example to show as ideally this plugin should be used with lot of state dependent behaviour and this is not the case.
|
168
|
-
Remember to put each class in its correct file following Rails naming conventions.
|
169
|
-
|
170
|
-
module BlogStates
|
171
|
-
class StateBase < StatePattern::State
|
172
|
-
def submit!
|
173
|
-
end
|
174
|
-
|
175
|
-
def publish!
|
176
|
-
end
|
177
|
-
|
178
|
-
def reject!
|
179
|
-
transition_to(Rejected)
|
180
|
-
stateable.save!
|
181
|
-
end
|
182
|
-
|
183
|
-
def verify!
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
class Published < StateBase
|
188
|
-
end
|
189
|
-
|
190
|
-
class Pending < StateBase
|
191
|
-
def publish!
|
192
|
-
transition_to(Published) if stateable.valid?
|
193
|
-
stateable.save!
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
class Unverified < StateBase
|
198
|
-
def submit!
|
199
|
-
if stateable.submitter.manager?
|
200
|
-
if stateable.profile_complete?
|
201
|
-
transition_to(Published)
|
202
|
-
else
|
203
|
-
transition_to(Pending)
|
204
|
-
end
|
205
|
-
|
206
|
-
stateable.save!
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def verify!
|
211
|
-
transition_to(Pending)
|
212
|
-
stateable.save!
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
class Rejected < StateBase
|
217
|
-
def publish!
|
218
|
-
transition_to(Published) if stateable.valid?
|
219
|
-
stateable.save!
|
220
|
-
end
|
221
|
-
|
222
|
-
def enter
|
223
|
-
Notifier.notify_blog_owner(stateable)
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
class Blog < ActiveRecord::Base
|
229
|
-
include StatePattern::ActiveRecord
|
230
|
-
set_initial_state Unverified
|
231
|
-
|
232
|
-
.
|
233
|
-
.
|
234
|
-
.
|
235
|
-
|
236
|
-
end
|
237
|
-
|
238
|
-
=== The state attribute
|
239
|
-
|
240
|
-
By default StatePattern::ActiveRecord expects a column named 'state' in the model. If you prefer to use another attribute do:
|
241
|
-
|
242
|
-
set_state_attribute :state_column
|
243
|
-
|
244
|
-
=== How do I decide? state_pattern or {AASM}[http://github.com/rubyist/aasm]?
|
245
|
-
|
246
|
-
* Lot of state dependent behavior? Lot of conditional logic depending on the state? => state_pattern
|
247
|
-
* Not much state dependent behavior? => AASM
|
248
|
-
|
249
|
-
== Thanks
|
250
|
-
|
251
|
-
* {Alvaro Gil}[http://github.com/zevarito] for being the first using this gem in a real Rails project.
|
252
|
-
* {Nicolás Sanguinetti}[http://github.com/foca] for his great feedback.
|
253
|
-
|
254
|
-
== Installation
|
255
|
-
|
256
|
-
sudo gem install state_pattern
|
257
|
-
|
258
|
-
== Collaborate
|
259
|
-
|
260
|
-
http://github.com/dcadenas/state_pattern
|
261
|
-
|
262
|
-
== Copyright
|
263
|
-
|
264
|
-
Copyright (c) 2009 Daniel Cadenas. See LICENSE for details.
|
Binary file
|