pipeline 0.0.1
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/.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
|