dry-transaction 0.8.0 → 0.9.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 +4 -4
- data/Gemfile +3 -2
- data/Gemfile.lock +27 -12
- data/README.md +30 -0
- data/lib/dry/transaction.rb +26 -3
- data/lib/dry/transaction/{sequence.rb → api.rb} +25 -30
- data/lib/dry/transaction/dsl.rb +14 -7
- data/lib/dry/transaction/result_matcher.rb +4 -4
- data/lib/dry/transaction/step.rb +9 -6
- data/lib/dry/transaction/step_adapters.rb +1 -1
- data/lib/dry/transaction/step_adapters/map.rb +3 -3
- data/lib/dry/transaction/step_adapters/raw.rb +3 -3
- data/lib/dry/transaction/step_adapters/tee.rb +3 -3
- data/lib/dry/transaction/step_adapters/try.rb +3 -3
- data/lib/dry/transaction/step_definition.rb +23 -0
- data/lib/dry/transaction/step_failure.rb +8 -4
- data/lib/dry/transaction/version.rb +2 -2
- data/spec/examples.txt +72 -59
- data/spec/integration/custom_matcher_spec.rb +9 -3
- data/spec/integration/custom_step_adapters_spec.rb +2 -2
- data/spec/integration/passing_step_arguments_spec.rb +1 -1
- data/spec/integration/transaction_spec.rb +17 -3
- data/spec/spec_helper.rb +2 -7
- data/spec/unit/step_definition_spec.rb +32 -0
- data/spec/unit/step_spec.rb +71 -0
- data/spec/unit/{sequence_spec.rb → transaction_spec.rb} +37 -1
- metadata +11 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19a5598d0c611fa9cd909c25cd76fccee443f925
|
|
4
|
+
data.tar.gz: f7dd7277b815393f51b7eb8cac00db2d8011ab83
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3310c5583f9fc9c3d84dad54db9b71908227309e4325adc9f38d3a26aca64a699a66e75d0a3d05173beaf002d246fea7ed434300793ad1308bd6638fbb404505
|
|
7
|
+
data.tar.gz: 6dea0341d58c8da07a06bb1ec2fbb82ca793a47969f6f4acacf29dd445996910fdbcc9f096f0d286b4aabcef6343a946262f3b98197155059010294e3f9eb448
|
data/Gemfile
CHANGED
|
@@ -3,11 +3,12 @@ source "https://rubygems.org"
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
5
|
group :test do
|
|
6
|
-
gem "
|
|
6
|
+
gem "simplecov"
|
|
7
|
+
gem "codeclimate-test-reporter"
|
|
7
8
|
gem "byebug", platform: :mri
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
group :tools do
|
|
11
12
|
gem "pry"
|
|
12
|
-
gem "
|
|
13
|
+
gem "rubocop"
|
|
13
14
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
dry-transaction (0.
|
|
4
|
+
dry-transaction (0.9.0)
|
|
5
5
|
dry-container (>= 0.2.8)
|
|
6
6
|
dry-matcher (>= 0.5.0)
|
|
7
7
|
dry-monads (>= 0.0.1)
|
|
@@ -10,27 +10,34 @@ PATH
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: https://rubygems.org/
|
|
12
12
|
specs:
|
|
13
|
+
ast (2.3.0)
|
|
13
14
|
byebug (8.2.2)
|
|
14
15
|
codeclimate-test-reporter (0.5.0)
|
|
15
16
|
simplecov (>= 0.7.1, < 1.0.0)
|
|
16
17
|
coderay (1.1.1)
|
|
17
|
-
concurrent-ruby (1.0.
|
|
18
|
+
concurrent-ruby (1.0.2)
|
|
18
19
|
diff-lcs (1.2.5)
|
|
19
20
|
docile (1.1.5)
|
|
20
|
-
dry-configurable (0.
|
|
21
|
+
dry-configurable (0.4.0)
|
|
21
22
|
concurrent-ruby (~> 1.0)
|
|
22
|
-
dry-container (0.
|
|
23
|
+
dry-container (0.6.0)
|
|
23
24
|
concurrent-ruby (~> 1.0)
|
|
24
25
|
dry-configurable (~> 0.1, >= 0.1.3)
|
|
25
|
-
dry-
|
|
26
|
-
dry-
|
|
26
|
+
dry-equalizer (0.2.0)
|
|
27
|
+
dry-matcher (0.6.0)
|
|
28
|
+
dry-monads (0.2.0)
|
|
29
|
+
dry-equalizer
|
|
27
30
|
json (1.8.3)
|
|
28
31
|
method_source (0.8.2)
|
|
32
|
+
parser (2.3.1.4)
|
|
33
|
+
ast (~> 2.2)
|
|
34
|
+
powerpack (0.1.1)
|
|
29
35
|
pry (0.10.3)
|
|
30
36
|
coderay (~> 1.1.0)
|
|
31
37
|
method_source (~> 0.8.1)
|
|
32
38
|
slop (~> 3.4)
|
|
33
|
-
|
|
39
|
+
rainbow (2.1.0)
|
|
40
|
+
rake (11.2.2)
|
|
34
41
|
rspec (3.3.0)
|
|
35
42
|
rspec-core (~> 3.3.0)
|
|
36
43
|
rspec-expectations (~> 3.3.0)
|
|
@@ -44,12 +51,20 @@ GEM
|
|
|
44
51
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
45
52
|
rspec-support (~> 3.3.0)
|
|
46
53
|
rspec-support (3.3.0)
|
|
54
|
+
rubocop (0.43.0)
|
|
55
|
+
parser (>= 2.3.1.1, < 3.0)
|
|
56
|
+
powerpack (~> 0.1)
|
|
57
|
+
rainbow (>= 1.99.1, < 3.0)
|
|
58
|
+
ruby-progressbar (~> 1.7)
|
|
59
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
|
60
|
+
ruby-progressbar (1.8.1)
|
|
47
61
|
simplecov (0.10.0)
|
|
48
62
|
docile (~> 1.1.0)
|
|
49
63
|
json (~> 1.8)
|
|
50
64
|
simplecov-html (~> 0.10.0)
|
|
51
65
|
simplecov-html (0.10.0)
|
|
52
66
|
slop (3.6.0)
|
|
67
|
+
unicode-display_width (1.1.1)
|
|
53
68
|
wisper (1.6.1)
|
|
54
69
|
yard (0.8.7.6)
|
|
55
70
|
|
|
@@ -57,16 +72,16 @@ PLATFORMS
|
|
|
57
72
|
ruby
|
|
58
73
|
|
|
59
74
|
DEPENDENCIES
|
|
60
|
-
bundler (~> 1.
|
|
75
|
+
bundler (~> 1.13.1)
|
|
61
76
|
byebug
|
|
62
77
|
codeclimate-test-reporter
|
|
63
|
-
dry-container
|
|
64
78
|
dry-transaction!
|
|
65
79
|
pry
|
|
66
|
-
rake (~>
|
|
80
|
+
rake (~> 11.2.2)
|
|
67
81
|
rspec (~> 3.3.0)
|
|
68
|
-
|
|
82
|
+
rubocop
|
|
83
|
+
simplecov
|
|
69
84
|
yard
|
|
70
85
|
|
|
71
86
|
BUNDLED WITH
|
|
72
|
-
1.
|
|
87
|
+
1.13.2
|
data/README.md
CHANGED
|
@@ -13,11 +13,41 @@
|
|
|
13
13
|
|
|
14
14
|
dry-transaction is a business transaction DSL. It provides a simple way to define a complex business transaction that includes processing by many different objects.
|
|
15
15
|
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Add this line to your application's Gemfile:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem 'dry-transaction'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
And then execute:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
$ bundle
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or install it yourself as:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
$ gem install dry-transaction
|
|
34
|
+
```
|
|
35
|
+
|
|
16
36
|
## Links
|
|
17
37
|
|
|
18
38
|
* [Documentation](http://dry-rb.org/gems/dry-transaction)
|
|
19
39
|
* [API documentation](http://www.rubydoc.info/github/dry-rb/dry-transaction)
|
|
20
40
|
|
|
41
|
+
## Development
|
|
42
|
+
|
|
43
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run
|
|
44
|
+
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
|
45
|
+
prompt that will allow you to experiment.
|
|
46
|
+
|
|
47
|
+
## Contributing
|
|
48
|
+
|
|
49
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/dry-rb/dry-transaction>.
|
|
50
|
+
|
|
21
51
|
## License
|
|
22
52
|
|
|
23
53
|
Copyright © 2015-2016 [Icelab](http://icelab.com.au/). dry-transaction is free software, and may be redistributed under the terms specified in the [license](LICENSE.md).
|
data/lib/dry/transaction.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "dry/monads/either"
|
|
2
2
|
require "dry/transaction/version"
|
|
3
3
|
require "dry/transaction/dsl"
|
|
4
|
+
require "dry/transaction/api"
|
|
4
5
|
|
|
5
6
|
module Dry
|
|
6
7
|
# Define a business transaction.
|
|
@@ -10,10 +11,10 @@ module Dry
|
|
|
10
11
|
#
|
|
11
12
|
# The operations should be addressable via `#[]` in a container object that
|
|
12
13
|
# you pass when creating the transaction. The operations must respond to
|
|
13
|
-
# `#call(*args
|
|
14
|
+
# `#call(input, *args)`.
|
|
14
15
|
#
|
|
15
16
|
# Each operation will be called in the order it was specified in your
|
|
16
|
-
# transaction, with its output
|
|
17
|
+
# transaction, with its output passed as the input to the next operation.
|
|
17
18
|
# Operations will only be called if the previous step was a success.
|
|
18
19
|
#
|
|
19
20
|
# A step is successful when it returns a [dry-monads](dry-monads) `Right` object
|
|
@@ -47,10 +48,32 @@ module Dry
|
|
|
47
48
|
# @option options [#[]] :step_adapters (Dry::Transaction::StepAdapters) a custom container of step adapters
|
|
48
49
|
# @option options [Dry::Matcher] :matcher (Dry::Transaction::ResultMatcher) a custom matcher object for result matching block API
|
|
49
50
|
#
|
|
50
|
-
# @return [Dry::Transaction
|
|
51
|
+
# @return [Dry::Transaction] the transaction object
|
|
51
52
|
#
|
|
52
53
|
# @api public
|
|
53
54
|
def self.Transaction(options = {}, &block)
|
|
54
55
|
Transaction::DSL.new(options, &block).call
|
|
55
56
|
end
|
|
57
|
+
|
|
58
|
+
# This is the class that actually stores the transaction.
|
|
59
|
+
# To be precise, it stores a series of steps that make up a transaction and
|
|
60
|
+
# a matcher for handling the result of the transaction.
|
|
61
|
+
#
|
|
62
|
+
# Never instantiate this class directly, it is intended to be created through
|
|
63
|
+
# the provided DSL.
|
|
64
|
+
class Transaction
|
|
65
|
+
include API
|
|
66
|
+
|
|
67
|
+
# @api private
|
|
68
|
+
attr_reader :steps
|
|
69
|
+
|
|
70
|
+
# @api private
|
|
71
|
+
attr_reader :matcher
|
|
72
|
+
|
|
73
|
+
# @api private
|
|
74
|
+
def initialize(steps, matcher)
|
|
75
|
+
@steps = steps
|
|
76
|
+
@matcher = matcher
|
|
77
|
+
end
|
|
78
|
+
end
|
|
56
79
|
end
|
|
@@ -1,23 +1,8 @@
|
|
|
1
1
|
require "dry/monads/either"
|
|
2
|
-
require "dry/transaction/dsl"
|
|
3
2
|
|
|
4
3
|
module Dry
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
include Dry::Monads::Either::Mixin
|
|
8
|
-
|
|
9
|
-
# @api private
|
|
10
|
-
attr_reader :steps
|
|
11
|
-
|
|
12
|
-
# @api private
|
|
13
|
-
attr_reader :matcher
|
|
14
|
-
|
|
15
|
-
# @api private
|
|
16
|
-
def initialize(steps, matcher)
|
|
17
|
-
@steps = steps
|
|
18
|
-
@matcher = matcher
|
|
19
|
-
end
|
|
20
|
-
|
|
4
|
+
class Transaction
|
|
5
|
+
module API
|
|
21
6
|
# Run the transaction.
|
|
22
7
|
#
|
|
23
8
|
# Each operation will be called in the order it was specified, with its
|
|
@@ -52,14 +37,14 @@ module Dry
|
|
|
52
37
|
assert_options_satisfy_step_arity(options)
|
|
53
38
|
|
|
54
39
|
steps = steps_with_options_applied(options)
|
|
55
|
-
result = steps.inject(Right(input), :bind)
|
|
40
|
+
result = steps.inject(Dry::Monads.Right(input), :bind)
|
|
56
41
|
|
|
57
42
|
if block
|
|
58
43
|
matcher.(result, &block)
|
|
59
44
|
else
|
|
60
45
|
result.or { |step_failure|
|
|
61
46
|
# Unwrap the value from the StepFailure and return it directly
|
|
62
|
-
Left(step_failure.value)
|
|
47
|
+
Dry::Monads.Left(step_failure.value)
|
|
63
48
|
}
|
|
64
49
|
end
|
|
65
50
|
end
|
|
@@ -131,14 +116,14 @@ module Dry
|
|
|
131
116
|
# step :another
|
|
132
117
|
# end
|
|
133
118
|
#
|
|
134
|
-
# @param other [Dry::Transaction
|
|
119
|
+
# @param other [Dry::Transaction] the transaction to prepend.
|
|
135
120
|
# Optional if you will define a transaction inline via a block.
|
|
136
121
|
# @param options [Hash] the options hash for defining a transaction inline
|
|
137
122
|
# via a block. Optional if the transaction is passed directly as
|
|
138
123
|
# `other`.
|
|
139
124
|
# @option options [#[]] :container the operations container
|
|
140
125
|
#
|
|
141
|
-
# @return [Dry::Transaction
|
|
126
|
+
# @return [Dry::Transaction] the modified transaction object
|
|
142
127
|
#
|
|
143
128
|
# @api public
|
|
144
129
|
def prepend(other = nil, **options, &block)
|
|
@@ -172,14 +157,14 @@ module Dry
|
|
|
172
157
|
# step :another
|
|
173
158
|
# end
|
|
174
159
|
#
|
|
175
|
-
# @param other [Dry::Transaction
|
|
160
|
+
# @param other [Dry::Transaction] the transaction to append.
|
|
176
161
|
# Optional if you will define a transaction inline via a block.
|
|
177
162
|
# @param options [Hash] the options hash for defining a transaction inline
|
|
178
163
|
# via a block. Optional if the transaction is passed directly as
|
|
179
164
|
# `other`.
|
|
180
165
|
# @option options [#[]] :container the operations container
|
|
181
166
|
#
|
|
182
|
-
# @return [Dry::Transaction
|
|
167
|
+
# @return [Dry::Transaction] the modified transaction object
|
|
183
168
|
#
|
|
184
169
|
# @api public
|
|
185
170
|
def append(other = nil, **options, &block)
|
|
@@ -215,7 +200,7 @@ module Dry
|
|
|
215
200
|
# step :another
|
|
216
201
|
# end
|
|
217
202
|
#
|
|
218
|
-
# @param other [Dry::Transaction
|
|
203
|
+
# @param other [Dry::Transaction] the transaction to append.
|
|
219
204
|
# Optional if you will define a transaction inline via a block.
|
|
220
205
|
# @param before [Symbol] the name of the step before which the
|
|
221
206
|
# transaction should be inserted (provide either this or `after`)
|
|
@@ -226,17 +211,19 @@ module Dry
|
|
|
226
211
|
# as `other`.
|
|
227
212
|
# @option options [#[]] :container the operations container
|
|
228
213
|
#
|
|
229
|
-
# @return [Dry::Transaction
|
|
214
|
+
# @return [Dry::Transaction] the modified transaction object
|
|
230
215
|
#
|
|
231
216
|
# @api public
|
|
232
217
|
def insert(other = nil, before: nil, after: nil, **options, &block)
|
|
233
218
|
insertion_step = before || after
|
|
234
|
-
|
|
219
|
+
match_insertion_step = proc { |step| step.step_name == insertion_step }
|
|
220
|
+
|
|
221
|
+
unless steps.any?(&match_insertion_step)
|
|
235
222
|
raise ArgumentError, "+#{insertion_step}+ is not a valid step name"
|
|
236
223
|
end
|
|
237
224
|
|
|
238
225
|
other = accept_or_build_transaction(other, **options, &block)
|
|
239
|
-
index = steps.index
|
|
226
|
+
index = steps.index(&match_insertion_step) + (after ? 1 : 0)
|
|
240
227
|
|
|
241
228
|
self.class.new(steps.dup.insert(index, *other.steps), matcher)
|
|
242
229
|
end
|
|
@@ -256,7 +243,7 @@ module Dry
|
|
|
256
243
|
# @param step [Symbol] the names of a step to remove
|
|
257
244
|
# @param ... [Symbol] more names of steps to remove
|
|
258
245
|
#
|
|
259
|
-
# @return [Dry::Transaction
|
|
246
|
+
# @return [Dry::Transaction] the modified transaction object
|
|
260
247
|
#
|
|
261
248
|
# @api public
|
|
262
249
|
def remove(*steps_to_remove)
|
|
@@ -265,14 +252,16 @@ module Dry
|
|
|
265
252
|
|
|
266
253
|
private
|
|
267
254
|
|
|
255
|
+
# @param options [Hash] step arguments keyed by step name
|
|
268
256
|
def assert_valid_options(options)
|
|
269
257
|
options.each_key do |step_name|
|
|
270
|
-
unless steps.
|
|
258
|
+
unless steps.any? { |step| step.step_name == step_name }
|
|
271
259
|
raise ArgumentError, "+#{step_name}+ is not a valid step name"
|
|
272
260
|
end
|
|
273
261
|
end
|
|
274
262
|
end
|
|
275
263
|
|
|
264
|
+
# @param options [Hash] step arguments keyed by step name
|
|
276
265
|
def assert_options_satisfy_step_arity(options)
|
|
277
266
|
steps.each do |step|
|
|
278
267
|
args_required = step.arity >= 0 ? step.arity : ~step.arity
|
|
@@ -284,6 +273,7 @@ module Dry
|
|
|
284
273
|
end
|
|
285
274
|
end
|
|
286
275
|
|
|
276
|
+
# @param options [Hash] step arguments keyed by step name
|
|
287
277
|
def steps_with_options_applied(options)
|
|
288
278
|
steps.map { |step|
|
|
289
279
|
if (args = options[step.step_name])
|
|
@@ -299,7 +289,12 @@ module Dry
|
|
|
299
289
|
raise ArgumentError, "a transaction must be provided or defined in a block"
|
|
300
290
|
end
|
|
301
291
|
|
|
302
|
-
other_transaction
|
|
292
|
+
if other_transaction
|
|
293
|
+
other_transaction
|
|
294
|
+
else
|
|
295
|
+
require "dry/transaction/dsl"
|
|
296
|
+
DSL.new(**options, &block).call
|
|
297
|
+
end
|
|
303
298
|
end
|
|
304
299
|
end
|
|
305
300
|
end
|
data/lib/dry/transaction/dsl.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
require "dry/transaction/result_matcher"
|
|
2
2
|
require "dry/transaction/step"
|
|
3
3
|
require "dry/transaction/step_adapters"
|
|
4
|
-
require "dry/transaction/
|
|
4
|
+
require "dry/transaction/step_definition"
|
|
5
5
|
|
|
6
6
|
module Dry
|
|
7
|
-
|
|
7
|
+
class Transaction
|
|
8
8
|
# @api private
|
|
9
9
|
class DSL < BasicObject
|
|
10
10
|
attr_reader :container
|
|
@@ -25,20 +25,27 @@ module Dry
|
|
|
25
25
|
step_adapters.key?(method_name)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def method_missing(method_name, *args)
|
|
28
|
+
def method_missing(method_name, *args, &block)
|
|
29
29
|
return super unless step_adapters.key?(method_name)
|
|
30
30
|
|
|
31
31
|
step_adapter = step_adapters[method_name]
|
|
32
32
|
step_name = args.first
|
|
33
33
|
options = args.last.is_a?(::Hash) ? args.last : {}
|
|
34
|
-
|
|
35
|
-
operation = container[operation_name]
|
|
34
|
+
with = options.delete(:with)
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
if with.respond_to?(:call)
|
|
37
|
+
operation_name = step_name
|
|
38
|
+
operation = StepDefinition.new(container, &with)
|
|
39
|
+
else
|
|
40
|
+
operation_name = with || step_name
|
|
41
|
+
operation = container[operation_name]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
steps << Step.new(step_adapter, step_name, operation_name, operation, options, &block)
|
|
38
45
|
end
|
|
39
46
|
|
|
40
47
|
def call
|
|
41
|
-
|
|
48
|
+
Transaction.new(steps, matcher)
|
|
42
49
|
end
|
|
43
50
|
end
|
|
44
51
|
end
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
require "dry-matcher"
|
|
2
2
|
|
|
3
3
|
module Dry
|
|
4
|
-
|
|
4
|
+
class Transaction
|
|
5
5
|
ResultMatcher = Dry::Matcher.new(
|
|
6
6
|
success: Dry::Matcher::Case.new(
|
|
7
7
|
match: -> result { result.right? },
|
|
8
|
-
resolve: -> result { result.value }
|
|
8
|
+
resolve: -> result { result.value }
|
|
9
9
|
),
|
|
10
10
|
failure: Dry::Matcher::Case.new(
|
|
11
11
|
match: -> result, step_name = nil {
|
|
12
12
|
if step_name
|
|
13
|
-
result.left? && result.value.step_name == step_name
|
|
13
|
+
result.left? && result.value.step.step_name == step_name
|
|
14
14
|
else
|
|
15
15
|
result.left?
|
|
16
16
|
end
|
|
17
17
|
},
|
|
18
|
-
resolve: -> result { result.value.value }
|
|
18
|
+
resolve: -> result { result.value.value }
|
|
19
19
|
)
|
|
20
20
|
)
|
|
21
21
|
end
|
data/lib/dry/transaction/step.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
require "dry/monads/either"
|
|
1
2
|
require "wisper"
|
|
2
3
|
require "dry/transaction/step_failure"
|
|
3
4
|
|
|
4
5
|
module Dry
|
|
5
|
-
|
|
6
|
+
class Transaction
|
|
6
7
|
# @api private
|
|
7
8
|
class Step
|
|
8
9
|
include Wisper::Publisher
|
|
@@ -13,31 +14,33 @@ module Dry
|
|
|
13
14
|
attr_reader :operation_name
|
|
14
15
|
attr_reader :operation
|
|
15
16
|
attr_reader :options
|
|
17
|
+
attr_reader :block
|
|
16
18
|
attr_reader :call_args
|
|
17
19
|
|
|
18
|
-
def initialize(step_adapter, step_name, operation_name, operation, options, call_args = [])
|
|
20
|
+
def initialize(step_adapter, step_name, operation_name, operation, options, call_args = [], &block)
|
|
19
21
|
@step_adapter = step_adapter
|
|
20
22
|
@step_name = step_name
|
|
21
23
|
@operation_name = operation_name
|
|
22
24
|
@operation = operation
|
|
23
25
|
@options = options
|
|
26
|
+
@block = block
|
|
24
27
|
@call_args = call_args
|
|
25
28
|
end
|
|
26
29
|
|
|
27
30
|
def with_call_args(*call_args)
|
|
28
|
-
self.class.new(step_adapter, step_name, operation_name, operation, options, call_args)
|
|
31
|
+
self.class.new(step_adapter, step_name, operation_name, operation, options, call_args, &block)
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
def call(input)
|
|
32
|
-
args =
|
|
33
|
-
result = step_adapter.call(self, *args)
|
|
35
|
+
args = [input] + call_args
|
|
36
|
+
result = step_adapter.call(self, *args, &block)
|
|
34
37
|
|
|
35
38
|
result.fmap { |value|
|
|
36
39
|
broadcast :"#{step_name}_success", value
|
|
37
40
|
value
|
|
38
41
|
}.or { |value|
|
|
39
42
|
broadcast :"#{step_name}_failure", *args, value
|
|
40
|
-
Left(StepFailure.new(
|
|
43
|
+
Left(StepFailure.new(self, value))
|
|
41
44
|
}
|
|
42
45
|
end
|
|
43
46
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module Dry
|
|
2
|
-
|
|
2
|
+
class Transaction
|
|
3
3
|
class StepAdapters
|
|
4
4
|
# @api private
|
|
5
5
|
class Map
|
|
6
6
|
include Dry::Monads::Either::Mixin
|
|
7
7
|
|
|
8
|
-
def call(step, *args
|
|
9
|
-
Right(step.operation.call(*args
|
|
8
|
+
def call(step, input, *args)
|
|
9
|
+
Right(step.operation.call(input, *args))
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
require "dry/monads/either"
|
|
2
2
|
|
|
3
3
|
module Dry
|
|
4
|
-
|
|
4
|
+
class Transaction
|
|
5
5
|
class StepAdapters
|
|
6
6
|
# @api private
|
|
7
7
|
class Raw
|
|
8
8
|
include Dry::Monads::Either::Mixin
|
|
9
9
|
|
|
10
|
-
def call(step, *args
|
|
11
|
-
result = step.operation.call(*args
|
|
10
|
+
def call(step, input, *args)
|
|
11
|
+
result = step.operation.call(input, *args)
|
|
12
12
|
|
|
13
13
|
unless result.is_a?(Dry::Monads::Either)
|
|
14
14
|
raise ArgumentError, "step +#{step.step_name}+ must return an Either object"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module Dry
|
|
2
|
-
|
|
2
|
+
class Transaction
|
|
3
3
|
class StepAdapters
|
|
4
4
|
# @api private
|
|
5
5
|
class Tee
|
|
6
6
|
include Dry::Monads::Either::Mixin
|
|
7
7
|
|
|
8
|
-
def call(step, *args
|
|
9
|
-
step.operation.call(*args
|
|
8
|
+
def call(step, input, *args)
|
|
9
|
+
step.operation.call(input, *args)
|
|
10
10
|
Right(input)
|
|
11
11
|
end
|
|
12
12
|
end
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
module Dry
|
|
2
|
-
|
|
2
|
+
class Transaction
|
|
3
3
|
class StepAdapters
|
|
4
4
|
# @api private
|
|
5
5
|
class Try
|
|
6
6
|
include Dry::Monads::Either::Mixin
|
|
7
7
|
|
|
8
|
-
def call(step, *args
|
|
8
|
+
def call(step, input, *args)
|
|
9
9
|
unless step.options[:catch]
|
|
10
10
|
raise ArgumentError, "+try+ steps require one or more exception classes provided via +catch:+"
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
Right(step.operation.call(*args
|
|
13
|
+
Right(step.operation.call(input, *args))
|
|
14
14
|
rescue *Array(step.options[:catch]) => e
|
|
15
15
|
e = step.options[:raise].new(e.message) if step.options[:raise]
|
|
16
16
|
Left(e)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Dry
|
|
2
|
+
class Transaction
|
|
3
|
+
# @api private
|
|
4
|
+
class StepDefinition
|
|
5
|
+
include Dry::Monads::Either::Mixin
|
|
6
|
+
|
|
7
|
+
def initialize(container, &block)
|
|
8
|
+
@container = container
|
|
9
|
+
@block = block
|
|
10
|
+
freeze
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(*args)
|
|
14
|
+
instance_exec(*args, &block)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
attr_reader :block
|
|
20
|
+
attr_reader :container
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
module Dry
|
|
2
|
-
|
|
2
|
+
class Transaction
|
|
3
|
+
# A wrapper for storing together the step that failed
|
|
4
|
+
# and value describing the failure.
|
|
3
5
|
class StepFailure
|
|
4
|
-
attr_reader :
|
|
6
|
+
attr_reader :step
|
|
5
7
|
attr_reader :value
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
# @param step [Step]
|
|
10
|
+
# @param value [Object]
|
|
11
|
+
def initialize(step, value)
|
|
12
|
+
@step = step
|
|
9
13
|
@value = value
|
|
10
14
|
end
|
|
11
15
|
end
|
data/spec/examples.txt
CHANGED
|
@@ -1,64 +1,77 @@
|
|
|
1
1
|
example_id | status | run_time |
|
|
2
2
|
-------------------------------------------------------- | ------ | --------------- |
|
|
3
|
-
./spec/integration/custom_matcher_spec.rb[1:1] | passed | 0.
|
|
4
|
-
./spec/integration/custom_step_adapters_spec.rb[1:1] | passed | 0.
|
|
5
|
-
./spec/integration/passing_step_arguments_spec.rb[1:1:1] | passed | 0.
|
|
6
|
-
./spec/integration/passing_step_arguments_spec.rb[1:2:1] | passed | 0.
|
|
7
|
-
./spec/integration/passing_step_arguments_spec.rb[1:3:1] | passed | 0.
|
|
8
|
-
./spec/integration/publishing_step_events_spec.rb[1:1:1] | passed | 0.
|
|
9
|
-
./spec/integration/publishing_step_events_spec.rb[1:1:2] | passed | 0.
|
|
10
|
-
./spec/integration/publishing_step_events_spec.rb[1:2:1] | passed | 0.
|
|
11
|
-
./spec/integration/publishing_step_events_spec.rb[1:2:2] | passed | 0.
|
|
12
|
-
./spec/integration/transaction_spec.rb[1:1:1] | passed | 0.
|
|
13
|
-
./spec/integration/transaction_spec.rb[1:1:2] | passed | 0.
|
|
14
|
-
./spec/integration/transaction_spec.rb[1:1:3] | passed | 0.
|
|
15
|
-
./spec/integration/transaction_spec.rb[1:1:4] | passed | 0.
|
|
16
|
-
./spec/integration/transaction_spec.rb[1:1:5] | passed | 0.
|
|
17
|
-
./spec/integration/transaction_spec.rb[1:2:1] | passed | 0.
|
|
18
|
-
./spec/integration/transaction_spec.rb[1:2:2] | passed | 0.
|
|
19
|
-
./spec/integration/transaction_spec.rb[1:2:3] | passed | 0.
|
|
20
|
-
./spec/integration/transaction_spec.rb[1:2:4] | passed | 0.
|
|
21
|
-
./spec/integration/transaction_spec.rb[1:2:5] | passed | 0.
|
|
22
|
-
./spec/integration/transaction_spec.rb[1:2:6] | passed | 0.
|
|
23
|
-
./spec/integration/transaction_spec.rb[1:3:1] | passed | 0.
|
|
24
|
-
./spec/integration/transaction_spec.rb[1:3:2] | passed | 0.
|
|
25
|
-
./spec/integration/transaction_spec.rb[1:3:3] | passed | 0.
|
|
26
|
-
./spec/integration/transaction_spec.rb[1:3:4] | passed | 0.
|
|
27
|
-
./spec/integration/transaction_spec.rb[1:3:5] | passed | 0.
|
|
28
|
-
./spec/integration/transaction_spec.rb[1:4:1] | passed | 0.
|
|
29
|
-
./spec/unit/
|
|
30
|
-
./spec/unit/sequence_spec.rb[1:1:2] | passed | 0.00016 seconds |
|
|
31
|
-
./spec/unit/sequence_spec.rb[1:1:3] | passed | 0.00013 seconds |
|
|
32
|
-
./spec/unit/sequence_spec.rb[1:1:4] | passed | 0.00015 seconds |
|
|
33
|
-
./spec/unit/sequence_spec.rb[1:2:1] | passed | 0.00018 seconds |
|
|
34
|
-
./spec/unit/sequence_spec.rb[1:2:2] | passed | 0.00016 seconds |
|
|
35
|
-
./spec/unit/sequence_spec.rb[1:2:3] | passed | 0.00015 seconds |
|
|
36
|
-
./spec/unit/sequence_spec.rb[1:2:4] | passed | 0.0002 seconds |
|
|
37
|
-
./spec/unit/sequence_spec.rb[1:3:1] | passed | 0.00017 seconds |
|
|
38
|
-
./spec/unit/sequence_spec.rb[1:3:2] | passed | 0.00017 seconds |
|
|
39
|
-
./spec/unit/sequence_spec.rb[1:4:1] | passed | 0.00022 seconds |
|
|
40
|
-
./spec/unit/sequence_spec.rb[1:4:2] | passed | 0.00019 seconds |
|
|
41
|
-
./spec/unit/sequence_spec.rb[1:4:3] | passed | 0.00013 seconds |
|
|
42
|
-
./spec/unit/sequence_spec.rb[1:4:4] | passed | 0.00014 seconds |
|
|
43
|
-
./spec/unit/sequence_spec.rb[1:4:5:1] | passed | 0.0002 seconds |
|
|
44
|
-
./spec/unit/sequence_spec.rb[1:4:5:2] | passed | 0.0002 seconds |
|
|
45
|
-
./spec/unit/sequence_spec.rb[1:4:6:1] | passed | 0.00022 seconds |
|
|
46
|
-
./spec/unit/sequence_spec.rb[1:4:6:2] | passed | 0.00018 seconds |
|
|
47
|
-
./spec/unit/step_adapters/map_spec.rb[1:1:1] | passed | 0.00011 seconds |
|
|
3
|
+
./spec/integration/custom_matcher_spec.rb[1:1] | passed | 0.00027 seconds |
|
|
4
|
+
./spec/integration/custom_step_adapters_spec.rb[1:1] | passed | 0.00042 seconds |
|
|
5
|
+
./spec/integration/passing_step_arguments_spec.rb[1:1:1] | passed | 0.00105 seconds |
|
|
6
|
+
./spec/integration/passing_step_arguments_spec.rb[1:2:1] | passed | 0.00122 seconds |
|
|
7
|
+
./spec/integration/passing_step_arguments_spec.rb[1:3:1] | passed | 0.00018 seconds |
|
|
8
|
+
./spec/integration/publishing_step_events_spec.rb[1:1:1] | passed | 0.00049 seconds |
|
|
9
|
+
./spec/integration/publishing_step_events_spec.rb[1:1:2] | passed | 0.00135 seconds |
|
|
10
|
+
./spec/integration/publishing_step_events_spec.rb[1:2:1] | passed | 0.0007 seconds |
|
|
11
|
+
./spec/integration/publishing_step_events_spec.rb[1:2:2] | passed | 0.00051 seconds |
|
|
12
|
+
./spec/integration/transaction_spec.rb[1:1:1] | passed | 0.00105 seconds |
|
|
13
|
+
./spec/integration/transaction_spec.rb[1:1:2] | passed | 0.00022 seconds |
|
|
14
|
+
./spec/integration/transaction_spec.rb[1:1:3] | passed | 0.00025 seconds |
|
|
15
|
+
./spec/integration/transaction_spec.rb[1:1:4] | passed | 0.00034 seconds |
|
|
16
|
+
./spec/integration/transaction_spec.rb[1:1:5] | passed | 0.00025 seconds |
|
|
17
|
+
./spec/integration/transaction_spec.rb[1:2:1] | passed | 0.00023 seconds |
|
|
18
|
+
./spec/integration/transaction_spec.rb[1:2:2] | passed | 0.00023 seconds |
|
|
19
|
+
./spec/integration/transaction_spec.rb[1:2:3] | passed | 0.00023 seconds |
|
|
20
|
+
./spec/integration/transaction_spec.rb[1:2:4] | passed | 0.00022 seconds |
|
|
21
|
+
./spec/integration/transaction_spec.rb[1:2:5] | passed | 0.00028 seconds |
|
|
22
|
+
./spec/integration/transaction_spec.rb[1:2:6] | passed | 0.00025 seconds |
|
|
23
|
+
./spec/integration/transaction_spec.rb[1:3:1] | passed | 0.00021 seconds |
|
|
24
|
+
./spec/integration/transaction_spec.rb[1:3:2] | passed | 0.00025 seconds |
|
|
25
|
+
./spec/integration/transaction_spec.rb[1:3:3] | passed | 0.00021 seconds |
|
|
26
|
+
./spec/integration/transaction_spec.rb[1:3:4] | passed | 0.00019 seconds |
|
|
27
|
+
./spec/integration/transaction_spec.rb[1:3:5] | passed | 0.00023 seconds |
|
|
28
|
+
./spec/integration/transaction_spec.rb[1:4:1] | passed | 0.00028 seconds |
|
|
29
|
+
./spec/unit/step_adapters/map_spec.rb[1:1:1] | passed | 0.00009 seconds |
|
|
48
30
|
./spec/unit/step_adapters/map_spec.rb[1:1:2] | passed | 0.0001 seconds |
|
|
49
|
-
./spec/unit/step_adapters/raw_spec.rb[1:1:1:1] | passed | 0.
|
|
50
|
-
./spec/unit/step_adapters/raw_spec.rb[1:1:2:1] | passed | 0.
|
|
51
|
-
./spec/unit/step_adapters/raw_spec.rb[1:1:2:2] | passed | 0.
|
|
52
|
-
./spec/unit/step_adapters/raw_spec.rb[1:1:3:1] | passed | 0.
|
|
53
|
-
./spec/unit/step_adapters/raw_spec.rb[1:1:3:2] | passed | 0.
|
|
54
|
-
./spec/unit/step_adapters/tee_spec.rb[1:1:1] | passed | 0.
|
|
55
|
-
./spec/unit/step_adapters/tee_spec.rb[1:1:2] | passed | 0.
|
|
56
|
-
./spec/unit/step_adapters/try_spec.rb[1:1:1:1] | passed | 0.
|
|
57
|
-
./spec/unit/step_adapters/try_spec.rb[1:1:2:1:1] | passed | 0.
|
|
31
|
+
./spec/unit/step_adapters/raw_spec.rb[1:1:1:1] | passed | 0.00015 seconds |
|
|
32
|
+
./spec/unit/step_adapters/raw_spec.rb[1:1:2:1] | passed | 0.00012 seconds |
|
|
33
|
+
./spec/unit/step_adapters/raw_spec.rb[1:1:2:2] | passed | 0.0001 seconds |
|
|
34
|
+
./spec/unit/step_adapters/raw_spec.rb[1:1:3:1] | passed | 0.00011 seconds |
|
|
35
|
+
./spec/unit/step_adapters/raw_spec.rb[1:1:3:2] | passed | 0.0006 seconds |
|
|
36
|
+
./spec/unit/step_adapters/tee_spec.rb[1:1:1] | passed | 0.00016 seconds |
|
|
37
|
+
./spec/unit/step_adapters/tee_spec.rb[1:1:2] | passed | 0.00011 seconds |
|
|
38
|
+
./spec/unit/step_adapters/try_spec.rb[1:1:1:1] | passed | 0.00018 seconds |
|
|
39
|
+
./spec/unit/step_adapters/try_spec.rb[1:1:2:1:1] | passed | 0.00014 seconds |
|
|
58
40
|
./spec/unit/step_adapters/try_spec.rb[1:1:2:1:2] | passed | 0.00016 seconds |
|
|
59
41
|
./spec/unit/step_adapters/try_spec.rb[1:1:2:1:3:1] | passed | 0.00015 seconds |
|
|
60
|
-
./spec/unit/step_adapters/try_spec.rb[1:1:2:1:3:2] | passed | 0.
|
|
61
|
-
./spec/unit/step_adapters/try_spec.rb[1:1:2:2:1] | passed | 0.
|
|
62
|
-
./spec/unit/step_adapters/try_spec.rb[1:1:2:2:2] | passed | 0.
|
|
63
|
-
./spec/unit/step_adapters/try_spec.rb[1:1:2:2:3:1] | passed | 0.
|
|
64
|
-
./spec/unit/step_adapters/try_spec.rb[1:1:2:2:3:2] | passed | 0.
|
|
42
|
+
./spec/unit/step_adapters/try_spec.rb[1:1:2:1:3:2] | passed | 0.00016 seconds |
|
|
43
|
+
./spec/unit/step_adapters/try_spec.rb[1:1:2:2:1] | passed | 0.00013 seconds |
|
|
44
|
+
./spec/unit/step_adapters/try_spec.rb[1:1:2:2:2] | passed | 0.00015 seconds |
|
|
45
|
+
./spec/unit/step_adapters/try_spec.rb[1:1:2:2:3:1] | passed | 0.00014 seconds |
|
|
46
|
+
./spec/unit/step_adapters/try_spec.rb[1:1:2:2:3:2] | passed | 0.00014 seconds |
|
|
47
|
+
./spec/unit/step_definition_spec.rb[1:1] | passed | 0.00015 seconds |
|
|
48
|
+
./spec/unit/step_definition_spec.rb[1:2:1] | passed | 0.00013 seconds |
|
|
49
|
+
./spec/unit/step_definition_spec.rb[1:3:1] | passed | 0.00013 seconds |
|
|
50
|
+
./spec/unit/step_definition_spec.rb[1:3:2] | passed | 0.00013 seconds |
|
|
51
|
+
./spec/unit/step_spec.rb[1:1:1:1] | passed | 0.00017 seconds |
|
|
52
|
+
./spec/unit/step_spec.rb[1:1:1:2] | passed | 0.00042 seconds |
|
|
53
|
+
./spec/unit/step_spec.rb[1:1:2:1] | passed | 0.00144 seconds |
|
|
54
|
+
./spec/unit/step_spec.rb[1:1:2:2] | passed | 0.00153 seconds |
|
|
55
|
+
./spec/unit/step_spec.rb[1:1:2:3] | passed | 0.00641 seconds |
|
|
56
|
+
./spec/unit/step_spec.rb[1:2:1:1] | passed | 0.00016 seconds |
|
|
57
|
+
./spec/unit/step_spec.rb[1:2:2:1] | passed | 0.00013 seconds |
|
|
58
|
+
./spec/unit/transaction_spec.rb[1:1:1] | passed | 0.00024 seconds |
|
|
59
|
+
./spec/unit/transaction_spec.rb[1:1:2] | passed | 0.00019 seconds |
|
|
60
|
+
./spec/unit/transaction_spec.rb[1:1:3] | passed | 0.00016 seconds |
|
|
61
|
+
./spec/unit/transaction_spec.rb[1:1:4] | passed | 0.00019 seconds |
|
|
62
|
+
./spec/unit/transaction_spec.rb[1:2:1] | passed | 0.00016 seconds |
|
|
63
|
+
./spec/unit/transaction_spec.rb[1:2:2] | passed | 0.00018 seconds |
|
|
64
|
+
./spec/unit/transaction_spec.rb[1:2:3] | passed | 0.00015 seconds |
|
|
65
|
+
./spec/unit/transaction_spec.rb[1:2:4] | passed | 0.00016 seconds |
|
|
66
|
+
./spec/unit/transaction_spec.rb[1:3:1] | passed | 0.00015 seconds |
|
|
67
|
+
./spec/unit/transaction_spec.rb[1:3:2] | passed | 0.0002 seconds |
|
|
68
|
+
./spec/unit/transaction_spec.rb[1:4:1] | passed | 0.00028 seconds |
|
|
69
|
+
./spec/unit/transaction_spec.rb[1:4:2] | passed | 0.00019 seconds |
|
|
70
|
+
./spec/unit/transaction_spec.rb[1:4:3] | passed | 0.00013 seconds |
|
|
71
|
+
./spec/unit/transaction_spec.rb[1:4:4] | passed | 0.00014 seconds |
|
|
72
|
+
./spec/unit/transaction_spec.rb[1:4:5:1] | passed | 0.00023 seconds |
|
|
73
|
+
./spec/unit/transaction_spec.rb[1:4:5:2] | passed | 0.00018 seconds |
|
|
74
|
+
./spec/unit/transaction_spec.rb[1:4:6:1] | passed | 0.0002 seconds |
|
|
75
|
+
./spec/unit/transaction_spec.rb[1:4:6:2] | passed | 0.00022 seconds |
|
|
76
|
+
./spec/unit/transaction_spec.rb[1:4:7:1] | passed | 0.00019 seconds |
|
|
77
|
+
./spec/unit/transaction_spec.rb[1:4:7:2] | passed | 0.0002 seconds |
|
|
@@ -5,7 +5,7 @@ RSpec.describe "Custom matcher" do
|
|
|
5
5
|
let(:transaction) {
|
|
6
6
|
Dry.Transaction(container: container, matcher: Test::CustomMatcher) do
|
|
7
7
|
step :process
|
|
8
|
-
step :validate
|
|
8
|
+
step :validate, failure: :bad_value
|
|
9
9
|
step :persist
|
|
10
10
|
end
|
|
11
11
|
}
|
|
@@ -29,7 +29,13 @@ RSpec.describe "Custom matcher" do
|
|
|
29
29
|
resolve: -> result { result.value }
|
|
30
30
|
),
|
|
31
31
|
nup: Dry::Matcher::Case.new(
|
|
32
|
-
match: -> result
|
|
32
|
+
match: -> result, failure = nil {
|
|
33
|
+
if failure
|
|
34
|
+
result.left? && result.value.step.options[:failure] == failure
|
|
35
|
+
else
|
|
36
|
+
result.left?
|
|
37
|
+
end
|
|
38
|
+
},
|
|
33
39
|
resolve: -> result { result.value.value }
|
|
34
40
|
)
|
|
35
41
|
)
|
|
@@ -39,7 +45,7 @@ RSpec.describe "Custom matcher" do
|
|
|
39
45
|
it "supports a custom matcher" do
|
|
40
46
|
matches = -> m {
|
|
41
47
|
m.yep { |v| "Yep! #{v[:email]}" }
|
|
42
|
-
m.nup { |v| "Nup. #{v.to_s}" }
|
|
48
|
+
m.nup(:bad_value) { |v| "Nup. #{v.to_s}" }
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
input = {"name" => "Jane", "email" => "jane@doe.com"}
|
|
@@ -23,8 +23,8 @@ RSpec.describe "Custom step adapters" do
|
|
|
23
23
|
class CustomStepAdapters < Dry::Transaction::StepAdapters
|
|
24
24
|
extend Dry::Monads::Either::Mixin
|
|
25
25
|
|
|
26
|
-
register :enqueue, -> step, *args
|
|
27
|
-
Test::QUEUE << step.operation.call(*args
|
|
26
|
+
register :enqueue, -> step, input, *args {
|
|
27
|
+
Test::QUEUE << step.operation.call(input, *args)
|
|
28
28
|
Right(input)
|
|
29
29
|
}
|
|
30
30
|
end
|
|
@@ -12,7 +12,7 @@ RSpec.describe "Passing additional arguments to step operations" do
|
|
|
12
12
|
let(:container) {
|
|
13
13
|
{
|
|
14
14
|
process: -> input { {name: input["name"], email: input["email"]} },
|
|
15
|
-
validate: ->
|
|
15
|
+
validate: -> input, allowed { !input[:email].include?(allowed) ? raise(Test::NotValidError, "email not allowed") : input },
|
|
16
16
|
persist: -> input { Test::DB << input and true }
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -3,7 +3,13 @@ RSpec.describe "Transactions" do
|
|
|
3
3
|
Dry.Transaction(container: container) do
|
|
4
4
|
map :process
|
|
5
5
|
step :verify
|
|
6
|
-
try :validate,
|
|
6
|
+
try :validate, with: -> input {
|
|
7
|
+
if input[:email].nil?
|
|
8
|
+
raise(container[:invalid_error], "email required")
|
|
9
|
+
else
|
|
10
|
+
input
|
|
11
|
+
end
|
|
12
|
+
}, catch: Test::NotValidError
|
|
7
13
|
tee :persist
|
|
8
14
|
end
|
|
9
15
|
}
|
|
@@ -12,8 +18,8 @@ RSpec.describe "Transactions" do
|
|
|
12
18
|
{
|
|
13
19
|
process: -> input { {name: input["name"], email: input["email"]} },
|
|
14
20
|
verify: -> input { Right(input) },
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
persist: -> input { Test::DB << input and true },
|
|
22
|
+
invalid_error: Test::NotValidError
|
|
17
23
|
}
|
|
18
24
|
}
|
|
19
25
|
|
|
@@ -53,6 +59,8 @@ RSpec.describe "Transactions" do
|
|
|
53
59
|
m.success do |value|
|
|
54
60
|
results << "success for #{value[:email]}"
|
|
55
61
|
end
|
|
62
|
+
|
|
63
|
+
m.failure { }
|
|
56
64
|
end
|
|
57
65
|
|
|
58
66
|
expect(results.first).to eq "success for jane@doe.com"
|
|
@@ -79,6 +87,8 @@ RSpec.describe "Transactions" do
|
|
|
79
87
|
results = []
|
|
80
88
|
|
|
81
89
|
transaction.call(input) do |m|
|
|
90
|
+
m.success { }
|
|
91
|
+
|
|
82
92
|
m.failure do |value|
|
|
83
93
|
results << "Failed: #{value}"
|
|
84
94
|
end
|
|
@@ -91,6 +101,8 @@ RSpec.describe "Transactions" do
|
|
|
91
101
|
results = []
|
|
92
102
|
|
|
93
103
|
transaction.call(input) do |m|
|
|
104
|
+
m.success { }
|
|
105
|
+
|
|
94
106
|
m.failure :validate do |value|
|
|
95
107
|
results << "Validation failure: #{value}"
|
|
96
108
|
end
|
|
@@ -103,6 +115,8 @@ RSpec.describe "Transactions" do
|
|
|
103
115
|
results = []
|
|
104
116
|
|
|
105
117
|
transaction.call(input) do |m|
|
|
118
|
+
m.success { }
|
|
119
|
+
|
|
106
120
|
m.failure :some_other_step do |value|
|
|
107
121
|
results << "Some other step failure"
|
|
108
122
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
if RUBY_ENGINE == "ruby"
|
|
2
|
-
require "codeclimate-test-reporter"
|
|
3
|
-
CodeClimate::TestReporter.start
|
|
4
|
-
|
|
1
|
+
if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.3"
|
|
5
2
|
require "simplecov"
|
|
6
|
-
SimpleCov.start
|
|
7
|
-
add_filter "/spec/"
|
|
8
|
-
end
|
|
3
|
+
SimpleCov.start
|
|
9
4
|
end
|
|
10
5
|
|
|
11
6
|
begin
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
RSpec.describe Dry::Transaction::StepDefinition do
|
|
2
|
+
let(:container) do
|
|
3
|
+
{ test: -> input { "This is a test with input: #{input.inspect}" } }
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
let(:step_definition) do
|
|
7
|
+
Dry::Transaction::StepDefinition.new(container) do |input|
|
|
8
|
+
Right(container[:test].call(input))
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it { expect(step_definition).to be_kind_of(Dry::Monads::Either::Mixin) }
|
|
13
|
+
|
|
14
|
+
describe '#initialize' do
|
|
15
|
+
subject! { step_definition }
|
|
16
|
+
|
|
17
|
+
it { is_expected.to be_frozen }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe '#call' do
|
|
21
|
+
let(:input) { { test: 'test' } }
|
|
22
|
+
|
|
23
|
+
subject!(:result) { step_definition.call(input) }
|
|
24
|
+
|
|
25
|
+
it do
|
|
26
|
+
expect(result.value).to eq(
|
|
27
|
+
"This is a test with input: {:test=>\"test\"}"
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
it { is_expected.to be_a(Dry::Monads::Either::Right) }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
RSpec.describe Dry::Transaction::Step do
|
|
2
|
+
let(:step_adapter) { ->(step, input, *args) { step.operation.call(input, *args) } }
|
|
3
|
+
let(:step_name) { :test }
|
|
4
|
+
let(:operation_name) { step_name }
|
|
5
|
+
|
|
6
|
+
subject(:step) { described_class.new(step_adapter, step_name, operation_name, operation, {}) }
|
|
7
|
+
|
|
8
|
+
describe "#call" do
|
|
9
|
+
let(:listener) do
|
|
10
|
+
Class.new do
|
|
11
|
+
def test_success(*args); end
|
|
12
|
+
alias_method :test_failure, :test_success
|
|
13
|
+
end.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
let(:input) { "input" }
|
|
17
|
+
subject { step.call(input) }
|
|
18
|
+
|
|
19
|
+
context "when operation succeeds" do
|
|
20
|
+
let(:operation) { proc { |input| Dry::Monads::Either::Right.new(input) } }
|
|
21
|
+
|
|
22
|
+
it { is_expected.to be_right }
|
|
23
|
+
|
|
24
|
+
it "publishes success" do
|
|
25
|
+
expect(listener).to receive(:test_success).with(input)
|
|
26
|
+
step.subscribe(listener)
|
|
27
|
+
subject
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context "when operation fails" do
|
|
32
|
+
let(:operation) { proc { |input| Dry::Monads::Either::Left.new("error") } }
|
|
33
|
+
|
|
34
|
+
it { is_expected.to be_left }
|
|
35
|
+
|
|
36
|
+
it "wraps value in StepFailure" do
|
|
37
|
+
aggregate_failures do
|
|
38
|
+
expect(subject.value).to be_a Dry::Transaction::StepFailure
|
|
39
|
+
expect(subject.value.value).to eq "error"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "publishes failure" do
|
|
44
|
+
expect(listener).to receive(:test_failure).with(input, "error")
|
|
45
|
+
step.subscribe(listener)
|
|
46
|
+
subject
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
describe "#arity" do
|
|
52
|
+
subject { step.arity }
|
|
53
|
+
|
|
54
|
+
context "when operation is a proc" do
|
|
55
|
+
let(:operation) { proc { |a, b| a + b } }
|
|
56
|
+
it { is_expected.to eq 2 }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context "when operation is an object with call method" do
|
|
60
|
+
let(:operation) do
|
|
61
|
+
Class.new do
|
|
62
|
+
def call(a, b, c)
|
|
63
|
+
a + b + c
|
|
64
|
+
end
|
|
65
|
+
end.new
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it { is_expected.to eq 3 }
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
RSpec.describe Dry::Transaction
|
|
1
|
+
RSpec.describe Dry::Transaction do
|
|
2
2
|
subject(:container) {
|
|
3
3
|
{
|
|
4
4
|
upcase: -> input { input.upcase },
|
|
@@ -169,5 +169,41 @@ RSpec.describe Dry::Transaction::Sequence do
|
|
|
169
169
|
expect(initial_transaction.call("hello world").right).to eq "DLROW OLLEH"
|
|
170
170
|
end
|
|
171
171
|
end
|
|
172
|
+
|
|
173
|
+
context "with code block explicitly passed to step" do
|
|
174
|
+
class WithBlockStepAdapters < ::Dry::Transaction::StepAdapters # :nodoc:
|
|
175
|
+
class WithBlock
|
|
176
|
+
include Dry::Monads::Either::Mixin
|
|
177
|
+
def call(step, input, *args, &cb)
|
|
178
|
+
Right(step.operation.((block_given? ? yield(input) : input), *args))
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
register :with_block, WithBlock.new
|
|
182
|
+
end
|
|
183
|
+
let!(:with_block_container) {
|
|
184
|
+
{ exclaim_all: -> input { input.split(" ").map { |str| "#{str}!" }.join(" ") } }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let!(:no_block_transaction) {
|
|
188
|
+
Dry.Transaction(container: with_block_container, step_adapters: WithBlockStepAdapters) do
|
|
189
|
+
with_block :exclaim_all
|
|
190
|
+
end
|
|
191
|
+
}
|
|
192
|
+
let!(:with_block_transaction) {
|
|
193
|
+
Dry.Transaction(container: with_block_container, step_adapters: WithBlockStepAdapters) do
|
|
194
|
+
with_block :exclaim_all do |input|
|
|
195
|
+
input.gsub(/(?<=\s|\A)/, '¡')
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
it "inserts normal bangs without block given" do
|
|
201
|
+
expect(no_block_transaction.call("hello world").right).to eq "hello! world!"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "inserts spanish bangs with block given" do
|
|
205
|
+
expect(with_block_transaction.call("hello world").right).to eq "¡hello! ¡world!"
|
|
206
|
+
end
|
|
207
|
+
end
|
|
172
208
|
end
|
|
173
209
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dry-transaction
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tim Riley
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2016-
|
|
11
|
+
date: 2016-12-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: dry-container
|
|
@@ -72,28 +72,28 @@ dependencies:
|
|
|
72
72
|
requirements:
|
|
73
73
|
- - "~>"
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: 1.
|
|
75
|
+
version: 1.13.1
|
|
76
76
|
type: :development
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: 1.
|
|
82
|
+
version: 1.13.1
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
84
84
|
name: rake
|
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
87
|
- - "~>"
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version:
|
|
89
|
+
version: 11.2.2
|
|
90
90
|
type: :development
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version:
|
|
96
|
+
version: 11.2.2
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: rspec
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -150,15 +150,16 @@ files:
|
|
|
150
150
|
- Rakefile
|
|
151
151
|
- lib/dry-transaction.rb
|
|
152
152
|
- lib/dry/transaction.rb
|
|
153
|
+
- lib/dry/transaction/api.rb
|
|
153
154
|
- lib/dry/transaction/dsl.rb
|
|
154
155
|
- lib/dry/transaction/result_matcher.rb
|
|
155
|
-
- lib/dry/transaction/sequence.rb
|
|
156
156
|
- lib/dry/transaction/step.rb
|
|
157
157
|
- lib/dry/transaction/step_adapters.rb
|
|
158
158
|
- lib/dry/transaction/step_adapters/map.rb
|
|
159
159
|
- lib/dry/transaction/step_adapters/raw.rb
|
|
160
160
|
- lib/dry/transaction/step_adapters/tee.rb
|
|
161
161
|
- lib/dry/transaction/step_adapters/try.rb
|
|
162
|
+
- lib/dry/transaction/step_definition.rb
|
|
162
163
|
- lib/dry/transaction/step_failure.rb
|
|
163
164
|
- lib/dry/transaction/version.rb
|
|
164
165
|
- spec/examples.txt
|
|
@@ -170,11 +171,13 @@ files:
|
|
|
170
171
|
- spec/spec_helper.rb
|
|
171
172
|
- spec/support/either_mixin.rb
|
|
172
173
|
- spec/support/test_module_constants.rb
|
|
173
|
-
- spec/unit/sequence_spec.rb
|
|
174
174
|
- spec/unit/step_adapters/map_spec.rb
|
|
175
175
|
- spec/unit/step_adapters/raw_spec.rb
|
|
176
176
|
- spec/unit/step_adapters/tee_spec.rb
|
|
177
177
|
- spec/unit/step_adapters/try_spec.rb
|
|
178
|
+
- spec/unit/step_definition_spec.rb
|
|
179
|
+
- spec/unit/step_spec.rb
|
|
180
|
+
- spec/unit/transaction_spec.rb
|
|
178
181
|
homepage: https://github.com/dry-rb/dry-transaction
|
|
179
182
|
licenses:
|
|
180
183
|
- MIT
|