dry-transaction 0.5.0 → 0.6.0

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