tchae 0.0.1

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.
@@ -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: []