dry-transaction 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8e09ce8534f0529de0e5feaeb72a882ac04b1b4a
4
- data.tar.gz: 511ab8cb1bb59ca3c40e506e39a95b9b7c47898e
3
+ metadata.gz: a1086edb5c79856eea4443feca03571381d01656
4
+ data.tar.gz: 396497b3dc1c00bac2b526d5dd9b2db3b94aec86
5
5
  SHA512:
6
- metadata.gz: 9e3cb97c3772c704e15c2e1bb76d58bd163c3eb88663d834d0964610f3eb413b216940f3a1271deea081e50745a13577ee144f6c25fc5cedd54955d47dbbc1a1
7
- data.tar.gz: bb448e0f12ec06cc3abf6f753bb84d2f1b09430ef28deda5b7763f81e2d06b22ed97c20ea695dcd5eb24737537acf4d30e001148f026a1f7d9a9a2e84baebfac
6
+ metadata.gz: 99f8334aabeda041d3d591affdde7030ddf9c227149a9f98230949adb076561fc903413bad9a51a634eeb26c6ccbe416bceb2d3a156dcf8a5d003b4d14df06e1
7
+ data.tar.gz: 1157a95e3126ea8108bbea2c795f1e094bcadd4ecddb26c1e62af2c1a9ebef7c32b181d9d67b3a62df0326f681c2ab7bd3448e302dcc42c43bfaa0c6a21e0a01
data/Gemfile CHANGED
@@ -1,3 +1,13 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
+
5
+ group :test do
6
+ gem "codeclimate-test-reporter", require: nil
7
+ gem "byebug", platform: :mri
8
+ end
9
+
10
+ group :tools do
11
+ gem "pry"
12
+ gem "dry-container"
13
+ end
data/Gemfile.lock CHANGED
@@ -1,17 +1,33 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dry-transaction (0.5.0)
4
+ dry-transaction (0.6.0)
5
+ dry-container (>= 0.2.8)
5
6
  kleisli
6
7
  wisper (>= 1.6.0)
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
10
11
  specs:
12
+ byebug (8.2.2)
13
+ codeclimate-test-reporter (0.5.0)
14
+ simplecov (>= 0.7.1, < 1.0.0)
15
+ coderay (1.1.1)
16
+ concurrent-ruby (1.0.1)
11
17
  diff-lcs (1.2.5)
12
18
  docile (1.1.5)
19
+ dry-configurable (0.1.4)
20
+ concurrent-ruby (~> 1.0)
21
+ dry-container (0.3.1)
22
+ concurrent-ruby (~> 1.0)
23
+ dry-configurable (~> 0.1, >= 0.1.3)
13
24
  json (1.8.3)
14
25
  kleisli (0.2.7)
26
+ method_source (0.8.2)
27
+ pry (0.10.3)
28
+ coderay (~> 1.1.0)
29
+ method_source (~> 0.8.1)
30
+ slop (~> 3.4)
15
31
  rake (10.4.2)
16
32
  rspec (3.3.0)
17
33
  rspec-core (~> 3.3.0)
@@ -31,6 +47,7 @@ GEM
31
47
  json (~> 1.8)
32
48
  simplecov-html (~> 0.10.0)
33
49
  simplecov-html (0.10.0)
50
+ slop (3.6.0)
34
51
  wisper (1.6.1)
35
52
  yard (0.8.7.6)
36
53
 
@@ -38,8 +55,12 @@ PLATFORMS
38
55
  ruby
39
56
 
40
57
  DEPENDENCIES
41
- bundler (~> 1.10)
58
+ bundler (~> 1.11.2)
59
+ byebug
60
+ codeclimate-test-reporter
61
+ dry-container
42
62
  dry-transaction!
63
+ pry
43
64
  rake (~> 10.4.2)
44
65
  rspec (~> 3.3.0)
45
66
  simplecov (~> 0.10.0)
data/README.md CHANGED
@@ -11,192 +11,12 @@
11
11
  [![Code Climate](https://img.shields.io/codeclimate/github/dry-rb/dry-transaction.svg)][code_climate]
12
12
  [![API Documentation Coverage](http://inch-ci.org/github/dry-rb/dry-transaction.svg)][inch]
13
13
 
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. It makes error handling a primary concern by using a “[Railway Oriented Programming](http://fsharpforfunandprofit.com/rop/)” approach for capturing and returning errors from any step in the transaction.
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
- dry-transaction is based on the following ideas:
16
+ ## Links
17
17
 
18
- * A business transaction is a series of operations where each can fail and stop processing.
19
- * A business transaction resolves its dependencies using an external container object and it doesn’t know any details about the individual operation objects except their identifiers.
20
- * A business transaction can describe its steps on an abstract level without being coupled to any details about how individual operations work.
21
- * A business transaction doesn’t have any state.
22
- * Each operation shouldn’t accumulate state, instead it should receive an input and return an output without causing any side-effects.
23
- * The only interface of a an operation is `#call(input)`.
24
- * Each operation provides a meaningful functionality and can be reused.
25
- * Errors in any operation can be easily caught and handled as part of the normal application flow.
26
-
27
- ## Why?
28
-
29
- Requiring a business transaction’s steps to exist as independent operations directly addressable via a container means that they can be tested in isolation and easily reused throughout your application. Following from this, keeping the business transaction to a series of high-level, declarative steps ensures that it’s easy to understand at a glance.
30
-
31
- The output of each step is wrapped in a [Kleisli](https://github.com/txus/kleisli) `Either` object (`Right` for success or `Left` for failure). This allows the steps to be chained together and ensures that processing stops in the case of a failure. Returning an `Either` from the overall transaction also allows for error handling to remain a primary concern without it getting in the way of tidy, straightforward operation logic. Wrapping the step output also means that you can work with a wide variety of operations within your application – they don’t need to return an `Either` already.
32
-
33
- ## Usage
34
-
35
- ### Container
36
-
37
- All you need to use dry-transaction is a container to hold your application’s operations. Each operation must respond to `#call(input)`.
38
-
39
- The operations will be resolved from the container via `#[]`. For our examples, we’ll use a plain hash:
40
-
41
- ```ruby
42
- container = {
43
- process: -> input { {name: input["name"], email: input["email"]} },
44
- validate: -> input { input[:email].nil? ? raise(ValidationFailure, "not valid") : input },
45
- persist: -> input { DB << input and true }
46
- }
47
- ```
48
-
49
- For larger apps, you may like to consider something like [dry-container](https://github.com/dryrb/dry-container).
50
-
51
- ### Defining a transaction
52
-
53
- Define a transaction to bring your opererations together:
54
-
55
- ```ruby
56
- save_user = Dry.Transaction(container: container) do
57
- map :process
58
- try :validate, catch: ValidationFailure
59
- tee :persist
60
- end
61
- ```
62
-
63
- Operations are formed into steps using _step adapters._ Step adapters wrap the output of your operations to make them easy to integrate into a transaction. The following adapters are available:
64
-
65
- * `step` – the operation already returns an `Either` object (`Right(output)` for success and `Left(output)` for failure), and needs no special handling.
66
- * `map` – any output is considered successful and returned as `Right(output)`
67
- * `try` – the operation may raise an exception in an error case. This is caught and returned as `Left(exception)`. The output is otherwise returned as `Right(output)`.
68
- * `tee` – the operation interacts with some external system and has no meaningful output. The original input is passed through and returned as `Right(input)`.
69
-
70
- ### Calling a transaction
71
-
72
- Calling a transaction will run its operations in their specified order, with the output of each operation becoming the input for the next.
73
-
74
- ```ruby
75
- DB = []
76
-
77
- save_user.call("name" => "Jane", "email" => "jane@doe.com")
78
- # => Right({:name=>"Jane", :email=>"jane@doe.com"})
79
-
80
- DB
81
- # => [{:name=>"Jane", :email=>"jane@doe.com"}]
82
- ```
83
-
84
- Each transaction returns a result value wrapped in a `Left` or `Right` object (based on the output of its final step). You can handle these results (including errors arising from particular steps) with a match block:
85
-
86
- ```ruby
87
- save_user.call(name: "Jane", email: "jane@doe.com") do |m|
88
- m.success do |value|
89
- puts "Succeeded!"
90
- end
91
-
92
- m.failure :validate do |error|
93
- # In a more realistic example, you’d loop through a list of messages in `errors`.
94
- puts "Please provide an email address."
95
- end
96
-
97
- m.failure do |error|
98
- puts "Couldn’t save this user."
99
- end
100
- end
101
- ```
102
-
103
- ### Passing additional step arguments
104
-
105
- Additional arguments for step operations can be passed at the time of calling your transaction. Provide these arguments as an array, and they’ll be [splatted](https://endofline.wordpress.com/2011/01/21/the-strange-ruby-splat/) into the front of the operation’s arguments. This means that transactions can effectively support operations with any sort of `#call(*args, input)` interface.
106
-
107
- ```ruby
108
- DB = []
109
-
110
- container = {
111
- process: -> input { {name: input["name"], email: input["email"]} },
112
- validate: -> allowed, input { input[:email].include?(allowed) ? raise(ValidationFailure, "not allowed") : input },
113
- persist: -> input { DB << input and true }
114
- }
115
-
116
- save_user = Dry.Transaction(container: container) do
117
- map :process
118
- try :validate, catch: ValidationFailure
119
- tee :persist
120
- end
121
-
122
- input = {"name" => "Jane", "email" => "jane@doe.com"}
123
- save_user.call(input, validate: ["doe.com"])
124
- # => Right({:name=>"Jane", :email=>"jane@doe.com"})
125
-
126
- save_user.call(input, validate: ["smith.com"])
127
- # => Left("not allowed")
128
- ```
129
-
130
- ### Subscribing to step notifications
131
-
132
- As well as pattern matching on the final transaction result, you can subscribe to individual steps and trigger specific behaviour based on their success or failure:
133
-
134
- ```ruby
135
- NOTIFICATIONS = []
136
-
137
- module UserPersistListener
138
- extend self
139
-
140
- def persist_success(user)
141
- NOTIFICATIONS << "#{user[:email]} persisted"
142
- end
143
-
144
- def persist_failure(user)
145
- NOTIFICATIONS << "#{user[:email]} failed to persist"
146
- end
147
- end
148
-
149
-
150
- input = {"name" => "Jane", "email" => "jane@doe.com"}
151
-
152
- save_user.subscribe(persist: UserPersistListener)
153
- save_user.call(input, validate: ["doe.com"])
154
-
155
- NOTIFICATIONS
156
- # => ["jane@doe.com persisted"]
157
- ```
158
-
159
- This pub/sub mechanism is provided by the [Wisper](https://github.com/krisleech/wisper) gem. You can subscribe to specific steps using the `#subscribe(step_name: listener)` API, or subscribe to all steps via `#subscribe(listener)`.
160
-
161
- ### Extending transactions
162
-
163
- You can extend existing transactions by inserting or removing steps. See the [API docs](http://www.rubydoc.info/github/dry-rb/dry-transaction/Dry/Transaction/Sequence) for more information.
164
-
165
- ### Working with a larger container
166
-
167
- In practice, your container won’t be a trivial collection of generically named operations. You can keep your transaction step names simple by using the `with:` option to provide the identifiers for the operations within your container:
168
-
169
- ```ruby
170
- save_user = Dry.Transaction(container: large_whole_app_container) do
171
- map :process, with: "attributes.user"
172
- try :validate, with: "validations.user", catch: ValidationFailure
173
- tee :persist, with: "persistance.commands.update_user"
174
- end
175
- ```
176
-
177
- ## Installation
178
-
179
- Add this line to your application’s `Gemfile`:
180
-
181
- ```ruby
182
- gem "dry-transaction"
183
- ```
184
-
185
- Run `bundle` to install the gem.
186
-
187
- ## Documentation
188
-
189
- View the [full API documentation](http://www.rubydoc.info/github/dry-rb/dry-transaction) on RubyDoc.info.
190
-
191
- ## Contributing
192
-
193
- Bug reports and pull requests are welcome on [GitHub](http://github.com/dry-rb/dry-transaction).
194
-
195
- ## Credits
196
-
197
- dry-transaction is developed and maintained by [Icelab](http://icelab.com.au/).
198
-
199
- dry-transaction’s error handling is based on Scott Wlaschin’s [Railway Oriented Programming](http://fsharpforfunandprofit.com/rop/), found via Zohaib Rauf’s [Railway Oriented Programming in Elixir](http://zohaib.me/railway-programming-pattern-in-elixir/) blog post. dry-transaction’s behavior as a business transaction library draws heavy inspiration from Piotr Solnica’s [Transflow](http://github.com/solnic/transflow) and Gilbert B Garza’s [Solid Use Case](https://github.com/mindeavor/solid_use_case). Josep M. Bach’s [Kleisli](https://github.com/txus/kleisli) gem makes functional programming patterns in Ruby accessible and fun. Thank you all!
18
+ * [Documentation](http://dry-rb.org/gems/dry-transaction)
19
+ * [API documentation](http://www.rubydoc.info/github/dry-rb/dry-transaction)
200
20
 
201
21
  ## License
202
22
 
@@ -1,41 +1,39 @@
1
1
  require "dry/transaction/step"
2
2
  require "dry/transaction/step_adapters"
3
- require "dry/transaction/step_adapters/base"
4
- require "dry/transaction/step_adapters/map"
5
- require "dry/transaction/step_adapters/raw"
6
- require "dry/transaction/step_adapters/tee"
7
- require "dry/transaction/step_adapters/try"
8
3
  require "dry/transaction/sequence"
9
4
 
10
5
  module Dry
11
6
  module Transaction
7
+ # @api private
12
8
  class DSL
13
- # @api private
14
- attr_reader :options
15
-
16
- # @api private
17
9
  attr_reader :container
18
-
19
- # @api private
10
+ attr_reader :step_adapters
20
11
  attr_reader :steps
21
12
 
22
- # @api private
23
13
  def initialize(options, &block)
24
- @options = options
25
14
  @container = options.fetch(:container)
15
+ @step_adapters = options.fetch(:step_adapters, StepAdapters)
26
16
  @steps = []
27
17
 
28
- instance_exec(&block)
18
+ instance_eval(&block)
19
+ end
20
+
21
+ def respond_to_missing?(method_name)
22
+ step_adapters.key?(method_name)
29
23
  end
30
24
 
31
- StepAdapters.each do |adapter_name, adapter_class|
32
- define_method adapter_name do |step_name, options = {}|
33
- operation = container[options.fetch(:with, step_name)]
34
- steps << Step.new(step_name, adapter_class.new(operation, options))
35
- end
25
+ def method_missing(method_name, *args)
26
+ return super unless step_adapters.key?(method_name)
27
+
28
+ step_adapter = step_adapters[method_name]
29
+ step_name = args.first
30
+ options = args.last.is_a?(Hash) ? args.last : {}
31
+ operation_name = options.delete(:with) || step_name
32
+ operation = container[operation_name]
33
+
34
+ steps << Step.new(step_adapter, step_name, operation_name, operation, options)
36
35
  end
37
36
 
38
- # @api private
39
37
  def call
40
38
  Sequence.new(steps)
41
39
  end
@@ -72,7 +72,7 @@ module Dry
72
72
  # my_transaction.subscribe(my_listener)
73
73
  #
74
74
  # @example Subscribing to notifications from specific steps
75
- # my_transaction.subscirbe(some_step: my_listener, another_step: another_listener)
75
+ # my_transaction.subscribe(some_step: my_listener, another_step: another_listener)
76
76
  #
77
77
  # Notifications are implemented using the [Wisper](wisper) gem.
78
78
  #
@@ -7,23 +7,29 @@ module Dry
7
7
  class Step
8
8
  include Wisper::Publisher
9
9
 
10
+ attr_reader :step_adapter
10
11
  attr_reader :step_name
12
+ attr_reader :operation_name
11
13
  attr_reader :operation
14
+ attr_reader :options
12
15
  attr_reader :call_args
13
16
 
14
- def initialize(step_name, operation, call_args = [])
17
+ def initialize(step_adapter, step_name, operation_name, operation, options, call_args = [])
18
+ @step_adapter = step_adapter
15
19
  @step_name = step_name
20
+ @operation_name = operation_name
16
21
  @operation = operation
22
+ @options = options
17
23
  @call_args = call_args
18
24
  end
19
25
 
20
26
  def with_call_args(*call_args)
21
- self.class.new(step_name, operation, call_args)
27
+ self.class.new(step_adapter, step_name, operation_name, operation, options, call_args)
22
28
  end
23
29
 
24
30
  def call(input)
25
31
  args = call_args + [input]
26
- result = operation.call(*args)
32
+ result = step_adapter.call(self, *args)
27
33
 
28
34
  result.fmap { |value|
29
35
  broadcast :"#{step_name}_success", value
@@ -35,7 +41,7 @@ module Dry
35
41
  end
36
42
 
37
43
  def arity
38
- operation.arity
44
+ operation.is_a?(Proc) ? operation.arity : operation.method(:call).arity
39
45
  end
40
46
  end
41
47
  end
@@ -1,27 +1,14 @@
1
- require "forwardable"
1
+ require "dry-container"
2
2
 
3
3
  module Dry
4
4
  module Transaction
5
- module StepAdapters
6
- @registry = {}
7
-
8
- class << self
9
- attr_reader :registry
10
- private :registry
11
-
12
- extend Forwardable
13
- def_delegators :registry, :[], :each
14
-
15
- # Register a step adapter.
16
- #
17
- # @param [Symbol] name the name to expose for adding steps to a transaction
18
- # @param klass the step adapter class
19
- #
20
- # @api public
21
- def register(name, klass)
22
- registry[name.to_sym] = klass
23
- end
24
- end
5
+ class StepAdapters
6
+ extend Dry::Container::Mixin
25
7
  end
26
8
  end
27
9
  end
10
+
11
+ require "dry/transaction/step_adapters/map"
12
+ require "dry/transaction/step_adapters/raw"
13
+ require "dry/transaction/step_adapters/tee"
14
+ require "dry/transaction/step_adapters/try"
@@ -1,14 +1,14 @@
1
1
  module Dry
2
2
  module Transaction
3
- module StepAdapters
3
+ class StepAdapters
4
4
  # @api private
5
- class Map < Base
6
- def call(*args, input)
7
- Right(operation.call(*args, input))
5
+ class Map
6
+ def call(step, *args, input)
7
+ Right(step.operation.call(*args, input))
8
8
  end
9
9
  end
10
10
 
11
- register :map, Map
11
+ register :map, Map.new
12
12
  end
13
13
  end
14
14
  end
@@ -1,14 +1,22 @@
1
+ require "kleisli"
2
+
1
3
  module Dry
2
4
  module Transaction
3
- module StepAdapters
5
+ class StepAdapters
4
6
  # @api private
5
- class Raw < Base
6
- def call(*args, input)
7
- operation.call(*args, input)
7
+ class Raw
8
+ def call(step, *args, input)
9
+ result = step.operation.call(*args, input)
10
+
11
+ unless result.is_a?(Kleisli::Either)
12
+ raise ArgumentError, "step +#{step.step_name}+ must return an Either object"
13
+ end
14
+
15
+ result
8
16
  end
9
17
  end
10
18
 
11
- register :step, Raw
19
+ register :step, Raw.new
12
20
  end
13
21
  end
14
22
  end
@@ -1,15 +1,15 @@
1
1
  module Dry
2
2
  module Transaction
3
- module StepAdapters
3
+ class StepAdapters
4
4
  # @api private
5
- class Tee < Base
6
- def call(*args, input)
7
- operation.call(*args, input)
5
+ class Tee
6
+ def call(step, *args, input)
7
+ step.operation.call(*args, input)
8
8
  Right(input)
9
9
  end
10
10
  end
11
11
 
12
- register :tee, Tee
12
+ register :tee, Tee.new
13
13
  end
14
14
  end
15
15
  end
@@ -1,21 +1,20 @@
1
1
  module Dry
2
2
  module Transaction
3
- module StepAdapters
3
+ class StepAdapters
4
4
  # @api private
5
- class Try < Base
6
- def initialize(*)
7
- super
8
- raise ArgumentError, "+try+ steps require one or more exception classes provided via +catch:+" unless options[:catch]
9
- end
5
+ class Try
6
+ def call(step, *args, input)
7
+ unless step.options[:catch]
8
+ raise ArgumentError, "+try+ steps require one or more exception classes provided via +catch:+"
9
+ end
10
10
 
11
- def call(*args, input)
12
- Right(operation.call(*args, input))
13
- rescue *Array(options[:catch]) => e
11
+ Right(step.operation.call(*args, input))
12
+ rescue *Array(step.options[:catch]) => e
14
13
  Left(e)
15
14
  end
16
15
  end
17
16
 
18
- register :try, Try
17
+ register :try, Try.new
19
18
  end
20
19
  end
21
20
  end
@@ -1,6 +1,6 @@
1
1
  module Dry
2
2
  # Business transaction DSL.
3
3
  module Transaction
4
- VERSION = "0.5.0".freeze
4
+ VERSION = "0.6.0".freeze
5
5
  end
6
6
  end
data/spec/examples.txt CHANGED
@@ -1,41 +1,43 @@
1
1
  example_id | status | run_time |
2
2
  -------------------------------------------------------- | ------ | --------------- |
3
- ./spec/integration/passing_step_arguments_spec.rb[1:1:1] | passed | 0.00101 seconds |
4
- ./spec/integration/passing_step_arguments_spec.rb[1:2:1] | passed | 0.00284 seconds |
5
- ./spec/integration/passing_step_arguments_spec.rb[1:3:1] | passed | 0.00047 seconds |
6
- ./spec/integration/publishing_step_events_spec.rb[1:1:1] | passed | 0.00063 seconds |
7
- ./spec/integration/publishing_step_events_spec.rb[1:1:2] | passed | 0.00518 seconds |
8
- ./spec/integration/publishing_step_events_spec.rb[1:2:1] | passed | 0.00041 seconds |
9
- ./spec/integration/publishing_step_events_spec.rb[1:2:2] | passed | 0.00036 seconds |
3
+ ./spec/integration/custom_step_adapters_spec.rb[1:1] | passed | 0.00032 seconds |
4
+ ./spec/integration/passing_step_arguments_spec.rb[1:1:1] | passed | 0.001 seconds |
5
+ ./spec/integration/passing_step_arguments_spec.rb[1:2:1] | passed | 0.00021 seconds |
6
+ ./spec/integration/passing_step_arguments_spec.rb[1:3:1] | passed | 0.0002 seconds |
7
+ ./spec/integration/publishing_step_events_spec.rb[1:1:1] | passed | 0.00089 seconds |
8
+ ./spec/integration/publishing_step_events_spec.rb[1:1:2] | passed | 0.00323 seconds |
9
+ ./spec/integration/publishing_step_events_spec.rb[1:2:1] | passed | 0.00071 seconds |
10
+ ./spec/integration/publishing_step_events_spec.rb[1:2:2] | passed | 0.00032 seconds |
10
11
  ./spec/integration/transaction_spec.rb[1:1:1] | passed | 0.00166 seconds |
11
- ./spec/integration/transaction_spec.rb[1:1:2] | passed | 0.00049 seconds |
12
- ./spec/integration/transaction_spec.rb[1:1:3] | passed | 0.00045 seconds |
13
- ./spec/integration/transaction_spec.rb[1:1:4] | passed | 0.00101 seconds |
14
- ./spec/integration/transaction_spec.rb[1:1:5] | passed | 0.00028 seconds |
15
- ./spec/integration/transaction_spec.rb[1:2:1] | passed | 0.00024 seconds |
16
- ./spec/integration/transaction_spec.rb[1:2:2] | passed | 0.00022 seconds |
17
- ./spec/integration/transaction_spec.rb[1:2:3] | passed | 0.00033 seconds |
18
- ./spec/integration/transaction_spec.rb[1:2:4] | passed | 0.00038 seconds |
19
- ./spec/integration/transaction_spec.rb[1:2:5] | passed | 0.00021 seconds |
20
- ./spec/integration/transaction_spec.rb[1:2:6] | passed | 0.00022 seconds |
21
- ./spec/integration/transaction_spec.rb[1:3:1] | passed | 0.00214 seconds |
22
- ./spec/integration/transaction_spec.rb[1:3:2] | passed | 0.00037 seconds |
12
+ ./spec/integration/transaction_spec.rb[1:1:2] | passed | 0.00031 seconds |
13
+ ./spec/integration/transaction_spec.rb[1:1:3] | passed | 0.00024 seconds |
14
+ ./spec/integration/transaction_spec.rb[1:1:4] | passed | 0.00033 seconds |
15
+ ./spec/integration/transaction_spec.rb[1:1:5] | passed | 0.00036 seconds |
16
+ ./spec/integration/transaction_spec.rb[1:2:1] | passed | 0.00029 seconds |
17
+ ./spec/integration/transaction_spec.rb[1:2:2] | passed | 0.00023 seconds |
18
+ ./spec/integration/transaction_spec.rb[1:2:3] | passed | 0.00025 seconds |
19
+ ./spec/integration/transaction_spec.rb[1:2:4] | passed | 0.00025 seconds |
20
+ ./spec/integration/transaction_spec.rb[1:2:5] | passed | 0.00026 seconds |
21
+ ./spec/integration/transaction_spec.rb[1:2:6] | passed | 0.00024 seconds |
22
+ ./spec/integration/transaction_spec.rb[1:3:1] | passed | 0.00242 seconds |
23
+ ./spec/integration/transaction_spec.rb[1:3:2] | passed | 0.00023 seconds |
23
24
  ./spec/integration/transaction_spec.rb[1:3:3] | passed | 0.00022 seconds |
24
- ./spec/unit/sequence_spec.rb[1:1:1] | passed | 0.00014 seconds |
25
- ./spec/unit/sequence_spec.rb[1:1:2] | passed | 0.00032 seconds |
26
- ./spec/unit/sequence_spec.rb[1:1:3] | passed | 0.00032 seconds |
27
- ./spec/unit/sequence_spec.rb[1:1:4] | passed | 0.00013 seconds |
28
- ./spec/unit/sequence_spec.rb[1:2:1] | passed | 0.00031 seconds |
29
- ./spec/unit/sequence_spec.rb[1:2:2] | passed | 0.00017 seconds |
30
- ./spec/unit/sequence_spec.rb[1:2:3] | passed | 0.00012 seconds |
31
- ./spec/unit/sequence_spec.rb[1:2:4] | passed | 0.00013 seconds |
32
- ./spec/unit/sequence_spec.rb[1:3:1] | passed | 0.00013 seconds |
33
- ./spec/unit/sequence_spec.rb[1:3:2] | passed | 0.00014 seconds |
34
- ./spec/unit/sequence_spec.rb[1:4:1] | passed | 0.00034 seconds |
35
- ./spec/unit/sequence_spec.rb[1:4:2] | passed | 0.00031 seconds |
36
- ./spec/unit/sequence_spec.rb[1:4:3] | passed | 0.00016 seconds |
37
- ./spec/unit/sequence_spec.rb[1:4:4] | passed | 0.00011 seconds |
38
- ./spec/unit/sequence_spec.rb[1:4:5:1] | passed | 0.00017 seconds |
39
- ./spec/unit/sequence_spec.rb[1:4:5:2] | passed | 0.00033 seconds |
40
- ./spec/unit/sequence_spec.rb[1:4:6:1] | passed | 0.00033 seconds |
41
- ./spec/unit/sequence_spec.rb[1:4:6:2] | passed | 0.00015 seconds |
25
+ ./spec/integration/transaction_spec.rb[1:4:1] | passed | 0.00026 seconds |
26
+ ./spec/unit/sequence_spec.rb[1:1:1] | passed | 0.00028 seconds |
27
+ ./spec/unit/sequence_spec.rb[1:1:2] | passed | 0.00017 seconds |
28
+ ./spec/unit/sequence_spec.rb[1:1:3] | passed | 0.00015 seconds |
29
+ ./spec/unit/sequence_spec.rb[1:1:4] | passed | 0.00017 seconds |
30
+ ./spec/unit/sequence_spec.rb[1:2:1] | passed | 0.0003 seconds |
31
+ ./spec/unit/sequence_spec.rb[1:2:2] | passed | 0.00021 seconds |
32
+ ./spec/unit/sequence_spec.rb[1:2:3] | passed | 0.0023 seconds |
33
+ ./spec/unit/sequence_spec.rb[1:2:4] | passed | 0.00161 seconds |
34
+ ./spec/unit/sequence_spec.rb[1:3:1] | passed | 0.00018 seconds |
35
+ ./spec/unit/sequence_spec.rb[1:3:2] | passed | 0.00018 seconds |
36
+ ./spec/unit/sequence_spec.rb[1:4:1] | passed | 0.00027 seconds |
37
+ ./spec/unit/sequence_spec.rb[1:4:2] | passed | 0.0003 seconds |
38
+ ./spec/unit/sequence_spec.rb[1:4:3] | passed | 0.00017 seconds |
39
+ ./spec/unit/sequence_spec.rb[1:4:4] | passed | 0.00014 seconds |
40
+ ./spec/unit/sequence_spec.rb[1:4:5:1] | passed | 0.0002 seconds |
41
+ ./spec/unit/sequence_spec.rb[1:4:5:2] | passed | 0.00018 seconds |
42
+ ./spec/unit/sequence_spec.rb[1:4:6:1] | passed | 0.00021 seconds |
43
+ ./spec/unit/sequence_spec.rb[1:4:6:2] | passed | 0.00021 seconds |
@@ -0,0 +1,37 @@
1
+ RSpec.describe "Custom step adapters" do
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
+ }
9
+
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
+ }
16
+ }
17
+
18
+ before do
19
+ Test::DB = []
20
+ Test::QUEUE = []
21
+
22
+ module Test
23
+ class CustomStepAdapters < Dry::Transaction::StepAdapters
24
+ register :enqueue, -> step, *args, input {
25
+ Test::QUEUE << step.operation.call(*args, input)
26
+ Right(input)
27
+ }
28
+ end
29
+ end
30
+ end
31
+
32
+ it "supports custom step adapters" do
33
+ input = {"name" => "Jane", "email" => "jane@doe.com"}
34
+ transaction.call(input)
35
+ expect(Test::QUEUE).to include("Delivered email to jane@doe.com")
36
+ end
37
+ end
@@ -136,4 +136,16 @@ RSpec.describe "Transactions" do
136
136
  expect(transaction.call(input).value).to eq "raw failure"
137
137
  end
138
138
  end
139
+
140
+ context "non-confirming raw step result" do
141
+ let(:input) { {"name" => "Jane", "email" => "jane@doe.com"} }
142
+
143
+ before do
144
+ container[:verify] = -> input { "failure" }
145
+ end
146
+
147
+ it "raises an exception" do
148
+ expect { transaction.call(input) }.to raise_error(ArgumentError)
149
+ end
150
+ end
139
151
  end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,11 @@
1
- require "simplecov"
2
- SimpleCov.minimum_coverage 100
3
- SimpleCov.start do
4
- add_filter "/spec/"
1
+ if RUBY_ENGINE == "ruby"
2
+ require "codeclimate-test-reporter"
3
+ CodeClimate::TestReporter.start
4
+
5
+ require "simplecov"
6
+ SimpleCov.start do
7
+ add_filter "/spec/"
8
+ end
5
9
  end
6
10
 
7
11
  require "dry-transaction"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-transaction
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.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-03-16 00:00:00.000000000 Z
11
+ date: 2016-04-05 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dry-container
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.8
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.8
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: kleisli
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +58,14 @@ dependencies:
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '1.10'
61
+ version: 1.11.2
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '1.10'
68
+ version: 1.11.2
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -127,7 +141,6 @@ files:
127
141
  - lib/dry/transaction/sequence.rb
128
142
  - lib/dry/transaction/step.rb
129
143
  - lib/dry/transaction/step_adapters.rb
130
- - lib/dry/transaction/step_adapters/base.rb
131
144
  - lib/dry/transaction/step_adapters/map.rb
132
145
  - lib/dry/transaction/step_adapters/raw.rb
133
146
  - lib/dry/transaction/step_adapters/tee.rb
@@ -135,6 +148,7 @@ files:
135
148
  - lib/dry/transaction/step_failure.rb
136
149
  - lib/dry/transaction/version.rb
137
150
  - spec/examples.txt
151
+ - spec/integration/custom_step_adapters_spec.rb
138
152
  - spec/integration/passing_step_arguments_spec.rb
139
153
  - spec/integration/publishing_step_events_spec.rb
140
154
  - spec/integration/transaction_spec.rb
@@ -1,20 +0,0 @@
1
- module Dry
2
- module Transaction
3
- module StepAdapters
4
- # @api private
5
- class Base
6
- attr_reader :operation
7
- attr_reader :options
8
-
9
- def initialize(operation, options)
10
- @operation = operation
11
- @options = options
12
- end
13
-
14
- def arity
15
- operation.is_a?(Proc) ? operation.arity : operation.method(:call).arity
16
- end
17
- end
18
- end
19
- end
20
- end