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