dry-transaction 0.9.0 → 0.10.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.
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,8 @@
1
- if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.3"
1
+ if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.4"
2
2
  require "simplecov"
3
- SimpleCov.start
3
+ SimpleCov.start do
4
+ add_filter "/spec/"
5
+ end
4
6
  end
5
7
 
6
8
  begin
@@ -8,6 +10,8 @@ begin
8
10
  rescue LoadError; end
9
11
 
10
12
  require "dry-transaction"
13
+ require "dry-matcher"
14
+ require "dry-monads"
11
15
 
12
16
  # Requires supporting ruby files with custom matchers and macros, etc, in
13
17
  # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
@@ -48,6 +48,35 @@ RSpec.describe Dry::Transaction::Step do
48
48
  end
49
49
  end
50
50
 
51
+ describe "#with" do
52
+ let(:operation) { proc { |a, b| a + b } }
53
+ context "without arguments" do
54
+ it "returns itself" do
55
+ expect(step.with).to eq step
56
+ end
57
+ end
58
+
59
+ context "with operation argument" do
60
+ it "returns new instance with only operation changed" do
61
+ new_operation = proc { |a,b| a * b }
62
+ new_step = step.with(operation: new_operation)
63
+ expect(new_step).to_not eq step
64
+ expect(new_step.operation_name).to eq step.operation_name
65
+ expect(new_step.operation).to_not eq step.operation
66
+ end
67
+ end
68
+
69
+ context "with call_args argument" do
70
+ let(:call_args) { [12] }
71
+ it "returns new instance with only call_args changed" do
72
+ new_step = step.with(call_args: call_args)
73
+ expect(new_step).to_not eq step
74
+ expect(new_step.operation_name).to eq step.operation_name
75
+ expect(new_step.call_args).to_not eq step.call_args
76
+ end
77
+ end
78
+ end
79
+
51
80
  describe "#arity" do
52
81
  subject { step.arity }
53
82
 
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.9.0
4
+ version: 0.10.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-12-18 00:00:00.000000000 Z
11
+ date: 2017-06-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-container
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 1.13.1
75
+ version: '1.15'
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.13.1
82
+ version: '1.15'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rake
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -150,8 +150,11 @@ files:
150
150
  - Rakefile
151
151
  - lib/dry-transaction.rb
152
152
  - lib/dry/transaction.rb
153
- - lib/dry/transaction/api.rb
153
+ - lib/dry/transaction/builder.rb
154
154
  - lib/dry/transaction/dsl.rb
155
+ - lib/dry/transaction/instance_methods.rb
156
+ - lib/dry/transaction/operation.rb
157
+ - lib/dry/transaction/operation_resolver.rb
155
158
  - lib/dry/transaction/result_matcher.rb
156
159
  - lib/dry/transaction/step.rb
157
160
  - lib/dry/transaction/step_adapters.rb
@@ -159,12 +162,11 @@ files:
159
162
  - lib/dry/transaction/step_adapters/raw.rb
160
163
  - lib/dry/transaction/step_adapters/tee.rb
161
164
  - lib/dry/transaction/step_adapters/try.rb
162
- - lib/dry/transaction/step_definition.rb
163
165
  - lib/dry/transaction/step_failure.rb
164
166
  - lib/dry/transaction/version.rb
165
167
  - spec/examples.txt
166
- - spec/integration/custom_matcher_spec.rb
167
168
  - spec/integration/custom_step_adapters_spec.rb
169
+ - spec/integration/operation_spec.rb
168
170
  - spec/integration/passing_step_arguments_spec.rb
169
171
  - spec/integration/publishing_step_events_spec.rb
170
172
  - spec/integration/transaction_spec.rb
@@ -175,9 +177,7 @@ files:
175
177
  - spec/unit/step_adapters/raw_spec.rb
176
178
  - spec/unit/step_adapters/tee_spec.rb
177
179
  - spec/unit/step_adapters/try_spec.rb
178
- - spec/unit/step_definition_spec.rb
179
180
  - spec/unit/step_spec.rb
180
- - spec/unit/transaction_spec.rb
181
181
  homepage: https://github.com/dry-rb/dry-transaction
182
182
  licenses:
183
183
  - MIT
@@ -198,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
198
198
  version: '0'
199
199
  requirements: []
200
200
  rubyforge_project:
201
- rubygems_version: 2.5.1
201
+ rubygems_version: 2.6.11
202
202
  signing_key:
203
203
  specification_version: 4
204
204
  summary: Business Transaction Flow DSL
@@ -1,301 +0,0 @@
1
- require "dry/monads/either"
2
-
3
- module Dry
4
- class Transaction
5
- module API
6
- # Run the transaction.
7
- #
8
- # Each operation will be called in the order it was specified, with its
9
- # output passed as input to the next operation. Operations will only be
10
- # called if the previous step was a success.
11
- #
12
- # If any of the operations require extra arguments beyond the main input
13
- # e.g. with a signature like `#call(something_else, input)`, then you
14
- # must pass the extra arguments as arrays for each step in the options
15
- # hash.
16
- #
17
- # @example Running a transaction
18
- # my_transaction.call(some_input)
19
- #
20
- # @example Running a transaction with extra step arguments
21
- # my_transaction.call(some_input, step_name: [extra_argument])
22
- #
23
- # The return value will be the output from the last operation, wrapped
24
- # in a [dry-monads](dry-monads) `Either` object, a `Right` for a successful
25
- # transaction or a `Left` for a failed transaction.
26
- #
27
- # [dry-monads]: https://rubygems.org/gems/dry-monads
28
- #
29
- # @param input
30
- # @param options [Hash] extra step arguments
31
- #
32
- # @return [Right, Left] output from the final step
33
- #
34
- # @api public
35
- def call(input, options = {}, &block)
36
- assert_valid_options(options)
37
- assert_options_satisfy_step_arity(options)
38
-
39
- steps = steps_with_options_applied(options)
40
- result = steps.inject(Dry::Monads.Right(input), :bind)
41
-
42
- if block
43
- matcher.(result, &block)
44
- else
45
- result.or { |step_failure|
46
- # Unwrap the value from the StepFailure and return it directly
47
- Dry::Monads.Left(step_failure.value)
48
- }
49
- end
50
- end
51
- alias_method :[], :call
52
-
53
- # Subscribe to notifications from steps.
54
- #
55
- # When each step completes, it will send a `[step_name]_success` or
56
- # `[step_name]_failure` message to any subscribers.
57
- #
58
- # For example, if you had a step called `persist`, then it would send
59
- # either `persist_success` or `persist_failure` messages to subscribers
60
- # after the operation completes.
61
- #
62
- # Pass a single object to subscribe to notifications from all steps, or
63
- # pass a hash with step names as keys to subscribe to notifications from
64
- # specific steps.
65
- #
66
- # @example Subscribing to notifications from all steps
67
- # my_transaction.subscribe(my_listener)
68
- #
69
- # @example Subscribing to notifications from specific steps
70
- # my_transaction.subscribe(some_step: my_listener, another_step: another_listener)
71
- #
72
- # Notifications are implemented using the [Wisper](wisper) gem.
73
- #
74
- # [wisper]: https://rubygems.org/gems/wisper
75
- #
76
- # @param listeners [Object, Hash{Symbol => Object}] the listener object or
77
- # hash of steps and listeners
78
- #
79
- # @api public
80
- def subscribe(listeners)
81
- if listeners.is_a?(Hash)
82
- listeners.each do |step_name, listener|
83
- steps.detect { |step| step.step_name == step_name }.subscribe(listener)
84
- end
85
- else
86
- steps.each do |step|
87
- step.subscribe(listeners)
88
- end
89
- end
90
-
91
- self
92
- end
93
-
94
- # Return a transaction with the steps from the provided transaction
95
- # prepended onto the beginning of the steps in `self`.
96
- #
97
- # @example Prepend an existing transaction
98
- # my_transaction = Dry.Transaction(container: container) do
99
- # step :first
100
- # step :second
101
- # end
102
- #
103
- # other_transaction = Dry.Transaction(container: container) do
104
- # step :another
105
- # end
106
- #
107
- # my_transaction.prepend(other_transaction)
108
- #
109
- # @example Prepend a transaction defined inline
110
- # my_transaction = Dry.Transaction(container: container) do
111
- # step :first
112
- # step :second
113
- # end
114
- #
115
- # my_transaction.prepend(container: container) do
116
- # step :another
117
- # end
118
- #
119
- # @param other [Dry::Transaction] the transaction to prepend.
120
- # Optional if you will define a transaction inline via a block.
121
- # @param options [Hash] the options hash for defining a transaction inline
122
- # via a block. Optional if the transaction is passed directly as
123
- # `other`.
124
- # @option options [#[]] :container the operations container
125
- #
126
- # @return [Dry::Transaction] the modified transaction object
127
- #
128
- # @api public
129
- def prepend(other = nil, **options, &block)
130
- other = accept_or_build_transaction(other, **options, &block)
131
-
132
- self.class.new(other.steps + steps, matcher)
133
- end
134
-
135
- # Return a transaction with the steps from the provided transaction
136
- # appended onto the end of the steps in `self`.
137
- #
138
- # @example Append an existing transaction
139
- # my_transaction = Dry.Transaction(container: container) do
140
- # step :first
141
- # step :second
142
- # end
143
- #
144
- # other_transaction = Dry.Transaction(container: container) do
145
- # step :another
146
- # end
147
- #
148
- # my_transaction.append(other_transaction)
149
- #
150
- # @example Append a transaction defined inline
151
- # my_transaction = Dry.Transaction(container: container) do
152
- # step :first
153
- # step :second
154
- # end
155
- #
156
- # my_transaction.append(container: container) do
157
- # step :another
158
- # end
159
- #
160
- # @param other [Dry::Transaction] the transaction to append.
161
- # Optional if you will define a transaction inline via a block.
162
- # @param options [Hash] the options hash for defining a transaction inline
163
- # via a block. Optional if the transaction is passed directly as
164
- # `other`.
165
- # @option options [#[]] :container the operations container
166
- #
167
- # @return [Dry::Transaction] the modified transaction object
168
- #
169
- # @api public
170
- def append(other = nil, **options, &block)
171
- other = accept_or_build_transaction(other, **options, &block)
172
-
173
- self.class.new(steps + other.steps, matcher)
174
- end
175
-
176
- # Return a transaction with the steps from the provided transaction
177
- # inserted into a specific place among the steps in `self`.
178
- #
179
- # Transactions can be inserted either before or after a named step.
180
- #
181
- # @example Insert an existing transaction (before a step)
182
- # my_transaction = Dry.Transaction(container: container) do
183
- # step :first
184
- # step :second
185
- # end
186
- #
187
- # other_transaction = Dry.Transaction(container: container) do
188
- # step :another
189
- # end
190
- #
191
- # my_transaction.insert(other_transaction, before: :second)
192
- #
193
- # @example Append a transaction defined inline (after a step)
194
- # my_transaction = Dry.Transaction(container: container) do
195
- # step :first
196
- # step :second
197
- # end
198
- #
199
- # my_transaction.insert(after: :first, container: container) do
200
- # step :another
201
- # end
202
- #
203
- # @param other [Dry::Transaction] the transaction to append.
204
- # Optional if you will define a transaction inline via a block.
205
- # @param before [Symbol] the name of the step before which the
206
- # transaction should be inserted (provide either this or `after`)
207
- # @param after [Symbol] the name of the step after which the transaction
208
- # should be inserted (provide either this or `before`)
209
- # @param options [Hash] the options hash for defining a transaction
210
- # inline via a block. Optional if the transaction is passed directly
211
- # as `other`.
212
- # @option options [#[]] :container the operations container
213
- #
214
- # @return [Dry::Transaction] the modified transaction object
215
- #
216
- # @api public
217
- def insert(other = nil, before: nil, after: nil, **options, &block)
218
- insertion_step = before || after
219
- match_insertion_step = proc { |step| step.step_name == insertion_step }
220
-
221
- unless steps.any?(&match_insertion_step)
222
- raise ArgumentError, "+#{insertion_step}+ is not a valid step name"
223
- end
224
-
225
- other = accept_or_build_transaction(other, **options, &block)
226
- index = steps.index(&match_insertion_step) + (after ? 1 : 0)
227
-
228
- self.class.new(steps.dup.insert(index, *other.steps), matcher)
229
- end
230
-
231
- # @overload remove(step, ...)
232
- # Return a transaction with steps removed.
233
- #
234
- # @example
235
- # my_transaction = Dry.Transaction(container: container) do
236
- # step :first
237
- # step :second
238
- # step :third
239
- # end
240
- #
241
- # my_transaction.remove(:first, :third)
242
- #
243
- # @param step [Symbol] the names of a step to remove
244
- # @param ... [Symbol] more names of steps to remove
245
- #
246
- # @return [Dry::Transaction] the modified transaction object
247
- #
248
- # @api public
249
- def remove(*steps_to_remove)
250
- self.class.new(steps.reject { |step| steps_to_remove.include?(step.step_name) }, matcher)
251
- end
252
-
253
- private
254
-
255
- # @param options [Hash] step arguments keyed by step name
256
- def assert_valid_options(options)
257
- options.each_key do |step_name|
258
- unless steps.any? { |step| step.step_name == step_name }
259
- raise ArgumentError, "+#{step_name}+ is not a valid step name"
260
- end
261
- end
262
- end
263
-
264
- # @param options [Hash] step arguments keyed by step name
265
- def assert_options_satisfy_step_arity(options)
266
- steps.each do |step|
267
- args_required = step.arity >= 0 ? step.arity : ~step.arity
268
- args_supplied = options.fetch(step.step_name, []).length + 1 # add 1 for main `input`
269
-
270
- if args_required > args_supplied
271
- raise ArgumentError, "not enough options for step +#{step.step_name}+"
272
- end
273
- end
274
- end
275
-
276
- # @param options [Hash] step arguments keyed by step name
277
- def steps_with_options_applied(options)
278
- steps.map { |step|
279
- if (args = options[step.step_name])
280
- step.with_call_args(*args)
281
- else
282
- step
283
- end
284
- }
285
- end
286
-
287
- def accept_or_build_transaction(other_transaction = nil, **options, &block)
288
- unless other_transaction || block
289
- raise ArgumentError, "a transaction must be provided or defined in a block"
290
- end
291
-
292
- if other_transaction
293
- other_transaction
294
- else
295
- require "dry/transaction/dsl"
296
- DSL.new(**options, &block).call
297
- end
298
- end
299
- end
300
- end
301
- end
@@ -1,23 +0,0 @@
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