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