teckel 0.2.0 → 0.7.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 +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
|