pipeline 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1 @@
1
+ --exclude 'spec/*,var/*,gems/*,/Library/*,/usr/*'
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format progress
@@ -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