teckel 0.2.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +111 -0
- data/LICENSE_LOGO +4 -0
- data/README.md +4 -4
- data/lib/teckel.rb +9 -4
- data/lib/teckel/chain.rb +31 -341
- data/lib/teckel/chain/config.rb +275 -0
- data/lib/teckel/chain/result.rb +38 -0
- data/lib/teckel/chain/runner.rb +62 -0
- data/lib/teckel/chain/step.rb +18 -0
- data/lib/teckel/config.rb +25 -28
- data/lib/teckel/contracts.rb +19 -0
- data/lib/teckel/operation.rb +84 -302
- data/lib/teckel/operation/config.rb +396 -0
- data/lib/teckel/operation/result.rb +92 -0
- data/lib/teckel/operation/runner.rb +74 -0
- data/lib/teckel/result.rb +52 -53
- data/lib/teckel/version.rb +1 -1
- data/spec/chain/around_hook_spec.rb +100 -0
- data/spec/chain/default_settings_spec.rb +39 -0
- data/spec/chain/inheritance_spec.rb +116 -0
- data/spec/chain/none_input_spec.rb +36 -0
- data/spec/chain/results_spec.rb +53 -0
- data/spec/chain_spec.rb +180 -0
- data/spec/config_spec.rb +26 -0
- data/spec/doctest_helper.rb +8 -0
- data/spec/operation/contract_trace_spec.rb +116 -0
- data/spec/operation/default_settings_spec.rb +94 -0
- data/spec/operation/fail_on_input_spec.rb +103 -0
- data/spec/operation/inheritance_spec.rb +94 -0
- data/spec/operation/result_spec.rb +55 -0
- data/spec/operation/results_spec.rb +117 -0
- data/spec/operation_spec.rb +483 -0
- data/spec/rb27/pattern_matching_spec.rb +193 -0
- data/spec/result_spec.rb +22 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/dry_base.rb +8 -0
- data/spec/support/fake_db.rb +12 -0
- data/spec/support/fake_models.rb +20 -0
- data/spec/teckel_spec.rb +7 -0
- metadata +68 -28
- data/.codeclimate.yml +0 -3
- data/.github/workflows/ci.yml +0 -92
- data/.github/workflows/pages.yml +0 -50
- data/.gitignore +0 -15
- data/.rspec +0 -3
- data/.rubocop.yml +0 -12
- data/.ruby-version +0 -1
- data/DEVELOPMENT.md +0 -32
- data/Gemfile +0 -16
- data/Rakefile +0 -35
- data/bin/console +0 -15
- data/bin/rake +0 -29
- data/bin/rspec +0 -29
- data/bin/rubocop +0 -18
- data/bin/setup +0 -8
- data/lib/teckel/none.rb +0 -18
- data/lib/teckel/operation/results.rb +0 -72
- data/teckel.gemspec +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df9c73577c9c93fbaaf2e7661a07bdc3b1b0397832a62a8edbe4eea846c4c029
|
4
|
+
data.tar.gz: 037041bf0eb6ae8bc41e354772d3ce1d04002a35ecccc0b1ecefa33a85845c3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f6f601e816c972aed8e511b05fdbc418a48aab10c2ca246d108661cc25a8d514a41944b2e01b9294757a2a717696491f20f601f5f60e53faa45651aec48bc7f
|
7
|
+
data.tar.gz: 68bc46e5ed21cf5e639ee1fc00265b278f9062f42de9d179a07559862990a078df9dcb2ef26956eb629cf08e71409b9b05bd363a11930cb64ecdee533d63d2ea
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,116 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
## 0.7.0
|
4
|
+
|
5
|
+
- Breaking: `Teckel::Chain` will not be required by default. require manually if needed `require "teckel/chain"` [GH-24]
|
6
|
+
- Breaking: Internally, `Teckel::Operation::Runner` instead of `:success` and `:failure` now only uses `:halt` as it's throw-catch symbol. [GH-26]
|
7
|
+
- Add: Using the default `Teckel::Operation::Runner`, `input_constructor` and `result_constructor` will be executed
|
8
|
+
within the context of the operation instance. This allows for `input_constructor` to call `fail!` and `success!`
|
9
|
+
without ever `call`ing the operation. [GH-26]
|
10
|
+
|
11
|
+
|
12
|
+
## 0.6.0
|
13
|
+
|
14
|
+
- Breaking: Operations return values will be ignored. [GH-21]
|
15
|
+
* You'll need to use `success!` or `failure!`
|
16
|
+
* `success!` and `failure!` are now implemented on the `Runner`, which makes it easier to change their behavior (including the one above).
|
17
|
+
|
18
|
+
## 0.5.0
|
19
|
+
|
20
|
+
- Fix: calling chain with settings and no input [GH-14]
|
21
|
+
- Add: Default settings for Operation and Chains [GH-17], [GH-18]
|
22
|
+
```ruby
|
23
|
+
class MyOperation
|
24
|
+
include Teckel::Operation
|
25
|
+
|
26
|
+
settings Struct.new(:logger)
|
27
|
+
|
28
|
+
# If your settings class can cope with no input and you want to make sure
|
29
|
+
# `settings` gets initialized and set.
|
30
|
+
# settings will be #<struct logger=nil>
|
31
|
+
default_settings!
|
32
|
+
|
33
|
+
# settings will be #<struct logger=MyGlobalLogger>
|
34
|
+
default_settings!(MyGlobalLogger)
|
35
|
+
|
36
|
+
# settings will be #<struct logger=#<Logger:<...>>
|
37
|
+
default_settings! -> { settings.new(Logger.new("/tmp/my.log")) }
|
38
|
+
end
|
39
|
+
|
40
|
+
class Chain
|
41
|
+
include Teckel::Chain
|
42
|
+
|
43
|
+
# set or overwrite operation settings
|
44
|
+
default_settings!(a: MyOtherLogger)
|
45
|
+
|
46
|
+
step :a, MyOperation
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
Internal:
|
51
|
+
- Move operation and chain config dsl methods into own module [GH-15]
|
52
|
+
- Code simplifications [GH-16]
|
53
|
+
|
54
|
+
## 0.4.0
|
55
|
+
|
56
|
+
- Moving verbose examples from API docs into github pages
|
57
|
+
- `#finalize!` no longer freezes the entire Operation or Chain class, only it's settings. [GH-13]
|
58
|
+
- Add simple support for using Base classes. [GH-10]
|
59
|
+
Removes global configuration `Teckel::Config.default_constructor`
|
60
|
+
```ruby
|
61
|
+
class ApplicationOperation
|
62
|
+
include Teckel::Operation
|
63
|
+
# you won't be able to overwrite any configuration in child classes,
|
64
|
+
# so take care which you want to declare
|
65
|
+
result!
|
66
|
+
settings Struct.new(:logger)
|
67
|
+
input_constructor :new
|
68
|
+
error Struct.new(:status, :messages)
|
69
|
+
|
70
|
+
def log(message)
|
71
|
+
return unless settings&.logger
|
72
|
+
logger << message
|
73
|
+
end
|
74
|
+
# you cannot call `finalize!` on partially declared Operations
|
75
|
+
end
|
76
|
+
```
|
77
|
+
- Add support for setting your own Result objects. [GH-9]
|
78
|
+
- They should include and implement `Teckel::Result` which is needed by `Chain`.
|
79
|
+
- `Chain::StepFailure` got replaced with `Chain::Result`.
|
80
|
+
- the `Teckel::Operation::Results` module was removed. To let Operation use the default Result object, use the new helper `result!` instead.
|
81
|
+
- Add "settings"/dependency injection to Operation and Chains. [GH-7]
|
82
|
+
```ruby
|
83
|
+
MyOperation.with(logger: STDOUT).call(params)
|
84
|
+
|
85
|
+
MyChain.with(some_step: { logger: STDOUT }).call(params)
|
86
|
+
```
|
87
|
+
- [GH-5] Add support for ruby 2.7 pattern matching on Operation and Chain results. Both, array and hash notations are supported:
|
88
|
+
```ruby
|
89
|
+
case MyOperation.call(params)
|
90
|
+
in [false, value]
|
91
|
+
# handle failure
|
92
|
+
in [true, value]
|
93
|
+
# handle success
|
94
|
+
end
|
95
|
+
|
96
|
+
case MyChain.call(params)
|
97
|
+
in { success: false, step: :foo, value: value }
|
98
|
+
# handle foo failure
|
99
|
+
in [success: false, step: :bar, value: value }
|
100
|
+
# handle bar failure
|
101
|
+
in { success: true, value: value }
|
102
|
+
# handle success
|
103
|
+
end
|
104
|
+
```
|
105
|
+
- Fix setting a config twice to raise an error
|
106
|
+
|
107
|
+
## 0.3.0
|
108
|
+
|
109
|
+
- `finalize!`'ing a Chain will also finalize all it's Operations
|
110
|
+
- Changed attribute naming of `StepFailure`:
|
111
|
+
+ `.operation` will now give the operation class of the step - was `.step` before
|
112
|
+
+ `.step` will now give the name of the step (which Operation failed) - was `.step_name` before
|
113
|
+
|
3
114
|
## 0.2.0
|
4
115
|
|
5
116
|
- Around Hooks for Chains
|
data/LICENSE_LOGO
ADDED
data/README.md
CHANGED
@@ -34,11 +34,11 @@ Working with [Interactor](https://github.com/collectiveidea/interactor), [Trailb
|
|
34
34
|
|
35
35
|
## Usage
|
36
36
|
|
37
|
-
For a full overview please see the
|
37
|
+
For a full overview please see the Docs:
|
38
38
|
|
39
|
-
* [Operations](https://fnordfish.github.io/teckel/
|
40
|
-
* [
|
41
|
-
* [Chains](https://fnordfish.github.io/teckel/
|
39
|
+
* [Operations](https://fnordfish.github.io/teckel/operations/basics/)
|
40
|
+
* [Result Objects](https://fnordfish.github.io/teckel/operations/result_objects/)
|
41
|
+
* [Chains](https://fnordfish.github.io/teckel/chains/basics/)
|
42
42
|
|
43
43
|
|
44
44
|
```ruby
|
data/lib/teckel.rb
CHANGED
@@ -3,14 +3,19 @@
|
|
3
3
|
require "teckel/version"
|
4
4
|
|
5
5
|
module Teckel
|
6
|
+
# Base error class for this lib
|
6
7
|
class Error < StandardError; end
|
8
|
+
|
9
|
+
# configuring the same value twice will raise this
|
7
10
|
class FrozenConfigError < Teckel::Error; end
|
11
|
+
|
12
|
+
# missing important configurations (like contracts) will raise this
|
8
13
|
class MissingConfigError < Teckel::Error; end
|
14
|
+
|
15
|
+
DEFAULT_CONSTRUCTOR = :[]
|
9
16
|
end
|
10
17
|
|
11
18
|
require_relative "teckel/config"
|
12
|
-
require_relative "teckel/
|
13
|
-
require_relative "teckel/operation"
|
19
|
+
require_relative "teckel/contracts"
|
14
20
|
require_relative "teckel/result"
|
15
|
-
require_relative "teckel/operation
|
16
|
-
require_relative "teckel/chain"
|
21
|
+
require_relative "teckel/operation"
|
data/lib/teckel/chain.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'chain/config'
|
4
|
+
require_relative 'chain/step'
|
5
|
+
require_relative 'chain/result'
|
6
|
+
require_relative 'chain/runner'
|
4
7
|
|
5
8
|
module Teckel
|
6
9
|
# Railway style execution of multiple Operations.
|
@@ -8,338 +11,49 @@ module Teckel
|
|
8
11
|
# - Runs multiple Operations (steps) in order.
|
9
12
|
# - The output of an earlier step is passed as input to the next step.
|
10
13
|
# - Any failure will stop the execution chain (none of the later steps is called).
|
11
|
-
# - All Operations (steps) must
|
12
|
-
#
|
13
|
-
# object like {Teckel::Result}
|
14
|
-
# - A failure response is wrapped into a {Teckel::Chain::StepFailure} giving
|
15
|
-
# additional information about which step failed
|
14
|
+
# - All Operations (steps) must return a {Teckel::Result}
|
15
|
+
# - The result is wrapped into a {Teckel::Chain::Result}
|
16
16
|
#
|
17
|
-
# @see Teckel::Operation
|
18
|
-
#
|
19
|
-
# @example Defining a simple Chain with three steps
|
20
|
-
# class CreateUser
|
21
|
-
# include ::Teckel::Operation::Results
|
22
|
-
#
|
23
|
-
# input Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer.optional)
|
24
|
-
# output Types.Instance(User)
|
25
|
-
# error Types::Hash.schema(message: Types::String, errors: Types::Array.of(Types::Hash))
|
26
|
-
#
|
27
|
-
# def call(input)
|
28
|
-
# user = User.new(name: input[:name], age: input[:age])
|
29
|
-
# if user.save
|
30
|
-
# success!(user)
|
31
|
-
# else
|
32
|
-
# fail!(message: "Could not save User", errors: user.errors)
|
33
|
-
# end
|
34
|
-
# end
|
35
|
-
# end
|
36
|
-
#
|
37
|
-
# class LogUser
|
38
|
-
# include ::Teckel::Operation::Results
|
39
|
-
#
|
40
|
-
# input Types.Instance(User)
|
41
|
-
# output input
|
42
|
-
#
|
43
|
-
# def call(usr)
|
44
|
-
# Logger.new(File::NULL).info("User #{usr.name} created")
|
45
|
-
# usr # we need to return the correct output type
|
46
|
-
# end
|
47
|
-
# end
|
48
|
-
#
|
49
|
-
# class AddFriend
|
50
|
-
# class << self
|
51
|
-
# # Don't actually do this! It's not safe and for generating the failure sample only.
|
52
|
-
# attr_accessor :fail_befriend
|
53
|
-
# end
|
54
|
-
#
|
55
|
-
# include ::Teckel::Operation::Results
|
56
|
-
#
|
57
|
-
# input Types.Instance(User)
|
58
|
-
# output Types::Hash.schema(user: Types.Instance(User), friend: Types.Instance(User))
|
59
|
-
# error Types::Hash.schema(message: Types::String)
|
60
|
-
#
|
61
|
-
# def call(user)
|
62
|
-
# if self.class.fail_befriend
|
63
|
-
# fail!(message: "Did not find a friend.")
|
64
|
-
# else
|
65
|
-
# { user: user, friend: User.new(name: "A friend", age: 42) }
|
66
|
-
# end
|
67
|
-
# end
|
68
|
-
# end
|
69
|
-
#
|
70
|
-
# class MyChain
|
71
|
-
# include Teckel::Chain
|
72
|
-
#
|
73
|
-
# step :create, CreateUser
|
74
|
-
# step :log, LogUser
|
75
|
-
# step :befriend, AddFriend
|
76
|
-
# end
|
77
|
-
#
|
78
|
-
# result = MyChain.call(name: "Bob", age: 23)
|
79
|
-
# result.is_a?(Teckel::Result) #=> true
|
80
|
-
# result.success[:user].is_a?(User) #=> true
|
81
|
-
# result.success[:friend].is_a?(User) #=> true
|
82
|
-
#
|
83
|
-
# AddFriend.fail_befriend = true
|
84
|
-
# failure_result = MyChain.call(name: "Bob", age: 23)
|
85
|
-
# failure_result.is_a?(Teckel::Chain::StepFailure) #=> true
|
86
|
-
#
|
87
|
-
# # additional step information
|
88
|
-
# failure_result.step_name #=> :befriend
|
89
|
-
# failure_result.step #=> AddFriend
|
90
|
-
#
|
91
|
-
# # otherwise behaves just like a normal +Result+
|
92
|
-
# failure_result.failure? #=> true
|
93
|
-
# failure_result.failure #=> {message: "Did not find a friend."}
|
94
|
-
#
|
95
|
-
# @example DB transaction around hook
|
96
|
-
# class CreateUser
|
97
|
-
# include ::Teckel::Operation::Results
|
98
|
-
#
|
99
|
-
# input Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer.optional)
|
100
|
-
# output Types.Instance(User)
|
101
|
-
# error Types::Hash.schema(message: Types::String, errors: Types::Array.of(Types::Hash))
|
102
|
-
#
|
103
|
-
# def call(input)
|
104
|
-
# user = User.new(name: input[:name], age: input[:age])
|
105
|
-
# if user.save
|
106
|
-
# success!(user)
|
107
|
-
# else
|
108
|
-
# fail!(message: "Could not safe User", errors: user.errors)
|
109
|
-
# end
|
110
|
-
# end
|
111
|
-
# end
|
112
|
-
#
|
113
|
-
# class AddFriend
|
114
|
-
# class << self
|
115
|
-
# # Don't actually do this! It's not safe and for generating the failure sample only.
|
116
|
-
# attr_accessor :fail_befriend
|
117
|
-
# end
|
118
|
-
#
|
119
|
-
# include ::Teckel::Operation::Results
|
120
|
-
#
|
121
|
-
# input Types.Instance(User)
|
122
|
-
# output Types::Hash.schema(user: Types.Instance(User), friend: Types.Instance(User))
|
123
|
-
# error Types::Hash.schema(message: Types::String)
|
124
|
-
#
|
125
|
-
# def call(user)
|
126
|
-
# if self.class.fail_befriend
|
127
|
-
# fail!(message: "Did not find a friend.")
|
128
|
-
# else
|
129
|
-
# { user: user, friend: User.new(name: "A friend", age: 42) }
|
130
|
-
# end
|
131
|
-
# end
|
132
|
-
# end
|
133
|
-
#
|
134
|
-
# LOG = []
|
135
|
-
#
|
136
|
-
# class MyChain
|
137
|
-
# include Teckel::Chain
|
138
|
-
#
|
139
|
-
# around ->(chain, input) {
|
140
|
-
# result = nil
|
141
|
-
# begin
|
142
|
-
# LOG << :before
|
143
|
-
#
|
144
|
-
# FakeDB.transaction do
|
145
|
-
# result = chain.call(input)
|
146
|
-
# raise FakeDB::Rollback if result.failure?
|
147
|
-
# end
|
148
|
-
#
|
149
|
-
# LOG << :after
|
150
|
-
# result
|
151
|
-
# rescue FakeDB::Rollback
|
152
|
-
# LOG << :rollback
|
153
|
-
# result
|
154
|
-
# end
|
155
|
-
# }
|
156
|
-
#
|
157
|
-
# step :create, CreateUser
|
158
|
-
# step :befriend, AddFriend
|
159
|
-
# end
|
160
|
-
#
|
161
|
-
# AddFriend.fail_befriend = true
|
162
|
-
# failure_result = MyChain.call(name: "Bob", age: 23)
|
163
|
-
# failure_result.is_a?(Teckel::Chain::StepFailure) #=> true
|
164
|
-
#
|
165
|
-
# # triggered DB rollback
|
166
|
-
# LOG #=> [:before, :rollback]
|
167
|
-
#
|
168
|
-
# # additional step information
|
169
|
-
# failure_result.step_name #=> :befriend
|
170
|
-
# failure_result.step #=> AddFriend
|
171
|
-
#
|
172
|
-
# # otherwise behaves just like a normal +Result+
|
173
|
-
# failure_result.failure? #=> true
|
174
|
-
# failure_result.failure #=> {message: "Did not find a friend."}
|
17
|
+
# @see Teckel::Operation#result!
|
175
18
|
module Chain
|
176
|
-
# Like {Teckel::Result Teckel::Result} but for failing Chains
|
177
|
-
#
|
178
|
-
# When a Chain fails, it stores the failed +Operation+ and it's name.
|
179
|
-
class StepFailure
|
180
|
-
extend Forwardable
|
181
|
-
|
182
|
-
def initialize(step, step_name, result)
|
183
|
-
@step, @step_name, @result = step, step_name, result
|
184
|
-
end
|
185
|
-
|
186
|
-
# @!attribute step [R]
|
187
|
-
# @return [Teckel::Operation] the failed Operation
|
188
|
-
attr_reader :step
|
189
|
-
|
190
|
-
# @!attribute step_name [R]
|
191
|
-
# @return [String] the step name of the failed Operation
|
192
|
-
attr_reader :step_name
|
193
|
-
|
194
|
-
# @!attribute result [R]
|
195
|
-
# @return [Teckel::Result] the failure Result
|
196
|
-
attr_reader :result
|
197
|
-
|
198
|
-
# @!method value
|
199
|
-
# Delegates to +result.value+
|
200
|
-
# @see Teckel::Result#value
|
201
|
-
# @!method successful?
|
202
|
-
# Delegates to +result.successful?+
|
203
|
-
# @see Teckel::Result#successful?
|
204
|
-
# @!method success
|
205
|
-
# Delegates to +result.success+
|
206
|
-
# @see Teckel::Result#success
|
207
|
-
# @!method failure?
|
208
|
-
# Delegates to +result.failure?+
|
209
|
-
# @see Teckel::Result#failure?
|
210
|
-
# @!method failure
|
211
|
-
# Delegates to +result.failure+
|
212
|
-
# @see Teckel::Result#failure
|
213
|
-
def_delegators :@result, :value, :successful?, :success, :failure?, :failure
|
214
|
-
end
|
215
|
-
|
216
|
-
# The default implementation for executing a {Chain}
|
217
|
-
#
|
218
|
-
# @!visibility protected
|
219
|
-
class Runner
|
220
|
-
def initialize(steps)
|
221
|
-
@steps = steps
|
222
|
-
end
|
223
|
-
attr_reader :steps
|
224
|
-
|
225
|
-
# Run steps
|
226
|
-
#
|
227
|
-
# @param input Any form of input the first steps +input+ class can handle
|
228
|
-
#
|
229
|
-
# @return [Teckel::Result,Teckel::Chain::StepFailure] The result object wrapping
|
230
|
-
# either the success or failure value. Note that the {StepFailure} behaves
|
231
|
-
# just like a {Teckel::Result} with added information about which step failed.
|
232
|
-
def call(input)
|
233
|
-
last_result = input
|
234
|
-
failed = nil
|
235
|
-
steps.each do |(name, step)|
|
236
|
-
last_result = step.call(last_result)
|
237
|
-
if last_result.failure?
|
238
|
-
failed = StepFailure.new(step, name, last_result)
|
239
|
-
break
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
failed || last_result
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
19
|
module ClassMethods
|
248
20
|
# The expected input for this chain
|
249
21
|
# @return [Class] The {Teckel::Operation.input} of the first step
|
250
22
|
def input
|
251
|
-
|
23
|
+
steps.first&.operation&.input
|
252
24
|
end
|
253
25
|
|
254
26
|
# The expected output for this chain
|
255
27
|
# @return [Class] The {Teckel::Operation.output} of the last step
|
256
28
|
def output
|
257
|
-
|
29
|
+
steps.last&.operation&.output
|
258
30
|
end
|
259
31
|
|
260
32
|
# List of all possible errors
|
261
33
|
# @return [<Class>] List of all steps {Teckel::Operation.error}s
|
262
34
|
def errors
|
263
|
-
|
264
|
-
err =
|
35
|
+
steps.each_with_object([]) do |step, m|
|
36
|
+
err = step.operation.error
|
265
37
|
m << err if err
|
266
38
|
end
|
267
39
|
end
|
268
40
|
|
269
|
-
# Declare a {Operation} as a named step
|
270
|
-
#
|
271
|
-
# @param name [String,Symbol] The name of the operation.
|
272
|
-
# This name is used in an error case to let you know which step failed.
|
273
|
-
# @param operation [Operation::Results] The operation to call.
|
274
|
-
# Must return a {Teckel::Result} object.
|
275
|
-
def step(name, operation)
|
276
|
-
@steps << [name, operation]
|
277
|
-
end
|
278
|
-
|
279
|
-
# Set or get the optional around hook.
|
280
|
-
# A Hook might be given as a block or anything callable. The execution of
|
281
|
-
# the chain is yielded to this hook. The first argument being the callable
|
282
|
-
# chain ({Runner}) and the second argument the +input+ data. The hook also
|
283
|
-
# needs to return the result.
|
284
|
-
#
|
285
|
-
# @param callable [Proc,{#call}] The hook to pass chain execution control to. (nil)
|
286
|
-
#
|
287
|
-
# @return [Proc,{#call}] The configured hook
|
288
|
-
#
|
289
|
-
# @example Around hook with block
|
290
|
-
# OUTPUTS = []
|
291
|
-
#
|
292
|
-
# class Echo
|
293
|
-
# include ::Teckel::Operation::Results
|
294
|
-
#
|
295
|
-
# input Hash
|
296
|
-
# output input
|
297
|
-
#
|
298
|
-
# def call(hsh)
|
299
|
-
# hsh
|
300
|
-
# end
|
301
|
-
# end
|
302
|
-
#
|
303
|
-
# class MyChain
|
304
|
-
# include Teckel::Chain
|
305
|
-
#
|
306
|
-
# around do |chain, input|
|
307
|
-
# OUTPUTS << "before start"
|
308
|
-
# result = chain.call(input)
|
309
|
-
# OUTPUTS << "after start"
|
310
|
-
# result
|
311
|
-
# end
|
312
|
-
#
|
313
|
-
# step :noop, Echo
|
314
|
-
# end
|
315
|
-
#
|
316
|
-
# result = MyChain.call(some: 'test')
|
317
|
-
# OUTPUTS #=> ["before start", "after start"]
|
318
|
-
# result.success #=> { some: "test" }
|
319
|
-
def around(callable = nil, &block)
|
320
|
-
@config.for(:around, callable || block)
|
321
|
-
end
|
322
|
-
|
323
|
-
# @!attribute [r] runner()
|
324
|
-
# @return [Class] The Runner class
|
325
|
-
# @!visibility protected
|
326
|
-
|
327
|
-
# Overwrite the default runner
|
328
|
-
# @param klass [Class] A class like the {Runner}
|
329
|
-
# @!visibility protected
|
330
|
-
def runner(klass = nil)
|
331
|
-
@config.for(:runner, klass) { Runner }
|
332
|
-
end
|
333
|
-
|
334
41
|
# The primary interface to call the chain with the given input.
|
335
42
|
#
|
336
43
|
# @param input Any form of input the first steps +input+ class can handle
|
337
44
|
#
|
338
|
-
# @return [Teckel::
|
339
|
-
#
|
340
|
-
|
341
|
-
|
342
|
-
|
45
|
+
# @return [Teckel::Chain::Result] The result object wrapping
|
46
|
+
# the result value, the success state and last executed step.
|
47
|
+
def call(input = nil)
|
48
|
+
default_settings = self.default_settings
|
49
|
+
|
50
|
+
runner =
|
51
|
+
if default_settings
|
52
|
+
self.runner.new(self, default_settings)
|
53
|
+
else
|
54
|
+
self.runner.new(self)
|
55
|
+
end
|
56
|
+
|
343
57
|
if around
|
344
58
|
around.call(runner, input)
|
345
59
|
else
|
@@ -347,45 +61,21 @@ module Teckel
|
|
347
61
|
end
|
348
62
|
end
|
349
63
|
|
350
|
-
#
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
freeze
|
356
|
-
@steps.freeze
|
357
|
-
@config.freeze
|
358
|
-
self
|
359
|
-
end
|
360
|
-
|
361
|
-
# @!visibility public
|
362
|
-
def dup
|
363
|
-
super.tap do |copy|
|
364
|
-
copy.instance_variable_set(:@steps, @steps.dup)
|
365
|
-
copy.instance_variable_set(:@config, @config.dup)
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
# @!visibility public
|
370
|
-
def clone
|
371
|
-
if frozen?
|
372
|
-
super
|
64
|
+
# @param settings [Hash{String,Symbol => Object}] Set settings for a step by it's name
|
65
|
+
def with(settings)
|
66
|
+
runner = self.runner.new(self, settings)
|
67
|
+
if around
|
68
|
+
->(input) { around.call(runner, input) }
|
373
69
|
else
|
374
|
-
|
375
|
-
copy.instance_variable_set(:@steps, @steps.dup)
|
376
|
-
copy.instance_variable_set(:@config, @config.dup)
|
377
|
-
end
|
70
|
+
runner
|
378
71
|
end
|
379
72
|
end
|
73
|
+
alias :set :with
|
380
74
|
end
|
381
75
|
|
382
76
|
def self.included(receiver)
|
77
|
+
receiver.extend Config
|
383
78
|
receiver.extend ClassMethods
|
384
|
-
|
385
|
-
receiver.class_eval do
|
386
|
-
@steps = []
|
387
|
-
@config = Config.new
|
388
|
-
end
|
389
79
|
end
|
390
80
|
end
|
391
81
|
end
|