dirty_pipeline 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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