workflow 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{LICENSE → MIT-LICENSE} +2 -1
- data/README.rdoc +97 -22
- data/Rakefile +4 -3
- data/lib/workflow.rb +30 -3
- data/test/test_helper.rb +16 -0
- metadata +6 -6
- data/test/test_workflow.rb +0 -252
data/{LICENSE → MIT-LICENSE}
RENAMED
@@ -1,3 +1,4 @@
|
|
1
|
+
Copyright (c) 2008-2009 Vodafone
|
1
2
|
Copyright (c) 2007-2008 Ryan Allen, FlashDen Pty Ltd
|
2
3
|
|
3
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
@@ -16,4 +17,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
17
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
18
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
19
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
THE SOFTWARE.
|
20
|
+
THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -71,23 +71,83 @@ are very problematic.
|
|
71
71
|
|
72
72
|
== Installation
|
73
73
|
|
74
|
-
|
74
|
+
gem install workflow
|
75
|
+
|
76
|
+
Alternatively you can just download the lib/workflow.rb and put it in the lib folder of your
|
75
77
|
Rails application.
|
76
78
|
|
77
|
-
|
79
|
+
|
80
|
+
== Migration from the original Ryan's library
|
81
|
+
|
82
|
+
Credit: Michael (rockrep)
|
83
|
+
|
84
|
+
* Accessing workflow specification
|
85
|
+
my_instance.workflow # old
|
86
|
+
MyClass.workflow_spec # new
|
87
|
+
|
88
|
+
* Accessing states, events, meta, e.g.
|
89
|
+
my_instance.workflow.states(:some_state).events(:some_event).meta[:some_meta_tag] # old
|
90
|
+
MyClass.workflow_spec.states[:some_state].events[:some_event].meta[:some_meta_tag] # new
|
91
|
+
|
92
|
+
* Causing state transitions
|
93
|
+
my_instance.workflow.my_event # old
|
94
|
+
my_instance.my_event! # new
|
95
|
+
|
96
|
+
* when using both a block and a callback method for an event, the block executes prior to the callback
|
78
97
|
|
79
98
|
|
80
99
|
== About
|
81
100
|
|
82
101
|
Author: Vladimir Dobriakov, http://www.innoq.com/blog/vd, http://blog.geekq.net/
|
83
102
|
|
84
|
-
|
103
|
+
Copyright (c) 2008-2009 Vodafone
|
104
|
+
|
105
|
+
Copyright (c) 2007-2008 Ryan Allen, FlashDen Pty Ltd
|
85
106
|
|
86
107
|
Based on the work of Ryan Allen and Scott Barron
|
87
108
|
|
109
|
+
Licensed under MIT license, see the MIT-LICENSE file.
|
110
|
+
|
111
|
+
|
112
|
+
== New in the version 0.3.0
|
113
|
+
|
114
|
+
Intermixing of transition graph definition (states, transitions)
|
115
|
+
on the one side and implementation of the actions on the other side
|
116
|
+
for a bigger state machine can introduce clutter.
|
117
|
+
|
118
|
+
To reduce this clutter it is now possible to use state entry- and
|
119
|
+
exit- hooks defined through a naming convention. For example, if there
|
120
|
+
is a state :pending, then instead of using a
|
121
|
+
block:
|
122
|
+
|
123
|
+
state :pending do
|
124
|
+
on_entry do
|
125
|
+
# your implementation here
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
you can hook in by defining method
|
130
|
+
|
131
|
+
def on_pending_exit(new_state, event, *args)
|
132
|
+
# your implementation here
|
133
|
+
end
|
134
|
+
|
135
|
+
anywhere in your class. You can also use a simpler function signature
|
136
|
+
like `def on_pending_exit(*args)` if your are not interested in arguments -
|
137
|
+
`def on_pending_exit()` with an empty list would not work.
|
138
|
+
|
139
|
+
If both a function with a name according to naming convention and the
|
140
|
+
on_entry/on_exit block are given, then only on_entry/on_exit block is used.
|
141
|
+
|
88
142
|
|
89
143
|
= Original readme
|
90
144
|
|
145
|
+
Disclaimer: my fork is not 100% API compatible to the original library by Ryan.
|
146
|
+
I'll update/merge the readme as soon as posssible.
|
147
|
+
In the mean time please use the original readme in conjunction with
|
148
|
+
the API changes and migration hints listed above.
|
149
|
+
|
150
|
+
|
91
151
|
=== New Mailing List!
|
92
152
|
|
93
153
|
Hi! We've now got a mailing list to talk about Workflow, and that's good! Come visit and post your problems or ideas or anything!!!
|
@@ -108,35 +168,50 @@ Now, all that's a mouthful, but we'll demonstrate the API bit by bit with a real
|
|
108
168
|
|
109
169
|
Let's say we're modeling article submission from journalists. An article is written, then submitted. When it's submitted, it's awaiting review. Someone reviews the article, and then either accepts or rejects it. Explaining all that is a pain in the arse. Here is the expression of this workflow using the API:
|
110
170
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
171
|
+
|
172
|
+
class Article
|
173
|
+
include Workflow
|
174
|
+
workflow do
|
175
|
+
state :new do
|
176
|
+
event :submit, :transitions_to => :awaiting_review
|
177
|
+
end
|
178
|
+
state :awaiting_review do
|
179
|
+
event :review, :transitions_to => :being_reviewed
|
180
|
+
end
|
181
|
+
state :being_reviewed do
|
182
|
+
event :accept, :transitions_to => :accepted
|
183
|
+
event :reject, :transitions_to => :rejected
|
184
|
+
end
|
185
|
+
state :accepted
|
186
|
+
state :rejected
|
121
187
|
end
|
122
|
-
state :accepted
|
123
|
-
state :rejected
|
124
188
|
end
|
125
|
-
|
189
|
+
|
126
190
|
Much better, isn't it!
|
127
191
|
|
128
|
-
|
192
|
+
Let's create an article instance and check in which state it is:
|
193
|
+
|
194
|
+
article = Article.new
|
195
|
+
article.accepted? # => false
|
196
|
+
article.new? # => true
|
197
|
+
|
198
|
+
You can also access the whole +current_state+ object including the list of possible events and other meta information:
|
199
|
+
|
200
|
+
article.current_state
|
201
|
+
=> #<Workflow::State:0x7f1e3d6731f0 @events={
|
202
|
+
:submit=>#<Workflow::Event:0x7f1e3d6730d8 @action=nil,
|
203
|
+
@transitions_to=:awaiting_review, @name=:submit, @meta={}>},
|
204
|
+
name:new, meta{}
|
129
205
|
|
130
|
-
workflow = Workflow.new('Article Workflow')
|
131
|
-
workflow.state # => :new
|
132
|
-
|
133
206
|
Now we can call the submit event, which transitions to the <tt>:awaiting_review</tt> state:
|
134
207
|
|
135
|
-
|
136
|
-
|
208
|
+
article.submit!
|
209
|
+
article.awaiting_review? # => true
|
137
210
|
|
138
211
|
Events are actually instance methods on a workflow, and depending on the state you're in, you'll have a different set of events used to transition to other states.
|
139
212
|
|
213
|
+
TODO - continue editing
|
214
|
+
|
140
215
|
Given this workflow is now <tt>:awaiting_approval</tt>, we have a <tt>:review</tt> event, that we call when someone begins to review the article, which puts the workflow into the <tt>:being_reviewed</tt> state.
|
141
216
|
|
142
217
|
States can also be queried via predicates for convenience like so:
|
data/Rakefile
CHANGED
@@ -8,11 +8,12 @@ task :default => [:test]
|
|
8
8
|
Rake::TestTask.new do |t|
|
9
9
|
t.verbose = true
|
10
10
|
t.warning = true
|
11
|
+
t.pattern = 'test/*_test.rb'
|
11
12
|
end
|
12
13
|
|
13
|
-
PKG_VERSION = "0.
|
14
|
+
PKG_VERSION = "0.3.0"
|
14
15
|
PKG_FILES = FileList[
|
15
|
-
'LICENSE',
|
16
|
+
'MIT-LICENSE',
|
16
17
|
'README.rdoc',
|
17
18
|
'Rakefile',
|
18
19
|
'lib/**/*.rb',
|
@@ -33,7 +34,7 @@ end
|
|
33
34
|
|
34
35
|
Rake::RDocTask.new do |rdoc|
|
35
36
|
rdoc.main = "README"
|
36
|
-
rdoc.rdoc_files.include("README", "lib/**/*.rb")
|
37
|
+
rdoc.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
37
38
|
rdoc.options << "-S"
|
38
39
|
end
|
39
40
|
|
data/lib/workflow.rb
CHANGED
@@ -55,6 +55,8 @@ module Workflow
|
|
55
55
|
|
56
56
|
class NoTransitionAllowed < Exception; end
|
57
57
|
|
58
|
+
class WorkflowError < Exception; end
|
59
|
+
|
58
60
|
class State
|
59
61
|
|
60
62
|
attr_accessor :name, :events, :meta, :on_entry, :on_exit
|
@@ -138,6 +140,7 @@ module Workflow
|
|
138
140
|
false
|
139
141
|
end
|
140
142
|
else
|
143
|
+
check_transition(event)
|
141
144
|
run_on_transition(current_state, spec.states[event.transitions_to], name, *args)
|
142
145
|
transition(current_state, spec.states[event.transitions_to], name, *args)
|
143
146
|
return_value
|
@@ -146,6 +149,16 @@ module Workflow
|
|
146
149
|
|
147
150
|
private
|
148
151
|
|
152
|
+
def check_transition(event)
|
153
|
+
# Create a meaningful error message instead of
|
154
|
+
# "undefined method `on_entry' for nil:NilClass"
|
155
|
+
# Reported by Kyle Burton
|
156
|
+
if !spec.states[event.transitions_to]
|
157
|
+
raise WorkflowError.new("Event[#{event.name}]'s " +
|
158
|
+
"transitions_to[#{event.transitions_to}] is not a declared state.")
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
149
162
|
def spec
|
150
163
|
c = self.class
|
151
164
|
# using a simple loop instead of class_inheritable_accessor to avoid
|
@@ -187,11 +200,23 @@ module Workflow
|
|
187
200
|
end
|
188
201
|
|
189
202
|
def run_on_entry(state, prior_state, triggering_event, *args)
|
190
|
-
|
203
|
+
if state.on_entry
|
204
|
+
instance_exec(prior_state.name, triggering_event, *args, &state.on_entry)
|
205
|
+
else
|
206
|
+
hook_name = "on_#{state}_entry"
|
207
|
+
self.send hook_name, prior_state, triggering_event, *args if self.respond_to? hook_name
|
208
|
+
end
|
191
209
|
end
|
192
210
|
|
193
211
|
def run_on_exit(state, new_state, triggering_event, *args)
|
194
|
-
|
212
|
+
if state
|
213
|
+
if state.on_exit
|
214
|
+
instance_exec(new_state.name, triggering_event, *args, &state.on_exit)
|
215
|
+
else
|
216
|
+
hook_name = "on_#{state}_exit"
|
217
|
+
self.send hook_name, new_state, triggering_event, *args if self.respond_to? hook_name
|
218
|
+
end
|
219
|
+
end
|
195
220
|
end
|
196
221
|
|
197
222
|
# load_workflow_state and persist_workflow_state
|
@@ -236,9 +261,11 @@ module Workflow
|
|
236
261
|
def self.included(klass)
|
237
262
|
klass.send :include, WorkflowInstanceMethods
|
238
263
|
klass.extend WorkflowClassMethods
|
239
|
-
if
|
264
|
+
if Object.const_defined?(:ActiveRecord)
|
265
|
+
if klass < ActiveRecord::Base
|
240
266
|
klass.send :include, ActiveRecordInstanceMethods
|
241
267
|
klass.before_validation :write_initial_state
|
268
|
+
end
|
242
269
|
end
|
243
270
|
end
|
244
271
|
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class << Test::Unit::TestCase
|
5
|
+
def test(name, &block)
|
6
|
+
test_name = :"test_#{name.gsub(' ','_')}"
|
7
|
+
raise ArgumentError, "#{test_name} is already defined" if self.instance_methods.include? test_name.to_s
|
8
|
+
if block
|
9
|
+
define_method test_name, &block
|
10
|
+
else
|
11
|
+
puts "PENDING: #{name}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Dobriakov
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-10 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -22,12 +22,12 @@ extensions: []
|
|
22
22
|
extra_rdoc_files: []
|
23
23
|
|
24
24
|
files:
|
25
|
-
- LICENSE
|
25
|
+
- MIT-LICENSE
|
26
26
|
- README.rdoc
|
27
27
|
- Rakefile
|
28
28
|
- lib/workflow.rb
|
29
|
-
- test/
|
30
|
-
has_rdoc:
|
29
|
+
- test/test_helper.rb
|
30
|
+
has_rdoc: true
|
31
31
|
homepage: http://blog.geekQ.net/
|
32
32
|
licenses: []
|
33
33
|
|
@@ -51,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements: []
|
52
52
|
|
53
53
|
rubyforge_project:
|
54
|
-
rubygems_version: 1.3.
|
54
|
+
rubygems_version: 1.3.3
|
55
55
|
signing_key:
|
56
56
|
specification_version: 3
|
57
57
|
summary: A replacement for acts_as_state_machine.
|
data/test/test_workflow.rb
DELETED
@@ -1,252 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'test/unit'
|
3
|
-
old_verbose, $VERBOSE = $VERBOSE, nil
|
4
|
-
require 'active_record'
|
5
|
-
require 'sqlite3'
|
6
|
-
$VERBOSE = old_verbose
|
7
|
-
require 'workflow'
|
8
|
-
require 'mocha'
|
9
|
-
#require 'ruby-debug'
|
10
|
-
|
11
|
-
ActiveRecord::Migration.verbose = false
|
12
|
-
|
13
|
-
class << Test::Unit::TestCase
|
14
|
-
def test(name, &block)
|
15
|
-
test_name = :"test_#{name.gsub(' ','_')}"
|
16
|
-
raise ArgumentError, "#{test_name} is already defined" if self.instance_methods.include? test_name.to_s
|
17
|
-
if block
|
18
|
-
define_method test_name, &block
|
19
|
-
else
|
20
|
-
puts "PENDING: #{name}"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Order < ActiveRecord::Base
|
26
|
-
include Workflow
|
27
|
-
workflow do
|
28
|
-
state :submitted do
|
29
|
-
event :accept, :transitions_to => :accepted, :meta => {:doc_weight => 8} do |reviewer, args|
|
30
|
-
end
|
31
|
-
end
|
32
|
-
state :accepted do
|
33
|
-
event :ship, :transitions_to => :shipped
|
34
|
-
end
|
35
|
-
state :shipped
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
class WorkflowTest < Test::Unit::TestCase
|
41
|
-
|
42
|
-
def exec(sql)
|
43
|
-
ActiveRecord::Base.connection.execute sql
|
44
|
-
end
|
45
|
-
|
46
|
-
def setup
|
47
|
-
old_verbose, $VERBOSE = $VERBOSE, nil # eliminate sqlite3 warning. TODO: delete as soon as sqlite-ruby is fixed
|
48
|
-
ActiveRecord::Base.establish_connection(
|
49
|
-
:adapter => "sqlite3",
|
50
|
-
:database => ":memory:" #"tmp/test"
|
51
|
-
)
|
52
|
-
ActiveRecord::Base.connection.reconnect! # eliminate ActiveRecord warning. TODO: delete as soon as ActiveRecord is fixed
|
53
|
-
|
54
|
-
ActiveRecord::Schema.define do
|
55
|
-
create_table :orders do |t|
|
56
|
-
t.string :title, :null => false
|
57
|
-
t.string :workflow_state
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
exec "INSERT INTO orders(title, workflow_state) VALUES('some order', 'accepted')"
|
62
|
-
$VERBOSE = old_verbose
|
63
|
-
end
|
64
|
-
|
65
|
-
def teardown
|
66
|
-
ActiveRecord::Base.connection.disconnect!
|
67
|
-
end
|
68
|
-
|
69
|
-
def assert_state(title, expected_state)
|
70
|
-
o = Order.find_by_title(title)
|
71
|
-
assert_equal expected_state, o.read_attribute(:workflow_state)
|
72
|
-
o
|
73
|
-
end
|
74
|
-
|
75
|
-
test 'immediatly save the new workflow_state on state machine transition' do
|
76
|
-
o = assert_state 'some order', 'accepted'
|
77
|
-
o.ship!
|
78
|
-
assert_state 'some order', 'shipped'
|
79
|
-
end
|
80
|
-
|
81
|
-
test 'persist workflow_state in the db and reload' do
|
82
|
-
o = assert_state 'some order', 'accepted'
|
83
|
-
assert_equal :accepted, o.current_state.name
|
84
|
-
o.ship!
|
85
|
-
o.save!
|
86
|
-
|
87
|
-
assert_state 'some order', 'shipped'
|
88
|
-
|
89
|
-
o.reload
|
90
|
-
assert_equal 'shipped', o.read_attribute(:workflow_state)
|
91
|
-
end
|
92
|
-
|
93
|
-
test 'access workflow specification' do
|
94
|
-
assert_equal 3, Order.workflow_spec.states.length
|
95
|
-
end
|
96
|
-
|
97
|
-
test 'current state object' do
|
98
|
-
o = assert_state 'some order', 'accepted'
|
99
|
-
assert_equal 'accepted', o.current_state.to_s
|
100
|
-
assert_equal 1, o.current_state.events.length
|
101
|
-
end
|
102
|
-
|
103
|
-
test 'on_entry and on_exit invoked' do
|
104
|
-
c = Class.new
|
105
|
-
callbacks = mock()
|
106
|
-
callbacks.expects(:my_on_exit_new).once
|
107
|
-
callbacks.expects(:my_on_entry_old).once
|
108
|
-
c.class_eval do
|
109
|
-
include Workflow
|
110
|
-
workflow do
|
111
|
-
state :new do
|
112
|
-
event :age, :transitions_to => :old
|
113
|
-
end
|
114
|
-
on_exit do
|
115
|
-
callbacks.my_on_exit_new
|
116
|
-
end
|
117
|
-
state :old
|
118
|
-
on_entry do
|
119
|
-
callbacks.my_on_entry_old
|
120
|
-
end
|
121
|
-
on_exit do
|
122
|
-
fail "wrong on_exit executed"
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
o = c.new
|
128
|
-
assert_equal 'new', o.current_state.to_s
|
129
|
-
o.age!
|
130
|
-
end
|
131
|
-
|
132
|
-
test 'on_transition invoked' do
|
133
|
-
callbacks = mock()
|
134
|
-
callbacks.expects(:on_tran).once # this is validated at the end
|
135
|
-
c = Class.new
|
136
|
-
c.class_eval do
|
137
|
-
include Workflow
|
138
|
-
workflow do
|
139
|
-
state :one do
|
140
|
-
event :increment, :transitions_to => :two
|
141
|
-
end
|
142
|
-
state :two
|
143
|
-
on_transition do |from, to, triggering_event, *event_args|
|
144
|
-
callbacks.on_tran
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
assert_not_nil c.workflow_spec.on_transition_proc
|
149
|
-
c.new.increment!
|
150
|
-
end
|
151
|
-
|
152
|
-
test 'access event meta information' do
|
153
|
-
c = Class.new
|
154
|
-
c.class_eval do
|
155
|
-
include Workflow
|
156
|
-
workflow do
|
157
|
-
state :main, :meta => {:importance => 8}
|
158
|
-
state :supplemental, :meta => {:importance => 1}
|
159
|
-
end
|
160
|
-
end
|
161
|
-
assert_equal 1, c.workflow_spec.states[:supplemental].meta[:importance]
|
162
|
-
end
|
163
|
-
|
164
|
-
test 'initial state' do
|
165
|
-
c = Class.new
|
166
|
-
c.class_eval do
|
167
|
-
include Workflow
|
168
|
-
workflow { state :one; state :two }
|
169
|
-
end
|
170
|
-
assert_equal 'one', c.new.current_state.to_s
|
171
|
-
end
|
172
|
-
|
173
|
-
test 'nil as initial state' do
|
174
|
-
exec "INSERT INTO orders(title, workflow_state) VALUES('nil state', NULL)"
|
175
|
-
o = Order.find_by_title('nil state')
|
176
|
-
assert o.submitted?, 'if workflow_state is nil, the initial state should be assumed'
|
177
|
-
assert !o.shipped?
|
178
|
-
end
|
179
|
-
|
180
|
-
test 'initial state immediately set as ActiveRecord attribute for new objects' do
|
181
|
-
o = Order.create(:title => 'new object')
|
182
|
-
assert_equal 'submitted', o.read_attribute(:workflow_state)
|
183
|
-
end
|
184
|
-
|
185
|
-
test 'question methods for state' do
|
186
|
-
o = assert_state 'some order', 'accepted'
|
187
|
-
assert o.accepted?
|
188
|
-
assert !o.shipped?
|
189
|
-
end
|
190
|
-
|
191
|
-
test 'correct exception for event, that is not allowed in current state' do
|
192
|
-
o = assert_state 'some order', 'accepted'
|
193
|
-
assert_raise Workflow::NoTransitionAllowed do
|
194
|
-
o.accept!
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
test 'multiple events with the same name and different arguments lists from different states'
|
199
|
-
|
200
|
-
test 'implicit transition callback' do
|
201
|
-
args = mock()
|
202
|
-
args.expects(:my_tran).once # this is validated at the end
|
203
|
-
c = Class.new
|
204
|
-
c.class_eval do
|
205
|
-
include Workflow
|
206
|
-
def my_transition(args)
|
207
|
-
args.my_tran
|
208
|
-
end
|
209
|
-
workflow do
|
210
|
-
state :one do
|
211
|
-
event :my_transition, :transitions_to => :two
|
212
|
-
end
|
213
|
-
state :two
|
214
|
-
end
|
215
|
-
end
|
216
|
-
c.new.my_transition!(args)
|
217
|
-
end
|
218
|
-
|
219
|
-
test 'Single table inheritance (STI)' do
|
220
|
-
class BigOrder < Order
|
221
|
-
end
|
222
|
-
|
223
|
-
bo = BigOrder.new
|
224
|
-
assert bo.submitted?
|
225
|
-
assert !bo.accepted?
|
226
|
-
end
|
227
|
-
|
228
|
-
test 'Two-level inheritance' do
|
229
|
-
class BigOrder < Order
|
230
|
-
end
|
231
|
-
|
232
|
-
class EvenBiggerOrder < BigOrder
|
233
|
-
end
|
234
|
-
|
235
|
-
assert EvenBiggerOrder.new.submitted?
|
236
|
-
end
|
237
|
-
|
238
|
-
test 'Iheritance with workflow definition override' do
|
239
|
-
class BigOrder < Order
|
240
|
-
end
|
241
|
-
|
242
|
-
class SpecialBigOrder < BigOrder
|
243
|
-
workflow do
|
244
|
-
state :start_big
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
special = SpecialBigOrder.new
|
249
|
-
assert_equal 'start_big', special.current_state.to_s
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|