dry-transaction 0.10.2 → 0.11.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.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +20 -14
  4. data/lib/dry/transaction/builder.rb +2 -4
  5. data/lib/dry/transaction/callable.rb +38 -0
  6. data/lib/dry/transaction/errors.rb +27 -0
  7. data/lib/dry/transaction/instance_methods.rb +22 -5
  8. data/lib/dry/transaction/operation.rb +4 -4
  9. data/lib/dry/transaction/operation_resolver.rb +5 -1
  10. data/lib/dry/transaction/result_matcher.rb +3 -3
  11. data/lib/dry/transaction/stack.rb +24 -0
  12. data/lib/dry/transaction/step.rb +37 -21
  13. data/lib/dry/transaction/step_adapter.rb +49 -0
  14. data/lib/dry/transaction/step_adapters/around.rb +25 -0
  15. data/lib/dry/transaction/step_adapters/check.rb +18 -0
  16. data/lib/dry/transaction/step_adapters/map.rb +3 -3
  17. data/lib/dry/transaction/step_adapters/raw.rb +6 -12
  18. data/lib/dry/transaction/step_adapters/tee.rb +4 -4
  19. data/lib/dry/transaction/step_adapters/try.rb +11 -8
  20. data/lib/dry/transaction/step_adapters.rb +2 -0
  21. data/lib/dry/transaction/version.rb +1 -1
  22. data/lib/dry/transaction.rb +14 -11
  23. data/spec/examples.txt +81 -65
  24. data/spec/integration/around_spec.rb +81 -0
  25. data/spec/integration/custom_step_adapters_spec.rb +6 -4
  26. data/spec/integration/operation_spec.rb +3 -3
  27. data/spec/integration/passing_step_arguments_spec.rb +1 -1
  28. data/spec/integration/publishing_step_events_spec.rb +36 -17
  29. data/spec/integration/transaction_spec.rb +165 -37
  30. data/spec/integration/transaction_without_steps_spec.rb +101 -0
  31. data/spec/spec_helper.rb +14 -5
  32. data/spec/support/container.rb +10 -0
  33. data/spec/support/database.rb +12 -0
  34. data/spec/support/db_transactions.rb +45 -0
  35. data/spec/support/result_mixin.rb +3 -0
  36. data/spec/unit/step_adapters/around_spec.rb +46 -0
  37. data/spec/unit/step_adapters/check_spec.rb +43 -0
  38. data/spec/unit/step_adapters/map_spec.rb +5 -12
  39. data/spec/unit/step_adapters/raw_spec.rb +16 -32
  40. data/spec/unit/step_adapters/tee_spec.rb +4 -10
  41. data/spec/unit/step_adapters/try_spec.rb +24 -33
  42. data/spec/unit/step_spec.rb +41 -10
  43. metadata +39 -21
  44. data/spec/support/either_mixin.rb +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 679662590f13d07a84ae193679d65f448ddb9e54
4
- data.tar.gz: 5810baddf36d0bfd0691acdcb663c5b866f04576
2
+ SHA256:
3
+ metadata.gz: 6e276d629eb7ee813600d0cfce597d0857370b5ee44472de1495faa16f7db44f
4
+ data.tar.gz: 240ce1181835f188c975415c947b2b10f8f45de45d2ea6e0293af360d680796b
5
5
  SHA512:
6
- metadata.gz: 57726a7d740fa0e5983690fe21956303d2f4db5a62ba16f2c8aa334c7dd40b92aefccdc3e13df2f568eed5cf2ee7b4707d0ad78a771c20c89652f29f3fea1049
7
- data.tar.gz: 3c9de9200a701e364c397279c3a277c5f575b725afa62c87b7102f6c31ae747b9b3f6f93b5798a99bd026062bb932aeac8ea78925d8147b678897fe0f8cd90b1
6
+ metadata.gz: b6d04f7008727c783260df83e97221a5ebecc9b703da004d860bc040ed5334869d4844d2977cc5575e7a59ebd4a5ed95a6745aaa50981f7fb97aa777511265c2
7
+ data.tar.gz: b242f6281051877a0bc85f962e8655f3c92439388fa5790ad793ad34afbaa15a628f6f0aa8bc3484d250dd8be515232d18accca218a39a51a88d3c86d1f4bfe2
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ gemspec
5
5
  group :test do
6
6
  gem "simplecov"
7
7
  gem "codeclimate-test-reporter"
8
- gem "byebug", platform: :mri
8
+ gem "pry-byebug", platform: :mri
9
9
  gem "dry-container"
10
10
  end
11
11
 
data/Gemfile.lock CHANGED
@@ -1,17 +1,17 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dry-transaction (0.10.2)
4
+ dry-transaction (0.11.0)
5
5
  dry-container (>= 0.2.8)
6
- dry-matcher (>= 0.5.0)
7
- dry-monads (>= 0.0.1)
8
- wisper (>= 1.6.0)
6
+ dry-events (>= 0.1.0)
7
+ dry-matcher (>= 0.7.0)
8
+ dry-monads (>= 0.4.0)
9
9
 
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
13
  ast (2.3.0)
14
- byebug (8.2.2)
14
+ byebug (9.1.0)
15
15
  codeclimate-test-reporter (0.5.0)
16
16
  simplecov (>= 0.7.1, < 1.0.0)
17
17
  coderay (1.1.1)
@@ -23,12 +23,16 @@ GEM
23
23
  dry-container (0.6.0)
24
24
  concurrent-ruby (~> 1.0)
25
25
  dry-configurable (~> 0.1, >= 0.1.3)
26
- dry-core (0.3.1)
26
+ dry-core (0.4.2)
27
27
  concurrent-ruby (~> 1.0)
28
28
  dry-equalizer (0.2.0)
29
- dry-matcher (0.6.0)
30
- dry-monads (0.3.1)
31
- dry-core
29
+ dry-events (0.1.0)
30
+ concurrent-ruby (~> 1.0)
31
+ dry-core (~> 0.4)
32
+ dry-equalizer (~> 0.2)
33
+ dry-matcher (0.7.0)
34
+ dry-monads (0.4.0)
35
+ dry-core (~> 0.3, >= 0.3.3)
32
36
  dry-equalizer
33
37
  json (1.8.6)
34
38
  method_source (0.8.2)
@@ -39,6 +43,9 @@ GEM
39
43
  coderay (~> 1.1.0)
40
44
  method_source (~> 0.8.1)
41
45
  slop (~> 3.4)
46
+ pry-byebug (3.5.0)
47
+ byebug (~> 9.1)
48
+ pry (~> 0.10)
42
49
  rainbow (2.1.0)
43
50
  rake (11.2.2)
44
51
  rspec (3.3.0)
@@ -68,7 +75,6 @@ GEM
68
75
  simplecov-html (0.10.0)
69
76
  slop (3.6.0)
70
77
  unicode-display_width (1.1.1)
71
- wisper (2.0.0)
72
78
  yard (0.8.7.6)
73
79
 
74
80
  PLATFORMS
@@ -76,16 +82,16 @@ PLATFORMS
76
82
 
77
83
  DEPENDENCIES
78
84
  bundler (~> 1.15)
79
- byebug
80
85
  codeclimate-test-reporter
81
86
  dry-container
82
87
  dry-transaction!
83
88
  pry
84
- rake (~> 11.2.2)
85
- rspec (~> 3.3.0)
89
+ pry-byebug
90
+ rake (~> 11.2, >= 11.2.2)
91
+ rspec (~> 3.3)
86
92
  rubocop
87
93
  simplecov
88
94
  yard
89
95
 
90
96
  BUNDLED WITH
91
- 1.15.1
97
+ 1.16.1
@@ -1,4 +1,3 @@
1
- require "dry/monads/either"
2
1
  require "dry/transaction/step"
3
2
  require "dry/transaction/dsl"
4
3
  require "dry/transaction/instance_methods"
@@ -17,9 +16,8 @@ module Dry
17
16
 
18
17
  def included(klass)
19
18
  klass.extend(dsl_mod)
20
- klass.send(:include, InstanceMethods)
21
- klass.send(:prepend, resolver_mod)
22
- klass.send(:include, Dry::Monads::Either::Mixin)
19
+ klass.include(InstanceMethods)
20
+ klass.prepend(resolver_mod)
23
21
  end
24
22
  end
25
23
  end
@@ -0,0 +1,38 @@
1
+ module Dry
2
+ module Transaction
3
+ # @api private
4
+ class Callable
5
+ def self.[](callable)
6
+ if callable.is_a?(self)
7
+ callable
8
+ elsif callable.nil?
9
+ nil
10
+ else
11
+ new(callable)
12
+ end
13
+ end
14
+
15
+ attr_reader :operation
16
+ attr_reader :arity
17
+
18
+ def initialize(operation)
19
+ @operation = case operation
20
+ when Proc, Method
21
+ operation
22
+ else
23
+ operation.method(:call)
24
+ end
25
+
26
+ @arity = @operation.arity
27
+ end
28
+
29
+ def call(*args, &block)
30
+ if arity.zero?
31
+ operation.(&block)
32
+ else
33
+ operation.(*args, &block)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ module Dry
2
+ module Transaction
3
+ class InvalidStepError < ArgumentError
4
+ def initialize(step_name)
5
+ super("step +`#{step_name}`+ must respond to `#call`")
6
+ end
7
+ end
8
+
9
+ class MissingStepError < ArgumentError
10
+ def initialize(step_name)
11
+ super("Definition for step +`#{step_name}`+ is missing")
12
+ end
13
+ end
14
+
15
+ class InvalidResultError < ArgumentError
16
+ def initialize(step_name)
17
+ super("step +#{step_name}+ must return a Result object")
18
+ end
19
+ end
20
+
21
+ class MissingCatchListError < ArgumentError
22
+ def initialize(step_name)
23
+ super("step +#{step_name}+ requires one or more exception classes provided via +catch:+")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,33 +1,38 @@
1
- require "dry/monads"
1
+ require "dry/monads/result"
2
2
  require "dry/transaction/result_matcher"
3
+ require "dry/transaction/stack"
3
4
 
4
5
  module Dry
5
6
  module Transaction
6
7
  module InstanceMethods
8
+ include Dry::Monads::Result::Mixin
9
+
7
10
  attr_reader :steps
8
11
  attr_reader :operations
9
12
  attr_reader :listeners
13
+ attr_reader :stack
10
14
 
11
15
  def initialize(steps: (self.class.steps), listeners: nil, **operations)
12
16
  @steps = steps.map { |step|
13
- operation = methods.include?(step.step_name) ? method(step.step_name) : operations[step.step_name]
17
+ operation = resolve_operation(step, operations)
14
18
  step.with(operation: operation)
15
19
  }
16
20
  @operations = operations
21
+ @stack = Stack.new(@steps)
17
22
  subscribe(listeners) unless listeners.nil?
18
23
  end
19
24
 
20
- def call(input, &block)
25
+ def call(input = nil, &block)
21
26
  assert_step_arity
22
27
 
23
- result = steps.inject(Dry::Monads.Right(input), :bind)
28
+ result = stack.(Success(input))
24
29
 
25
30
  if block
26
31
  ResultMatcher.(result, &block)
27
32
  else
28
33
  result.or { |step_failure|
29
34
  # Unwrap the value from the StepFailure and return it directly
30
- Dry::Monads.Left(step_failure.value)
35
+ Failure(step_failure.value)
31
36
  }
32
37
  end
33
38
  end
@@ -76,6 +81,18 @@ module Dry
76
81
  operation.(*args, &block)
77
82
  end
78
83
 
84
+ def resolve_operation(step, **operations)
85
+ if methods.include?(step.step_name) || private_methods.include?(step.step_name)
86
+ method(step.step_name)
87
+ elsif operations[step.step_name].nil?
88
+ raise MissingStepError.new(step.step_name)
89
+ elsif operations[step.step_name].respond_to?(:call)
90
+ operations[step.step_name]
91
+ else
92
+ raise InvalidStepError.new(step.step_name)
93
+ end
94
+ end
95
+
79
96
  def assert_valid_step_args(step_args)
80
97
  step_args.each_key do |step_name|
81
98
  unless steps.any? { |step| step.step_name == step_name }
@@ -1,14 +1,14 @@
1
- require "dry/monads/either"
1
+ require "dry/monads/result"
2
2
  require "dry/matcher"
3
- require "dry/matcher/either_matcher"
3
+ require "dry/matcher/result_matcher"
4
4
 
5
5
  module Dry
6
6
  module Transaction
7
7
  module Operation
8
8
  def self.included(klass)
9
9
  klass.class_eval do
10
- include Dry::Monads::Either::Mixin
11
- include Dry::Matcher.for(:call, with: Dry::Matcher::EitherMatcher)
10
+ include Dry::Monads::Result::Mixin
11
+ include Dry::Matcher.for(:call, with: Dry::Matcher::ResultMatcher)
12
12
  end
13
13
  end
14
14
  end
@@ -6,7 +6,11 @@ module Dry
6
6
  define_method :initialize do |**kwargs|
7
7
  operation_kwargs = self.class.steps.select(&:operation_name).map { |step|
8
8
  operation = kwargs.fetch(step.step_name) {
9
- ops_container and ops_container.key?(step.operation_name) and ops_container[step.operation_name]
9
+ if ops_container && ops_container.key?(step.operation_name)
10
+ ops_container[step.operation_name]
11
+ else
12
+ nil
13
+ end
10
14
  }
11
15
 
12
16
  [step.step_name, operation]
@@ -5,17 +5,17 @@ module Dry
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.step_name == step_name
13
+ result.left? && result.left.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.left.value }
19
19
  )
20
20
  )
21
21
  end
@@ -0,0 +1,24 @@
1
+ module Dry
2
+ module Transaction
3
+ # @api private
4
+ class Stack
5
+ RETURN = -> x { x }
6
+
7
+ def initialize(steps)
8
+ @stack = compile(steps)
9
+ end
10
+
11
+ def call(m)
12
+ @stack.(m)
13
+ end
14
+
15
+ private
16
+
17
+ def compile(steps)
18
+ steps.reverse.reduce(RETURN) do |next_step, step|
19
+ proc { |m| m.bind { |value| step.(value, next_step) } }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,62 +1,78 @@
1
- require "dry/monads/either"
2
- require "wisper"
1
+ require "dry/monads/result"
2
+ require 'dry/events/publisher'
3
3
  require "dry/transaction/step_failure"
4
+ require "dry/transaction/step_adapter"
4
5
 
5
6
  module Dry
6
7
  module Transaction
7
8
  # @api private
8
9
  class Step
9
10
  UNDEFINED = Object.new.freeze
11
+ RETURN = -> x { x }
10
12
 
11
- include Wisper::Publisher
12
- include Dry::Monads::Either::Mixin
13
+ include Dry::Events::Publisher[name || object_id]
14
+ include Dry::Monads::Result::Mixin
15
+
16
+ register_event(:step)
17
+ register_event(:step_succeeded)
18
+ register_event(:step_failed)
13
19
 
14
20
  attr_reader :step_adapter
15
21
  attr_reader :step_name
16
22
  attr_reader :operation_name
17
- attr_reader :operation
18
- attr_reader :options
19
23
  attr_reader :call_args
20
24
 
21
25
  def initialize(step_adapter, step_name, operation_name, operation, options, call_args = [])
22
- @step_adapter = step_adapter
26
+ @step_adapter = StepAdapter[step_adapter, operation, **options, step_name: step_name]
23
27
  @step_name = step_name
24
28
  @operation_name = operation_name
25
- @operation = operation
26
- @options = options
27
29
  @call_args = call_args
28
30
  end
29
31
 
30
32
  def with(operation: UNDEFINED, call_args: UNDEFINED)
31
33
  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
+ new_operation = operation == UNDEFINED ? step_adapter.operation : operation
36
+ new_call_args = call_args == UNDEFINED ? self.call_args : Array(call_args)
34
37
 
35
38
  self.class.new(
36
39
  step_adapter,
37
40
  step_name,
38
41
  operation_name,
39
42
  new_operation,
40
- options,
41
- new_call_args,
43
+ step_adapter.options,
44
+ new_call_args
42
45
  )
43
46
  end
44
47
 
45
- def call(input)
46
- args = [input] + Array(call_args)
47
- result = step_adapter.call(self, *args)
48
+ def call(input, continue = RETURN)
49
+ args = [input, *call_args]
50
+
51
+ if step_adapter.yields?
52
+ with_broadcast(args) { step_adapter.(args, &continue) }
53
+ else
54
+ continue.(with_broadcast(args) { step_adapter.(args) })
55
+ end
56
+ end
48
57
 
49
- result.fmap { |value|
50
- broadcast :"#{step_name}_success", value
58
+ def with_broadcast(args)
59
+ publish(:step, step_name: step_name, args: args)
60
+
61
+ yield.fmap { |value|
62
+ publish(:step_succeeded, step_name: step_name, args: args, value: value)
51
63
  value
52
64
  }.or { |value|
53
- broadcast :"#{step_name}_failure", *args, value
54
- Left(StepFailure.new(self, value))
65
+ publish(:step_failed, step_name: step_name, args: args, value: value)
66
+ Failure(StepFailure.new(self, value))
55
67
  }
56
68
  end
57
69
 
58
70
  def arity
59
- operation.is_a?(Proc) ? operation.arity : operation.method(:call).arity
71
+ step_adapter.operation.arity
72
+ end
73
+
74
+ def operation
75
+ step_adapter.operation
60
76
  end
61
77
  end
62
78
  end
@@ -0,0 +1,49 @@
1
+ require "dry/transaction/callable"
2
+
3
+ module Dry
4
+ module Transaction
5
+ # @api private
6
+ class StepAdapter
7
+ def self.[](adapter, operation, options)
8
+ if adapter.is_a?(self)
9
+ adapter.with(operation, options)
10
+ else
11
+ new(adapter, operation, options)
12
+ end
13
+ end
14
+
15
+ attr_reader :adapter
16
+ attr_reader :operation
17
+ attr_reader :options
18
+
19
+ def initialize(adapter, operation, options)
20
+ @adapter = case adapter
21
+ when Proc, Method
22
+ adapter
23
+ else
24
+ adapter.method(:call)
25
+ end
26
+
27
+ @operation = Callable[operation]
28
+
29
+ @options = options
30
+
31
+ @yields = @adapter.
32
+ parameters.
33
+ any? { |type, _| type == :block }
34
+ end
35
+
36
+ def yields?
37
+ @yields
38
+ end
39
+
40
+ def call(args, &block)
41
+ adapter.(operation, options, args, &block)
42
+ end
43
+
44
+ def with(operation = self.operation, new_options = {})
45
+ self.class.new(adapter, operation, options.merge(new_options))
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ require "dry/monads/result"
2
+ require "dry/transaction/errors"
3
+
4
+ module Dry
5
+ module Transaction
6
+ class StepAdapters
7
+ # @api private
8
+ class Around
9
+ include Dry::Monads::Result::Mixin
10
+
11
+ def call(operation, options, args, &block)
12
+ result = operation.(*args, &block)
13
+
14
+ unless result.is_a?(Dry::Monads::Result)
15
+ raise InvalidResultError.new(options[:step_name])
16
+ end
17
+
18
+ result
19
+ end
20
+ end
21
+
22
+ register :around, Around.new
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ module Dry
2
+ module Transaction
3
+ class StepAdapters
4
+ # @api private
5
+ class Check
6
+ include Dry::Monads::Either::Mixin
7
+
8
+ def call(operation, _options, args)
9
+ input = args[0]
10
+ res = operation.(*args)
11
+ res == true || res.is_a?(Success) ? Success(input) : Failure(input)
12
+ end
13
+ end
14
+
15
+ register :check, Check.new
16
+ end
17
+ end
18
+ end
@@ -3,10 +3,10 @@ module Dry
3
3
  class StepAdapters
4
4
  # @api private
5
5
  class Map
6
- include Dry::Monads::Either::Mixin
6
+ include Dry::Monads::Result::Mixin
7
7
 
8
- def call(step, input, *args)
9
- Right(step.operation.call(input, *args))
8
+ def call(operation, _options, args)
9
+ Success(operation.(*args))
10
10
  end
11
11
  end
12
12
 
@@ -1,20 +1,14 @@
1
- require "dry/monads/either"
1
+ require "dry/monads/result"
2
+ require "dry/transaction/errors"
3
+ require "dry/transaction/step_adapters/around"
2
4
 
3
5
  module Dry
4
6
  module Transaction
5
7
  class StepAdapters
6
8
  # @api private
7
- class Raw
8
- include Dry::Monads::Either::Mixin
9
-
10
- def call(step, input, *args)
11
- result = step.operation.call(input, *args)
12
-
13
- unless result.is_a?(Dry::Monads::Either)
14
- raise ArgumentError, "step +#{step.step_name}+ must return an Either object"
15
- end
16
-
17
- result
9
+ class Raw < Around
10
+ def call(operation, options, args)
11
+ super(operation, options, args, &nil)
18
12
  end
19
13
  end
20
14
 
@@ -3,11 +3,11 @@ module Dry
3
3
  class StepAdapters
4
4
  # @api private
5
5
  class Tee
6
- include Dry::Monads::Either::Mixin
6
+ include Dry::Monads::Result::Mixin
7
7
 
8
- def call(step, input, *args)
9
- step.operation.call(input, *args)
10
- Right(input)
8
+ def call(operation, _options, args)
9
+ operation.(*args)
10
+ Success(args[0])
11
11
  end
12
12
  end
13
13
 
@@ -1,19 +1,22 @@
1
+ require "dry/transaction/errors"
2
+
1
3
  module Dry
2
4
  module Transaction
3
5
  class StepAdapters
4
6
  # @api private
5
7
  class Try
6
- include Dry::Monads::Either::Mixin
8
+ include Dry::Monads::Result::Mixin
7
9
 
8
- def call(step, input, *args)
9
- unless step.options[:catch]
10
- raise ArgumentError, "+try+ steps require one or more exception classes provided via +catch:+"
10
+ def call(operation, options, args)
11
+ unless options[:catch]
12
+ raise MissingCatchListError.new(options[:step_name])
11
13
  end
12
14
 
13
- Right(step.operation.call(input, *args))
14
- rescue *Array(step.options[:catch]) => e
15
- e = step.options[:raise].new(e.message) if step.options[:raise]
16
- Left(e)
15
+ result = operation.(*args)
16
+ Success(result)
17
+ rescue *Array(options[:catch]) => e
18
+ e = options[:raise].new(e.message) if options[:raise]
19
+ Failure(e)
17
20
  end
18
21
  end
19
22
 
@@ -8,7 +8,9 @@ module Dry
8
8
  end
9
9
  end
10
10
 
11
+ require "dry/transaction/step_adapters/check"
11
12
  require "dry/transaction/step_adapters/map"
12
13
  require "dry/transaction/step_adapters/raw"
13
14
  require "dry/transaction/step_adapters/tee"
14
15
  require "dry/transaction/step_adapters/try"
16
+ require "dry/transaction/step_adapters/around"
@@ -1,6 +1,6 @@
1
1
  module Dry
2
2
  # Business transaction DSL.
3
3
  module Transaction
4
- VERSION = "0.10.2".freeze
4
+ VERSION = "0.11.0".freeze
5
5
  end
6
6
  end