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