tchae 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3223e8b9f142d6df07e3140a7469647fb6b285d4324d8720957ebee438dfdab7
4
+ data.tar.gz: f0602b5cb2d128f42d097744299e226c504835d231f099f8cace33566033778c
5
+ SHA512:
6
+ metadata.gz: 6354b81ae20b79d584d6c6b34e0cdb293f11f6cfebf8e39c2a7dd90f72401af33c8355acba4945ee33c190322c9e4769c7284145a0103eaec57ad070306b4c21
7
+ data.tar.gz: 5fa9f4da97ca1063e73814dd5c54d4848d1ee08eeea055ea2fe0ca16c6752107c92cf913f3f5ef05ffefca4158c901e84e00387b3fa4aedb556cb8b1a6cc945e
@@ -0,0 +1,2 @@
1
+ require_relative 'tchae/version'
2
+ require_relative 'tchae/core'
@@ -0,0 +1,407 @@
1
+ class Object
2
+ def class_respond_to?(*args)
3
+ self.class.respond_to?(*args)
4
+ end
5
+ end
6
+
7
+ module Tchae
8
+ EMPTY_ARRAY = [].freeze
9
+
10
+ def self.Wrapper(handling = ::Tchae::Handling::RAISE)
11
+ MethodValidator.new(handling)
12
+ end
13
+
14
+ # wrap method with validation, and on invalid,
15
+ # exit with control flow and retun a wrapped error object
16
+ # on success return a wrapped return object
17
+ def self.do_method_wrapping_return(obj, wrapper, tea_methodsymb,
18
+ proclmbda, params, kwparms)
19
+ unless params.empty?
20
+ if (err = wrapper.args.positional.valid_or_return_error(params))
21
+ return Return(nil, err)
22
+ end
23
+ end
24
+ unless kwparms.empty?
25
+ if (err = wrapper.args.keyword.valid_or_return_error(kwparms))
26
+ return Return(nil, err)
27
+ end
28
+ end
29
+
30
+ unchecked_result = if proclmbda.arity.zero?
31
+ obj.__send__(tea_methodsymb)
32
+ elsif kwparms.empty?
33
+ obj.__send__(tea_methodsymb, *params)
34
+ elsif params.empty?
35
+ obj.__send__(tea_methodsymb, **kwparms)
36
+ else
37
+ obj.__send__(tea_methodsymb, *params, **kwparms)
38
+ end
39
+
40
+ wrapper.result.valid_or_return_error unchecked_result
41
+ end
42
+
43
+ # wrap method with validation, and on invalid,
44
+ # exit by raising exception
45
+
46
+ def self.do_method_wrapping_raise(obj, wrapper, tea_methodsymb,
47
+ proclmbda, params, kwparms)
48
+
49
+ wrapper.args.positional.valid_or_raise_exception(params)
50
+ wrapper.args.keyword.valid_or_raise_exception(kwparms)
51
+ unchecked_result = if proclmbda.arity.zero?
52
+ obj.__send__(tea_methodsymb)
53
+ elsif kwparms.empty?
54
+ obj.__send__(tea_methodsymb, *params)
55
+ elsif params.empty?
56
+ obj.__send__(tea_methodsymb, **kwparms)
57
+ else
58
+ obj.__send__(tea_methodsymb, *params, **kwparms)
59
+ end
60
+ wrapper.result.valid_or_raise_exception unchecked_result
61
+ end
62
+
63
+ class Error
64
+ attr_reader :msg
65
+ def initialize(input); end
66
+ end
67
+
68
+ class ReturnTypeError < Error
69
+ end
70
+
71
+ class ArgumentValueError < Error
72
+ end
73
+
74
+ class ArgumentTypeError < Error
75
+ end
76
+
77
+ class ReturnTypeException < TypeError
78
+ end
79
+
80
+ class ArgumentValueException < ArgumentError
81
+ end
82
+
83
+ class ArgumentTypeException < TypeError
84
+ end
85
+
86
+ class Validator
87
+ attr_reader :message
88
+
89
+ def initialize(proc, msg: nil)
90
+ @proc = proc
91
+ @message = msg.nil? ? "does not fulfill #{proc}.to_s" : msg
92
+ end
93
+
94
+ def execute(input)
95
+ input.instance_exec(&@proc)
96
+ end
97
+
98
+ def valid_or_raise_exception(input, eklass)
99
+ raise eklass unless execute(input)
100
+
101
+ input
102
+ end
103
+
104
+ def valid_or_return_error(input, eklass)
105
+ execute(input) ? input : return_error(input, eklass)
106
+ end
107
+
108
+ def return_error(input, eklass)
109
+ eklass.new(input)
110
+ end
111
+
112
+ def wrapped_result_or_error(result, eklass)
113
+ execute(result) ? Return(result) : Return(nil, return_error(result, eklass))
114
+ end
115
+ end
116
+
117
+ # same as Validator but with Arity 1 execution semantics
118
+ class Validator1 < Validator
119
+ def execute(input)
120
+ @proc.call(input)
121
+ end
122
+ end
123
+
124
+ class ResultWrapper
125
+ attr_reader :result
126
+ attr_reader :error
127
+ def initialize(res, err = nil)
128
+ @result = res
129
+ @error = err
130
+ end
131
+ end
132
+
133
+ class Result
134
+ def initialize(wrapper)
135
+ @wrapper = wrapper
136
+ @valtor = nil
137
+ end
138
+
139
+ def expect(constraint = nil, &proc)
140
+ @valtor = if block_given?
141
+ raise(ArgumentError, 'The Result.expect method accepts either a block or an argument but not both') if constraint
142
+
143
+ Tchae(proc)
144
+ else
145
+ Tchae(constraint)
146
+ end
147
+ @wrapper
148
+ end
149
+
150
+ def valid_or_raise_exception(result)
151
+ return result unless @valtor
152
+
153
+ @valtor.valid_or_raise_exception(result, ::Tchae::ReturnTypeException)
154
+ end
155
+
156
+ def valid_or_return_error(result)
157
+ return Return(result) unless @valtor
158
+
159
+ @valtor.wrapped_result_or_error(result, ::Tchae::ReturnTypeError)
160
+ end
161
+ end
162
+
163
+ class PositionalArgs < Array
164
+ def initialize(wrapper)
165
+ super()
166
+ @wrapper = wrapper
167
+ end
168
+
169
+ def arg(&proc)
170
+ push Tchae(proc)
171
+ @wrapper
172
+ end
173
+
174
+ def expect(constraints = EMPTY_ARRAY, &proc)
175
+ constraints.each { |inp| push Tchae(inp) }
176
+ instance_exec(&proc) if block_given?
177
+ @wrapper
178
+ end
179
+
180
+ def valid_or_raise_exception(params)
181
+ params.zip(self).each do |parm, valtor|
182
+ next unless valtor
183
+
184
+ valtor.valid_or_raise_exception(parm, ::Tchae::ArgumentTypeException)
185
+ end
186
+ end
187
+
188
+ def valid_or_return_error(params)
189
+ # returns nil if everything valid or an Error object otherwise
190
+ valid = true
191
+ failed = params.zip(self).each do |parm, valtor|
192
+ next unless valtor
193
+
194
+ unless valtor.execute(parm)
195
+ valid = false
196
+ break valtor.return_error(parm, ::Tchae::ArgumentTypeError)
197
+ end
198
+ end
199
+ valid ? nil : failed
200
+ end
201
+ end
202
+
203
+ class KeywordArgs < Hash
204
+ def initialize(wrapper)
205
+ super()
206
+ @wrapper = wrapper
207
+ end
208
+
209
+ def expect(**keywprocs)
210
+ keywprocs.each { |argsymb, proc| self[argsymb] = Tchae(proc) }
211
+ @wrapper
212
+ end
213
+
214
+ def valid_or_raise_exception(keywps)
215
+ each do |key, valtor|
216
+ next unless valtor
217
+ next unless ((parm = keywps[key]) if keywps.key?(key))
218
+
219
+ valtor.valid_or_raise_exception(parm, ::Tchae::ArgumentTypeException)
220
+ end
221
+ end
222
+
223
+ def valid_or_return_error(keywps)
224
+ # returns nil if everything valid or an Error object otherwise
225
+ valid = true
226
+ failed = each do |key, valtor|
227
+ next unless valtor
228
+ next unless ((parm = keywps[key]) if keywps.key?(key))
229
+
230
+ unless valtor.execute(parm)
231
+ valid = false
232
+ break valtor.return_error(parm, ::Tchae::ArgumentTypeError)
233
+ end
234
+ end
235
+ valid ? nil : failed
236
+ end
237
+ end
238
+
239
+ class Arguments
240
+ attr_reader :positional
241
+ attr_reader :keyword
242
+ def initialize(wrapper)
243
+ @wrapper = wrapper
244
+ @positional = PositionalArgs.new(wrapper)
245
+ @keyword = KeywordArgs.new(wrapper)
246
+ end
247
+ end
248
+ module Handling
249
+ RAISE = :raise_exception
250
+ RETURN = :return_wrapper
251
+ ALL = [RAISE, RETURN].freeze
252
+ end
253
+ end
254
+
255
+ # Central validator factory
256
+ def Tchae(inp)
257
+ case inp
258
+ when Class
259
+ Tchae::Validator.new -> { is_a?(inp) }, msg: "is not a #{inp}"
260
+ when Symbol
261
+ Tchae::Validator.new -> { respond_to?(inp) }, msg: "does not respond to #{inp}"
262
+ when Proc
263
+ Tchae::Validator.new inp
264
+ when Array
265
+ Tchae::Validator1.new ->(value) { inp.find(value) }
266
+ else
267
+ Tchae::Validator.new inp.to_proc
268
+ end
269
+ end
270
+
271
+ module Tchae
272
+ class MethodValidator
273
+ HANDLING_VTOR = Tchae(Handling::ALL).freeze
274
+ attr_reader :result
275
+ attr_reader :args
276
+ attr_accessor :handling
277
+ def initialize(handling = ::Tchae::Handling::RAISE)
278
+ HANDLING_VTOR.valid_or_raise_exception(handling, ArgumentValueError)
279
+ @result = Result.new(self)
280
+ @args = Arguments.new(self)
281
+ @handling = handling
282
+ end
283
+ end
284
+
285
+ NilWrapper = MethodValidator.new.freeze
286
+
287
+ module PI
288
+ def create_validated_method(methodname,
289
+ wrapper = Tchae::NilWrapper,
290
+ lambda: nil,
291
+ &proc)
292
+ proclmbda = if lambda.nil?
293
+ proc
294
+ else
295
+ raise ArgumentError, 'Please provide a block or the :lambda parameter but not both' if block_given?
296
+
297
+ lambda
298
+ end
299
+
300
+ if wrapper.handling == ::Tchae::Handling::RETURN
301
+ create_valid_or_return_method(methodname, wrapper, &proclmbda)
302
+ else # RAISE
303
+ create_valid_or_raise_method(methodname, wrapper, &proclmbda)
304
+ end
305
+ end
306
+
307
+ def create_valid_or_raise_method(methodname,
308
+ wrapper = Tchae::NilWrapper, &proc)
309
+ if block_given?
310
+ tea_methodsymb = "__tea_#{methodname}".to_sym
311
+ define_method "__tea_#{methodname}", proc
312
+ define_method(methodname) do |*params, **kwparms|
313
+ Tchae.do_method_wrapping_raise(self, wrapper,
314
+ tea_methodsymb, proc,
315
+ params, kwparms)
316
+ end
317
+ else
318
+ define_method(methodname) {}
319
+ end
320
+ end
321
+
322
+ def create_valid_or_return_method(methodname,
323
+ wrapper = Tchae::NilWrapper,
324
+ &proc)
325
+ if block_given?
326
+ tea_methodsymb = "__tea_#{methodname}".to_sym
327
+ define_method "__tea_#{methodname}", proc
328
+ define_method(methodname) do |*params, **kwparms|
329
+ Tchae.do_method_wrapping_return(self,
330
+ wrapper,
331
+ tea_methodsymb,
332
+ proc,
333
+ params,
334
+ kwparms)
335
+ end
336
+ else
337
+ define_method(methodname) {}
338
+ end
339
+ end
340
+ end
341
+
342
+ module PISingleton
343
+ def create_validated_singleton_method(methodname,
344
+ wrapper = Tchae::NilWrapper,
345
+ lambda: nil,
346
+ &proc)
347
+
348
+ proclmbda = if lambda.nil?
349
+ proc
350
+ else
351
+ raise ArgumentError, 'Please provide a block or the :lambda parameter but not both' if block_given?
352
+
353
+ lambda
354
+ end
355
+
356
+ if wrapper.handling == ::Tchae::Handling::RETURN
357
+ create_valid_or_return_singleton_method(methodname, wrapper, &proclmbda)
358
+ else # RAISE
359
+ create_valid_or_raise_singleton_method(methodname, wrapper, &proclmbda)
360
+ end
361
+ end
362
+
363
+ def create_valid_or_raise_singleton_method(methodname,
364
+ wrapper = Tchae::NilWrapper, &proc)
365
+ if block_given?
366
+ tea_methodsymb = "__tea_#{methodname}".to_sym
367
+ define_singleton_method "__tea_#{methodname}", proc
368
+ define_singleton_method(methodname) do |*params, **kwparms|
369
+ Tchae.do_method_wrapping_raise(self, wrapper,
370
+ tea_methodsymb, proc,
371
+ params, kwparms)
372
+ end
373
+ else
374
+ define_singleton_method(methodname) {}
375
+ end
376
+ end
377
+
378
+ def create_valid_or_return_singleton_method(methodname,
379
+ wrapper = Tchae::NilWrapper, &proc)
380
+ if block_given?
381
+ tea_methodsymb = "__tea_#{methodname}".to_sym
382
+ define_singleton_method "__tea_#{methodname}", proc
383
+ define_singleton_method(methodname) do |*params, **kwparms|
384
+ Tchae.do_method_wrapping_return(self, wrapper,
385
+ tea_methodsymb, proc,
386
+ params, kwparms)
387
+ end
388
+ else
389
+ define_singleton_method(methodname) {}
390
+ end
391
+ end
392
+ end
393
+ end
394
+
395
+ # central return wrapper
396
+ def Return(result, error = nil)
397
+ Tchae::ResultWrapper.new(result, error)
398
+ end
399
+ class Object
400
+ extend Tchae::PI
401
+ include Tchae::PISingleton
402
+ end
403
+
404
+ class Module
405
+ include Tchae::PI
406
+ include Tchae::PISingleton
407
+ end
@@ -0,0 +1,3 @@
1
+ module Tchae
2
+ VERSION = '0.0.1'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tchae
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - D.M.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-08-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '12.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '12.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.51'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.51'
41
+ description: Tchae is a lightweight ruby validation library
42
+ email: dev@aithscel.eu
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/tchae.rb
48
+ - lib/tchae/core.rb
49
+ - lib/tchae/version.rb
50
+ homepage: https://gitlab.com/dm0da/tchae
51
+ licenses:
52
+ - MIT
53
+ metadata:
54
+ bug_tracker_uri: https://gitlab.com/dm0da/tchae/issues
55
+ changelog_uri: https://gitlab.com/dm0da/tchae/blob/master/CHANGELOG
56
+ source_code_uri: https://gitlab.com/dm0da/tchae/tree/master
57
+ wiki_uri: https://gitlab.com/dm0da/tchae/wikis/home
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 2.4.0
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.1.2
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Tchae is a lightweight ruby validation library
77
+ test_files: []