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.
@@ -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