deep-hash-struct 0.1.2 → 0.1.3
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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +847 -1
- data/deep-hash-struct.gemspec +1 -1
- data/lib/deep/hash/struct/dashboard/table/row.rb +44 -0
- data/lib/deep/hash/struct/dashboard/table.rb +143 -0
- data/lib/deep/hash/struct/dashboard.rb +48 -0
- data/lib/deep/hash/struct/error.rb +14 -0
- data/lib/deep/hash/struct/pp/dashboard/table/row.rb +48 -0
- data/lib/deep/hash/struct/pp/dashboard/table.rb +62 -0
- data/lib/deep/hash/struct/pp/dashboard.rb +29 -0
- data/lib/deep/hash/struct/pp/wrapper.rb +41 -0
- data/lib/deep/hash/struct/pp.rb +5 -0
- data/lib/deep/hash/struct/version.rb +1 -1
- data/lib/deep/hash/struct/wrapper.rb +588 -0
- data/lib/deep/hash/struct.rb +4 -502
- metadata +14 -4
- data/CODE_OF_CONDUCT.md +0 -74
@@ -0,0 +1,588 @@
|
|
1
|
+
module Deep
|
2
|
+
module Hash
|
3
|
+
module Struct
|
4
|
+
class Wrapper
|
5
|
+
include PP::Wrapper
|
6
|
+
|
7
|
+
attr_accessor :parent_ins,
|
8
|
+
:parent_key,
|
9
|
+
:chain
|
10
|
+
|
11
|
+
attr_reader :default
|
12
|
+
|
13
|
+
alias chain? chain
|
14
|
+
|
15
|
+
def initialize(h = {}, opt = {})
|
16
|
+
self.chain = opt[:chain].nil? ? false : opt[:chain]
|
17
|
+
self.parent_ins = opt[:parent_ins]
|
18
|
+
self.parent_key = opt[:parent_key]
|
19
|
+
return if h.nil? || h.count.zero?
|
20
|
+
wrap h, self
|
21
|
+
end
|
22
|
+
|
23
|
+
def keys
|
24
|
+
instance_variables.reject { |k| k =~ /default|parent_ins|parent_key|chain/ }.map do |k|
|
25
|
+
k.to_s[1..-1].to_sym
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def values
|
30
|
+
keys.map do |k|
|
31
|
+
self[k]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def [](k)
|
36
|
+
__send__ k
|
37
|
+
end
|
38
|
+
|
39
|
+
def []=(k, v)
|
40
|
+
__send__ "#{k}=", v
|
41
|
+
end
|
42
|
+
alias add []=
|
43
|
+
|
44
|
+
def ==(hash)
|
45
|
+
if hash.class == self.class
|
46
|
+
to_h == hash.to_h
|
47
|
+
else
|
48
|
+
to_h == hash
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def ===(klass)
|
53
|
+
self.class == klass
|
54
|
+
end
|
55
|
+
|
56
|
+
def each
|
57
|
+
if block_given?
|
58
|
+
keys.each do |k|
|
59
|
+
yield k, self[k]
|
60
|
+
end
|
61
|
+
else
|
62
|
+
to_h.each
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def each_with_index
|
67
|
+
index = 0
|
68
|
+
keys.each do |k|
|
69
|
+
yield [k, self[k]], index
|
70
|
+
index += 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def each_with_object(init)
|
75
|
+
each do |k, v|
|
76
|
+
yield [k, v], init
|
77
|
+
end
|
78
|
+
|
79
|
+
init
|
80
|
+
end
|
81
|
+
|
82
|
+
def each_key
|
83
|
+
keys.each do |k|
|
84
|
+
yield k
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def each_value
|
89
|
+
values.each do |v|
|
90
|
+
yield v
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def count
|
95
|
+
keys.size
|
96
|
+
end
|
97
|
+
alias size count
|
98
|
+
|
99
|
+
def blank?
|
100
|
+
keys.size.zero?
|
101
|
+
end
|
102
|
+
|
103
|
+
def present?
|
104
|
+
!blank?
|
105
|
+
end
|
106
|
+
|
107
|
+
def dig(*syms)
|
108
|
+
syms.inject(self) do |h, k|
|
109
|
+
h[k]
|
110
|
+
end
|
111
|
+
rescue
|
112
|
+
nil
|
113
|
+
end
|
114
|
+
|
115
|
+
def merge(hash)
|
116
|
+
klass = dup
|
117
|
+
|
118
|
+
hash.each do |k, v|
|
119
|
+
klass[k] = v
|
120
|
+
end
|
121
|
+
|
122
|
+
klass
|
123
|
+
end
|
124
|
+
|
125
|
+
def merge!(hash)
|
126
|
+
hash.each do |k, v|
|
127
|
+
self[k] = v
|
128
|
+
end
|
129
|
+
self
|
130
|
+
end
|
131
|
+
alias update merge!
|
132
|
+
|
133
|
+
def deep_merge(hash, klass = self)
|
134
|
+
klass = klass.deep_dup
|
135
|
+
|
136
|
+
hash.each do |k , v|
|
137
|
+
klass[k] = hash?(v) ? klass.deep_merge(v, klass[k]) : v
|
138
|
+
end
|
139
|
+
|
140
|
+
klass
|
141
|
+
end
|
142
|
+
|
143
|
+
def deep_merge!(hash, klass = self)
|
144
|
+
hash.each do |k, v|
|
145
|
+
klass[k] = hash?(v) ? deep_merge!(v, klass[k]) : v
|
146
|
+
end
|
147
|
+
|
148
|
+
klass
|
149
|
+
end
|
150
|
+
|
151
|
+
def reverse_merge(hash)
|
152
|
+
klass = dup
|
153
|
+
|
154
|
+
hash.each do |k, v|
|
155
|
+
klass[k] = v if klass.exclude?(k)
|
156
|
+
end
|
157
|
+
|
158
|
+
klass
|
159
|
+
end
|
160
|
+
|
161
|
+
def reverse_merge!(hash)
|
162
|
+
hash.each do |k, v|
|
163
|
+
self[k] = v if exclude?(k)
|
164
|
+
end
|
165
|
+
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
def reverse_deep_merge(hash, klass = self)
|
170
|
+
klass = klass.deep_dup
|
171
|
+
|
172
|
+
hash.each do |k , v|
|
173
|
+
next if !hash?(v) && klass.include?(k)
|
174
|
+
klass[k] = hash?(v) ? klass.reverse_deep_merge(v, klass[k]) : v
|
175
|
+
end
|
176
|
+
|
177
|
+
klass
|
178
|
+
end
|
179
|
+
|
180
|
+
def reverse_deep_merge!(hash)
|
181
|
+
hash.each do |k, v|
|
182
|
+
next if !hash?(v) && include?(k)
|
183
|
+
self[k] = hash?(v) ? reverse_deep_merge(v, self[k]) : v
|
184
|
+
end
|
185
|
+
|
186
|
+
self
|
187
|
+
end
|
188
|
+
|
189
|
+
def fetch(k, msg = nil)
|
190
|
+
v = self[k]
|
191
|
+
if block_given?
|
192
|
+
if v.class == self.class || v.nil?
|
193
|
+
yield k
|
194
|
+
else
|
195
|
+
v
|
196
|
+
end
|
197
|
+
else
|
198
|
+
case v
|
199
|
+
when self.class
|
200
|
+
if v.blank?
|
201
|
+
msg
|
202
|
+
else
|
203
|
+
to_h
|
204
|
+
end
|
205
|
+
when nil
|
206
|
+
msg
|
207
|
+
else
|
208
|
+
v
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def map
|
214
|
+
if block_given?
|
215
|
+
vs = []
|
216
|
+
|
217
|
+
each do |k, v|
|
218
|
+
vs << yield(k, v)
|
219
|
+
end
|
220
|
+
|
221
|
+
vs
|
222
|
+
else
|
223
|
+
to_h.map
|
224
|
+
end
|
225
|
+
end
|
226
|
+
alias collect map
|
227
|
+
|
228
|
+
def map_key
|
229
|
+
vs = []
|
230
|
+
|
231
|
+
keys.each do |k|
|
232
|
+
vs << yield(k)
|
233
|
+
end
|
234
|
+
|
235
|
+
vs
|
236
|
+
end
|
237
|
+
|
238
|
+
def map_value
|
239
|
+
vs = []
|
240
|
+
|
241
|
+
values.each do |v|
|
242
|
+
vs << yield(v)
|
243
|
+
end
|
244
|
+
|
245
|
+
vs
|
246
|
+
end
|
247
|
+
|
248
|
+
def fetch_values(*syms)
|
249
|
+
syms.map do |sym|
|
250
|
+
if block_given?
|
251
|
+
if keys.include?(sym)
|
252
|
+
self[sym]
|
253
|
+
else
|
254
|
+
yield sym
|
255
|
+
end
|
256
|
+
else
|
257
|
+
raise KeyError, "key not found: :#{sym}" unless keys.include?(sym)
|
258
|
+
self[sym]
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def values_at(*syms)
|
264
|
+
syms.map do |sym|
|
265
|
+
v = self[sym]
|
266
|
+
(v.nil? || (v.class == self.class && v.blank?)) ? nil : v
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def invert
|
271
|
+
each_with_object({}) do |(k, v), h|
|
272
|
+
h[v] = k
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def delete(k)
|
277
|
+
remove_instance_variable "@#{k}"
|
278
|
+
end
|
279
|
+
|
280
|
+
def delete_if
|
281
|
+
each do |k, v|
|
282
|
+
remove_instance_variable "@#{k}" if yield(k, v)
|
283
|
+
end
|
284
|
+
self
|
285
|
+
end
|
286
|
+
|
287
|
+
def find
|
288
|
+
klass = self.class.new
|
289
|
+
|
290
|
+
each do |k, v|
|
291
|
+
if yield(k, v)
|
292
|
+
klass[k] = v
|
293
|
+
break
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
klass
|
298
|
+
end
|
299
|
+
|
300
|
+
def select
|
301
|
+
klass = self.class.new
|
302
|
+
|
303
|
+
each do |k, v|
|
304
|
+
klass[k] = v if yield(k, v)
|
305
|
+
end
|
306
|
+
|
307
|
+
klass
|
308
|
+
end
|
309
|
+
alias find_all select
|
310
|
+
|
311
|
+
def reject
|
312
|
+
klass = self.class.new
|
313
|
+
|
314
|
+
each do |k, v|
|
315
|
+
klass[k] = v unless yield(k, v)
|
316
|
+
end
|
317
|
+
|
318
|
+
klass
|
319
|
+
end
|
320
|
+
|
321
|
+
def reject!
|
322
|
+
each do |k, v|
|
323
|
+
remove_instance_variable "@#{k}" if yield(k, v)
|
324
|
+
end
|
325
|
+
self
|
326
|
+
end
|
327
|
+
|
328
|
+
def inject(init = 0)
|
329
|
+
value = init.dup
|
330
|
+
each do |k, v|
|
331
|
+
val = yield init.dup, [k, v]
|
332
|
+
next if val.nil?
|
333
|
+
value += val
|
334
|
+
end
|
335
|
+
value
|
336
|
+
end
|
337
|
+
alias reduce inject
|
338
|
+
|
339
|
+
def clear
|
340
|
+
remove_instance_variable "@default" unless default.nil?
|
341
|
+
each_key do |k|
|
342
|
+
remove_instance_variable "@#{k}"
|
343
|
+
end
|
344
|
+
self
|
345
|
+
end
|
346
|
+
|
347
|
+
def replace(other)
|
348
|
+
h = if other.class == self.class
|
349
|
+
other.to_hash
|
350
|
+
elsif hash? other
|
351
|
+
other
|
352
|
+
else
|
353
|
+
other.to_hash
|
354
|
+
end
|
355
|
+
|
356
|
+
clear
|
357
|
+
wrap h, self
|
358
|
+
end
|
359
|
+
|
360
|
+
def flatten
|
361
|
+
to_h.flatten
|
362
|
+
end
|
363
|
+
|
364
|
+
def has_key?(k)
|
365
|
+
keys.include? k
|
366
|
+
end
|
367
|
+
alias include? has_key?
|
368
|
+
|
369
|
+
def has_keys?(*syms)
|
370
|
+
syms.inject(self) do |klass, sym|
|
371
|
+
val = klass[sym]
|
372
|
+
return false if val.class == self.class && val.blank?
|
373
|
+
klass[sym]
|
374
|
+
end
|
375
|
+
true
|
376
|
+
end
|
377
|
+
|
378
|
+
def exclude?(k)
|
379
|
+
!include? k
|
380
|
+
end
|
381
|
+
|
382
|
+
def sort
|
383
|
+
to_h.sort
|
384
|
+
end
|
385
|
+
|
386
|
+
def shift
|
387
|
+
k = keys.first
|
388
|
+
[k, remove_instance_variable("@#{k}")]
|
389
|
+
end
|
390
|
+
|
391
|
+
def to_a
|
392
|
+
to_h.to_a
|
393
|
+
end
|
394
|
+
alias to_ary to_a
|
395
|
+
|
396
|
+
def compact
|
397
|
+
klass = dup
|
398
|
+
|
399
|
+
klass.each do |k, v|
|
400
|
+
klass.remove_instance_variable("@#{k}") if (v.class == self.class && v.blank?) || v.nil?
|
401
|
+
end
|
402
|
+
|
403
|
+
klass
|
404
|
+
end
|
405
|
+
|
406
|
+
def compact!
|
407
|
+
each do |k, v|
|
408
|
+
remove_instance_variable("@#{k}") if (v.class == self.class && v.blank?) || v.nil?
|
409
|
+
end
|
410
|
+
|
411
|
+
self
|
412
|
+
end
|
413
|
+
|
414
|
+
def deep_compact
|
415
|
+
klass = deep_dup
|
416
|
+
|
417
|
+
klass.each do |k, v|
|
418
|
+
flag = if v.class == self.class
|
419
|
+
klass[k] = v.deep_compact
|
420
|
+
klass[k].present?
|
421
|
+
else
|
422
|
+
!v.nil?
|
423
|
+
end
|
424
|
+
next if flag
|
425
|
+
klass.remove_instance_variable("@#{k}")
|
426
|
+
end
|
427
|
+
|
428
|
+
klass
|
429
|
+
end
|
430
|
+
|
431
|
+
def deep_compact!
|
432
|
+
each do |k, v|
|
433
|
+
flag = if v.class == self.class
|
434
|
+
self[k] = v.deep_compact
|
435
|
+
self[k].present?
|
436
|
+
else
|
437
|
+
!v.nil?
|
438
|
+
end
|
439
|
+
next if flag
|
440
|
+
remove_instance_variable("@#{k}")
|
441
|
+
end
|
442
|
+
|
443
|
+
self
|
444
|
+
end
|
445
|
+
|
446
|
+
def slice(*syms)
|
447
|
+
klass = self.class.new
|
448
|
+
|
449
|
+
each_key do |k|
|
450
|
+
klass[k] = self[k] if syms.map(&:to_sym).include?(k)
|
451
|
+
end
|
452
|
+
|
453
|
+
klass
|
454
|
+
end
|
455
|
+
|
456
|
+
def slice!(*syms)
|
457
|
+
each_key do |k|
|
458
|
+
remove_instance_variable("@#{k}") unless syms.map(&:to_sym).include?(k)
|
459
|
+
end
|
460
|
+
|
461
|
+
self
|
462
|
+
end
|
463
|
+
|
464
|
+
def to_hash
|
465
|
+
deep_hash self
|
466
|
+
end
|
467
|
+
alias to_h to_hash
|
468
|
+
|
469
|
+
def to_json
|
470
|
+
to_h.to_json
|
471
|
+
end
|
472
|
+
|
473
|
+
def max_stages(klass = self, i = 1)
|
474
|
+
klass.map_value do |v|
|
475
|
+
case v
|
476
|
+
when self.class
|
477
|
+
max_stages v, i + 1
|
478
|
+
else
|
479
|
+
i
|
480
|
+
end
|
481
|
+
end.sort.last || 0
|
482
|
+
end
|
483
|
+
|
484
|
+
def min_stages(klass = self, i = 1)
|
485
|
+
klass.map_value do |v|
|
486
|
+
case v
|
487
|
+
|
488
|
+
when self.class
|
489
|
+
min_stages v, i + 1
|
490
|
+
else
|
491
|
+
i
|
492
|
+
end
|
493
|
+
end.delete_if(&:zero?).sort.first || 0
|
494
|
+
end
|
495
|
+
|
496
|
+
def to_table(side_name = nil)
|
497
|
+
if min_stages >= 2
|
498
|
+
values.flat_map(&:keys).uniq.map do |side|
|
499
|
+
map_value { |v| v[side] }.unshift side
|
500
|
+
end.unshift keys.unshift(side_name)
|
501
|
+
else
|
502
|
+
deep_table to_h
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def deep_dup(klass = self)
|
507
|
+
new_klass = self.class.new
|
508
|
+
|
509
|
+
klass.each do |k, v|
|
510
|
+
new_klass[k] = hash?(v) ? v.deep_dup(v) : v
|
511
|
+
end
|
512
|
+
|
513
|
+
new_klass
|
514
|
+
end
|
515
|
+
|
516
|
+
private
|
517
|
+
|
518
|
+
def method_missing(method_name, arg = nil)
|
519
|
+
name = method_name.to_s.delete("=")
|
520
|
+
|
521
|
+
if method_name.to_s.end_with? "="
|
522
|
+
setting_chain_instance!(self, name, arg)
|
523
|
+
elsif block_given?
|
524
|
+
__send__ "#{name}=", yield
|
525
|
+
else
|
526
|
+
val = instance_variable_get "@#{name}"
|
527
|
+
return val unless val.nil?
|
528
|
+
return self.class.new({}, chain: true, parent_ins: self, parent_key: method_name) if default.nil?
|
529
|
+
default
|
530
|
+
end
|
531
|
+
rescue => e
|
532
|
+
e.backtrace.shift
|
533
|
+
raise
|
534
|
+
end
|
535
|
+
|
536
|
+
def chain!
|
537
|
+
self.chain = true
|
538
|
+
end
|
539
|
+
|
540
|
+
def setting_chain_instance!(klass, key, val)
|
541
|
+
klass.instance_variable_set "@#{key}", hash?(val) && (!(self === val.class) || klass.chain?) ? wrap(val) : val
|
542
|
+
return val unless klass.chain?
|
543
|
+
setting_chain_instance!(klass.parent_ins, klass.parent_key, klass)
|
544
|
+
end
|
545
|
+
|
546
|
+
def deep_hash(klass)
|
547
|
+
h = {}
|
548
|
+
|
549
|
+
klass.each do |k, v|
|
550
|
+
h[k] = if v.class == self.class
|
551
|
+
deep_hash(v) if v.present?
|
552
|
+
else
|
553
|
+
v
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
h
|
558
|
+
end
|
559
|
+
|
560
|
+
def deep_table(hash)
|
561
|
+
hash.to_a.transpose.map do |val|
|
562
|
+
val.map do |v|
|
563
|
+
if hash? v
|
564
|
+
deep_table v
|
565
|
+
else
|
566
|
+
v
|
567
|
+
end
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
def wrap(hash, klass = nil)
|
573
|
+
base = klass || self.class.new
|
574
|
+
|
575
|
+
hash.each do |k, v|
|
576
|
+
base[k] = hash?(v) ? wrap(v) : v
|
577
|
+
end
|
578
|
+
|
579
|
+
base
|
580
|
+
end
|
581
|
+
|
582
|
+
def hash?(val)
|
583
|
+
!val.is_a?(Array) && val.respond_to?(:each)
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
end
|
588
|
+
end
|