primitive_wrapper 0.1.0

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