dirty_pipeline 0.2.0 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c67b4adb4c6e578a8add63619a5836d1d9d820c1b4c07fd53f2dd1877f1851d0
4
- data.tar.gz: 94d3c03c15d6e74079f3e64b985392f88b1c07247c394de1bf58e2feb1b94693
3
+ metadata.gz: 6b8f5e951d183bae44a012daeccb345d84b0ccc0dd26edeb1006cfe67cca27ba
4
+ data.tar.gz: 2b166ca245ea06ede75099e5c511749e9b9af04414446948e81db9ad5ce2b485
5
5
  SHA512:
6
- metadata.gz: 52c62a8e38f6ab759bf7368b0f32e86008894b48e4cc4a4fd93643d84848c436fb8b2aa93d200faa29e98431bc8136805b45fa76ba4d76e72cd262450a5f16da
7
- data.tar.gz: 0e83e12bdfb27820726ebd789b0d6226df9cdce1256d3a9cd5e4137656ef83ec8ccbaaf8cb6ccc12d4727c588bc5b8676760b3712df8bb601505301d92ba77e1
6
+ metadata.gz: 0ca3237ae6a6f282040216add989af7a84201d9d1a09be280d177c83de6ff752d55e7d5b3394c1f1e887677ec3e6280feff90f450df5ae18a550873f49bd80b9
7
+ data.tar.gz: 0c45a1200ffa8074fda41c1cf7839444d2f72d74a3bf1d4959f8f2a599ffac6aff0eed0a00f973799e4a99f4ef7e4f0fc983301bd39eba0306386592f651aace
@@ -76,105 +76,53 @@ module DirtyPipeline
76
76
  storage.reset_pipeline_status!
77
77
  end
78
78
 
79
- attr_reader :subject, :error, :storage
79
+ def clear!
80
+ storage.clear!
81
+ end
82
+
83
+ def cache
84
+ storage.store["cache"]
85
+ end
86
+
87
+ attr_reader :subject, :error, :storage, :status
80
88
  def initialize(subject)
81
89
  @subject = subject
82
90
  @storage = Storage.new(subject, self.class.pipeline_storage)
83
91
  @locker = Locker.new(@subject, @storage)
92
+ @status = Status.new(self)
84
93
  end
85
94
 
86
95
  def call(*args)
87
- return self if succeeded == false
88
- self.succeeded = nil
89
- after_commit = nil
90
-
91
- # transaction with support of external calls
92
- transaction(*args) do |destination, action, *transition_args|
93
- output = {}
94
- fail_cause = nil
95
-
96
- output, *after_commit = catch(:success) do
97
- fail_cause = catch(:fail_with_error) do
98
- return Abort() if catch(:abort) do
99
- throw :success, action.(subject, *transition_args)
96
+ Result() do
97
+ after_commit = nil
98
+ # transaction with support of external calls
99
+ transaction(*args) do |destination, action, *targs|
100
+ output = {}
101
+ fail_cause = nil
102
+
103
+ output, *after_commit = catch(:success) do
104
+ fail_cause = catch(:fail_with_error) do
105
+ Abort() if catch(:abort) do
106
+ throw :success, action.(self, *targs)
107
+ end
100
108
  end
109
+ nil
101
110
  end
102
- nil
103
- end
104
111
 
105
- if fail_cause
106
- ExpectedError(fail_cause)
107
- else
108
- Success(destination, output)
112
+ if fail_cause
113
+ ExpectedError(fail_cause)
114
+ else
115
+ Success(destination, output)
116
+ end
109
117
  end
110
- end
111
118
 
112
- Array(after_commit).each { |cb| cb.call(subject) } if after_commit
113
- self
114
- end
115
-
116
- def clear!
117
- storage.clear!
118
- end
119
-
120
- def success?
121
- succeeded
122
- end
123
-
124
- def when_success(callback = nil)
125
- return self unless success?
126
- if block_given?
127
- yield(self)
128
- else
129
- callback.call(self)
130
- end
131
- self
132
- end
133
-
134
- def when_failed(callback = nil)
135
- return self unless storage.failed?
136
- if block_given?
137
- yield(self)
138
- else
139
- callback.call(self)
140
- end
141
- self
142
- end
143
-
144
- def errored?
145
- return if succeeded.nil?
146
- ready? && !succeeded
147
- end
148
-
149
- def when_error(callback = nil)
150
- return self unless errored?
151
- if block_given?
152
- yield(self)
153
- else
154
- callback.call(self)
119
+ Array(after_commit).each { |cb| cb.call(subject) } if after_commit
155
120
  end
156
- self
157
- end
158
-
159
- def ready?
160
- storage.pipeline_status.nil?
161
- end
162
-
163
- def when_processing(callback = nil)
164
- return self unless storage.processing?
165
- if block_given?
166
- yield(self)
167
- else
168
- callback.call(self)
169
- end
170
- self
171
121
  end
172
122
 
173
123
  private
174
124
 
175
- attr_writer :error
176
125
  attr_reader :locker
177
- attr_accessor :succeeded, :previous_status
178
126
 
179
127
  def find_subject_args
180
128
  subject.id
@@ -188,6 +136,10 @@ module DirtyPipeline
188
136
  self.class.cleanup_delay || DEFAULT_CLEANUP_DELAY
189
137
  end
190
138
 
139
+ def Result()
140
+ status.wrap { yield }
141
+ end
142
+
191
143
  def Retry(error, *args)
192
144
  storage.save_retry!(error)
193
145
  Shipping::PipelineWorker.perform_in(
@@ -199,25 +151,26 @@ module DirtyPipeline
199
151
  end
200
152
 
201
153
  def ExpectedError(cause)
202
- self.error = cause
154
+ status.error = cause
203
155
  storage.fail_event!
204
- self.succeeded = false
156
+ status.succeeded = false
205
157
  end
206
158
 
207
- def Error(error)
159
+ def Exception(error)
208
160
  storage.save_exception!(error)
209
- self.error = error
210
- self.succeeded = false
161
+ status.error = error
162
+ status.succeeded = false
211
163
  end
212
164
 
213
165
  def Abort()
214
- self.succeeded = false
166
+ status.succeeded = false
215
167
  throw :abort_transaction, true
216
168
  end
217
169
 
218
170
  def Success(destination, output)
171
+ cache.clear
219
172
  storage.complete!(output, destination)
220
- self.succeeded = true
173
+ status.succeeded = true
221
174
  end
222
175
 
223
176
  def try_again?(max_attempts_count)
@@ -252,6 +205,7 @@ module DirtyPipeline
252
205
  destination, action, max_attempts_count =
253
206
  find_transition(transition).values_at(:to, :action, :attempts)
254
207
 
208
+ status.action_pool.unshift(action)
255
209
  subject.transaction(requires_new: true) do
256
210
  raise ActiveRecord::Rollback if catch(:abort_transaction) do
257
211
  yield(destination, action, *transition_args); nil
@@ -261,10 +215,16 @@ module DirtyPipeline
261
215
  if try_again?(max_attempts_count)
262
216
  Retry(error)
263
217
  else
264
- # FIXME: Somehow :error is a Hash, all the time
265
- Error(error)
218
+ Exception(error)
266
219
  end
267
220
  raise
221
+ ensure
222
+ if status.succeeded == false
223
+ status.action_pool.each do |reversable_action|
224
+ next unless reversable_action.respond_to?(:undo)
225
+ reversable_action.undo(self, *transition_args)
226
+ end
227
+ end
268
228
  end
269
229
  end
270
230
  end
@@ -56,9 +56,6 @@ module DirtyPipeline
56
56
  @transition_args = locker.storage.last_event["input"]
57
57
  end
58
58
 
59
- # RETRY MODE
60
- # if state is not RETRY_STATE - finish
61
- # if state is RETRY_STATE - start
62
59
  def skip_any_action?
63
60
  storage.status != Storage::RETRY_STATUS
64
61
  end
@@ -0,0 +1,71 @@
1
+ module DirtyPipeline
2
+ class Status < SimpleDelegator
3
+ attr_accessor :error, :succeeded, :action_pool
4
+ attr_reader :storage, :pipeline
5
+ def initialize(*)
6
+ super
7
+ @storage = __getobj__.storage
8
+ @action_pool = []
9
+ end
10
+
11
+ def wrap
12
+ return self if succeeded == false
13
+ self.succeeded = nil
14
+ yield
15
+ self
16
+ end
17
+
18
+ def success?
19
+ succeeded
20
+ end
21
+
22
+ def when_success(callback = nil)
23
+ return self unless success?
24
+ if block_given?
25
+ yield(self)
26
+ else
27
+ callback.call(self)
28
+ end
29
+ self
30
+ end
31
+
32
+ def when_failed(callback = nil)
33
+ return self unless storage.failed?
34
+ if block_given?
35
+ yield(self)
36
+ else
37
+ callback.call(self)
38
+ end
39
+ self
40
+ end
41
+
42
+ def errored?
43
+ return if succeeded.nil?
44
+ ready? && !succeeded
45
+ end
46
+
47
+ def when_error(callback = nil)
48
+ return self unless errored?
49
+ if block_given?
50
+ yield(self)
51
+ else
52
+ callback.call(self)
53
+ end
54
+ self
55
+ end
56
+
57
+ def ready?
58
+ storage.pipeline_status.nil?
59
+ end
60
+
61
+ def when_processing(callback = nil)
62
+ return self unless storage.processing?
63
+ if block_given?
64
+ yield(self)
65
+ else
66
+ callback.call(self)
67
+ end
68
+ self
69
+ end
70
+ end
71
+ end
@@ -17,7 +17,7 @@ module DirtyPipeline
17
17
  def init_store(store_field)
18
18
  self.store = subject.send(store_field).to_h
19
19
  clear! if store.empty?
20
- return if (store.keys & %w(status events errors state)).size == 4
20
+ return if (store.keys & %w(cache status events errors state)).size == 5
21
21
  raise InvalidPipelineStorage, store
22
22
  end
23
23
 
@@ -27,6 +27,7 @@ module DirtyPipeline
27
27
  "status" => nil,
28
28
  "pipeline_status" => nil,
29
29
  "state" => {},
30
+ "cache" => {},
30
31
  "events" => [],
31
32
  "errors" => [],
32
33
  )
@@ -99,6 +100,7 @@ module DirtyPipeline
99
100
 
100
101
  def commit_pipeline_status!(value = nil)
101
102
  self.pipeline_status = value
103
+ store["cache"].clear
102
104
  commit!
103
105
  end
104
106
  alias :reset_pipeline_status! :commit_pipeline_status!
@@ -15,11 +15,25 @@ module DirtyPipeline
15
15
  throw :success, result
16
16
  end
17
17
 
18
+ def self.undo(*args, **kwargs)
19
+ pipeline = args.shift
20
+ instance = new(pipeline, *args, **kwargs)
21
+ return unless instance.respond_to?(:undo)
22
+ instance.undo(pipeline.subject)
23
+ end
24
+
18
25
  def self.call(*args, **kwargs)
19
- subject = args.shift
20
- instance = new(*args, **kwargs)
21
- instance.compensate(subject) if instance.respond_to?(:compensate)
22
- instance.call(subject)
26
+ pipeline = args.shift
27
+ new(pipeline, *args, **kwargs).call(pipeline.subject)
28
+ end
29
+
30
+ attr_reader :pipeline
31
+ def initialize(pipeline, *, **)
32
+ @pipeline = pipeline
33
+ end
34
+
35
+ def fetch(key)
36
+ pipeline.cache.fetch(key) { pipeline.cache[key] = yield }
23
37
  end
24
38
  end
25
39
  end
@@ -1,3 +1,3 @@
1
1
  module DirtyPipeline
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -4,6 +4,7 @@ module DirtyPipeline
4
4
  require_relative "dirty_pipeline/locker.rb"
5
5
  require_relative "dirty_pipeline/storage.rb"
6
6
  require_relative "dirty_pipeline/transition.rb"
7
+ require_relative "dirty_pipeline/status.rb"
7
8
  require_relative "dirty_pipeline/base.rb"
8
9
 
9
10
  # This method should yield raw Redis connection
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dirty_pipeline
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Dolganov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-23 00:00:00.000000000 Z
11
+ date: 2018-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -73,6 +73,7 @@ files:
73
73
  - lib/dirty_pipeline.rb
74
74
  - lib/dirty_pipeline/base.rb
75
75
  - lib/dirty_pipeline/locker.rb
76
+ - lib/dirty_pipeline/status.rb
76
77
  - lib/dirty_pipeline/storage.rb
77
78
  - lib/dirty_pipeline/transition.rb
78
79
  - lib/dirty_pipeline/version.rb