teckel 0.7.0 → 0.8.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 +20 -0
- data/README.md +3 -3
- data/lib/teckel/chain/config.rb +19 -8
- data/lib/teckel/chain/runner.rb +1 -1
- data/lib/teckel/chain.rb +4 -2
- data/lib/teckel/config.rb +2 -2
- data/lib/teckel/operation/config.rb +36 -32
- data/lib/teckel/operation/result.rb +8 -8
- data/lib/teckel/operation/runner.rb +4 -4
- data/lib/teckel/operation.rb +35 -16
- data/lib/teckel/result.rb +4 -2
- data/lib/teckel/version.rb +1 -1
- data/spec/chain_spec.rb +86 -11
- data/spec/operation/config_spec.rb +227 -0
- data/spec/operation/contract_trace_spec.rb +2 -0
- data/spec/operation/default_settings_spec.rb +26 -0
- data/spec/operation/result_spec.rb +0 -6
- data/spec/operation_spec.rb +57 -9
- data/spec/spec_helper.rb +9 -2
- metadata +21 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 213ed6f9b3d25db8b7fc284ced55aa1a3a71b4ec54f10fd3ed3bf4e0a315dbd3
|
4
|
+
data.tar.gz: d013f110763117b066987bfb6c085e8682db372856ea7ab27f4803858be96e3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82db9106f0da688411be738866692455653eb59298afa01ba24500975b0498bf9a164f9f59e2741a4ed9c0c252174bd86fa71dd4204338fe04424d961c21621b
|
7
|
+
data.tar.gz: 6c693373c212e3b127dde042fdaba1ab7865d813cba17d0624a73a62e62589fc68bfe86ed93b9e7c32295deb6053411e4ea0cf57f0897b6f8e7e339ce60dd7be
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# Changes
|
2
2
|
|
3
|
+
## 0.8.0
|
4
|
+
|
5
|
+
- Add mutation testing (currently about 80% covered)
|
6
|
+
- Breaking: `Teckel::Operation::Result` no longer converts the `successful` value to a boolean.
|
7
|
+
When using the default result implementation, nothing changes for you.
|
8
|
+
When you manually pass anything else into it, `succesful?` will return this value. The `failure` and `success` methods
|
9
|
+
work on the "Truthy" value of `successful`
|
10
|
+
```ruby
|
11
|
+
result = Result.new(some_value, 42)
|
12
|
+
result.successful?
|
13
|
+
# => 42
|
14
|
+
```
|
15
|
+
- Change: `freeze`ing an `Operation` or `Chain` will also freeze their internal config (input, output, steps, etc.)
|
16
|
+
|
17
|
+
Internal:
|
18
|
+
|
19
|
+
- Some refactoring to cover mutations
|
20
|
+
- Extracted creating the runable `Operation` instance into a new, public `runable(settings = UNDEFINED)` method.
|
21
|
+
Mainly to reduce code duplication but this also makes testing, stubbing and mocking easier.
|
22
|
+
|
3
23
|
## 0.7.0
|
4
24
|
|
5
25
|
- Breaking: `Teckel::Chain` will not be required by default. require manually if needed `require "teckel/chain"` [GH-24]
|
data/README.md
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
Ruby service classes with enforced<sup name="footnote-1-source">[1](#footnote-1)</sup> input, output and error data structure definition.
|
4
4
|
|
5
5
|
[][gem]
|
6
|
-
[][ci]
|
7
7
|
[](https://codeclimate.com/github/fnordfish/teckel/maintainability)
|
8
8
|
[](https://codeclimate.com/github/fnordfish/teckel/test_coverage)
|
9
|
-
[][inch]
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
@@ -95,5 +95,5 @@ Please also see [DEVELOPMENT.md](DEVELOPMENT.md) for planned features and genera
|
|
95
95
|
- <a name="footnote-1">1</a>: Obviously, it's still Ruby and you can cheat. Don’t! [↩](#footnote-1-source)
|
96
96
|
|
97
97
|
[gem]: https://rubygems.org/gems/teckel
|
98
|
-
[ci]: https://github.com/fnordfish/teckel/actions
|
98
|
+
[ci]: https://github.com/fnordfish/teckel/actions/workflows/specs.yml
|
99
99
|
[inch]: http://inch-ci.org/github/fnordfish/teckel
|
data/lib/teckel/chain/config.rb
CHANGED
@@ -148,7 +148,7 @@ module Teckel
|
|
148
148
|
} || raise(MissingConfigError, "Missing result_constructor config for #{self}")
|
149
149
|
end
|
150
150
|
|
151
|
-
# Declare default settings operation
|
151
|
+
# Declare default settings operation in this chain should use when called without
|
152
152
|
# {Teckel::Chain::ClassMethods#with #with}.
|
153
153
|
#
|
154
154
|
# Explicit call-time settings will *not* get merged with declared default setting.
|
@@ -227,7 +227,7 @@ module Teckel
|
|
227
227
|
# @return [self]
|
228
228
|
# @!visibility public
|
229
229
|
def dup
|
230
|
-
dup_config(super)
|
230
|
+
dup_config(super())
|
231
231
|
end
|
232
232
|
|
233
233
|
# Produces a clone of this chain.
|
@@ -237,15 +237,25 @@ module Teckel
|
|
237
237
|
# @!visibility public
|
238
238
|
def clone
|
239
239
|
if frozen?
|
240
|
-
super
|
240
|
+
super()
|
241
241
|
else
|
242
|
-
dup_config(super)
|
242
|
+
dup_config(super())
|
243
243
|
end
|
244
244
|
end
|
245
245
|
|
246
|
+
# Prevents further modifications to this chain and its config
|
247
|
+
#
|
248
|
+
# @return [self]
|
249
|
+
# @!visibility public
|
250
|
+
def freeze
|
251
|
+
steps.freeze
|
252
|
+
@config.freeze
|
253
|
+
super()
|
254
|
+
end
|
255
|
+
|
246
256
|
# @!visibility private
|
247
257
|
def inherited(subclass)
|
248
|
-
super
|
258
|
+
super(dup_config(subclass))
|
249
259
|
end
|
250
260
|
|
251
261
|
# @!visibility private
|
@@ -264,10 +274,11 @@ module Teckel
|
|
264
274
|
end
|
265
275
|
|
266
276
|
def build_constructor(on, sym_or_proc)
|
267
|
-
|
268
|
-
|
269
|
-
elsif sym_or_proc.respond_to?(:call)
|
277
|
+
case sym_or_proc
|
278
|
+
when Proc
|
270
279
|
sym_or_proc
|
280
|
+
when Symbol
|
281
|
+
on.public_method(sym_or_proc) if on.respond_to?(sym_or_proc)
|
271
282
|
end
|
272
283
|
end
|
273
284
|
end
|
data/lib/teckel/chain/runner.rb
CHANGED
data/lib/teckel/chain.rb
CHANGED
data/lib/teckel/config.rb
CHANGED
@@ -35,12 +35,12 @@ module Teckel
|
|
35
35
|
# @!visibility private
|
36
36
|
def freeze
|
37
37
|
@config.freeze
|
38
|
-
super
|
38
|
+
super()
|
39
39
|
end
|
40
40
|
|
41
41
|
# @!visibility private
|
42
42
|
def dup
|
43
|
-
super.tap do |copy|
|
43
|
+
super().tap do |copy|
|
44
44
|
copy.instance_variable_set(:@config, @config.dup)
|
45
45
|
end
|
46
46
|
end
|
@@ -12,7 +12,7 @@ module Teckel
|
|
12
12
|
# @return [Class] The +input+ class
|
13
13
|
def input(klass = nil)
|
14
14
|
@config.for(:input, klass) { self::Input if const_defined?(:Input) } ||
|
15
|
-
raise(
|
15
|
+
raise(MissingConfigError, "Missing input config for #{self}")
|
16
16
|
end
|
17
17
|
|
18
18
|
# @overload input_constructor()
|
@@ -57,8 +57,7 @@ module Teckel
|
|
57
57
|
#
|
58
58
|
# MyOperation.input_constructor.is_a?(Proc) #=> true
|
59
59
|
def input_constructor(sym_or_proc = nil)
|
60
|
-
get_set_constructor(:input_constructor, input, sym_or_proc)
|
61
|
-
raise(MissingConfigError, "Missing input_constructor config for #{self}")
|
60
|
+
get_set_constructor(:input_constructor, input, sym_or_proc)
|
62
61
|
end
|
63
62
|
|
64
63
|
# @overload output()
|
@@ -72,7 +71,7 @@ module Teckel
|
|
72
71
|
# @return [Class] The +output+ class
|
73
72
|
def output(klass = nil)
|
74
73
|
@config.for(:output, klass) { self::Output if const_defined?(:Output) } ||
|
75
|
-
raise(
|
74
|
+
raise(MissingConfigError, "Missing output config for #{self}")
|
76
75
|
end
|
77
76
|
|
78
77
|
# @overload output_constructor()
|
@@ -103,8 +102,7 @@ module Teckel
|
|
103
102
|
# output_constructor ->(name, options) { Output.new(name: name, **options) }
|
104
103
|
# end
|
105
104
|
def output_constructor(sym_or_proc = nil)
|
106
|
-
get_set_constructor(:output_constructor, output, sym_or_proc)
|
107
|
-
raise(MissingConfigError, "Missing output_constructor config for #{self}")
|
105
|
+
get_set_constructor(:output_constructor, output, sym_or_proc)
|
108
106
|
end
|
109
107
|
|
110
108
|
# @overload error()
|
@@ -117,7 +115,7 @@ module Teckel
|
|
117
115
|
# @return [Class,nil] The +error+ class or +nil+ if it does not error
|
118
116
|
def error(klass = nil)
|
119
117
|
@config.for(:error, klass) { self::Error if const_defined?(:Error) } ||
|
120
|
-
raise(
|
118
|
+
raise(MissingConfigError, "Missing error config for #{self}")
|
121
119
|
end
|
122
120
|
|
123
121
|
# @overload error_constructor()
|
@@ -148,8 +146,7 @@ module Teckel
|
|
148
146
|
# error_constructor ->(name, options) { Error.new(name: name, **options) }
|
149
147
|
# end
|
150
148
|
def error_constructor(sym_or_proc = nil)
|
151
|
-
get_set_constructor(:error_constructor, error, sym_or_proc)
|
152
|
-
raise(MissingConfigError, "Missing error_constructor config for #{self}")
|
149
|
+
get_set_constructor(:error_constructor, error, sym_or_proc)
|
153
150
|
end
|
154
151
|
|
155
152
|
# @!endgroup
|
@@ -219,12 +216,9 @@ module Teckel
|
|
219
216
|
#
|
220
217
|
# (Like calling +MyOperation.with(arg1, arg2, ...)+)
|
221
218
|
def default_settings!(*args)
|
222
|
-
callable =
|
223
|
-
|
224
|
-
|
225
|
-
elsif args.length == 1
|
226
|
-
build_constructor(settings, args.first)
|
227
|
-
end
|
219
|
+
callable = if args.size.equal?(1)
|
220
|
+
build_constructor(settings, args.first)
|
221
|
+
end
|
228
222
|
|
229
223
|
callable ||= -> { settings_constructor.call(*args) }
|
230
224
|
|
@@ -246,7 +240,7 @@ module Teckel
|
|
246
240
|
# @param klass [Class] A class like the {Runner}
|
247
241
|
# @!visibility protected
|
248
242
|
def runner(klass = nil)
|
249
|
-
@config.for(:runner, klass) {
|
243
|
+
@config.for(:runner, klass) { Runner }
|
250
244
|
end
|
251
245
|
|
252
246
|
# @overload result()
|
@@ -301,8 +295,8 @@ module Teckel
|
|
301
295
|
# @note Don't use in conjunction with {result} or {result_constructor}
|
302
296
|
# @return [nil]
|
303
297
|
def result!
|
304
|
-
@config.for(:result,
|
305
|
-
@config.for(:result_constructor,
|
298
|
+
@config.for(:result, Result)
|
299
|
+
@config.for(:result_constructor, Result.method(:new))
|
306
300
|
nil
|
307
301
|
end
|
308
302
|
|
@@ -340,9 +334,7 @@ module Teckel
|
|
340
334
|
# @return [self]
|
341
335
|
# @!visibility public
|
342
336
|
def dup
|
343
|
-
super
|
344
|
-
copy.instance_variable_set(:@config, @config.dup)
|
345
|
-
end
|
337
|
+
dup_config(super())
|
346
338
|
end
|
347
339
|
|
348
340
|
# Produces a clone of this operation and all it's configuration
|
@@ -351,18 +343,24 @@ module Teckel
|
|
351
343
|
# @!visibility public
|
352
344
|
def clone
|
353
345
|
if frozen?
|
354
|
-
super
|
346
|
+
super()
|
355
347
|
else
|
356
|
-
super
|
357
|
-
copy.instance_variable_set(:@config, @config.dup)
|
358
|
-
end
|
348
|
+
dup_config(super())
|
359
349
|
end
|
360
350
|
end
|
361
351
|
|
352
|
+
# Prevents further modifications to this operation and its config
|
353
|
+
#
|
354
|
+
# @return [self]
|
355
|
+
# @!visibility public
|
356
|
+
def freeze
|
357
|
+
@config.freeze
|
358
|
+
super()
|
359
|
+
end
|
360
|
+
|
362
361
|
# @!visibility private
|
363
362
|
def inherited(subclass)
|
364
|
-
subclass
|
365
|
-
super subclass
|
363
|
+
super(dup_config(subclass))
|
366
364
|
end
|
367
365
|
|
368
366
|
# @!visibility private
|
@@ -376,19 +374,25 @@ module Teckel
|
|
376
374
|
|
377
375
|
private
|
378
376
|
|
377
|
+
def dup_config(other_class)
|
378
|
+
other_class.instance_variable_set(:@config, @config.dup)
|
379
|
+
other_class
|
380
|
+
end
|
381
|
+
|
379
382
|
def get_set_constructor(name, on, sym_or_proc)
|
380
|
-
constructor = build_constructor(on, sym_or_proc)
|
383
|
+
constructor = build_constructor(on, sym_or_proc)
|
381
384
|
|
382
385
|
@config.for(name, constructor) {
|
383
|
-
build_constructor(on,
|
386
|
+
build_constructor(on, DEFAULT_CONSTRUCTOR)
|
384
387
|
}
|
385
388
|
end
|
386
389
|
|
387
390
|
def build_constructor(on, sym_or_proc)
|
388
|
-
|
389
|
-
|
390
|
-
elsif sym_or_proc.respond_to?(:call)
|
391
|
+
case sym_or_proc
|
392
|
+
when Proc
|
391
393
|
sym_or_proc
|
394
|
+
when Symbol
|
395
|
+
on.public_method(sym_or_proc) if on.respond_to?(sym_or_proc)
|
392
396
|
end
|
393
397
|
end
|
394
398
|
end
|
@@ -38,16 +38,16 @@ module Teckel
|
|
38
38
|
include Teckel::Result
|
39
39
|
|
40
40
|
# @param value [Object] The result value
|
41
|
-
# @param
|
42
|
-
def initialize(value,
|
41
|
+
# @param successful [Boolean] whether this is a successful result
|
42
|
+
def initialize(value, successful)
|
43
43
|
@value = value
|
44
|
-
@
|
44
|
+
@successful = successful
|
45
45
|
end
|
46
46
|
|
47
47
|
# Whether this is a success result
|
48
48
|
# @return [Boolean]
|
49
49
|
def successful?
|
50
|
-
@
|
50
|
+
@successful
|
51
51
|
end
|
52
52
|
|
53
53
|
# @!attribute [r] value
|
@@ -59,8 +59,8 @@ module Teckel
|
|
59
59
|
# @param default [Mixed] return this default value if it's not a failure result
|
60
60
|
# @return [Mixed] the value/payload
|
61
61
|
def failure(default = nil, &block)
|
62
|
-
return
|
63
|
-
return yield(
|
62
|
+
return value unless @successful
|
63
|
+
return yield(value) if block
|
64
64
|
|
65
65
|
default
|
66
66
|
end
|
@@ -70,8 +70,8 @@ module Teckel
|
|
70
70
|
# @param default [Mixed] return this default value if it's not a success result
|
71
71
|
# @return [Mixed] the value/payload
|
72
72
|
def success(default = nil, &block)
|
73
|
-
return
|
74
|
-
return yield(
|
73
|
+
return value if @successful
|
74
|
+
return yield(value) if block
|
75
75
|
|
76
76
|
default
|
77
77
|
end
|
@@ -29,7 +29,7 @@ module Teckel
|
|
29
29
|
|
30
30
|
op = operation.new
|
31
31
|
op.runner = self
|
32
|
-
op.settings = settings
|
32
|
+
op.settings = settings unless settings.eql?(UNDEFINED)
|
33
33
|
|
34
34
|
@instance = op
|
35
35
|
end
|
@@ -37,7 +37,7 @@ module Teckel
|
|
37
37
|
# This is just here to raise a meaningful error.
|
38
38
|
# @!visibility private
|
39
39
|
def with(*)
|
40
|
-
raise
|
40
|
+
raise Error, "Operation already has settings assigned."
|
41
41
|
end
|
42
42
|
|
43
43
|
# Halt any further execution with a output value
|
@@ -46,7 +46,7 @@ module Teckel
|
|
46
46
|
# @!visibility protected
|
47
47
|
def success!(*args)
|
48
48
|
value =
|
49
|
-
if args.size
|
49
|
+
if args.size.equal?(1) && operation.output === args.first # rubocop:disable Style/CaseEquality
|
50
50
|
args.first
|
51
51
|
else
|
52
52
|
operation.output_constructor.call(*args)
|
@@ -61,7 +61,7 @@ module Teckel
|
|
61
61
|
# @!visibility protected
|
62
62
|
def fail!(*args)
|
63
63
|
value =
|
64
|
-
if args.size
|
64
|
+
if args.size.equal?(1) && operation.error === args.first # rubocop:disable Style/CaseEquality
|
65
65
|
args.first
|
66
66
|
else
|
67
67
|
operation.error_constructor.call(*args)
|
data/lib/teckel/operation.rb
CHANGED
@@ -58,6 +58,9 @@ module Teckel
|
|
58
58
|
# @!visibility public
|
59
59
|
module Operation
|
60
60
|
module ClassMethods
|
61
|
+
# @!visibility private
|
62
|
+
UNDEFINED = Object.new
|
63
|
+
|
61
64
|
# Invoke the Operation
|
62
65
|
#
|
63
66
|
# @param input Any form of input your {Teckel::Operation::Config#input input} class can handle via the given
|
@@ -66,21 +69,15 @@ module Teckel
|
|
66
69
|
# {Teckel::Operation::Config#output output} class
|
67
70
|
# @!visibility public
|
68
71
|
def call(input = nil)
|
69
|
-
|
70
|
-
|
71
|
-
if default_settings
|
72
|
-
runner.new(self, default_settings.call)
|
73
|
-
else
|
74
|
-
runner.new(self)
|
75
|
-
end.call(input)
|
72
|
+
runable.call(input)
|
76
73
|
end
|
77
74
|
|
78
|
-
# Provide {InstanceMethods#settings() settings} to the
|
75
|
+
# Provide {InstanceMethods#settings() settings} to the operation.
|
79
76
|
#
|
80
77
|
# This method is intended to be called on the operation class outside of
|
81
|
-
# it's definition, prior to
|
78
|
+
# it's definition, prior to invoking {#call}.
|
82
79
|
#
|
83
|
-
# @param
|
80
|
+
# @param settings Any form of settings your {Teckel::Operation::Config#settings settings} class can handle via the given
|
84
81
|
# {Teckel::Operation::Config#settings_constructor settings_constructor}
|
85
82
|
# @return [Class] The configured {Teckel::Operation::Config#runner runner}
|
86
83
|
# @!visibility public
|
@@ -111,11 +108,31 @@ module Teckel
|
|
111
108
|
# MyOperation.with(false).call
|
112
109
|
# MyOperation.call
|
113
110
|
# LOG #=> []
|
114
|
-
def with(
|
115
|
-
|
111
|
+
def with(settings)
|
112
|
+
runable(settings_constructor.call(settings))
|
116
113
|
end
|
117
114
|
alias :set :with
|
118
115
|
|
116
|
+
# Constructs a Runner instance for {call} and {with}.
|
117
|
+
#
|
118
|
+
# @note This method is public to make testing, stubbing and mocking easier.
|
119
|
+
# Your normal application code should use {with} and/or {call}
|
120
|
+
#
|
121
|
+
# @param settings Optional. Any form of settings your
|
122
|
+
# {Teckel::Operation::Config#settings settings} class can handle via the
|
123
|
+
# given {Teckel::Operation::Config#settings_constructor settings_constructor}
|
124
|
+
# @return [Class] The configured {Teckel::Operation::Config#runner runner}
|
125
|
+
# @!visibility public
|
126
|
+
def runable(settings = UNDEFINED)
|
127
|
+
if settings != UNDEFINED
|
128
|
+
runner.new(self, settings)
|
129
|
+
elsif default_settings
|
130
|
+
runner.new(self, default_settings.call)
|
131
|
+
else
|
132
|
+
runner.new(self)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
119
136
|
# Convenience method for setting {Teckel::Operation::Config#input input},
|
120
137
|
# {Teckel::Operation::Config#output output} or
|
121
138
|
# {Teckel::Operation::Config#error error} to the
|
@@ -145,7 +162,7 @@ module Teckel
|
|
145
162
|
#
|
146
163
|
# MyOperation.call #=> nil
|
147
164
|
def none
|
148
|
-
|
165
|
+
Contracts::None
|
149
166
|
end
|
150
167
|
end
|
151
168
|
|
@@ -187,9 +204,11 @@ module Teckel
|
|
187
204
|
end
|
188
205
|
|
189
206
|
def self.included(receiver)
|
190
|
-
receiver.
|
191
|
-
|
192
|
-
|
207
|
+
receiver.class_eval do
|
208
|
+
extend Config
|
209
|
+
extend ClassMethods
|
210
|
+
include InstanceMethods
|
211
|
+
end
|
193
212
|
end
|
194
213
|
end
|
195
214
|
end
|
data/lib/teckel/result.rb
CHANGED
data/lib/teckel/version.rb
CHANGED
data/spec/chain_spec.rb
CHANGED
@@ -67,6 +67,11 @@ module TeckelChainTest
|
|
67
67
|
end
|
68
68
|
|
69
69
|
RSpec.describe Teckel::Chain do
|
70
|
+
let(:frozen_error) do
|
71
|
+
# different ruby versions raise different errors
|
72
|
+
defined?(FrozenError) ? FrozenError : RuntimeError
|
73
|
+
end
|
74
|
+
|
70
75
|
it 'Chain input points to first step input' do
|
71
76
|
expect(TeckelChainTest::Chain.input).to eq(TeckelChainTest::CreateUser.input)
|
72
77
|
end
|
@@ -85,8 +90,7 @@ RSpec.describe Teckel::Chain do
|
|
85
90
|
|
86
91
|
context "success" do
|
87
92
|
it "result matches" do
|
88
|
-
result =
|
89
|
-
TeckelChainTest::Chain.
|
93
|
+
result = TeckelChainTest::Chain.
|
90
94
|
with(befriend: nil).
|
91
95
|
call(name: "Bob", age: 23)
|
92
96
|
|
@@ -96,8 +100,7 @@ RSpec.describe Teckel::Chain do
|
|
96
100
|
|
97
101
|
context "failure" do
|
98
102
|
it "returns a Result for invalid input" do
|
99
|
-
result =
|
100
|
-
TeckelChainTest::Chain.
|
103
|
+
result = TeckelChainTest::Chain.
|
101
104
|
with(befriend: :fail).
|
102
105
|
call(name: "Bob", age: 0)
|
103
106
|
|
@@ -108,8 +111,7 @@ RSpec.describe Teckel::Chain do
|
|
108
111
|
end
|
109
112
|
|
110
113
|
it "returns a Result for failed step" do
|
111
|
-
result =
|
112
|
-
TeckelChainTest::Chain.
|
114
|
+
result = TeckelChainTest::Chain.
|
113
115
|
with(befriend: :fail).
|
114
116
|
call(name: "Bob", age: 23)
|
115
117
|
|
@@ -121,11 +123,6 @@ RSpec.describe Teckel::Chain do
|
|
121
123
|
end
|
122
124
|
|
123
125
|
describe "#finalize!" do
|
124
|
-
let(:frozen_error) do
|
125
|
-
# different ruby versions raise different errors
|
126
|
-
defined?(FrozenError) ? FrozenError : RuntimeError
|
127
|
-
end
|
128
|
-
|
129
126
|
subject { TeckelChainTest::Chain.dup }
|
130
127
|
|
131
128
|
it "freezes the Chain class and operation classes" do
|
@@ -177,4 +174,82 @@ RSpec.describe Teckel::Chain do
|
|
177
174
|
expect(subject.call).to eq(:mocked)
|
178
175
|
end
|
179
176
|
end
|
177
|
+
|
178
|
+
describe "#clone" do
|
179
|
+
subject { TeckelChainTest::Chain.dup }
|
180
|
+
let(:klone) { subject.clone }
|
181
|
+
|
182
|
+
it 'clones' do
|
183
|
+
expect(klone.object_id).not_to be_eql(subject.object_id)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'clones config' do
|
187
|
+
orig_config = subject.instance_variable_get(:@config)
|
188
|
+
klone_config = klone.instance_variable_get(:@config)
|
189
|
+
expect(klone_config.object_id).not_to be_eql(orig_config.object_id)
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'clones steps' do
|
193
|
+
orig_settings = subject.instance_variable_get(:@config).instance_variable_get(:@config)[:steps]
|
194
|
+
klone_settings = klone.instance_variable_get(:@config).instance_variable_get(:@config)[:steps]
|
195
|
+
|
196
|
+
expect(orig_settings).to be_a(Array)
|
197
|
+
expect(klone_settings).to be_a(Array)
|
198
|
+
expect(klone_settings.object_id).not_to be_eql(orig_settings.object_id)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "frozen" do
|
203
|
+
subject { TeckelChainTest::Chain.dup }
|
204
|
+
|
205
|
+
it "also freezes the config" do
|
206
|
+
expect { subject.freeze }.to change {
|
207
|
+
[
|
208
|
+
subject.frozen?,
|
209
|
+
subject.instance_variable_get(:@config).frozen?
|
210
|
+
]
|
211
|
+
}.from([false, false]).to([true, true])
|
212
|
+
end
|
213
|
+
|
214
|
+
it "prevents changes to steps" do
|
215
|
+
subject.freeze
|
216
|
+
expect {
|
217
|
+
subject.class_eval do
|
218
|
+
step :yet_other, TeckelChainTest::AddFriend
|
219
|
+
end
|
220
|
+
}.to raise_error(frozen_error)
|
221
|
+
end
|
222
|
+
|
223
|
+
it "prevents changes to config" do
|
224
|
+
subject.freeze
|
225
|
+
expect {
|
226
|
+
subject.class_eval do
|
227
|
+
default_settings!(a: { say: "Chain Default" })
|
228
|
+
end
|
229
|
+
}.to raise_error(frozen_error)
|
230
|
+
end
|
231
|
+
|
232
|
+
describe '#clone' do
|
233
|
+
subject { TeckelChainTest::Chain.dup }
|
234
|
+
|
235
|
+
it 'clones the class' do
|
236
|
+
subject.freeze
|
237
|
+
klone = subject.clone
|
238
|
+
|
239
|
+
expect(klone).to be_frozen
|
240
|
+
expect(klone.object_id).not_to be_eql(subject.object_id)
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'cloned class uses the same, frozen config' do
|
244
|
+
subject.freeze
|
245
|
+
klone = subject.clone
|
246
|
+
|
247
|
+
orig_config = subject.instance_variable_get(:@config)
|
248
|
+
klone_config = klone.instance_variable_get(:@config)
|
249
|
+
|
250
|
+
expect(klone_config).to be_frozen
|
251
|
+
expect(klone_config.object_id).to be_eql(orig_config.object_id)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
180
255
|
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ostruct"
|
4
|
+
|
5
|
+
RSpec.describe Teckel::Operation do
|
6
|
+
let(:operation) do
|
7
|
+
Class.new do
|
8
|
+
include Teckel::Operation
|
9
|
+
input none
|
10
|
+
output ->(o) { o }
|
11
|
+
error none
|
12
|
+
|
13
|
+
def call(_)
|
14
|
+
success! settings
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:blank_operation) do
|
20
|
+
Class.new do
|
21
|
+
include Teckel::Operation
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe ".settings" do
|
26
|
+
specify "no settings" do
|
27
|
+
expect(operation.settings).to eq(Teckel::Contracts::None)
|
28
|
+
expect(operation.settings_constructor).to eq(Teckel::Contracts::None.method(:new))
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "with settings klass" do
|
32
|
+
settings_klass = Struct.new(:name)
|
33
|
+
operation.settings(settings_klass)
|
34
|
+
expect(operation.settings).to eq(settings_klass)
|
35
|
+
end
|
36
|
+
|
37
|
+
specify "without settings class, with settings constructor as proc" do
|
38
|
+
settings_const = if RUBY_VERSION < '2.6.0'
|
39
|
+
->(sets) { sets.map { |k, v| [k.to_s, v.to_i] }.to_h }
|
40
|
+
else
|
41
|
+
->(sets) { sets.to_h { |k, v| [k.to_s, v.to_i] } }
|
42
|
+
end
|
43
|
+
|
44
|
+
operation.settings_constructor(settings_const)
|
45
|
+
|
46
|
+
expect(operation.settings).to eq(Teckel::Contracts::None)
|
47
|
+
expect(operation.settings_constructor).to eq(settings_const)
|
48
|
+
|
49
|
+
runner = operation.with(key: "1")
|
50
|
+
expect(runner).to be_a(Teckel::Operation::Runner)
|
51
|
+
expect(runner.settings).to eq({ "key" => 1 })
|
52
|
+
end
|
53
|
+
|
54
|
+
specify "with settings class, with settings constructor as symbol" do
|
55
|
+
settings_klass = Struct.new(:name) do
|
56
|
+
def self.make_one(opts)
|
57
|
+
new(opts[:name])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
operation.settings(settings_klass)
|
62
|
+
operation.settings_constructor(:make_one)
|
63
|
+
|
64
|
+
expect(operation.settings).to eq(settings_klass)
|
65
|
+
expect(operation.settings_constructor).to eq(settings_klass.method(:make_one))
|
66
|
+
|
67
|
+
runner = operation.with(name: "value")
|
68
|
+
expect(runner).to be_a(Teckel::Operation::Runner)
|
69
|
+
expect(runner.settings).to be_a(settings_klass)
|
70
|
+
expect(runner.settings.name).to eq("value")
|
71
|
+
end
|
72
|
+
|
73
|
+
specify "with settings class as constant" do
|
74
|
+
settings_klass = Struct.new(:name)
|
75
|
+
operation.const_set(:Settings, settings_klass)
|
76
|
+
|
77
|
+
expect(operation.settings).to eq(settings_klass)
|
78
|
+
expect(operation.settings_constructor).to eq(settings_klass.method(:[]))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe ".default_settings" do
|
83
|
+
specify "no default_settings" do
|
84
|
+
expect(operation.default_settings).to be_nil
|
85
|
+
expect(operation.runner).to receive(:new).with(operation).and_call_original
|
86
|
+
|
87
|
+
operation.call
|
88
|
+
end
|
89
|
+
|
90
|
+
specify "default_settings!() with no default_settings" do
|
91
|
+
operation.default_settings!
|
92
|
+
expect(operation.default_settings).to be_a(Proc)
|
93
|
+
|
94
|
+
expect(operation.default_settings).to receive(:call).with(no_args).and_wrap_original do |original_method, *args, &block|
|
95
|
+
settings = original_method.call(*args, &block)
|
96
|
+
expect(settings).to be_nil
|
97
|
+
expect(operation.runner).to receive(:new).with(operation, settings).and_call_original
|
98
|
+
settings
|
99
|
+
end
|
100
|
+
|
101
|
+
operation.call
|
102
|
+
end
|
103
|
+
|
104
|
+
specify "default_settings!() with default_settings" do
|
105
|
+
settings_klass = Struct.new(:name)
|
106
|
+
|
107
|
+
operation.settings(settings_klass)
|
108
|
+
operation.default_settings!
|
109
|
+
|
110
|
+
expect(operation.default_settings).to be_a(Proc)
|
111
|
+
|
112
|
+
expect(operation.default_settings).to receive(:call).with(no_args).and_wrap_original do |original_method, *args, &block|
|
113
|
+
settings = original_method.call(*args, &block)
|
114
|
+
expect(settings).to be_a(settings_klass).and have_attributes(name: nil)
|
115
|
+
expect(operation.runner).to receive(:new).with(operation, settings).and_call_original
|
116
|
+
settings
|
117
|
+
end
|
118
|
+
|
119
|
+
operation.call
|
120
|
+
end
|
121
|
+
|
122
|
+
specify "default_settings!(arg) with default_settings" do
|
123
|
+
settings_klass = Struct.new(:name)
|
124
|
+
|
125
|
+
operation.settings(settings_klass)
|
126
|
+
operation.default_settings!("Bob")
|
127
|
+
|
128
|
+
expect(operation.default_settings).to be_a(Proc)
|
129
|
+
|
130
|
+
expect(operation.default_settings).to receive(:call).with(no_args).and_wrap_original do |original_method, *args, &block|
|
131
|
+
settings = original_method.call(*args, &block)
|
132
|
+
expect(settings).to be_a(settings_klass).and have_attributes(name: "Bob")
|
133
|
+
expect(operation.runner).to receive(:new).with(operation, settings).and_call_original
|
134
|
+
settings
|
135
|
+
end
|
136
|
+
|
137
|
+
expect(operation.call).to be_a(Struct).and have_attributes(name: "Bob")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
%i[input output error].each do |meth|
|
142
|
+
describe ".#{meth}" do
|
143
|
+
specify "missing .#{meth} config raises MissingConfigError" do
|
144
|
+
expect {
|
145
|
+
blank_operation.public_send(meth)
|
146
|
+
}.to raise_error(Teckel::MissingConfigError, "Missing #{meth} config for #{blank_operation}")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe ".#{meth}_constructor" do
|
151
|
+
specify "missing .#{meth}_constructor config raises MissingConfigError for missing #{meth}" do
|
152
|
+
expect {
|
153
|
+
blank_operation.public_send(:"#{meth}_constructor")
|
154
|
+
}.to raise_error(Teckel::MissingConfigError, "Missing #{meth} config for #{blank_operation}")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
specify "default settings config" do
|
160
|
+
expect(blank_operation.settings).to eq(Teckel::Contracts::None)
|
161
|
+
end
|
162
|
+
|
163
|
+
specify "default settings_constructor" do
|
164
|
+
expect(blank_operation.settings_constructor).to eq(Teckel::Contracts::None.method(:[]))
|
165
|
+
end
|
166
|
+
|
167
|
+
specify "default settings_constructor with settings config set" do
|
168
|
+
settings_klass = Struct.new(:name)
|
169
|
+
blank_operation.settings(settings_klass)
|
170
|
+
|
171
|
+
expect(blank_operation.settings_constructor).to eq(settings_klass.method(:[]))
|
172
|
+
end
|
173
|
+
|
174
|
+
specify "unsupported constructor method" do
|
175
|
+
blank_operation.settings(Class.new)
|
176
|
+
expect {
|
177
|
+
blank_operation.settings_constructor(:nope)
|
178
|
+
}.to raise_error(Teckel::MissingConfigError, "Missing settings_constructor config for #{blank_operation}")
|
179
|
+
|
180
|
+
expect {
|
181
|
+
blank_operation.settings_constructor
|
182
|
+
}.to raise_error(Teckel::MissingConfigError, "Missing settings_constructor config for #{blank_operation}")
|
183
|
+
end
|
184
|
+
|
185
|
+
describe "result" do
|
186
|
+
specify "default result config" do
|
187
|
+
expect(blank_operation.result).to eq(Teckel::Operation::ValueResult)
|
188
|
+
end
|
189
|
+
|
190
|
+
specify "default result_constructor" do
|
191
|
+
expect(blank_operation.result_constructor).to eq(Teckel::Operation::ValueResult.method(:[]))
|
192
|
+
end
|
193
|
+
|
194
|
+
specify "default result_constructor with settings config set" do
|
195
|
+
result_klass = OpenStruct.new
|
196
|
+
blank_operation.result(result_klass)
|
197
|
+
|
198
|
+
expect(blank_operation.result_constructor).to eq(result_klass.method(:[]))
|
199
|
+
end
|
200
|
+
|
201
|
+
specify "unsupported constructor method" do
|
202
|
+
blank_operation.result(Class.new)
|
203
|
+
expect {
|
204
|
+
blank_operation.result_constructor(:nope)
|
205
|
+
}.to raise_error(Teckel::MissingConfigError, "Missing result_constructor config for #{blank_operation}")
|
206
|
+
|
207
|
+
expect {
|
208
|
+
blank_operation.result_constructor
|
209
|
+
}.to raise_error(Teckel::MissingConfigError, "Missing result_constructor config for #{blank_operation}")
|
210
|
+
end
|
211
|
+
|
212
|
+
specify "with result class as constant" do
|
213
|
+
result_klass = OpenStruct.new
|
214
|
+
blank_operation.const_set(:Result, result_klass)
|
215
|
+
|
216
|
+
expect(blank_operation.result).to eq(result_klass)
|
217
|
+
expect(blank_operation.result_constructor).to eq(result_klass.method(:[]))
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe "result!" do
|
222
|
+
specify "default result config" do
|
223
|
+
blank_operation.result!
|
224
|
+
expect(blank_operation.result).to eq(Teckel::Operation::Result)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -26,6 +26,7 @@ module TeckelOperationContractTrace
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
# rubocop:disable Style/EvalWithLocation
|
29
30
|
# Hack to get reliable stack traces
|
30
31
|
eval <<~RUBY, binding, "operation_success_error.rb"
|
31
32
|
module TeckelOperationContractTrace
|
@@ -77,6 +78,7 @@ eval <<~RUBY, binding, "operation_input_error.rb"
|
|
77
78
|
end
|
78
79
|
end
|
79
80
|
RUBY
|
81
|
+
# rubocop:enable Style/EvalWithLocation
|
80
82
|
|
81
83
|
RSpec.describe Teckel::Operation do
|
82
84
|
context "contract errors include meaningful trace" do
|
@@ -89,6 +89,32 @@ RSpec.describe Teckel::Operation do
|
|
89
89
|
default_settings!(:default_value)
|
90
90
|
end
|
91
91
|
)
|
92
|
+
|
93
|
+
it_behaves_like(
|
94
|
+
"operation with default settings",
|
95
|
+
Class.new(TeckelOperationDefaultSettings::BaseOperation) do
|
96
|
+
settings Struct.new(:injected)
|
97
|
+
|
98
|
+
default_settings!("default_value")
|
99
|
+
|
100
|
+
output_constructor ->(out) { out&.to_sym }
|
101
|
+
end
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "with default constructor and simple Settings class responding to passed default setting" do
|
106
|
+
it_behaves_like(
|
107
|
+
"operation with default settings",
|
108
|
+
Class.new(TeckelOperationDefaultSettings::BaseOperation) do
|
109
|
+
settings(Struct.new(:injected) do
|
110
|
+
def self.default
|
111
|
+
new(:default_value)
|
112
|
+
end
|
113
|
+
end)
|
114
|
+
|
115
|
+
default_settings!(:default)
|
116
|
+
end
|
117
|
+
)
|
92
118
|
end
|
93
119
|
end
|
94
120
|
end
|
@@ -3,21 +3,15 @@
|
|
3
3
|
RSpec.describe Teckel::Operation::Result do
|
4
4
|
let(:failure_value) { "some error" }
|
5
5
|
let(:failed_result) { Teckel::Operation::Result.new(failure_value, false) }
|
6
|
-
let(:failed_nil_result) { Teckel::Operation::Result.new(failure_value, nil) }
|
7
6
|
|
8
7
|
let(:success_value) { "some success" }
|
9
8
|
let(:successful_result) { Teckel::Operation::Result.new(success_value, true) }
|
10
|
-
let(:successful_1_result) { Teckel::Operation::Result.new(success_value, 1) }
|
11
9
|
|
12
10
|
it { expect(successful_result.successful?).to eq(true) }
|
13
|
-
it { expect(successful_1_result.successful?).to eq(true) }
|
14
11
|
it { expect(failed_result.successful?).to eq(false) }
|
15
|
-
it { expect(failed_nil_result.successful?).to eq(false) }
|
16
12
|
|
17
13
|
it { expect(successful_result.failure?).to eq(false) }
|
18
|
-
it { expect(successful_1_result.failure?).to eq(false) }
|
19
14
|
it { expect(failed_result.failure?).to eq(true) }
|
20
|
-
it { expect(failed_nil_result.failure?).to eq(true) }
|
21
15
|
|
22
16
|
it { expect(successful_result.value).to eq(success_value) }
|
23
17
|
it { expect(failed_result.value).to eq(failure_value) }
|
data/spec/operation_spec.rb
CHANGED
@@ -229,6 +229,11 @@ module TeckelOperationInjectSettingsTest
|
|
229
229
|
end
|
230
230
|
|
231
231
|
RSpec.describe Teckel::Operation do
|
232
|
+
let(:frozen_error) do
|
233
|
+
# different ruby versions raise different errors
|
234
|
+
defined?(FrozenError) ? FrozenError : RuntimeError
|
235
|
+
end
|
236
|
+
|
232
237
|
context "predefined classes" do
|
233
238
|
specify "Input" do
|
234
239
|
expect(TeckelOperationPredefinedClassesTest::CreateUser.input).to eq(TeckelOperationPredefinedClassesTest::CreateUserInput)
|
@@ -331,8 +336,7 @@ RSpec.describe Teckel::Operation do
|
|
331
336
|
end
|
332
337
|
|
333
338
|
it "uses injected data" do
|
334
|
-
result =
|
335
|
-
TeckelOperationInjectSettingsTest::MyOperation.
|
339
|
+
result = TeckelOperationInjectSettingsTest::MyOperation.
|
336
340
|
with(injected: [:stuff]).
|
337
341
|
call
|
338
342
|
|
@@ -342,10 +346,10 @@ RSpec.describe Teckel::Operation do
|
|
342
346
|
end
|
343
347
|
|
344
348
|
specify "calling `with` multiple times raises an error" do
|
345
|
-
op = TeckelOperationInjectSettingsTest::MyOperation.with(injected: :
|
349
|
+
op = TeckelOperationInjectSettingsTest::MyOperation.with(injected: :stuff1)
|
346
350
|
|
347
351
|
expect {
|
348
|
-
op.with(more: :
|
352
|
+
op.with(more: :stuff2)
|
349
353
|
}.to raise_error(Teckel::Error, "Operation already has settings assigned.")
|
350
354
|
end
|
351
355
|
end
|
@@ -396,11 +400,6 @@ RSpec.describe Teckel::Operation do
|
|
396
400
|
end
|
397
401
|
|
398
402
|
describe "#finalize!" do
|
399
|
-
let(:frozen_error) do
|
400
|
-
# different ruby versions raise different errors
|
401
|
-
defined?(FrozenError) ? FrozenError : RuntimeError
|
402
|
-
end
|
403
|
-
|
404
403
|
subject do
|
405
404
|
Class.new do
|
406
405
|
include ::Teckel::Operation
|
@@ -480,4 +479,53 @@ RSpec.describe Teckel::Operation do
|
|
480
479
|
}.to raise_error Teckel::FrozenConfigError, "Configuration input is already set"
|
481
480
|
end
|
482
481
|
end
|
482
|
+
|
483
|
+
describe "frozen" do
|
484
|
+
subject do
|
485
|
+
Class.new do
|
486
|
+
include ::Teckel::Operation
|
487
|
+
|
488
|
+
input none
|
489
|
+
output none
|
490
|
+
error none
|
491
|
+
|
492
|
+
def call(_input); end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
it "also freezes the config" do
|
497
|
+
expect { subject.freeze }.to change {
|
498
|
+
[
|
499
|
+
subject.frozen?,
|
500
|
+
subject.instance_variable_get(:@config).frozen?
|
501
|
+
]
|
502
|
+
}.from([false, false]).to([true, true])
|
503
|
+
end
|
504
|
+
|
505
|
+
it "prevents changes to config" do
|
506
|
+
subject.freeze
|
507
|
+
expect { subject.settings Struct.new(:test) }.to raise_error(frozen_error)
|
508
|
+
end
|
509
|
+
|
510
|
+
describe '#clone' do
|
511
|
+
it 'clones the class' do
|
512
|
+
subject.freeze
|
513
|
+
klone = subject.clone
|
514
|
+
|
515
|
+
expect(klone).to be_frozen
|
516
|
+
expect(klone.object_id).not_to be_eql(subject.object_id)
|
517
|
+
end
|
518
|
+
|
519
|
+
it 'cloned class uses the same, frozen config' do
|
520
|
+
subject.freeze
|
521
|
+
klone = subject.clone
|
522
|
+
|
523
|
+
orig_config = subject.instance_variable_get(:@config)
|
524
|
+
klone_config = klone.instance_variable_get(:@config)
|
525
|
+
|
526
|
+
expect(klone_config).to be_frozen
|
527
|
+
expect(klone_config.object_id).to be_eql(orig_config.object_id)
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
483
531
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,8 +3,15 @@
|
|
3
3
|
require "bundler/setup"
|
4
4
|
if ENV['COVERAGE'] == 'true'
|
5
5
|
require 'simplecov'
|
6
|
-
|
7
|
-
SimpleCov.formatter =
|
6
|
+
|
7
|
+
SimpleCov.formatter = case ENV['SIMPLECOV']&.downcase
|
8
|
+
when 'html'
|
9
|
+
SimpleCov::Formatter::HTMLFormatter
|
10
|
+
else
|
11
|
+
require 'simplecov_json_formatter'
|
12
|
+
SimpleCov::Formatter::JSONFormatter
|
13
|
+
end
|
14
|
+
|
8
15
|
SimpleCov.start do
|
9
16
|
add_filter %r{^/spec/}
|
10
17
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: teckel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Schulze
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mutant-rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: yard
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,6 +129,7 @@ files:
|
|
115
129
|
- spec/chain_spec.rb
|
116
130
|
- spec/config_spec.rb
|
117
131
|
- spec/doctest_helper.rb
|
132
|
+
- spec/operation/config_spec.rb
|
118
133
|
- spec/operation/contract_trace_spec.rb
|
119
134
|
- spec/operation/default_settings_spec.rb
|
120
135
|
- spec/operation/fail_on_input_spec.rb
|
@@ -133,10 +148,10 @@ homepage: https://github.com/fnordfish/teckel
|
|
133
148
|
licenses:
|
134
149
|
- Apache-2.0
|
135
150
|
metadata:
|
136
|
-
changelog_uri: https://github.com/fnordfish/teckel/blob/
|
151
|
+
changelog_uri: https://github.com/fnordfish/teckel/blob/main/CHANGELOG.md
|
137
152
|
source_code_uri: https://github.com/fnordfish/teckel
|
138
153
|
bug_tracker_uri: https://github.com/fnordfish/teckel/issues
|
139
|
-
documentation_uri: https://www.rubydoc.info/gems/teckel/0.
|
154
|
+
documentation_uri: https://www.rubydoc.info/gems/teckel/0.8.0
|
140
155
|
user_docs_uri: https://fnordfish.github.io/teckel/
|
141
156
|
post_install_message:
|
142
157
|
rdoc_options: []
|
@@ -153,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
168
|
- !ruby/object:Gem::Version
|
154
169
|
version: '0'
|
155
170
|
requirements: []
|
156
|
-
rubygems_version: 3.2.
|
171
|
+
rubygems_version: 3.2.15
|
157
172
|
signing_key:
|
158
173
|
specification_version: 4
|
159
174
|
summary: Operations with enforced in/out/err data structures
|
@@ -166,6 +181,7 @@ test_files:
|
|
166
181
|
- spec/chain_spec.rb
|
167
182
|
- spec/config_spec.rb
|
168
183
|
- spec/doctest_helper.rb
|
184
|
+
- spec/operation/config_spec.rb
|
169
185
|
- spec/operation/contract_trace_spec.rb
|
170
186
|
- spec/operation/default_settings_spec.rb
|
171
187
|
- spec/operation/fail_on_input_spec.rb
|