primitive_wrapper 0.1.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.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "primitive_wrapper"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,610 @@
1
+ require "primitive_wrapper/version"
2
+ require "blockify"
3
+
4
+
5
+ #
6
+ # Standard Object upgrades
7
+ #
8
+ class Object
9
+ def prim_value
10
+ self
11
+ end
12
+ def to_wrapper
13
+ return Bit.new(nil) if self.nil?
14
+ return Bool.new(true) if self==true
15
+ return Bool.new(false) if self==false
16
+ return Int.new(self) if self.kind_of? Integer
17
+ return FloatW.new(self) if self.kind_of? Float
18
+ return Number.new(self) if self.kind_of? Numeric
19
+ return Datum.new(self) if self.kind_of? String
20
+ return SymbolW.new(self) if self.kind_of? Symbol
21
+ return Property.new(self) if self.kind_of? Hash
22
+ return Value.new(self)
23
+ end
24
+ end
25
+
26
+ class Symbol
27
+ def ~
28
+ self
29
+ end
30
+ end
31
+
32
+ class TrueClass
33
+ def ~
34
+ self
35
+ end
36
+ end
37
+
38
+ class FalseClass
39
+ def ~
40
+ self
41
+ end
42
+ end
43
+
44
+ class NilClass
45
+ def ~
46
+ self
47
+ end
48
+ end
49
+
50
+ #
51
+ # BASE CLASS == Value ... a wrapper for any ruby object
52
+ #
53
+ class Value
54
+ def self.raise_on_freeze
55
+ @freeze_raise = true
56
+ end
57
+ def self.ignore_on_freeze
58
+ @freeze_raise = false
59
+ end
60
+ def self.freeze_raise?
61
+ @freeze_raise
62
+ end
63
+ ignore_on_freeze
64
+
65
+ def initialize(obj=nil)
66
+ obj = obj.prim_value
67
+ ensure_valid(obj)
68
+ @value = obj
69
+ end
70
+
71
+ def ensure_valid(obj, mess = "Incompatible type")
72
+ unless valid_type(obj)
73
+ raise mess
74
+ end
75
+ end
76
+
77
+ def freeze # never allow the wrapper to be frozen
78
+ raise "this object cannot be frozen" if self.class.freeze_raise?
79
+ end
80
+
81
+ def val
82
+ @value
83
+ end
84
+
85
+ def ~
86
+ @value
87
+ end
88
+
89
+ def val=(dat)
90
+ replace(dat)
91
+ end
92
+
93
+ def prim_value
94
+ @value
95
+ end
96
+
97
+ def unwrap
98
+ @value
99
+ end
100
+
101
+ def ==(other)
102
+ other.prim_value == @value
103
+ end
104
+
105
+ def !=(other)
106
+ other.prim_value != @value
107
+ end
108
+
109
+ def valid_type(prm=nil)
110
+ true
111
+ end
112
+
113
+ def replace(other)
114
+ @value = other if valid_type(other)
115
+ end
116
+
117
+ def to_s
118
+ @value.to_s
119
+ end
120
+
121
+ def inspect
122
+ str = @value.nil? ? "nil" : @value.inspect
123
+ "(#{self.class}==>#{str})"
124
+ end
125
+ end
126
+
127
+ class Bool < Value
128
+ def valid_type(prm)
129
+ return true if prm.kind_of? TrueClass
130
+ return true if prm.kind_of? FalseClass
131
+ return true if prm.kind_of? TrueW
132
+ return true if prm.kind_of? FalseW
133
+ return true if prm.kind_of? Bool
134
+ false
135
+ end
136
+
137
+ def |(other)
138
+ Bool.new(@value | other.prim_value)
139
+ end
140
+
141
+ def &(other)
142
+ Bool.new(@value & other.prim_value)
143
+ end
144
+
145
+ def ^(other)
146
+ Bool.new(@value ^ other.prim_value)
147
+ end
148
+
149
+ def !
150
+ Bool.new(!@value)
151
+ end
152
+
153
+ def to_i
154
+ @value ? 1:0
155
+ end
156
+ def to_int
157
+ Int.new(@value ? 1:0)
158
+ end
159
+ end
160
+
161
+ class Bit < Bool
162
+ def valid_type(prm)
163
+ return true if prm.kind_of? TrueClass
164
+ return true if prm.kind_of? FalseClass
165
+ return true if prm.kind_of? NilClass
166
+ return true if prm.kind_of? Bool
167
+ false
168
+ end
169
+ def |(other)
170
+ @value | other.prim_value
171
+ end
172
+
173
+ def &(other)
174
+ @value & other.prim_value
175
+ end
176
+
177
+ def ^(other)
178
+ @value ^ other.prim_value
179
+ end
180
+
181
+ def !
182
+ !@value
183
+ end
184
+ def to_i
185
+ @value ? 1:0
186
+ end
187
+ def to_int
188
+ Int.new(@value ? 1:0)
189
+ end
190
+ end
191
+
192
+ class FixedBit < Bit
193
+ def initialize
194
+ raise "FixedBit cannot create instance"
195
+ end
196
+ def replace(other)
197
+ raise "can't assign primitive type"
198
+ end
199
+ end
200
+
201
+ class FalseW < FixedBit
202
+ def initialize
203
+ @value = false
204
+ end
205
+ end
206
+
207
+ class TrueW < FixedBit
208
+ def initialize
209
+ @value = true
210
+ end
211
+ end
212
+
213
+ class Null < FixedBit
214
+ def initialize
215
+ @value = nil
216
+ end
217
+ end
218
+
219
+ class SymbolW < Value
220
+ def valid_type(prm)
221
+ return true if prm.kind_of? Symbol
222
+ return true if prm.kind_of? SymbolW
223
+ false
224
+ end
225
+
226
+ FIX_ARGS = lambda do |elm|
227
+ rtn = elm
228
+ if elm.kind_of? Symbol
229
+ rtn = elm.to_s
230
+ elsif elm.kind_of? SymbolW
231
+ rtn = elm.to_s
232
+ end
233
+ rtn
234
+ end
235
+
236
+ def self.bestow_string_mutate_method(meth_def_name, meth_call_name)
237
+ define_method meth_def_name do |*args, &block|
238
+ prms = args.blockify_elements &FIX_ARGS
239
+ str = @value.to_s
240
+ str.send(meth_call_name, *prms, &block)
241
+ @value = str.to_sym
242
+ end
243
+ end
244
+
245
+ def self.bestow_string_non_mutate_method(meth_def_name, meth_call_name, return_type = SymbolW)
246
+ if return_type==Symbol
247
+ define_method meth_def_name do |*args, &block|
248
+ prms = args.blockify_elements &FIX_ARGS
249
+ @value.to_s.send(meth_call_name, *prms, &block).to_sym
250
+ end
251
+ elsif return_type==String
252
+ define_method meth_def_name do |*args, &block|
253
+ prms = args.blockify_elements &FIX_ARGS
254
+ @value.to_s.send(meth_call_name, *prms, &block).to_s
255
+ end
256
+ elsif return_type==SymbolW
257
+ define_method meth_def_name do |*args, &block|
258
+ prms = args.blockify_elements &FIX_ARGS
259
+ SymbolW.new @value.to_s.send(meth_call_name, *prms, &block).to_s.to_sym
260
+ end
261
+ else # default type
262
+ define_method meth_def_name do |*args, &block|
263
+ prms = args.blockify_elements &FIX_ARGS
264
+ @value.to_s.send(meth_call_name, *prms, &block)
265
+ end
266
+ end
267
+ end
268
+
269
+ def self.bestow_symbol_method(meth_def_name, meth_call_name, mutate_self=false)
270
+ if mutate_self
271
+ define_method meth_def_name do |*args, &block|
272
+ margs = args.blockify_elements { |t| t.prim_value }
273
+ tval = @value.send(meth_call_name, *margs, &block)
274
+ ensure_valid(tval, "symbol mutate method not valid")
275
+ @value = tval
276
+ return self
277
+ end
278
+ else
279
+ define_method meth_def_name do |*args, &block|
280
+ margs = args.blockify_elements { |t| t.prim_value }
281
+ rtn = @value.send(meth_call_name, *margs, &block)
282
+ return rtn
283
+ end
284
+ end
285
+ end
286
+
287
+ def self.bestow_string_mutate_methods(meths)
288
+ meths.each do |meth|
289
+ bestow_string_mutate_method(meth, meth)
290
+ end
291
+ end
292
+
293
+ def self.bestow_string_non_mutate_methods(meths, return_type = SymbolW)
294
+ meths.each do |meth|
295
+ bestow_string_non_mutate_method(meth, meth, return_type)
296
+ end
297
+ end
298
+
299
+ def self.bestow_symbol_methods(meths, mutate_self=false)
300
+ meths.each do |meth|
301
+ dmeth=meth
302
+ if mutate_self
303
+ dmeth = (meth.to_s + '!').to_sym
304
+ end
305
+ bestow_symbol_method(dmeth, meth, mutate_self)
306
+ end
307
+ end
308
+
309
+ # create non-mutate SymbolW methods
310
+ bestow_symbol_methods( [:next, :succ, :[], :length, :size, :upcase, :downcase, :capitalize, :swapcase, :<, :>, :<=, :>=, :between?, :empty? ], false)
311
+
312
+ # create mutate SymbolW methods ... these will auto-bang!
313
+ bestow_symbol_methods([:next, :succ, :upcase, :downcase, :capitalize, :swapcase, :succ], true)
314
+
315
+ # string non-mutate methods
316
+ bestow_string_non_mutate_methods([:match, :include?], nil)
317
+ bestow_string_non_mutate_methods([:prepend], SymbolW)
318
+ bestow_string_non_mutate_method(:append, :<<, SymbolW)
319
+
320
+ # string mutate methods
321
+ bestow_string_mutate_method(:prepend!, :prepend)
322
+ bestow_string_mutate_method(:append!, :<<)
323
+ bestow_string_mutate_method(:[]=, :[]=)
324
+
325
+ # why did I redefine this? don't remember ... investigate later
326
+ def include? pat
327
+ self.to_s.include? pat.to_s
328
+ end
329
+
330
+ # old-fashioned defines:
331
+ def to_sym
332
+ @value
333
+ end
334
+ def to_str
335
+ @value.to_s
336
+ end
337
+ end
338
+
339
+ class ValueAdd < Value
340
+ def valid_type(prm) # must override
341
+ false
342
+ end
343
+ def self.bestow_methods(*args)
344
+ args = args.first if args.first.kind_of? Array
345
+ args.each do |meth|
346
+ define_method meth do |*args, &block|
347
+ @value.send(meth, *args, &block)
348
+ end
349
+ end
350
+ end
351
+ def self.capture_base_methods(type, except=Object)
352
+ add_me = type.instance_methods - except.instance_methods - Value.instance_methods - [:singleton_method_added]
353
+ bestow_methods add_me
354
+ end
355
+ end
356
+
357
+ class Int < ValueAdd
358
+ capture_base_methods(Fixnum) # Bignum also works here ... :to_f, :* etc
359
+ def valid_type(prm)
360
+ return true if prm.kind_of? Integer
361
+ return true if prm.kind_of? Int
362
+ false
363
+ end
364
+ def inc # simulates post-increment
365
+ @value+= 1
366
+ return @value-1
367
+ end
368
+ def dec # simulates post-deccrement
369
+ @value-= 1
370
+ return @value+1
371
+ end
372
+ def to_int
373
+ self
374
+ end
375
+ def ~
376
+ ~@value
377
+ end
378
+ end
379
+
380
+ class FloatW < ValueAdd
381
+ capture_base_methods(Float)
382
+ def valid_type(prm)
383
+ return true if prm.kind_of? Float
384
+ return true if prm.kind_of? FloatW
385
+ false
386
+ end
387
+ def to_int
388
+ Int.new @value.to_i
389
+ end
390
+ end
391
+
392
+ class Number < ValueAdd
393
+ capture_base_methods(Fixnum)
394
+ capture_base_methods(Float, self)
395
+ capture_base_methods(Complex, self)
396
+ capture_base_methods(Rational, self)
397
+ def valid_type(prm)
398
+ return true if prm.kind_of? Numeric
399
+ return true if prm.kind_of? Number
400
+ false
401
+ end
402
+ def ~ # make sure capture did not muck things up
403
+ @value # If you need ~ use Int instead of Number
404
+ end
405
+ def to_int
406
+ Int.new @value.to_i
407
+ end
408
+ end
409
+
410
+ # string, number
411
+ class Datum < ValueAdd
412
+ capture_base_methods(Number)
413
+ capture_base_methods(String, self)
414
+ def valid_type(prm)
415
+ return true if prm.kind_of? Numeric
416
+ return true if prm.kind_of? String
417
+ return true if prm.kind_of? Datum
418
+ false
419
+ end
420
+
421
+ # methods that broke ... fixed here
422
+ def to_s
423
+ @value.to_s
424
+ end
425
+ def ~ # make sure capture did not muck things up
426
+ @value # If you need ~ use Int instead of Number
427
+ end
428
+ def to_int
429
+ Int.new @value.to_i
430
+ end
431
+ end
432
+
433
+ class Property < Value
434
+ GOOD_KEY_RGX = /\A[a-zA-Z_][a-zA-Z_0-9]*\z/
435
+
436
+ def initialize(hash={})
437
+ self.val=(hash)
438
+ end
439
+
440
+ def self.bad_key?(idx)
441
+ @badkeys ||= {}
442
+ true==@badkeys[idx]
443
+ end
444
+
445
+ def self.good_candidate_name? name
446
+ name = name.prim_value
447
+ return false unless name.kind_of? Symbol
448
+ tst = GOOD_KEY_RGX.match name
449
+ return false if tst.nil?
450
+ true
451
+ end
452
+
453
+ def self.good_key?(idx) # syntax and not bad ... does not check to see if taken as only instansts can do that
454
+ idx = idx.prim_value
455
+ return false unless good_candidate_name? idx
456
+ return false if bad_key?(idx)
457
+ true
458
+ end
459
+
460
+ def self.reserve_property_names!(*names)
461
+ @badkeys ||= {}
462
+ names = names.first if names.first.kind_of? Array
463
+ names.each do |key|
464
+ next if key[-1]=='!'
465
+ next if key[-1]=='?'
466
+ kw = key[-1]=='=' ? key[0..-2] : key[0..-1]
467
+ md = GOOD_KEY_RGX.match kw
468
+ next if md.nil? # bad key
469
+ @badkeys[key]=true
470
+ end
471
+ end
472
+ reserve_property_names!(self.instance_methods)
473
+ reserve_property_names!(Object.new.private_methods)
474
+
475
+ # assign replacement hash
476
+ def val=(hash)
477
+ ensure_valid(hash)
478
+ hash = hash.prim_value
479
+ @value = hash # keep this as prime as possible
480
+ rekey!
481
+ end
482
+
483
+ def rekey!
484
+ @good_keys = {} # these keys can be properties
485
+ @hold_keys = {} # these keys can't
486
+ @value.each_pair do |pkey,val|
487
+ key = pkey.prim_value
488
+ if self.class.good_key? key
489
+ @good_keys[key.prim_value]=true
490
+ else
491
+ @hold_keys[key.prim_value]=true
492
+ end
493
+ end
494
+ end
495
+
496
+ def method_missing(method, *args, &block)
497
+ flag_good = @good_keys.include? method
498
+ unless flag_good
499
+ unless method[-1]=='='
500
+ if @value.include? method
501
+ rekey!
502
+ flag_good = @good_keys.include? method
503
+ end
504
+ end
505
+ end
506
+ raise "Illegal Block attachment" unless block.nil?
507
+ if flag_good
508
+ raise "*** ArgumentError Exception: wrong number of arguments (given #{args.count}, expected 0)" unless args.empty?
509
+ return @value[method]
510
+ end
511
+ if method[-1]=='=' # assignment
512
+ key = method[0..-2].to_sym
513
+ if args.count==1
514
+ if @good_keys.include? key
515
+ @value[key] = args[0]
516
+ return true
517
+ end
518
+ # creating a new key here ... good or bad?
519
+ if self.class.good_key? key
520
+ @good_keys[key]=true
521
+ else
522
+ @hold_keys[key]=true
523
+ end
524
+ return @value[key] = args[0]
525
+ end
526
+ raise "*** ArgumentError Exception: wrong number of arguments (given #{args.count}, expected 1)"
527
+ else
528
+ # lookup failed ... punt
529
+ super
530
+ end
531
+ end
532
+
533
+ def split!
534
+ good = {}
535
+ bad = {}
536
+ @good_keys.each_pair do |key,val|
537
+ good[key] = @value[key]
538
+ end
539
+ @hold_keys.each_pair do |key,val|
540
+ bad[key] = @value[key]
541
+ end
542
+ return [good,bad]
543
+ end
544
+
545
+ def [](idx)
546
+ @value[idx]
547
+ end
548
+
549
+ def []=(idx,val)
550
+ idx = idx.prim_value # in case it is wrapped
551
+ rtn = @value[idx] = val
552
+ # now put key in one of two bins
553
+ if self.class.good_key? idx
554
+ @good_keys[idx]=true
555
+ else
556
+ @hold_keys[idx]=true
557
+ end
558
+ return rtn
559
+ end
560
+
561
+ def import_hash!(hash)
562
+ hash.each_pair do |key,val|
563
+ self[key]=val
564
+ end
565
+ end
566
+
567
+ def valid_type(prm)
568
+ return true if prm.kind_of? Hash
569
+ return true if prm.kind_of? Property
570
+ false
571
+ end
572
+
573
+ def defined_properties!
574
+ rekey!
575
+ @good_keys.keys.sort
576
+ end
577
+ def deferred_properties!
578
+ rekey!
579
+ @hold_keys.keys # can't sort bad keys without a ton of work
580
+ end
581
+
582
+ def property?(tst)
583
+ rekey!
584
+ tst = tst.prim_value
585
+ return true if @good_keys.include? tst
586
+ false
587
+ end
588
+
589
+ def deferred?(tst)
590
+ rekey!
591
+ tst = tst.prim_value
592
+ return true if @hold_keys.include? tst
593
+ false
594
+ end
595
+
596
+ def define_properties!(ary, dflt="")
597
+ ary.each do |item|
598
+ key = item.prim_value
599
+ raise "*** Property Error: must use Symbol type to define properties" unless key.kind_of? Symbol
600
+ raise "*** Property Error: property #{key.inspect} already defined" if @good_keys.include? key
601
+ raise "*** Property Error: property #{key.inspect} violates naming convention" unless self.class.good_candidate_name? key
602
+ raise "*** Property Error: property #{key.inspect} is reserved" if self.class.bad_key? key
603
+ @good_keys[key]=true
604
+ @value[key]=dflt
605
+ end
606
+ end
607
+
608
+ end
609
+
610
+