dry-transaction 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,9 +3,11 @@ require "wisper"
3
3
  require "dry/transaction/step_failure"
4
4
 
5
5
  module Dry
6
- class Transaction
6
+ module Transaction
7
7
  # @api private
8
8
  class Step
9
+ UNDEFINED = Object.new.freeze
10
+
9
11
  include Wisper::Publisher
10
12
  include Dry::Monads::Either::Mixin
11
13
 
@@ -14,26 +16,35 @@ module Dry
14
16
  attr_reader :operation_name
15
17
  attr_reader :operation
16
18
  attr_reader :options
17
- attr_reader :block
18
19
  attr_reader :call_args
19
20
 
20
- def initialize(step_adapter, step_name, operation_name, operation, options, call_args = [], &block)
21
+ def initialize(step_adapter, step_name, operation_name, operation, options, call_args = [])
21
22
  @step_adapter = step_adapter
22
23
  @step_name = step_name
23
24
  @operation_name = operation_name
24
25
  @operation = operation
25
26
  @options = options
26
- @block = block
27
27
  @call_args = call_args
28
28
  end
29
29
 
30
- def with_call_args(*call_args)
31
- self.class.new(step_adapter, step_name, operation_name, operation, options, call_args, &block)
30
+ def with(operation: UNDEFINED, call_args: UNDEFINED)
31
+ return self if operation == UNDEFINED && call_args == UNDEFINED
32
+ new_operation = operation == UNDEFINED ? self.operation : operation
33
+ new_call_args = call_args == UNDEFINED ? self.call_args : call_args
34
+
35
+ self.class.new(
36
+ step_adapter,
37
+ step_name,
38
+ operation_name,
39
+ new_operation,
40
+ options,
41
+ new_call_args,
42
+ )
32
43
  end
33
44
 
34
45
  def call(input)
35
- args = [input] + call_args
36
- result = step_adapter.call(self, *args, &block)
46
+ args = [input] + Array(call_args)
47
+ result = step_adapter.call(self, *args)
37
48
 
38
49
  result.fmap { |value|
39
50
  broadcast :"#{step_name}_success", value
@@ -1,7 +1,7 @@
1
1
  require "dry-container"
2
2
 
3
3
  module Dry
4
- class Transaction
4
+ module Transaction
5
5
  class StepAdapters
6
6
  extend Dry::Container::Mixin
7
7
  end
@@ -1,5 +1,5 @@
1
1
  module Dry
2
- class Transaction
2
+ module Transaction
3
3
  class StepAdapters
4
4
  # @api private
5
5
  class Map
@@ -1,7 +1,7 @@
1
1
  require "dry/monads/either"
2
2
 
3
3
  module Dry
4
- class Transaction
4
+ module Transaction
5
5
  class StepAdapters
6
6
  # @api private
7
7
  class Raw
@@ -1,5 +1,5 @@
1
1
  module Dry
2
- class Transaction
2
+ module Transaction
3
3
  class StepAdapters
4
4
  # @api private
5
5
  class Tee
@@ -1,5 +1,5 @@
1
1
  module Dry
2
- class Transaction
2
+ module Transaction
3
3
  class StepAdapters
4
4
  # @api private
5
5
  class Try
@@ -1,13 +1,11 @@
1
1
  module Dry
2
- class Transaction
2
+ module Transaction
3
3
  # A wrapper for storing together the step that failed
4
4
  # and value describing the failure.
5
5
  class StepFailure
6
6
  attr_reader :step
7
7
  attr_reader :value
8
8
 
9
- # @param step [Step]
10
- # @param value [Object]
11
9
  def initialize(step, value)
12
10
  @step = step
13
11
  @value = value
@@ -1,6 +1,6 @@
1
1
  module Dry
2
2
  # Business transaction DSL.
3
- class Transaction
4
- VERSION = "0.9.0".freeze
3
+ module Transaction
4
+ VERSION = "0.10.0".freeze
5
5
  end
6
6
  end
data/spec/examples.txt CHANGED
@@ -1,77 +1,62 @@
1
1
  example_id | status | run_time |
2
2
  -------------------------------------------------------- | ------ | --------------- |
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 |
30
- ./spec/unit/step_adapters/map_spec.rb[1:1:2] | passed | 0.0001 seconds |
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 |
40
- ./spec/unit/step_adapters/try_spec.rb[1:1:2:1:2] | passed | 0.00016 seconds |
41
- ./spec/unit/step_adapters/try_spec.rb[1:1:2:1:3:1] | passed | 0.00015 seconds |
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 |
3
+ ./spec/integration/custom_step_adapters_spec.rb[1:1] | passed | 0.00312 seconds |
4
+ ./spec/integration/operation_spec.rb[1:1] | passed | 0.00014 seconds |
5
+ ./spec/integration/operation_spec.rb[1:2] | passed | 0.00016 seconds |
6
+ ./spec/integration/passing_step_arguments_spec.rb[1:1:1] | passed | 0.00048 seconds |
7
+ ./spec/integration/passing_step_arguments_spec.rb[1:2:1] | passed | 0.00038 seconds |
8
+ ./spec/integration/passing_step_arguments_spec.rb[1:3:1] | passed | 0.00067 seconds |
9
+ ./spec/integration/publishing_step_events_spec.rb[1:1:1] | passed | 0.00057 seconds |
10
+ ./spec/integration/publishing_step_events_spec.rb[1:1:2] | passed | 0.00054 seconds |
11
+ ./spec/integration/publishing_step_events_spec.rb[1:2:1] | passed | 0.00225 seconds |
12
+ ./spec/integration/publishing_step_events_spec.rb[1:2:2] | passed | 0.00048 seconds |
13
+ ./spec/integration/transaction_spec.rb[1:1:1] | passed | 0.00102 seconds |
14
+ ./spec/integration/transaction_spec.rb[1:1:2] | passed | 0.00083 seconds |
15
+ ./spec/integration/transaction_spec.rb[1:1:3] | passed | 0.00054 seconds |
16
+ ./spec/integration/transaction_spec.rb[1:1:4] | passed | 0.00207 seconds |
17
+ ./spec/integration/transaction_spec.rb[1:1:5] | passed | 0.00081 seconds |
18
+ ./spec/integration/transaction_spec.rb[1:2:1] | passed | 0.00032 seconds |
19
+ ./spec/integration/transaction_spec.rb[1:3:1] | passed | 0.00041 seconds |
20
+ ./spec/integration/transaction_spec.rb[1:4:1] | passed | 0.00032 seconds |
21
+ ./spec/integration/transaction_spec.rb[1:5:1] | passed | 0.00032 seconds |
22
+ ./spec/integration/transaction_spec.rb[1:6:1] | passed | 0.00098 seconds |
23
+ ./spec/integration/transaction_spec.rb[1:7:1] | passed | 0.00034 seconds |
24
+ ./spec/integration/transaction_spec.rb[1:7:2] | passed | 0.00035 seconds |
25
+ ./spec/integration/transaction_spec.rb[1:7:3] | passed | 0.00231 seconds |
26
+ ./spec/integration/transaction_spec.rb[1:7:4] | passed | 0.00035 seconds |
27
+ ./spec/integration/transaction_spec.rb[1:7:5] | passed | 0.00037 seconds |
28
+ ./spec/integration/transaction_spec.rb[1:7:6] | passed | 0.00038 seconds |
29
+ ./spec/integration/transaction_spec.rb[1:8:1] | passed | 0.00032 seconds |
30
+ ./spec/integration/transaction_spec.rb[1:8:2] | passed | 0.0003 seconds |
31
+ ./spec/integration/transaction_spec.rb[1:8:3] | passed | 0.00032 seconds |
32
+ ./spec/integration/transaction_spec.rb[1:8:4] | passed | 0.00033 seconds |
33
+ ./spec/integration/transaction_spec.rb[1:8:5] | passed | 0.00038 seconds |
34
+ ./spec/integration/transaction_spec.rb[1:9:1] | passed | 0.00031 seconds |
35
+ ./spec/unit/step_adapters/map_spec.rb[1:1:1] | passed | 0.00137 seconds |
36
+ ./spec/unit/step_adapters/map_spec.rb[1:1:2] | passed | 0.00124 seconds |
37
+ ./spec/unit/step_adapters/raw_spec.rb[1:1:1:1] | passed | 0.00217 seconds |
38
+ ./spec/unit/step_adapters/raw_spec.rb[1:1:2:1] | passed | 0.00011 seconds |
39
+ ./spec/unit/step_adapters/raw_spec.rb[1:1:2:2] | passed | 0.00011 seconds |
40
+ ./spec/unit/step_adapters/raw_spec.rb[1:1:3:1] | passed | 0.00013 seconds |
41
+ ./spec/unit/step_adapters/raw_spec.rb[1:1:3:2] | passed | 0.00011 seconds |
42
+ ./spec/unit/step_adapters/tee_spec.rb[1:1:1] | passed | 0.0001 seconds |
43
+ ./spec/unit/step_adapters/tee_spec.rb[1:1:2] | passed | 0.00015 seconds |
44
+ ./spec/unit/step_adapters/try_spec.rb[1:1:1:1] | passed | 0.00015 seconds |
45
+ ./spec/unit/step_adapters/try_spec.rb[1:1:2:1:1] | passed | 0.00013 seconds |
46
+ ./spec/unit/step_adapters/try_spec.rb[1:1:2:1:2] | passed | 0.00019 seconds |
47
+ ./spec/unit/step_adapters/try_spec.rb[1:1:2:1:3:1] | passed | 0.00012 seconds |
48
+ ./spec/unit/step_adapters/try_spec.rb[1:1:2:1:3:2] | passed | 0.00015 seconds |
49
+ ./spec/unit/step_adapters/try_spec.rb[1:1:2:2:1] | passed | 0.00047 seconds |
50
+ ./spec/unit/step_adapters/try_spec.rb[1:1:2:2:2] | passed | 0.00011 seconds |
51
+ ./spec/unit/step_adapters/try_spec.rb[1:1:2:2:3:1] | passed | 0.00012 seconds |
52
+ ./spec/unit/step_adapters/try_spec.rb[1:1:2:2:3:2] | passed | 0.00051 seconds |
53
+ ./spec/unit/step_spec.rb[1:1:1:1] | passed | 0.00057 seconds |
54
+ ./spec/unit/step_spec.rb[1:1:1:2] | passed | 0.00086 seconds |
55
+ ./spec/unit/step_spec.rb[1:1:2:1] | passed | 0.00305 seconds |
56
+ ./spec/unit/step_spec.rb[1:1:2:2] | passed | 0.00432 seconds |
57
+ ./spec/unit/step_spec.rb[1:1:2:3] | passed | 0.01511 seconds |
58
+ ./spec/unit/step_spec.rb[1:2:1:1] | passed | 0.00011 seconds |
59
+ ./spec/unit/step_spec.rb[1:2:2:1] | passed | 0.00012 seconds |
60
+ ./spec/unit/step_spec.rb[1:2:3:1] | passed | 0.00013 seconds |
61
+ ./spec/unit/step_spec.rb[1:3:1:1] | passed | 0.00014 seconds |
62
+ ./spec/unit/step_spec.rb[1:3:2:1] | passed | 0.00013 seconds |
@@ -1,18 +1,12 @@
1
1
  RSpec.describe "Custom step adapters" do
2
2
  let(:transaction) {
3
- Dry.Transaction(container: container, step_adapters: Test::CustomStepAdapters) do
4
- map :process
5
- tee :persist
6
- enqueue :deliver
7
- end
8
- }
3
+ Class.new do
4
+ include Dry::Transaction(container: Test::Container, step_adapters: Test::CustomStepAdapters)
9
5
 
10
- let(:container) {
11
- {
12
- process: -> input { {name: input["name"], email: input["email"]} },
13
- persist: -> input { Test::DB << input and true },
14
- deliver: -> input { "Delivered email to #{input[:email]}" },
15
- }
6
+ map :process, with: :process
7
+ tee :persist, with: :persist
8
+ enqueue :deliver, with: :deliver
9
+ end.new
16
10
  }
17
11
 
18
12
  before do
@@ -20,6 +14,12 @@ RSpec.describe "Custom step adapters" do
20
14
  Test::QUEUE = []
21
15
 
22
16
  module Test
17
+ Container = {
18
+ process: -> input { {name: input["name"], email: input["email"]} },
19
+ persist: -> input { Test::DB << input and true },
20
+ deliver: -> input { "Delivered email to #{input[:email]}" },
21
+ }
22
+
23
23
  class CustomStepAdapters < Dry::Transaction::StepAdapters
24
24
  extend Dry::Monads::Either::Mixin
25
25
 
@@ -0,0 +1,30 @@
1
+ require "dry/transaction/operation"
2
+
3
+ RSpec.describe Dry::Transaction::Operation do
4
+ subject(:operation) {
5
+ Class.new do
6
+ include Dry::Transaction::Operation
7
+
8
+ def call(input)
9
+ Right(input)
10
+ end
11
+ end.new
12
+ }
13
+
14
+ it "mixes in the Either monad constructors" do
15
+ expect(operation.("hello")).to be_right
16
+ end
17
+
18
+ it "supports pattern matching when called with a block" do
19
+ result = operation.("hello") do |m|
20
+ m.success do |v|
21
+ "Success: #{v}"
22
+ end
23
+ m.failure do |v|
24
+ "Failure: #{v}"
25
+ end
26
+ end
27
+
28
+ expect(result).to eq "Success: hello"
29
+ end
30
+ end
@@ -1,20 +1,14 @@
1
1
  RSpec.describe "Passing additional arguments to step operations" do
2
- let(:call_transaction) { transaction.call(input, step_options) }
2
+ let(:call_transaction) { transaction.with_step_args(step_options).call(input) }
3
3
 
4
4
  let(:transaction) {
5
- Dry.Transaction(container: container) do
6
- map :process
7
- try :validate, catch: Test::NotValidError
8
- tee :persist
9
- end
10
- }
5
+ Class.new do
6
+ include Dry::Transaction(container: Test::Container)
11
7
 
12
- let(:container) {
13
- {
14
- process: -> input { {name: input["name"], email: input["email"]} },
15
- validate: -> input, allowed { !input[:email].include?(allowed) ? raise(Test::NotValidError, "email not allowed") : input },
16
- persist: -> input { Test::DB << input and true }
17
- }
8
+ map :process, with: :process
9
+ try :validate, with: :validate, catch: Test::NotValidError
10
+ tee :persist, with: :persist
11
+ end.new
18
12
  }
19
13
 
20
14
  let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} }
@@ -22,6 +16,13 @@ RSpec.describe "Passing additional arguments to step operations" do
22
16
  before do
23
17
  Test::NotValidError = Class.new(StandardError)
24
18
  Test::DB = []
19
+ module Test
20
+ Container = {
21
+ process: -> input { {name: input["name"], email: input["email"]} },
22
+ validate: -> input, allowed { !input[:email].include?(allowed) ? raise(Test::NotValidError, "email not allowed") : input },
23
+ persist: -> input { Test::DB << input and true }
24
+ }
25
+ end
25
26
  end
26
27
 
27
28
  context "required arguments provided" do
@@ -1,24 +1,26 @@
1
1
  RSpec.describe "publishing step events" do
2
2
  let(:transaction) {
3
- Dry.Transaction(container: container) do
4
- map :process
5
- step :verify
6
- tee :persist
7
- end
8
- }
3
+ Class.new do
4
+ include Dry::Transaction(container: Test::Container)
9
5
 
10
- let(:container) {
11
- {
12
- process: -> input { {name: input["name"]} },
13
- verify: -> input { input[:name].to_s != "" ? Right(input) : Left("no name") },
14
- persist: -> input { Test::DB << input and true }
15
- }
6
+ map :process, with: :process
7
+ step :verify, with: :verify
8
+ tee :persist, with: :persist
9
+ end.new
16
10
  }
17
11
 
18
12
  let(:subscriber) { spy(:subscriber) }
19
13
 
20
14
  before do
21
15
  Test::DB = []
16
+
17
+ module Test
18
+ Container = {
19
+ process: -> input { {name: input["name"]} },
20
+ verify: -> input { input[:name].to_s != "" ? Dry::Monads.Right(input) : Dry::Monads.Left("no name") },
21
+ persist: -> input { Test::DB << input and true }
22
+ }
23
+ end
22
24
  end
23
25
 
24
26
  context "subscribing to all step events" do
@@ -1,31 +1,25 @@
1
1
  RSpec.describe "Transactions" do
2
2
  let(:transaction) {
3
- Dry.Transaction(container: container) do
4
- map :process
5
- step :verify
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
13
- tee :persist
14
- end
3
+ Class.new do
4
+ include Dry::Transaction(container: Test::Container)
5
+ map :process
6
+ step :verify
7
+ try :validate, catch: Test::NotValidError
8
+ tee :persist
9
+ end.new(**dependencies)
15
10
  }
16
11
 
17
- let(:container) {
18
- {
19
- process: -> input { {name: input["name"], email: input["email"]} },
20
- verify: -> input { Right(input) },
21
- persist: -> input { Test::DB << input and true },
22
- invalid_error: Test::NotValidError
23
- }
24
- }
12
+ let(:dependencies) { {} }
25
13
 
26
14
  before do
27
15
  Test::NotValidError = Class.new(StandardError)
28
16
  Test::DB = []
17
+ Test::Container = {
18
+ process: -> input { {name: input["name"], email: input["email"]} },
19
+ verify: -> input { Right(input) },
20
+ validate: -> input { input[:email].nil? ? raise(Test::NotValidError, "email required") : input },
21
+ persist: -> input { Test::DB << input and true },
22
+ }
29
23
  end
30
24
 
31
25
  context "successful" do
@@ -67,6 +61,130 @@ RSpec.describe "Transactions" do
67
61
  end
68
62
  end
69
63
 
64
+ context "different step names" do
65
+ before do
66
+ module Test
67
+ ContainerNames = {
68
+ process_step: -> input { {name: input["name"], email: input["email"]} },
69
+ verify_step: -> input { Dry::Monads.Right(input) },
70
+ persist_step: -> input { Test::DB << input and true },
71
+ }
72
+ end
73
+ end
74
+
75
+ let(:transaction) {
76
+ Class.new do
77
+ include Dry::Transaction(container: Test::ContainerNames)
78
+
79
+ map :process, with: :process_step
80
+ step :verify, with: :verify_step
81
+ tee :persist, with: :persist_step
82
+ end.new(**dependencies)
83
+ }
84
+
85
+ it "supports steps using differently named container operations" do
86
+ transaction.call("name" => "Jane", "email" => "jane@doe.com")
87
+ expect(Test::DB).to include(name: "Jane", email: "jane@doe.com")
88
+ end
89
+ end
90
+
91
+ describe "operation injection" do
92
+ let(:transaction) {
93
+ Class.new do
94
+ include Dry::Transaction(container: Test::Container)
95
+ map :process
96
+ step :verify_step, with: :verify
97
+ tee :persist
98
+ end.new(**dependencies)
99
+ }
100
+
101
+ let(:dependencies) {
102
+ {verify_step: -> input { Dry::Monads.Right(input.merge(foo: :bar)) }}
103
+ }
104
+
105
+ it "calls injected operations" do
106
+ transaction.call("name" => "Jane", "email" => "jane@doe.com")
107
+
108
+ expect(Test::DB).to include(name: "Jane", email: "jane@doe.com", foo: :bar)
109
+ end
110
+ end
111
+
112
+ context "wrapping operations with local methods" do
113
+ let(:transaction) do
114
+ Class.new do
115
+ include Dry::Transaction(container: Test::Container)
116
+
117
+ map :process, with: :process
118
+ step :verify, with: :verify
119
+ tee :persist, with: :persist
120
+
121
+ def verify(input)
122
+ new_input = input.merge(greeting: "hello!")
123
+ super(new_input)
124
+ end
125
+ end.new(**dependencies)
126
+ end
127
+
128
+ let(:dependencies) { {} }
129
+
130
+ it "allows local methods to run operations via super" do
131
+ transaction.call("name" => "Jane", "email" => "jane@doe.com")
132
+
133
+ expect(Test::DB).to include(name: "Jane", email: "jane@doe.com", greeting: "hello!")
134
+ end
135
+ end
136
+
137
+ context "local step definition" do
138
+ let(:transaction) do
139
+ Class.new do
140
+ include Dry::Transaction(container: Test::Container)
141
+
142
+ map :process, with: :process
143
+ step :verify
144
+ tee :persist, with: :persist
145
+
146
+ def verify(input)
147
+ Right(input.keys)
148
+ end
149
+ end.new
150
+ end
151
+
152
+ it "execute step only defined as local method" do
153
+ transaction.call("name" => "Jane", "email" => "jane@doe.com")
154
+
155
+ expect(Test::DB).to include([:name, :email])
156
+ end
157
+ end
158
+
159
+ context "all steps are local methods" do
160
+ let(:transaction) do
161
+ Class.new do
162
+ include Dry::Transaction
163
+
164
+ map :process
165
+ step :verify
166
+ tee :persist
167
+
168
+ def process(input)
169
+ input.to_a
170
+ end
171
+
172
+ def verify(input)
173
+ Dry::Monads.Right(input)
174
+ end
175
+
176
+ def persist(input)
177
+ Test::DB << input and true
178
+ end
179
+ end.new
180
+ end
181
+
182
+ it "executes succesfully" do
183
+ transaction.call("name" => "Jane", "email" => "jane@doe.com")
184
+ expect(Test::DB).to include([["name", "Jane"], ["email", "jane@doe.com"]])
185
+ end
186
+ end
187
+
70
188
  context "failed in a try step" do
71
189
  let(:input) { {"name" => "Jane"} }
72
190
 
@@ -134,7 +252,7 @@ RSpec.describe "Transactions" do
134
252
  let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} }
135
253
 
136
254
  before do
137
- container[:verify] = -> input { Left("raw failure") }
255
+ Test::Container[:verify] = -> input { Left("raw failure") }
138
256
  end
139
257
 
140
258
  it "does not run subsequent operations" do
@@ -167,7 +285,7 @@ RSpec.describe "Transactions" do
167
285
  let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} }
168
286
 
169
287
  before do
170
- container[:verify] = -> input { "failure" }
288
+ Test::Container[:verify] = -> input { "failure" }
171
289
  end
172
290
 
173
291
  it "raises an exception" do