workflow 0.8.1 → 0.8.3
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/Gemfile +9 -0
- data/README.markdown +32 -0
- data/Rakefile +22 -16
- data/VERSION +1 -1
- data/lib/workflow.rb +49 -8
- data/test/advanced_hooks_and_validation_test.rb +3 -2
- data/test/main_test.rb +40 -1
- data/test/on_error_test.rb +52 -0
- data/workflow.gemspec +36 -19
- metadata +161 -39
data/Gemfile
ADDED
data/README.markdown
CHANGED
@@ -67,6 +67,18 @@ of possible events and other meta information:
|
|
67
67
|
@transitions_to=:awaiting_review, @name=:submit, @meta={}>},
|
68
68
|
name:new, meta{}
|
69
69
|
|
70
|
+
On Ruby 1.9 and above, you can check whether a state comes before or
|
71
|
+
after another state (by the order they were defined):
|
72
|
+
|
73
|
+
article.current_state
|
74
|
+
=> being_reviewed
|
75
|
+
article.current_state < :accepted
|
76
|
+
=> true
|
77
|
+
article.current_state >= :accepted
|
78
|
+
=> false
|
79
|
+
article.between? :awaiting_review, :rejected
|
80
|
+
=> true
|
81
|
+
|
70
82
|
Now we can call the submit event, which transitions to the
|
71
83
|
<tt>:awaiting_review</tt> state:
|
72
84
|
|
@@ -330,6 +342,26 @@ example][advanced_hooks_and_validation_test].
|
|
330
342
|
|
331
343
|
[advanced_hooks_and_validation_test]: http://github.com/geekq/workflow/blob/master/test/advanced_hooks_and_validation_test.rb
|
332
344
|
|
345
|
+
### on_error
|
346
|
+
|
347
|
+
If you want to do custom exception handling internal to workflow, you can define an `on_error` hook in your workflow.
|
348
|
+
For example:
|
349
|
+
|
350
|
+
workflow do
|
351
|
+
state :first do
|
352
|
+
event :forward, :transitions_to => :second
|
353
|
+
end
|
354
|
+
state :second
|
355
|
+
|
356
|
+
on_error do |error, from, to, event, *args|
|
357
|
+
Log.info "Exception(#error.class) on #{from} -> #{to}"
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
If forward! results in an exception, `on_error` is invoked and the workflow stays in a 'first' state. This capability
|
362
|
+
is particularly useful if your errors are transient and you want to queue up a job to retry in the future without
|
363
|
+
affecting the existing workflow state.
|
364
|
+
|
333
365
|
### Guards
|
334
366
|
|
335
367
|
If you want to halt the transition conditionally, you can just raise an
|
data/Rakefile
CHANGED
@@ -1,10 +1,20 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'bundler'
|
2
3
|
require 'rubygems/package_task'
|
3
4
|
require 'rake/testtask'
|
4
5
|
require 'rdoc/task'
|
5
6
|
|
6
7
|
task :default => [:test]
|
7
8
|
|
9
|
+
begin
|
10
|
+
Bundler.setup(:default, :development)
|
11
|
+
rescue Bundler::BundlerError => e
|
12
|
+
$stderr.puts e.message
|
13
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
14
|
+
exit e.status_code
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'rake'
|
8
18
|
Rake::TestTask.new do |t|
|
9
19
|
t.verbose = true
|
10
20
|
t.warning = true
|
@@ -16,16 +26,16 @@ Rake::RDocTask.new do |rdoc|
|
|
16
26
|
rdoc.options << "-S"
|
17
27
|
end
|
18
28
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
+
require 'jeweler'
|
30
|
+
|
31
|
+
Jeweler::Tasks.new do |gemspec|
|
32
|
+
gemspec.name = "workflow"
|
33
|
+
gemspec.rubyforge_project = 'workflow'
|
34
|
+
gemspec.email = "vladimir@geekq.net"
|
35
|
+
gemspec.homepage = "http://www.geekq.net/workflow/"
|
36
|
+
gemspec.authors = ["Vladimir Dobriakov"]
|
37
|
+
gemspec.summary = "A replacement for acts_as_state_machine."
|
38
|
+
gemspec.description = <<-EOS
|
29
39
|
Workflow is a finite-state-machine-inspired API for modeling and interacting
|
30
40
|
with what we tend to refer to as 'workflow'.
|
31
41
|
|
@@ -34,11 +44,7 @@ begin
|
|
34
44
|
* various hooks for single transitions, entering state etc.
|
35
45
|
* convenient access to the workflow specification: list states, possible events
|
36
46
|
for particular state
|
37
|
-
|
47
|
+
EOS
|
38
48
|
|
39
|
-
|
40
|
-
end
|
41
|
-
rescue LoadError
|
42
|
-
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
49
|
+
Jeweler::GemcutterTasks.new
|
43
50
|
end
|
44
|
-
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.8.
|
1
|
+
0.8.3
|
data/lib/workflow.rb
CHANGED
@@ -6,7 +6,7 @@ module Workflow
|
|
6
6
|
class Specification
|
7
7
|
|
8
8
|
attr_accessor :states, :initial_state, :meta,
|
9
|
-
:on_transition_proc, :before_transition_proc, :after_transition_proc
|
9
|
+
:on_transition_proc, :before_transition_proc, :after_transition_proc, :on_error_proc
|
10
10
|
|
11
11
|
def initialize(meta = {}, &specification)
|
12
12
|
@states = Hash.new
|
@@ -22,7 +22,7 @@ module Workflow
|
|
22
22
|
|
23
23
|
def state(name, meta = {:meta => {}}, &events_and_etc)
|
24
24
|
# meta[:meta] to keep the API consistent..., gah
|
25
|
-
new_state = Workflow::State.new(name, meta[:meta])
|
25
|
+
new_state = Workflow::State.new(name, self, meta[:meta])
|
26
26
|
@initial_state = new_state if @states.empty?
|
27
27
|
@states[name.to_sym] = new_state
|
28
28
|
@scoped_state = new_state
|
@@ -57,6 +57,10 @@ module Workflow
|
|
57
57
|
def on_transition(&proc)
|
58
58
|
@on_transition_proc = proc
|
59
59
|
end
|
60
|
+
|
61
|
+
def on_error(&proc)
|
62
|
+
@on_error_proc = proc
|
63
|
+
end
|
60
64
|
end
|
61
65
|
|
62
66
|
class TransitionHalted < Exception
|
@@ -79,9 +83,26 @@ module Workflow
|
|
79
83
|
class State
|
80
84
|
|
81
85
|
attr_accessor :name, :events, :meta, :on_entry, :on_exit
|
82
|
-
|
83
|
-
|
84
|
-
|
86
|
+
attr_reader :spec
|
87
|
+
|
88
|
+
def initialize(name, spec, meta = {})
|
89
|
+
@name, @spec, @events, @meta = name, spec, Hash.new, meta
|
90
|
+
end
|
91
|
+
|
92
|
+
unless RUBY_VERSION < '1.9'
|
93
|
+
include Comparable
|
94
|
+
|
95
|
+
def <=>(other_state)
|
96
|
+
states = spec.states.keys
|
97
|
+
raise ArgumentError, "state `#{other_state}' does not exist" unless other_state.in? states
|
98
|
+
if states.index(self.to_sym) < states.index(other_state.to_sym)
|
99
|
+
-1
|
100
|
+
elsif states.index(self.to_sym) > states.index(other_state.to_sym)
|
101
|
+
1
|
102
|
+
else
|
103
|
+
0
|
104
|
+
end
|
105
|
+
end
|
85
106
|
end
|
86
107
|
|
87
108
|
def to_s
|
@@ -143,6 +164,7 @@ module Workflow
|
|
143
164
|
end
|
144
165
|
|
145
166
|
module WorkflowInstanceMethods
|
167
|
+
|
146
168
|
def current_state
|
147
169
|
loaded_state = load_workflow_state
|
148
170
|
res = spec.states[loaded_state.to_sym] if loaded_state
|
@@ -177,7 +199,12 @@ module Workflow
|
|
177
199
|
run_before_transition(from, to, name, *args)
|
178
200
|
return false if @halted
|
179
201
|
|
180
|
-
|
202
|
+
begin
|
203
|
+
return_value = run_action(event.action, *args) || run_action_callback(event.name, *args)
|
204
|
+
rescue Exception => e
|
205
|
+
run_on_error(e, from, to, name, *args)
|
206
|
+
end
|
207
|
+
|
181
208
|
return false if @halted
|
182
209
|
|
183
210
|
run_on_transition(from, to, name, *args)
|
@@ -236,6 +263,15 @@ module Workflow
|
|
236
263
|
spec.before_transition_proc
|
237
264
|
end
|
238
265
|
|
266
|
+
def run_on_error(error, from, to, event, *args)
|
267
|
+
if spec.on_error_proc
|
268
|
+
instance_exec(error, from.name, to.name, event, *args, &spec.on_error_proc)
|
269
|
+
halt(error.message)
|
270
|
+
else
|
271
|
+
raise error
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
239
275
|
def run_on_transition(from, to, event, *args)
|
240
276
|
instance_exec(from.name, to.name, event, *args, &spec.on_transition_proc) if spec.on_transition_proc
|
241
277
|
end
|
@@ -248,9 +284,14 @@ module Workflow
|
|
248
284
|
def run_action(action, *args)
|
249
285
|
instance_exec(*args, &action) if action
|
250
286
|
end
|
287
|
+
|
288
|
+
def has_callback?(action)
|
289
|
+
self.respond_to?(action) or self.class.private_method_defined?(action)
|
290
|
+
end
|
251
291
|
|
252
292
|
def run_action_callback(action_name, *args)
|
253
|
-
|
293
|
+
action = action_name.to_sym
|
294
|
+
self.send(action, *args) if has_callback?(action)
|
254
295
|
end
|
255
296
|
|
256
297
|
def run_on_entry(state, prior_state, triggering_event, *args)
|
@@ -297,7 +338,7 @@ module Workflow
|
|
297
338
|
# On transition the new workflow state is immediately saved in the
|
298
339
|
# database.
|
299
340
|
def persist_workflow_state(new_value)
|
300
|
-
|
341
|
+
update_attributes self.class.workflow_column => new_value
|
301
342
|
end
|
302
343
|
|
303
344
|
private
|
@@ -51,8 +51,9 @@ class Article < ActiveRecord::Base
|
|
51
51
|
}
|
52
52
|
end
|
53
53
|
|
54
|
-
singleton.send :define_method, :
|
55
|
-
|
54
|
+
singleton.send :define_method, :validate_for_transition, &validations
|
55
|
+
validate_for_transition
|
56
|
+
halt! "Event[#{triggering_event}]'s transitions_to[#{to}] is not valid." unless self.errors.empty?
|
56
57
|
end
|
57
58
|
end
|
58
59
|
end
|
data/test/main_test.rb
CHANGED
@@ -244,6 +244,17 @@ class MainTest < ActiveRecordTestCase
|
|
244
244
|
assert !o.shipped?
|
245
245
|
end
|
246
246
|
|
247
|
+
unless RUBY_VERSION < '1.9'
|
248
|
+
test 'compare states' do
|
249
|
+
o = assert_state 'some order', 'accepted'
|
250
|
+
assert o.current_state < :shipped
|
251
|
+
assert o.current_state > :submitted
|
252
|
+
assert_raise ArgumentError do
|
253
|
+
o.current_state > :unknown
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
247
258
|
test 'correct exception for event, that is not allowed in current state' do
|
248
259
|
o = assert_state 'some order', 'accepted'
|
249
260
|
assert_raise Workflow::NoTransitionAllowed do
|
@@ -268,8 +279,36 @@ class MainTest < ActiveRecordTestCase
|
|
268
279
|
end
|
269
280
|
state :two
|
270
281
|
end
|
282
|
+
|
283
|
+
private
|
284
|
+
def another_transition(args)
|
285
|
+
args.another_tran
|
286
|
+
end
|
287
|
+
end
|
288
|
+
a = c.new
|
289
|
+
a.my_transition!(args)
|
290
|
+
end
|
291
|
+
|
292
|
+
test '#53 Support for private transition callbacks' do
|
293
|
+
args = mock()
|
294
|
+
args.expects(:log).once
|
295
|
+
c = Class.new
|
296
|
+
c.class_eval do
|
297
|
+
include Workflow
|
298
|
+
workflow do
|
299
|
+
state :new do
|
300
|
+
event :assign, :transitions_to => :assigned
|
301
|
+
end
|
302
|
+
state :assigned
|
303
|
+
end
|
304
|
+
|
305
|
+
private
|
306
|
+
def assign(args)
|
307
|
+
args.log('Assigned')
|
308
|
+
end
|
271
309
|
end
|
272
|
-
c.new
|
310
|
+
a = c.new
|
311
|
+
a.assign!(args)
|
273
312
|
end
|
274
313
|
|
275
314
|
test 'Single table inheritance (STI)' do
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
require 'workflow'
|
3
|
+
|
4
|
+
class OnErrorTest < Test::Unit::TestCase
|
5
|
+
# A class that does not handle errors in an error block
|
6
|
+
class NoErrorBlock
|
7
|
+
include Workflow
|
8
|
+
workflow do
|
9
|
+
state :first do
|
10
|
+
event :forward, :transitions_to => :second do
|
11
|
+
raise "This is some random runtime error"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
state :second
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# A class that handles errors in an error block
|
19
|
+
class ErrorBlock
|
20
|
+
attr_reader :errors
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@errors = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
include Workflow
|
27
|
+
workflow do
|
28
|
+
state :first do
|
29
|
+
event :forward, :transitions_to => :second do
|
30
|
+
raise "This is some random runtime error"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
state :second
|
34
|
+
on_error { |error, from, to, event, *args| @errors.merge!({:error => error.class, :from => from, :to => to, :event => event, :args => args}) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
test 'that an exception is raised if there is no associated on_error block' do
|
40
|
+
flow = NoErrorBlock.new
|
41
|
+
assert_raise( RuntimeError, "This is some random runtime error" ) { flow.forward! }
|
42
|
+
assert_equal(true, flow.first?)
|
43
|
+
end
|
44
|
+
|
45
|
+
test 'that on_error block is called when an exception is raised and the transition is halted' do
|
46
|
+
flow = ErrorBlock.new
|
47
|
+
assert_nothing_raised { flow.forward! }
|
48
|
+
assert_equal({:error => RuntimeError, :from=>:first, :to=>:second, :event=>:forward, :args=>[]}, flow.errors)
|
49
|
+
# transition should not happen
|
50
|
+
assert_equal(true, flow.first?)
|
51
|
+
end
|
52
|
+
end
|
data/workflow.gemspec
CHANGED
@@ -4,26 +4,19 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.8.
|
7
|
+
s.name = "workflow"
|
8
|
+
s.version = "0.8.3"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Vladimir Dobriakov"]
|
12
|
-
s.date =
|
13
|
-
s.description =
|
14
|
-
|
15
|
-
|
16
|
-
* nice DSL to describe your states, events and transitions
|
17
|
-
* robust integration with ActiveRecord and non relational data stores
|
18
|
-
* various hooks for single transitions, entering state etc.
|
19
|
-
* convenient access to the workflow specification: list states, possible events
|
20
|
-
for particular state
|
21
|
-
}
|
22
|
-
s.email = %q{vladimir@geekq.net}
|
12
|
+
s.date = "2012-12-04"
|
13
|
+
s.description = " Workflow is a finite-state-machine-inspired API for modeling and interacting\n with what we tend to refer to as 'workflow'.\n\n * nice DSL to describe your states, events and transitions\n * robust integration with ActiveRecord and non relational data stores\n * various hooks for single transitions, entering state etc.\n * convenient access to the workflow specification: list states, possible events\n for particular state\n"
|
14
|
+
s.email = "vladimir@geekq.net"
|
23
15
|
s.extra_rdoc_files = [
|
24
16
|
"README.markdown"
|
25
17
|
]
|
26
18
|
s.files = [
|
19
|
+
"Gemfile",
|
27
20
|
"MIT-LICENSE",
|
28
21
|
"README.markdown",
|
29
22
|
"Rakefile",
|
@@ -34,26 +27,50 @@ Gem::Specification.new do |s|
|
|
34
27
|
"test/couchtiny_example.rb",
|
35
28
|
"test/main_test.rb",
|
36
29
|
"test/multiple_workflows_test.rb",
|
30
|
+
"test/on_error_test.rb",
|
37
31
|
"test/readme_example.rb",
|
38
32
|
"test/test_helper.rb",
|
39
33
|
"test/without_active_record_test.rb",
|
40
34
|
"workflow.gemspec",
|
41
35
|
"workflow.rb"
|
42
36
|
]
|
43
|
-
s.homepage =
|
37
|
+
s.homepage = "http://www.geekq.net/workflow/"
|
44
38
|
s.require_paths = ["lib"]
|
45
|
-
s.rubyforge_project =
|
46
|
-
s.rubygems_version =
|
47
|
-
s.summary =
|
39
|
+
s.rubyforge_project = "workflow"
|
40
|
+
s.rubygems_version = "1.8.24"
|
41
|
+
s.summary = "A replacement for acts_as_state_machine."
|
48
42
|
|
49
43
|
if s.respond_to? :specification_version then
|
50
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
51
44
|
s.specification_version = 3
|
52
45
|
|
53
|
-
if Gem::Version.new(Gem::
|
46
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<workflow>, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
49
|
+
s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
|
50
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
51
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
52
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
53
|
+
s.add_development_dependency(%q<activerecord>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
54
55
|
else
|
56
|
+
s.add_dependency(%q<workflow>, [">= 0"])
|
57
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
58
|
+
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
59
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
60
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
61
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
62
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
63
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
55
64
|
end
|
56
65
|
else
|
66
|
+
s.add_dependency(%q<workflow>, [">= 0"])
|
67
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
68
|
+
s.add_dependency(%q<bundler>, [">= 1.0.0"])
|
69
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
70
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
71
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
72
|
+
s.add_dependency(%q<activerecord>, [">= 0"])
|
73
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
57
74
|
end
|
58
75
|
end
|
59
76
|
|
metadata
CHANGED
@@ -1,32 +1,157 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: workflow
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 8
|
8
|
-
- 1
|
9
|
-
version: 0.8.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.8.3
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Vladimir Dobriakov
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
12
|
+
date: 2012-12-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: workflow
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rdoc
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '3.12'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.12'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.0.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.0.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: jeweler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.8.4
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.8.4
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rake
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: mocha
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: activerecord
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: sqlite3
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
description: ! " Workflow is a finite-state-machine-inspired API for modeling and
|
143
|
+
interacting\n with what we tend to refer to as 'workflow'.\n\n * nice DSL
|
144
|
+
to describe your states, events and transitions\n * robust integration with ActiveRecord
|
145
|
+
and non relational data stores\n * various hooks for single transitions, entering
|
146
|
+
state etc.\n * convenient access to the workflow specification: list states,
|
147
|
+
possible events\n for particular state\n"
|
22
148
|
email: vladimir@geekq.net
|
23
149
|
executables: []
|
24
|
-
|
25
150
|
extensions: []
|
26
|
-
|
27
|
-
extra_rdoc_files:
|
151
|
+
extra_rdoc_files:
|
28
152
|
- README.markdown
|
29
|
-
files:
|
153
|
+
files:
|
154
|
+
- Gemfile
|
30
155
|
- MIT-LICENSE
|
31
156
|
- README.markdown
|
32
157
|
- Rakefile
|
@@ -37,40 +162,37 @@ files:
|
|
37
162
|
- test/couchtiny_example.rb
|
38
163
|
- test/main_test.rb
|
39
164
|
- test/multiple_workflows_test.rb
|
165
|
+
- test/on_error_test.rb
|
40
166
|
- test/readme_example.rb
|
41
167
|
- test/test_helper.rb
|
42
168
|
- test/without_active_record_test.rb
|
43
169
|
- workflow.gemspec
|
44
170
|
- workflow.rb
|
45
|
-
has_rdoc: true
|
46
171
|
homepage: http://www.geekq.net/workflow/
|
47
172
|
licenses: []
|
48
|
-
|
49
173
|
post_install_message:
|
50
174
|
rdoc_options: []
|
51
|
-
|
52
|
-
require_paths:
|
175
|
+
require_paths:
|
53
176
|
- lib
|
54
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
-
requirements:
|
63
|
-
- - ">="
|
64
|
-
- !ruby/object:Gem::Version
|
65
|
-
segments:
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
none: false
|
179
|
+
requirements:
|
180
|
+
- - ! '>='
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
version: '0'
|
183
|
+
segments:
|
66
184
|
- 0
|
67
|
-
|
185
|
+
hash: 3639477061676612937
|
186
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
|
+
none: false
|
188
|
+
requirements:
|
189
|
+
- - ! '>='
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
68
192
|
requirements: []
|
69
|
-
|
70
193
|
rubyforge_project: workflow
|
71
|
-
rubygems_version: 1.
|
194
|
+
rubygems_version: 1.8.24
|
72
195
|
signing_key:
|
73
196
|
specification_version: 3
|
74
197
|
summary: A replacement for acts_as_state_machine.
|
75
198
|
test_files: []
|
76
|
-
|