workflow 0.6.0 → 0.7.0
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.
- data/README.markdown +25 -3
- data/VERSION +1 -1
- data/lib/workflow.rb +18 -14
- data/test/main_test.rb +56 -3
- data/test/multiple_workflows_test.rb +16 -0
- metadata +3 -3
data/README.markdown
CHANGED
@@ -83,6 +83,14 @@ Alternatively you can just download the lib/workflow.rb and put it in
|
|
83
83
|
the lib folder of your Rails or Ruby application.
|
84
84
|
|
85
85
|
|
86
|
+
Ruby 1.9
|
87
|
+
--------
|
88
|
+
|
89
|
+
Workflow gem does not work with some (but very widespread) Ruby 1.9
|
90
|
+
builds due to a known bug in Ruby 1.9. Either compile your Ruby 1.9 from
|
91
|
+
source or [comment out some lines in workflow](http://github.com/geekq/workflow/issues#issue/6)
|
92
|
+
(reduces functionality).
|
93
|
+
|
86
94
|
Examples
|
87
95
|
--------
|
88
96
|
|
@@ -370,7 +378,11 @@ Then you define your different classes:
|
|
370
378
|
Another solution would be to connect different workflows to object
|
371
379
|
instances via metaclass, e.g.
|
372
380
|
|
381
|
+
# Load an object from the database
|
373
382
|
booking = Booking.find(1234)
|
383
|
+
|
384
|
+
# Now define a workflow - exclusively for this object,
|
385
|
+
# probably depending on some condition or database field
|
374
386
|
if # some condition
|
375
387
|
class << booking
|
376
388
|
include Workflow
|
@@ -381,7 +393,9 @@ instances via metaclass, e.g.
|
|
381
393
|
end
|
382
394
|
# if some other condition, use a different workflow
|
383
395
|
|
384
|
-
|
396
|
+
You can also encapsulate this in a class method or even put in some
|
397
|
+
ActiveRecord callback. Please also have a look at [the full working
|
398
|
+
example][multiple_workflow_test]!
|
385
399
|
|
386
400
|
[STI]: http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
|
387
401
|
[ActiveRecord]: http://api.rubyonrails.org/classes/ActiveRecord/Base.html
|
@@ -432,6 +446,14 @@ when using both a block and a callback method for an event, the block executes p
|
|
432
446
|
Changelog
|
433
447
|
---------
|
434
448
|
|
449
|
+
### New in the version 0.7.0
|
450
|
+
|
451
|
+
* fix issue#10 Workflow::create_workflow_diagram documentation and path
|
452
|
+
escaping
|
453
|
+
* fix issue#7 workflow_column does not work STI (single table
|
454
|
+
inheritance) ActiveRecord models
|
455
|
+
* fix issue#5 Diagram generation fails for models in modules
|
456
|
+
|
435
457
|
### New in the version 0.6.0
|
436
458
|
|
437
459
|
* enable multiple workflows by connecting workflow to object instances
|
@@ -440,8 +462,8 @@ Changelog
|
|
440
462
|
|
441
463
|
### New in the version 0.5.0
|
442
464
|
|
443
|
-
* change the behaviour of halt! to immediately raise an
|
444
|
-
also http://github.com/geekq/workflow/issues/#issue/3
|
465
|
+
* fix issue#3 change the behaviour of halt! to immediately raise an
|
466
|
+
exception. See also http://github.com/geekq/workflow/issues/#issue/3
|
445
467
|
|
446
468
|
### New in the version 0.4.0
|
447
469
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/lib/workflow.rb
CHANGED
@@ -100,10 +100,11 @@ module Workflow
|
|
100
100
|
def workflow_column(column_name=nil)
|
101
101
|
if column_name
|
102
102
|
@workflow_state_column_name = column_name.to_sym
|
103
|
-
else
|
104
|
-
@workflow_state_column_name ||= :workflow_state
|
105
103
|
end
|
106
|
-
|
104
|
+
if !@workflow_state_column_name && superclass.respond_to?(:workflow_column)
|
105
|
+
@workflow_state_column_name = superclass.workflow_column
|
106
|
+
end
|
107
|
+
@workflow_state_column_name ||= :workflow_state
|
107
108
|
end
|
108
109
|
|
109
110
|
def workflow(&specification)
|
@@ -160,8 +161,10 @@ module Workflow
|
|
160
161
|
else
|
161
162
|
check_transition(event)
|
162
163
|
run_on_transition(current_state, spec.states[event.transitions_to], name, *args)
|
163
|
-
transition(
|
164
|
-
|
164
|
+
transition_value = transition(
|
165
|
+
current_state, spec.states[event.transitions_to], name, *args
|
166
|
+
)
|
167
|
+
return_value.nil? ? transition_value : return_value
|
165
168
|
end
|
166
169
|
end
|
167
170
|
|
@@ -205,8 +208,9 @@ module Workflow
|
|
205
208
|
|
206
209
|
def transition(from, to, name, *args)
|
207
210
|
run_on_exit(from, to, name, *args)
|
208
|
-
persist_workflow_state to.to_s
|
211
|
+
val = persist_workflow_state to.to_s
|
209
212
|
run_on_entry(to, from, name, *args)
|
213
|
+
val
|
210
214
|
end
|
211
215
|
|
212
216
|
def run_on_transition(from, to, event, *args)
|
@@ -306,13 +310,13 @@ module Workflow
|
|
306
310
|
end
|
307
311
|
|
308
312
|
# Generates a `dot` graph of the workflow.
|
309
|
-
# Prerequisite: the `dot` binary.
|
310
|
-
# You can use
|
313
|
+
# Prerequisite: the `dot` binary. (Download from http://www.graphviz.org/)
|
314
|
+
# You can use this method in your own Rakefile like this:
|
311
315
|
#
|
312
316
|
# namespace :doc do
|
313
317
|
# desc "Generate a graph of the workflow."
|
314
|
-
# task :workflow do
|
315
|
-
# Workflow::create_workflow_diagram(Order
|
318
|
+
# task :workflow => :environment do # needs access to the Rails environment
|
319
|
+
# Workflow::create_workflow_diagram(Order)
|
316
320
|
# end
|
317
321
|
# end
|
318
322
|
#
|
@@ -331,8 +335,8 @@ module Workflow
|
|
331
335
|
# @param klass A class with the Workflow mixin, for which you wish the graphical workflow representation
|
332
336
|
# @param [String] target_dir Directory, where to save the dot and the pdf files
|
333
337
|
# @param [String] graph_options You can change graph orientation, size etc. See graphviz documentation
|
334
|
-
def self.create_workflow_diagram(klass, target_dir, graph_options='rankdir="LR", size="7,11.6", ratio="fill"')
|
335
|
-
workflow_name = "#{klass.name.tableize}_workflow"
|
338
|
+
def self.create_workflow_diagram(klass, target_dir='.', graph_options='rankdir="LR", size="7,11.6", ratio="fill"')
|
339
|
+
workflow_name = "#{klass.name.tableize}_workflow".gsub('/', '_')
|
336
340
|
fname = File.join(target_dir, "generated_#{workflow_name}")
|
337
341
|
File.open("#{fname}.dot", 'w') do |file|
|
338
342
|
file.puts %Q|
|
@@ -357,11 +361,11 @@ digraph #{workflow_name} {
|
|
357
361
|
file.puts "}"
|
358
362
|
file.puts
|
359
363
|
end
|
360
|
-
`dot -Tpdf -o#{fname}.pdf #{fname}.dot`
|
364
|
+
`dot -Tpdf -o'#{fname}.pdf' '#{fname}.dot'`
|
361
365
|
puts "
|
362
366
|
Please run the following to open the generated file:
|
363
367
|
|
364
|
-
open #{fname}.pdf
|
368
|
+
open '#{fname}.pdf'
|
365
369
|
|
366
370
|
"
|
367
371
|
end
|
data/test/main_test.rb
CHANGED
@@ -5,6 +5,7 @@ require 'active_record'
|
|
5
5
|
require 'sqlite3'
|
6
6
|
require 'workflow'
|
7
7
|
require 'mocha'
|
8
|
+
require 'stringio'
|
8
9
|
#require 'ruby-debug'
|
9
10
|
|
10
11
|
ActiveRecord::Migration.verbose = false
|
@@ -21,7 +22,6 @@ class Order < ActiveRecord::Base
|
|
21
22
|
end
|
22
23
|
state :shipped
|
23
24
|
end
|
24
|
-
|
25
25
|
end
|
26
26
|
|
27
27
|
class LegacyOrder < ActiveRecord::Base
|
@@ -39,9 +39,26 @@ class LegacyOrder < ActiveRecord::Base
|
|
39
39
|
end
|
40
40
|
state :shipped
|
41
41
|
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Image < ActiveRecord::Base
|
45
|
+
include Workflow
|
46
|
+
|
47
|
+
workflow_column :status
|
48
|
+
|
49
|
+
workflow do
|
50
|
+
state :unconverted do
|
51
|
+
event :convert, :transitions_to => :converted
|
52
|
+
end
|
53
|
+
state :converted
|
54
|
+
end
|
55
|
+
end
|
42
56
|
|
57
|
+
class SmallImage < Image
|
43
58
|
end
|
44
59
|
|
60
|
+
class SpecialSmallImage < SmallImage
|
61
|
+
end
|
45
62
|
|
46
63
|
class MainTest < ActiveRecordTestCase
|
47
64
|
|
@@ -66,6 +83,13 @@ class MainTest < ActiveRecordTestCase
|
|
66
83
|
|
67
84
|
exec "INSERT INTO legacy_orders(title, foo_bar) VALUES('some order', 'accepted')"
|
68
85
|
|
86
|
+
ActiveRecord::Schema.define do
|
87
|
+
create_table :images do |t|
|
88
|
+
t.string :title, :null => false
|
89
|
+
t.string :state
|
90
|
+
t.string :type
|
91
|
+
end
|
92
|
+
end
|
69
93
|
end
|
70
94
|
|
71
95
|
def assert_state(title, expected_state, klass = Order)
|
@@ -76,13 +100,13 @@ class MainTest < ActiveRecordTestCase
|
|
76
100
|
|
77
101
|
test 'immediately save the new workflow_state on state machine transition' do
|
78
102
|
o = assert_state 'some order', 'accepted'
|
79
|
-
o.ship!
|
103
|
+
assert o.ship!
|
80
104
|
assert_state 'some order', 'shipped'
|
81
105
|
end
|
82
106
|
|
83
107
|
test 'immediately save the new workflow_state on state machine transition with custom column name' do
|
84
108
|
o = assert_state 'some order', 'accepted', LegacyOrder
|
85
|
-
o.ship!
|
109
|
+
assert o.ship!
|
86
110
|
assert_state 'some order', 'shipped', LegacyOrder
|
87
111
|
end
|
88
112
|
|
@@ -257,6 +281,12 @@ class MainTest < ActiveRecordTestCase
|
|
257
281
|
assert !bo.accepted?
|
258
282
|
end
|
259
283
|
|
284
|
+
test 'STI when parent changed the workflow_state column' do
|
285
|
+
assert_equal 'status', Image.workflow_column.to_s
|
286
|
+
assert_equal 'status', SmallImage.workflow_column.to_s
|
287
|
+
assert_equal 'status', SpecialSmallImage.workflow_column.to_s
|
288
|
+
end
|
289
|
+
|
260
290
|
test 'Two-level inheritance' do
|
261
291
|
class BigOrder < Order
|
262
292
|
end
|
@@ -407,5 +437,28 @@ class MainTest < ActiveRecordTestCase
|
|
407
437
|
assert article.rejected?, 'Transition should happen now'
|
408
438
|
end
|
409
439
|
|
440
|
+
test 'workflow graph generation' do
|
441
|
+
Dir.chdir('tmp') do
|
442
|
+
capture_streams do
|
443
|
+
Workflow::create_workflow_diagram(Order)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
test 'workflow graph generation in path with spaces' do
|
449
|
+
`mkdir -p '/tmp/Workflow test'`
|
450
|
+
capture_streams do
|
451
|
+
Workflow::create_workflow_diagram(Order, '/tmp/Workflow test')
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
def capture_streams
|
456
|
+
old_stdout = $stdout
|
457
|
+
$stdout = captured_stdout = StringIO.new
|
458
|
+
yield
|
459
|
+
$stdout = old_stdout
|
460
|
+
captured_stdout
|
461
|
+
end
|
462
|
+
|
410
463
|
end
|
411
464
|
|
@@ -41,6 +41,13 @@ class MultipleWorkflowsTest < ActiveRecordTestCase
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
44
|
+
|
45
|
+
def metaclass; class << self; self; end; end
|
46
|
+
|
47
|
+
def workflow_spec
|
48
|
+
metaclass.workflow_spec
|
49
|
+
end
|
50
|
+
|
44
51
|
end
|
45
52
|
|
46
53
|
booking1 = Booking.find_by_title('booking1')
|
@@ -56,6 +63,15 @@ class MultipleWorkflowsTest < ActiveRecordTestCase
|
|
56
63
|
assert booking2.initial?
|
57
64
|
booking2.progress!
|
58
65
|
assert booking2.intermediate?, 'booking2 should transition to the "intermediate" state'
|
66
|
+
|
67
|
+
assert booking1.workflow_spec, 'can access the individual workflow specification'
|
68
|
+
assert_equal 2, booking1.workflow_spec.states.length
|
69
|
+
assert_equal 3, booking2.workflow_spec.states.length
|
70
|
+
end
|
71
|
+
|
72
|
+
class Object
|
73
|
+
# The hidden singleton lurks behind everyone
|
74
|
+
def metaclass; class << self; self; end; end
|
59
75
|
end
|
60
76
|
|
61
77
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 7
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.7.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Vladimir Dobriakov
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-09-08 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|