pipeline 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/CHANGELOG +12 -0
- data/LICENSE +20 -0
- data/README.rdoc +67 -0
- data/Rakefile +104 -0
- data/VERSION +1 -0
- data/examples/auto_recoverable_pipeline.rb +35 -0
- data/examples/cancelling_pipeline.rb +32 -0
- data/examples/two_step_pipeline.rb +27 -0
- data/examples/user_recoverable_pipeline.rb +33 -0
- data/generators/pipeline/pipeline_generator.rb +12 -0
- data/generators/pipeline/templates/pipeline_instances_migration.rb +16 -0
- data/generators/pipeline/templates/pipeline_stages_migration.rb +19 -0
- data/init.rb +1 -0
- data/lib/pipeline.rb +16 -0
- data/lib/pipeline/api_methods.rb +23 -0
- data/lib/pipeline/base.rb +67 -0
- data/lib/pipeline/core_ext/symbol_attribute.rb +29 -0
- data/lib/pipeline/core_ext/transactional_attribute.rb +26 -0
- data/lib/pipeline/errors.rb +24 -0
- data/lib/pipeline/stage/base.rb +60 -0
- data/pipeline.gemspec +86 -0
- data/spec/database_integration_helper.rb +42 -0
- data/spec/pipeline/api_methods_spec.rb +98 -0
- data/spec/pipeline/base_spec.rb +383 -0
- data/spec/pipeline/core_ext/symbol_attribute_spec.rb +38 -0
- data/spec/pipeline/core_ext/transactional_attribute_spec.rb +22 -0
- data/spec/pipeline/errors_spec.rb +46 -0
- data/spec/pipeline/stage/base_spec.rb +203 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +10 -0
- metadata +117 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
# Reusing stage table to simplify database integration tests
|
4
|
+
class FakeForSymbolAttribute < ActiveRecord::Base
|
5
|
+
set_table_name :pipeline_stages
|
6
|
+
|
7
|
+
symbol_attr :status
|
8
|
+
end
|
9
|
+
|
10
|
+
module Pipeline
|
11
|
+
describe SymbolAttribute do
|
12
|
+
before(:each) do
|
13
|
+
FakeForSymbolAttribute.delete_all
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should extend active record to allow symbol attributes to be saved as string" do
|
17
|
+
obj = FakeForSymbolAttribute.new(:status => 'started')
|
18
|
+
obj.save!
|
19
|
+
obj.status.should == :started
|
20
|
+
obj.reload.status.should == :started
|
21
|
+
|
22
|
+
obj.status = 'finished'
|
23
|
+
obj.save!
|
24
|
+
obj.status.should == :finished
|
25
|
+
obj.reload.status.should == :finished
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should extend Symbol to allow symbol attributes in conditions" do
|
29
|
+
objs = FakeForSymbolAttribute.find(:all, :conditions => ['status = ?', :started])
|
30
|
+
objs.should be_empty
|
31
|
+
|
32
|
+
FakeForSymbolAttribute.create(:status => :started)
|
33
|
+
|
34
|
+
objs = FakeForSymbolAttribute.find(:all, :conditions => ['status = ?', :started])
|
35
|
+
objs.size.should == 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
# Reusing stage table to simplify database integration tests
|
4
|
+
class FakeForTransactionalAttribute < ActiveRecord::Base
|
5
|
+
set_table_name :pipeline_stages
|
6
|
+
|
7
|
+
transactional_attr :status
|
8
|
+
end
|
9
|
+
|
10
|
+
module Pipeline
|
11
|
+
describe TransactionalAttribute do
|
12
|
+
it "should extend active record to allow transactional attributes to be saved in a nested transaction" do
|
13
|
+
obj = FakeForTransactionalAttribute.create(:status => "started")
|
14
|
+
obj.status.should == "started"
|
15
|
+
obj.reload.status.should == "started"
|
16
|
+
|
17
|
+
obj.status = "finished"
|
18
|
+
obj.status.should == "finished"
|
19
|
+
obj.reload.status.should == "finished"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
module Pipeline
|
4
|
+
describe InvalidPipelineError do
|
5
|
+
it "should accept description message when raised" do
|
6
|
+
lambda {raise InvalidPipelineError.new, "message"}.should raise_error(InvalidPipelineError, "message")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe InvalidStatusError do
|
11
|
+
it "should accept status name as symbol" do
|
12
|
+
lambda {raise InvalidStatusError.new(:started)}.should raise_error(InvalidStatusError, "Status is already started")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should accept status name as string" do
|
16
|
+
lambda {raise InvalidStatusError.new("in progress")}.should raise_error(InvalidStatusError, "Status is already in progress")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should replace underscores with spaces" do
|
20
|
+
lambda {raise InvalidStatusError.new(:in_progress)}.should raise_error(InvalidStatusError, "Status is already in progress")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe IrrecoverableError do
|
25
|
+
it "should accept description message when raised" do
|
26
|
+
lambda {raise IrrecoverableError.new, "message"}.should raise_error(IrrecoverableError, "message")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe RecoverableError do
|
31
|
+
it "should accept description message when raised" do
|
32
|
+
lambda {raise RecoverableError.new, "message"}.should raise_error(RecoverableError, "message")
|
33
|
+
lambda {raise RecoverableError.new("message")}.should raise_error(RecoverableError, "message")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "might require user input" do
|
37
|
+
error = RecoverableError.new("message", true)
|
38
|
+
error.should be_input_required
|
39
|
+
end
|
40
|
+
|
41
|
+
it "doesn't require user input by default" do
|
42
|
+
error = RecoverableError.new
|
43
|
+
error.should_not be_input_required
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
|
2
|
+
|
3
|
+
class SampleStage < Pipeline::Stage::Base
|
4
|
+
def run
|
5
|
+
@executed = true
|
6
|
+
end
|
7
|
+
|
8
|
+
def executed?
|
9
|
+
!!@executed
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module Pipeline
|
14
|
+
module Stage
|
15
|
+
describe Base do
|
16
|
+
|
17
|
+
describe "- chaining" do
|
18
|
+
class Step1 < Base; end
|
19
|
+
class Step2 < Base; end
|
20
|
+
class Step3 < Base; end
|
21
|
+
|
22
|
+
it "should start as itself" do
|
23
|
+
Step1.build_chain.should == [Step1]
|
24
|
+
Step2.build_chain.should == [Step2]
|
25
|
+
Step3.build_chain.should == [Step3]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should allow chaining" do
|
29
|
+
(Step1 >> Step2).build_chain.should == [Step1, Step2]
|
30
|
+
(Step2 >> Step1).build_chain.should == [Step2, Step1]
|
31
|
+
(Step1 >> Step2 >> Step3).build_chain.should == [Step1, Step2, Step3]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "- setup" do
|
36
|
+
it "should set default name" do
|
37
|
+
Base.new.name.should == "Pipeline::Stage::Base"
|
38
|
+
SampleStage.new.name.should == "SampleStage"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should allow specifying a name on creation" do
|
42
|
+
Base.new(:name => "My Name").name.should == "My Name"
|
43
|
+
SampleStage.new(:name => "Customized Name").name.should == "Customized Name"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should start with status not_started" do
|
47
|
+
Base.new.status.should == :not_started
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should validate status" do
|
51
|
+
lambda {Base.new(:status => :something_else)}.should raise_error
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "- persistence" do
|
56
|
+
before(:each) do
|
57
|
+
@stage = SampleStage.new
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should persist stage" do
|
61
|
+
@stage.id.should be_nil
|
62
|
+
lambda {@stage.save!}.should_not raise_error
|
63
|
+
@stage.id.should_not be_nil
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should allow retrieval by id" do
|
67
|
+
@stage.save!
|
68
|
+
|
69
|
+
s = SampleStage.find(@stage.id)
|
70
|
+
s.should === @stage
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should persist type as single table inheritance" do
|
74
|
+
@stage.save!
|
75
|
+
stage = Base.find(@stage.id)
|
76
|
+
stage.should be_an_instance_of(SampleStage)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "- execution (success)" do
|
82
|
+
before(:each) do
|
83
|
+
@stage = SampleStage.new
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should update status after finished" do
|
87
|
+
@stage.perform
|
88
|
+
@stage.status.should == :completed
|
89
|
+
@stage.should be_completed
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should save status" do
|
93
|
+
@stage.save!
|
94
|
+
@stage.perform
|
95
|
+
@stage.reload.status.should == :completed
|
96
|
+
@stage.reload.should be_completed
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should increment attempts" do
|
100
|
+
@stage.stub!(:run).and_raise(StandardError.new)
|
101
|
+
lambda {@stage.perform}.should raise_error(StandardError)
|
102
|
+
@stage.attempts.should == 1
|
103
|
+
|
104
|
+
lambda {@stage.perform}.should raise_error(StandardError)
|
105
|
+
@stage.attempts.should == 2
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should call template method #run" do
|
109
|
+
@stage.should_not be_executed
|
110
|
+
@stage.perform
|
111
|
+
@stage.should be_executed
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "- execution (failure)" do
|
116
|
+
before(:each) do
|
117
|
+
@stage = SampleStage.new
|
118
|
+
@stage.stub!(:run).and_raise(StandardError.new)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should re-raise error" do
|
122
|
+
lambda {@stage.perform}.should raise_error
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should update status on irrecoverable error" do
|
126
|
+
@stage.should_receive(:run).and_raise(IrrecoverableError.new)
|
127
|
+
lambda {@stage.perform}.should raise_error(IrrecoverableError)
|
128
|
+
@stage.status.should == :failed
|
129
|
+
@stage.reload.status.should == :failed
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should update message on irrecoverable error" do
|
133
|
+
@stage.should_receive(:run).and_raise(IrrecoverableError.new("message"))
|
134
|
+
lambda {@stage.perform}.should raise_error(IrrecoverableError)
|
135
|
+
@stage.message.should == "message"
|
136
|
+
@stage.reload.message.should == "message"
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should update status on recoverable error (not requiring input)" do
|
140
|
+
@stage.should_receive(:run).and_raise(RecoverableError.new)
|
141
|
+
lambda {@stage.perform}.should raise_error(RecoverableError)
|
142
|
+
@stage.status.should == :failed
|
143
|
+
@stage.reload.status.should == :failed
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should update status on recoverable error (requiring input)" do
|
147
|
+
@stage.should_receive(:run).and_raise(RecoverableError.new("message", true))
|
148
|
+
lambda {@stage.perform}.should raise_error(RecoverableError)
|
149
|
+
@stage.status.should == :failed
|
150
|
+
@stage.reload.status.should == :failed
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should update message on recoverable error" do
|
154
|
+
@stage.should_receive(:run).and_raise(RecoverableError.new("message"))
|
155
|
+
lambda {@stage.perform}.should raise_error(RecoverableError)
|
156
|
+
@stage.message.should == "message"
|
157
|
+
@stage.reload.message.should == "message"
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "- execution (in progress)" do
|
163
|
+
it "should set status to in_progress" do
|
164
|
+
stage = SampleStage.new
|
165
|
+
stage.send(:_setup)
|
166
|
+
|
167
|
+
stage.status.should == :in_progress
|
168
|
+
stage.reload.status.should == :in_progress
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "- execution (state transitions)" do
|
173
|
+
it "should execute if status is :not_started" do
|
174
|
+
stage = SampleStage.new
|
175
|
+
|
176
|
+
lambda {stage.perform}.should_not raise_error(InvalidStatusError)
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should execute if status is :failed (for retrying)" do
|
180
|
+
stage = SampleStage.new
|
181
|
+
stage.send(:status=, :failed)
|
182
|
+
|
183
|
+
lambda {stage.perform}.should_not raise_error(InvalidStatusError)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should not execute if status is :in_progress" do
|
187
|
+
stage = SampleStage.new
|
188
|
+
stage.send(:status=, :in_progress)
|
189
|
+
|
190
|
+
lambda {stage.perform}.should raise_error(InvalidStatusError, "Status is already in progress")
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should not execute if status is :completed" do
|
194
|
+
stage = SampleStage.new
|
195
|
+
stage.send(:status=, :completed)
|
196
|
+
|
197
|
+
lambda {stage.perform}.should raise_error(InvalidStatusError, "Status is already completed")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
data/spec/rcov.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--exclude 'spec/*,var/*,gems/*,/Library/*,/usr/*'
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'spec'
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), '..', 'init')
|
4
|
+
require File.join(File.dirname(__FILE__), 'database_integration_helper')
|
5
|
+
|
6
|
+
ActiveRecord::Base.logger = Logger.new('pipeline.log')
|
7
|
+
|
8
|
+
at_exit do
|
9
|
+
File.delete("pipeline.log") if File.exists?("pipeline.log")
|
10
|
+
end
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pipeline
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Danilo Sato
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-30 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activerecord
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "2.0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: collectiveidea-delayed_job
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.8.0
|
34
|
+
version:
|
35
|
+
description: Pipeline is a Rails plugin/gem to run asynchronous processes in a configurable pipeline.
|
36
|
+
email: danilo@dtsato.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- CHANGELOG
|
46
|
+
- LICENSE
|
47
|
+
- README.rdoc
|
48
|
+
- Rakefile
|
49
|
+
- VERSION
|
50
|
+
- examples/auto_recoverable_pipeline.rb
|
51
|
+
- examples/cancelling_pipeline.rb
|
52
|
+
- examples/two_step_pipeline.rb
|
53
|
+
- examples/user_recoverable_pipeline.rb
|
54
|
+
- generators/pipeline/pipeline_generator.rb
|
55
|
+
- generators/pipeline/templates/pipeline_instances_migration.rb
|
56
|
+
- generators/pipeline/templates/pipeline_stages_migration.rb
|
57
|
+
- init.rb
|
58
|
+
- lib/pipeline.rb
|
59
|
+
- lib/pipeline/api_methods.rb
|
60
|
+
- lib/pipeline/base.rb
|
61
|
+
- lib/pipeline/core_ext/symbol_attribute.rb
|
62
|
+
- lib/pipeline/core_ext/transactional_attribute.rb
|
63
|
+
- lib/pipeline/errors.rb
|
64
|
+
- lib/pipeline/stage/base.rb
|
65
|
+
- pipeline.gemspec
|
66
|
+
- spec/database_integration_helper.rb
|
67
|
+
- spec/pipeline/api_methods_spec.rb
|
68
|
+
- spec/pipeline/base_spec.rb
|
69
|
+
- spec/pipeline/core_ext/symbol_attribute_spec.rb
|
70
|
+
- spec/pipeline/core_ext/transactional_attribute_spec.rb
|
71
|
+
- spec/pipeline/errors_spec.rb
|
72
|
+
- spec/pipeline/stage/base_spec.rb
|
73
|
+
- spec/rcov.opts
|
74
|
+
- spec/spec.opts
|
75
|
+
- spec/spec_helper.rb
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: http://github.com/dtsato/pipeline
|
78
|
+
licenses: []
|
79
|
+
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options:
|
82
|
+
- --main
|
83
|
+
- README.rdoc
|
84
|
+
- --inline-source
|
85
|
+
- --line-numbers
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: "0"
|
93
|
+
version:
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: "0"
|
99
|
+
version:
|
100
|
+
requirements: []
|
101
|
+
|
102
|
+
rubyforge_project: pipeline
|
103
|
+
rubygems_version: 1.3.4
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: A Rails plugin/gem to run asynchronous processes in a configurable pipeline
|
107
|
+
test_files:
|
108
|
+
- spec/database_integration_helper.rb
|
109
|
+
- spec/pipeline/api_methods_spec.rb
|
110
|
+
- spec/pipeline/base_spec.rb
|
111
|
+
- spec/pipeline/core_ext/symbol_attribute_spec.rb
|
112
|
+
- spec/pipeline/core_ext/transactional_attribute_spec.rb
|
113
|
+
- spec/pipeline/errors_spec.rb
|
114
|
+
- spec/pipeline/stage/base_spec.rb
|
115
|
+
- spec/rcov.opts
|
116
|
+
- spec/spec.opts
|
117
|
+
- spec/spec_helper.rb
|