teckel 0.1.0 → 0.2.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/.codeclimate.yml +3 -0
- data/.github/workflows/ci.yml +25 -0
- data/.gitignore +2 -0
- data/CHANGELOG.md +12 -0
- data/DEVELOPMENT.md +8 -4
- data/Gemfile +8 -0
- data/README.md +22 -14
- data/Rakefile +5 -0
- data/lib/teckel.rb +3 -0
- data/lib/teckel/chain.rb +230 -25
- data/lib/teckel/config.rb +40 -48
- data/lib/teckel/none.rb +18 -0
- data/lib/teckel/operation.rb +183 -110
- data/lib/teckel/operation/results.rb +12 -11
- data/lib/teckel/result.rb +2 -2
- data/lib/teckel/version.rb +1 -1
- data/teckel.gemspec +0 -1
- metadata +5 -24
- data/Gemfile.lock +0 -71
data/lib/teckel/config.rb
CHANGED
@@ -2,10 +2,22 @@
|
|
2
2
|
|
3
3
|
module Teckel
|
4
4
|
class Config
|
5
|
-
class FrozenConfigError < Teckel::Error; end
|
6
|
-
|
7
5
|
@default_constructor = :[]
|
8
6
|
class << self
|
7
|
+
# @!attribute [r] default_constructor()
|
8
|
+
# The default constructor method for +input+, +output+ and +error+ class (default: +:[]+)
|
9
|
+
# @return [Class] The Output class
|
10
|
+
|
11
|
+
# @!method default_constructor(sym_or_proc)
|
12
|
+
# Set the default constructor method for +input+, +output+ and +error+ class
|
13
|
+
#
|
14
|
+
# defaults to +:[]+
|
15
|
+
#
|
16
|
+
# @param sym_or_proc [Symbol,#call] The method name on the +input+,
|
17
|
+
# +output+ and +error+ class or a callable which accepts the
|
18
|
+
# +input+, +output+ or +error+
|
19
|
+
#
|
20
|
+
# @return [Symbol,#call]
|
9
21
|
def default_constructor(sym_or_proc = nil)
|
10
22
|
return @default_constructor if sym_or_proc.nil?
|
11
23
|
|
@@ -13,57 +25,37 @@ module Teckel
|
|
13
25
|
end
|
14
26
|
end
|
15
27
|
|
28
|
+
# @!visibility private
|
16
29
|
def initialize
|
17
|
-
@
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def input_constructor(sym_or_proc = nil)
|
35
|
-
return (@input_constructor || self.class.default_constructor) if sym_or_proc.nil?
|
36
|
-
raise FrozenConfigError unless @input_constructor.nil?
|
37
|
-
|
38
|
-
@input_constructor = sym_or_proc
|
39
|
-
end
|
40
|
-
|
41
|
-
def output(klass = nil)
|
42
|
-
return @output_class if klass.nil?
|
43
|
-
raise FrozenConfigError unless @output_class.nil?
|
44
|
-
|
45
|
-
@output_class = klass
|
46
|
-
end
|
47
|
-
|
48
|
-
def output_constructor(sym_or_proc = nil)
|
49
|
-
return (@output_constructor || self.class.default_constructor) if sym_or_proc.nil?
|
50
|
-
raise FrozenConfigError unless @output_constructor.nil?
|
51
|
-
|
52
|
-
@output_constructor = sym_or_proc
|
30
|
+
@config = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
# @!visibility private
|
34
|
+
def for(key, value = nil, &block)
|
35
|
+
if value.nil?
|
36
|
+
if block
|
37
|
+
@config[key] ||= @config.fetch(key, &block)
|
38
|
+
else
|
39
|
+
@config[key]
|
40
|
+
end
|
41
|
+
elsif @config.key?(key)
|
42
|
+
raise FrozenConfigError, "Configuration #{key} is already set"
|
43
|
+
else
|
44
|
+
@config[key] = value
|
45
|
+
end
|
53
46
|
end
|
54
47
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
@error_class = klass
|
48
|
+
# @!visibility private
|
49
|
+
def freeze
|
50
|
+
@config.freeze
|
51
|
+
super
|
60
52
|
end
|
61
53
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
54
|
+
# @!visibility private
|
55
|
+
def dup
|
56
|
+
super.tap do |copy|
|
57
|
+
copy.instance_variable_set(:@config, @config.dup)
|
58
|
+
end
|
67
59
|
end
|
68
60
|
end
|
69
61
|
end
|
data/lib/teckel/none.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Teckel
|
4
|
+
# Simple type object for enforcing +input+, +output+ or +error+ data to be
|
5
|
+
# not set (or +nil+)
|
6
|
+
class None
|
7
|
+
class << self
|
8
|
+
# Always return nil
|
9
|
+
# @return nil
|
10
|
+
# @raise [ArgumentError] when called with any non-nil arguments
|
11
|
+
def [](*args)
|
12
|
+
raise ArgumentError, "None called with arguments" if args.any?(&:itself)
|
13
|
+
end
|
14
|
+
|
15
|
+
alias :new :[]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/teckel/operation.rb
CHANGED
@@ -9,10 +9,14 @@ module Teckel
|
|
9
9
|
# the constants +Input+, +Output+ and +Error+, the second way is to use the
|
10
10
|
# +input+. +output+ and +error+ methods to point them to anonymous classes.
|
11
11
|
#
|
12
|
-
# If you like "traditional" result objects
|
13
|
-
# see
|
12
|
+
# If you like "traditional" result objects to ask +successful?+ or +failure?+ on,
|
13
|
+
# see {Teckel::Operation::Results Teckel::Operation::Results}
|
14
14
|
#
|
15
|
-
#
|
15
|
+
# By default, +input+. +output+ and +error+ classes are build using +:[]+
|
16
|
+
# (eg: +Input[some: :param]+).
|
17
|
+
# Use {ClassMethods#input_constructor input_constructor},
|
18
|
+
# {ClassMethods#output_constructor output_constructor} and
|
19
|
+
# {ClassMethods#error_constructor error_constructor} to change them.
|
16
20
|
#
|
17
21
|
# @example class definitions via constants
|
18
22
|
# class CreateUserViaConstants
|
@@ -38,13 +42,13 @@ module Teckel
|
|
38
42
|
# error_constructor :new
|
39
43
|
#
|
40
44
|
# # @param [CreateUser::Input]
|
41
|
-
# # @return [User
|
45
|
+
# # @return [User,CreateUser::Error]
|
42
46
|
# def call(input)
|
43
47
|
# user = ::User.new(name: input.name, age: input.age)
|
44
|
-
# if user.
|
48
|
+
# if user.save
|
45
49
|
# user
|
46
50
|
# else
|
47
|
-
# fail!(message: "Could not
|
51
|
+
# fail!(message: "Could not save User", errors: user.errors)
|
48
52
|
# end
|
49
53
|
# end
|
50
54
|
# end
|
@@ -60,14 +64,13 @@ module Teckel
|
|
60
64
|
# error Types::Hash.schema(message: Types::String, errors: Types::Array.of(Types::Hash))
|
61
65
|
#
|
62
66
|
# # @param [Hash<name: String, age: Integer>]
|
63
|
-
# # @return [User
|
67
|
+
# # @return [User,Hash<message: String, errors: [Hash]>]
|
64
68
|
# def call(input)
|
65
69
|
# user = User.new(name: input[:name], age: input[:age])
|
66
|
-
# if user.
|
67
|
-
# # exits early with success, prevents any further execution
|
68
|
-
# success!(user)
|
70
|
+
# if user.save
|
71
|
+
# success!(user) # exits early with success, prevents any further execution
|
69
72
|
# else
|
70
|
-
# fail!(message: "Could not
|
73
|
+
# fail!(message: "Could not save User", errors: user.errors)
|
71
74
|
# end
|
72
75
|
# end
|
73
76
|
# end
|
@@ -76,7 +79,7 @@ module Teckel
|
|
76
79
|
# CreateUserViaMethods.call(name: "Bob", age: 23).is_a?(User) #=> true
|
77
80
|
#
|
78
81
|
# # A failure call:
|
79
|
-
# CreateUserViaMethods.call(name: "Bob", age: 10).eql?(message: "Could not
|
82
|
+
# CreateUserViaMethods.call(name: "Bob", age: 10).eql?(message: "Could not save User", errors: [{age: "underage"}]) #=> true
|
80
83
|
#
|
81
84
|
# # Build your Input, Output and Error classes in a way that let you know:
|
82
85
|
# begin; CreateUserViaMethods.call(unwanted: "input"); rescue => e; e end.is_a?(::Dry::Types::MissingKeyError) #=> true
|
@@ -84,8 +87,54 @@ module Teckel
|
|
84
87
|
# # Feed an instance of the input class directly to call:
|
85
88
|
# CreateUserViaMethods.call(CreateUserViaMethods.input[name: "Bob", age: 23]).is_a?(User) #=> true
|
86
89
|
#
|
87
|
-
#
|
90
|
+
# @!visibility public
|
88
91
|
module Operation
|
92
|
+
# The default implementation for executing a single {Operation}
|
93
|
+
#
|
94
|
+
# @!visibility protected
|
95
|
+
class Runner
|
96
|
+
# @!visibility private
|
97
|
+
UNDEFINED = Object.new.freeze
|
98
|
+
|
99
|
+
def initialize(operation)
|
100
|
+
@operation = operation
|
101
|
+
end
|
102
|
+
attr_reader :operation
|
103
|
+
|
104
|
+
def call(input)
|
105
|
+
err = catch(:failure) do
|
106
|
+
simple_return = UNDEFINED
|
107
|
+
out = catch(:success) do
|
108
|
+
simple_return = @operation.new.call(build_input(input))
|
109
|
+
end
|
110
|
+
return simple_return == UNDEFINED ? build_output(*out) : build_output(simple_return)
|
111
|
+
end
|
112
|
+
build_error(*err)
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def build_input(input)
|
118
|
+
operation.input_constructor.call(input)
|
119
|
+
end
|
120
|
+
|
121
|
+
def build_output(*args)
|
122
|
+
if args.size == 1 && operation.output === args.first # rubocop:disable Style/CaseEquality
|
123
|
+
args.first
|
124
|
+
else
|
125
|
+
operation.output_constructor.call(*args)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def build_error(*args)
|
130
|
+
if args.size == 1 && operation.error === args.first # rubocop:disable Style/CaseEquality
|
131
|
+
args.first
|
132
|
+
else
|
133
|
+
operation.error_constructor.call(*args)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
89
138
|
module ClassMethods
|
90
139
|
# @!attribute [r] input()
|
91
140
|
# Get the configured class wrapping the input data structure.
|
@@ -96,11 +145,8 @@ module Teckel
|
|
96
145
|
# @param klass [Class] The +input+ class
|
97
146
|
# @return [Class] The +input+ class
|
98
147
|
def input(klass = nil)
|
99
|
-
|
100
|
-
|
101
|
-
@input_class = @config.input(klass)
|
102
|
-
@input_class ||= self::Input if const_defined?(:Input)
|
103
|
-
@input_class
|
148
|
+
@config.for(:input, klass) { self::Input if const_defined?(:Input) } ||
|
149
|
+
raise(Teckel::MissingConfigError, "Missing input config for #{self}")
|
104
150
|
end
|
105
151
|
|
106
152
|
# @!attribute [r] input_constructor()
|
@@ -109,9 +155,9 @@ module Teckel
|
|
109
155
|
|
110
156
|
# @!method input_constructor(sym_or_proc)
|
111
157
|
# Define how to build the +input+.
|
112
|
-
# @param sym_or_proc [Symbol
|
158
|
+
# @param sym_or_proc [Symbol, #call]
|
113
159
|
# - Either a +Symbol+ representing the _public_ method to call on the +input+ class.
|
114
|
-
# - Or
|
160
|
+
# - Or anything that response to +#call+ (like a +Proc+).
|
115
161
|
# @return [#call] The callable constructor
|
116
162
|
#
|
117
163
|
# @example simple symbol to method constructor
|
@@ -119,7 +165,7 @@ module Teckel
|
|
119
165
|
# include Teckel::Operation
|
120
166
|
#
|
121
167
|
# class Input
|
122
|
-
#
|
168
|
+
# def initialize(name:, age:); end
|
123
169
|
# end
|
124
170
|
#
|
125
171
|
# # If you need more control over how to build a new +Input+ instance
|
@@ -134,7 +180,7 @@ module Teckel
|
|
134
180
|
# include Teckel::Operation
|
135
181
|
#
|
136
182
|
# class Input
|
137
|
-
#
|
183
|
+
# def initialize(*args, **opts); end
|
138
184
|
# end
|
139
185
|
#
|
140
186
|
# # If you need more control over how to build a new +Input+ instance
|
@@ -143,16 +189,9 @@ module Teckel
|
|
143
189
|
# end
|
144
190
|
#
|
145
191
|
# MyOperation.input_constructor.is_a?(Proc) #=> true
|
146
|
-
def input_constructor(sym_or_proc =
|
147
|
-
|
148
|
-
|
149
|
-
constructor = @config.input_constructor(sym_or_proc)
|
150
|
-
@input_constructor =
|
151
|
-
if constructor.is_a?(Symbol) && input.respond_to?(constructor)
|
152
|
-
input.public_method(constructor)
|
153
|
-
elsif sym_or_proc.respond_to?(:call)
|
154
|
-
sym_or_proc
|
155
|
-
end
|
192
|
+
def input_constructor(sym_or_proc = Config.default_constructor)
|
193
|
+
@config.for(:input_constructor) { build_counstructor(input, sym_or_proc) } ||
|
194
|
+
raise(MissingConfigError, "Missing input_constructor config for #{self}")
|
156
195
|
end
|
157
196
|
|
158
197
|
# @!attribute [r] output()
|
@@ -164,11 +203,8 @@ module Teckel
|
|
164
203
|
# @param klass [Class] The +output+ class
|
165
204
|
# @return [Class] The +output+ class
|
166
205
|
def output(klass = nil)
|
167
|
-
|
168
|
-
|
169
|
-
@output_class = @config.output(klass)
|
170
|
-
@output_class ||= self::Output if const_defined?(:Output)
|
171
|
-
@output_class
|
206
|
+
@config.for(:output, klass) { self::Output if const_defined?(:Output) } ||
|
207
|
+
raise(Teckel::MissingConfigError, "Missing output config for #{self}")
|
172
208
|
end
|
173
209
|
|
174
210
|
# @!attribute [r] output_constructor()
|
@@ -177,9 +213,9 @@ module Teckel
|
|
177
213
|
|
178
214
|
# @!method output_constructor(sym_or_proc)
|
179
215
|
# Define how to build the +output+.
|
180
|
-
# @param sym_or_proc [Symbol
|
216
|
+
# @param sym_or_proc [Symbol, #call]
|
181
217
|
# - Either a +Symbol+ representing the _public_ method to call on the +output+ class.
|
182
|
-
# - Or
|
218
|
+
# - Or anything that response to +#call+ (like a +Proc+).
|
183
219
|
# @return [#call] The callable constructor
|
184
220
|
#
|
185
221
|
# @example
|
@@ -187,7 +223,7 @@ module Teckel
|
|
187
223
|
# include Teckel::Operation
|
188
224
|
#
|
189
225
|
# class Output
|
190
|
-
#
|
226
|
+
# def initialize(*args, **opts); end
|
191
227
|
# end
|
192
228
|
#
|
193
229
|
# # MyOperation.call("foo", "bar") # -> Output.new("foo", "bar")
|
@@ -197,16 +233,9 @@ module Teckel
|
|
197
233
|
# # MyOperation.call("foo", opt: "bar") # -> Output.new(name: "foo", opt: "bar")
|
198
234
|
# output_constructor ->(name, options) { Output.new(name: name, **options) }
|
199
235
|
# end
|
200
|
-
def output_constructor(sym_or_proc =
|
201
|
-
|
202
|
-
|
203
|
-
constructor = @config.output_constructor(sym_or_proc)
|
204
|
-
@output_constructor =
|
205
|
-
if constructor.is_a?(Symbol) && output.respond_to?(constructor)
|
206
|
-
output.public_method(constructor)
|
207
|
-
elsif sym_or_proc.respond_to?(:call)
|
208
|
-
sym_or_proc
|
209
|
-
end
|
236
|
+
def output_constructor(sym_or_proc = Config.default_constructor)
|
237
|
+
@config.for(:output_constructor) { build_counstructor(output, sym_or_proc) } ||
|
238
|
+
raise(MissingConfigError, "Missing output_constructor config for #{self}")
|
210
239
|
end
|
211
240
|
|
212
241
|
# @!attribute [r] error()
|
@@ -216,13 +245,10 @@ module Teckel
|
|
216
245
|
# @!method error(klass)
|
217
246
|
# Set the class wrapping the error data structure.
|
218
247
|
# @param klass [Class] The +error+ class
|
219
|
-
# @return [Class] The +error+ class
|
248
|
+
# @return [Class,nil] The +error+ class or +nil+ if it does not error
|
220
249
|
def error(klass = nil)
|
221
|
-
|
222
|
-
|
223
|
-
@error_class = @config.error(klass)
|
224
|
-
@error_class ||= self::Error if const_defined?(:Error)
|
225
|
-
@error_class
|
250
|
+
@config.for(:error, klass) { self::Error if const_defined?(:Error) } ||
|
251
|
+
raise(Teckel::MissingConfigError, "Missing error config for #{self}")
|
226
252
|
end
|
227
253
|
|
228
254
|
# @!attribute [r] error_constructor()
|
@@ -231,9 +257,9 @@ module Teckel
|
|
231
257
|
|
232
258
|
# @!method error_constructor(sym_or_proc)
|
233
259
|
# Define how to build the +error+.
|
234
|
-
# @param sym_or_proc [Symbol
|
260
|
+
# @param sym_or_proc [Symbol, #call]
|
235
261
|
# - Either a +Symbol+ representing the _public_ method to call on the +error+ class.
|
236
|
-
# - Or
|
262
|
+
# - Or anything that response to +#call+ (like a +Proc+).
|
237
263
|
# @return [#call] The callable constructor
|
238
264
|
#
|
239
265
|
# @example
|
@@ -241,7 +267,7 @@ module Teckel
|
|
241
267
|
# include Teckel::Operation
|
242
268
|
#
|
243
269
|
# class Error
|
244
|
-
#
|
270
|
+
# def initialize(*args, **opts); end
|
245
271
|
# end
|
246
272
|
#
|
247
273
|
# # MyOperation.call("foo", "bar") # -> Error.new("foo", "bar")
|
@@ -251,72 +277,126 @@ module Teckel
|
|
251
277
|
# # MyOperation.call("foo", opt: "bar") # -> Error.new(name: "foo", opt: "bar")
|
252
278
|
# error_constructor ->(name, options) { Error.new(name: name, **options) }
|
253
279
|
# end
|
254
|
-
def error_constructor(sym_or_proc =
|
255
|
-
|
256
|
-
|
257
|
-
constructor = @config.error_constructor(sym_or_proc)
|
258
|
-
@error_constructor =
|
259
|
-
if constructor.is_a?(Symbol) && error.respond_to?(constructor)
|
260
|
-
error.public_method(constructor)
|
261
|
-
elsif sym_or_proc.respond_to?(:call)
|
262
|
-
sym_or_proc
|
263
|
-
end
|
280
|
+
def error_constructor(sym_or_proc = Config.default_constructor)
|
281
|
+
@config.for(:error_constructor) { build_counstructor(error, sym_or_proc) } ||
|
282
|
+
raise(MissingConfigError, "Missing error_constructor config for #{self}")
|
264
283
|
end
|
265
284
|
|
266
|
-
#
|
285
|
+
# Convenience method for setting {#input}, {#output} or {#error} to the {None} value.
|
286
|
+
# @return [None]
|
287
|
+
# @example Enforcing nil input, output or error
|
288
|
+
# class MyOperation
|
289
|
+
# include Teckel::Operation
|
267
290
|
#
|
268
|
-
#
|
269
|
-
#
|
270
|
-
|
271
|
-
|
291
|
+
# input none
|
292
|
+
#
|
293
|
+
# # same as
|
294
|
+
# output Teckel::None
|
295
|
+
#
|
296
|
+
# error none
|
297
|
+
#
|
298
|
+
# def call(_) # you still need to take than +nil+ input when using `input none`
|
299
|
+
# # when using `error none`:
|
300
|
+
# # `fail!` works, but `fail!("data")` raises an error
|
301
|
+
#
|
302
|
+
# # when using `output none`:
|
303
|
+
# # `success!` works, but `success!("data")` raises an error
|
304
|
+
# # same thing when using simple return values as success:
|
305
|
+
# # take care to not return anything
|
306
|
+
# nil
|
307
|
+
# end
|
308
|
+
# end
|
309
|
+
#
|
310
|
+
# MyOperation.call #=> nil
|
311
|
+
def none
|
312
|
+
None
|
272
313
|
end
|
273
|
-
end
|
274
314
|
|
275
|
-
|
315
|
+
# @!attribute [r] runner()
|
316
|
+
# @return [Class] The Runner class
|
276
317
|
# @!visibility protected
|
277
|
-
def call!(input)
|
278
|
-
catch(:failure) do
|
279
|
-
out = catch(:success) do
|
280
|
-
simple_ret = call(build_input(input))
|
281
|
-
build_output(simple_ret)
|
282
|
-
end
|
283
|
-
return out
|
284
|
-
end
|
285
|
-
end
|
286
318
|
|
319
|
+
# Overwrite the default runner
|
320
|
+
# @param klass [Class] A class like the {Runner}
|
287
321
|
# @!visibility protected
|
288
|
-
def
|
289
|
-
|
322
|
+
def runner(klass = nil)
|
323
|
+
@config.for(:runner, klass) { Runner }
|
290
324
|
end
|
291
325
|
|
292
|
-
#
|
293
|
-
|
294
|
-
|
326
|
+
# Invoke the Operation
|
327
|
+
#
|
328
|
+
# @param input Any form of input your +input+ class can handle via the given +input_constructor+
|
329
|
+
# @return Either An instance of your defined +error+ class or +output+ class
|
330
|
+
# @!visibility public
|
331
|
+
def call(input = nil)
|
332
|
+
runner.new(self).call(input)
|
295
333
|
end
|
296
334
|
|
297
|
-
private
|
335
|
+
# @!visibility private
|
336
|
+
# @return [nil]
|
337
|
+
def define!
|
338
|
+
%i[input input_constructor output output_constructor error error_constructor runner].each { |e|
|
339
|
+
public_send(e)
|
340
|
+
}
|
341
|
+
nil
|
342
|
+
end
|
298
343
|
|
299
|
-
|
300
|
-
|
344
|
+
# Disallow any further changes to this Operation.
|
345
|
+
# Make sure all configurations are set.
|
346
|
+
#
|
347
|
+
# @raise [MissingConfigError]
|
348
|
+
# @return [self] Frozen self
|
349
|
+
# @!visibility public
|
350
|
+
def finalize!
|
351
|
+
define!
|
352
|
+
freeze
|
353
|
+
@config.freeze
|
354
|
+
self
|
301
355
|
end
|
302
356
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
self.class.output_constructor.call(*args)
|
357
|
+
# @!visibility public
|
358
|
+
def dup
|
359
|
+
super.tap do |copy|
|
360
|
+
copy.instance_variable_set(:@config, @config.dup)
|
308
361
|
end
|
309
362
|
end
|
310
363
|
|
311
|
-
|
312
|
-
|
313
|
-
|
364
|
+
# @!visibility public
|
365
|
+
def clone
|
366
|
+
if frozen?
|
367
|
+
super
|
314
368
|
else
|
315
|
-
|
369
|
+
super.tap do |copy|
|
370
|
+
copy.instance_variable_set(:@config, @config.dup)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
private
|
376
|
+
|
377
|
+
def build_counstructor(on, sym_or_proc)
|
378
|
+
if sym_or_proc.is_a?(Symbol) && on.respond_to?(sym_or_proc)
|
379
|
+
on.public_method(sym_or_proc)
|
380
|
+
elsif sym_or_proc.respond_to?(:call)
|
381
|
+
sym_or_proc
|
316
382
|
end
|
317
383
|
end
|
318
384
|
end
|
319
385
|
|
386
|
+
module InstanceMethods
|
387
|
+
# Halt any further execution with a +output+ value
|
388
|
+
# @!visibility protected
|
389
|
+
def success!(*args)
|
390
|
+
throw :success, args
|
391
|
+
end
|
392
|
+
|
393
|
+
# Halt any further execution with an +error+ value
|
394
|
+
# @!visibility protected
|
395
|
+
def fail!(*args)
|
396
|
+
throw :failure, args
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
320
400
|
def self.included(receiver)
|
321
401
|
receiver.extend ClassMethods
|
322
402
|
receiver.send :include, InstanceMethods
|
@@ -324,14 +404,7 @@ module Teckel
|
|
324
404
|
receiver.class_eval do
|
325
405
|
@config = Config.new
|
326
406
|
|
327
|
-
@
|
328
|
-
@input_constructor = nil
|
329
|
-
|
330
|
-
@output_class = nil
|
331
|
-
@output_constructor = nil
|
332
|
-
|
333
|
-
@error_class = nil
|
334
|
-
@error_constructor = nil
|
407
|
+
@runner = Runner
|
335
408
|
|
336
409
|
protected :success!, :fail!
|
337
410
|
end
|