subhash 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/subhash.rb CHANGED
@@ -17,6 +17,9 @@
17
17
 
18
18
  require 'rubygems'
19
19
  require 'yaml'
20
+ require 'rh'
21
+ require 'ihash'
22
+ require 'iarray'
20
23
 
21
24
  # Adding rh_clone at object level. This be able to use a generic rh_clone
22
25
  # redefined per object Hash and Array.
@@ -24,831 +27,12 @@ class Object
24
27
  alias_method :rh_clone, :clone
25
28
  end
26
29
 
27
- # Rh common module included in Hash and Array class.
28
- module Rh
29
- public
30
-
31
- def merge_cleanup!
32
- _rh_remove_control(self)
33
- end
34
-
35
- def merge_cleanup
36
- _rh_remove_control(rh_clone)
37
- end
38
-
39
- private
40
-
41
- # Function which will parse arrays in hierarchie and will remove any control
42
- # element (index 0)
43
- def _rh_remove_control(result)
44
- return unless [Hash, Array].include?(result.class)
45
-
46
- if result.is_a?(Hash)
47
- result.each { |elem| _rh_remove_control(elem) }
48
- else
49
- result.delete_at(0) if result[0].is_a?(Hash) && result[0].key?(:__control)
50
- result.each_index { |index| _rh_remove_control(result[index]) }
51
- end
52
- result
53
- end
54
-
55
- # Internal function to determine if result and data key contains both Hash or
56
- # Array and if so, do the merge task on those sub Hash/Array
57
- #
58
- def _rh_merge_recursive(result, key, data)
59
- return false unless [Array, Hash].include?(data.class)
60
-
61
- value = data[key]
62
- return false unless [Array, Hash].include?(value.class) &&
63
- value.class == result[key].class
64
-
65
- if object_id == result.object_id
66
- result[key].rh_merge!(value)
67
- else
68
- result[key] = result[key].rh_merge(value)
69
- end
70
-
71
- true
72
- end
73
-
74
- # Internal function to determine if changing from Hash/Array to anything else
75
- # is authorized or not.
76
- #
77
- # The structure is changing if `result` or `value` move from Hash/Array to any
78
- # other type.
79
- #
80
- # * *Args*:
81
- # - result: Merged Hash or Array structure.
82
- # - key : Key in result and data.
83
- # - data : Hash or Array structure to merge.
84
- #
85
- # * *returns*:
86
- # - +true+ : if :__struct_changing == true
87
- # - +false+ : otherwise.
88
- def _rh_struct_changing_ok?(result, key, data)
89
- return true unless [Array, Hash].include?(data[key].class) ||
90
- [Array, Hash].include?(result[key].class)
91
-
92
- # result or value are structure (Hash or Array)
93
- if result.is_a?(Hash)
94
- control = result[:__struct_changing]
95
- else
96
- control = result[0][:__struct_changing]
97
- key -= 1
98
- end
99
- return true if control.is_a?(Array) && control.include?(key)
100
-
101
- false
102
- end
103
-
104
- # Internal function to determine if a data merged can be updated by any
105
- # other object like Array, String, etc...
106
- #
107
- # The decision is given by a :__unset setting.
108
- #
109
- # * *Args*:
110
- # - Hash/Array data to replace.
111
- # - key: string or symbol.
112
- #
113
- # * *returns*:
114
- # - +false+ : if key is found in :__protected Array.
115
- # - +true+ : otherwise.
116
- def _rh_merge_ok?(result, key)
117
- if result.is_a?(Hash)
118
- control = result[:__protected]
119
- else
120
- control = result[0][:__protected]
121
- key -= 1
122
- end
123
-
124
- return false if control.is_a?(Array) && control.include?(key)
125
-
126
- true
127
- end
128
-
129
- def _rh_control_tags
130
- [:__remove, :__remove_index, :__add, :__add_index,
131
- :__protected, :__struct_changing, :__control]
132
- end
133
- end
134
-
135
- # Recursive Hash added to the Hash class
136
- class Hash
137
- # Recursive Hash deep level found counter
138
- # This function will returns the count of deep level of recursive hash.
139
- # * *Args* :
140
- # - +p+ : Array of string or symbols. keys tree to follow and check
141
- # existence in yVal.
142
- #
143
- # * *Returns* :
144
- # - +integer+ : Represents how many deep level was found in the recursive
145
- # hash
146
- #
147
- # * *Raises* :
148
- # No exceptions
149
- #
150
- # Example: (implemented in spec)
151
- #
152
- # yVal = { :test => {:test2 => 'value1', :test3 => 'value2'},
153
- # :test4 => 'value3'}
154
- #
155
- # yVal can be represented like:
156
- #
157
- # yVal:
158
- # test:
159
- # test2 = 'value1'
160
- # test3 = 'value2'
161
- # test4 = 'value3'
162
- #
163
- # so:
164
- # # test is found
165
- # yVal.rh_lexist?(:test) => 1
166
- #
167
- # # no test5
168
- # yVal.rh_lexist?(:test5) => 0
169
- #
170
- # # :test/:test2 tree is found
171
- # yVal.rh_lexist?(:test, :test2) => 2
172
- #
173
- # # :test/:test2 is found (value = 2), but :test5 was not found in this tree
174
- # yVal.rh_lexist?(:test, :test2, :test5) => 2
175
- #
176
- # # :test was found. but :test/:test5 tree was not found. so level 1, ok.
177
- # yVal.rh_lexist?(:test, :test5 ) => 1
178
- #
179
- # # it is like searching for nothing...
180
- # yVal.rh_lexist? => 0
181
-
182
- def rh_lexist?(*p)
183
- p = p.flatten
184
-
185
- return 0 if p.length == 0
186
-
187
- if p.length == 1
188
- return 1 if self.key?(p[0])
189
- return 0
30
+ if RUBY_VERSION.match(/1\.8/)
31
+ # Added missing <=> function in Symbol of ruby 1.8
32
+ class Symbol
33
+ def <=>(other)
34
+ return to_s <=> other if other.is_a?(String)
35
+ to_s <=> other.to_s
190
36
  end
191
- return 0 unless self.key?(p[0])
192
- ret = 0
193
- ret = self[p[0]].rh_lexist?(p.drop(1)) if self[p[0]].is_a?(Hash)
194
- 1 + ret
195
- end
196
-
197
- # Recursive Hash deep level existence
198
- #
199
- # * *Args* :
200
- # - +p+ : Array of string or symbols. keys tree to follow and check
201
- # existence in yVal.
202
- #
203
- # * *Returns* :
204
- # - +boolean+ : Returns True if the deep level of recursive hash is found.
205
- # false otherwise
206
- #
207
- # * *Raises* :
208
- # No exceptions
209
- #
210
- # Example:(implemented in spec)
211
- #
212
- # yVal = { :test => {:test2 => 'value1', :test3 => 'value2'},
213
- # :test4 => 'value3'}
214
- #
215
- # yVal can be represented like:
216
- #
217
- # yVal:
218
- # test:
219
- # test2 = 'value1'
220
- # test3 = 'value2'
221
- # test4 = 'value3'
222
- #
223
- # so:
224
- # # test is found
225
- # yVal.rh_exist?(:test) => True
226
- #
227
- # # no test5
228
- # yVal.rh_exist?(:test5) => False
229
- #
230
- # # :test/:test2 tree is found
231
- # yVal.rh_exist?(:test, :test2) => True
232
- #
233
- # # :test/:test2 is found (value = 2), but :test5 was not found in this tree
234
- # yVal.rh_exist?(:test, :test2, :test5) => False
235
- #
236
- # # :test was found. but :test/:test5 tree was not found. so level 1, ok.
237
- # yVal.rh_exist?(:test, :test5 ) => False
238
- #
239
- # # it is like searching for nothing...
240
- # yVal.rh_exist? => nil
241
- def rh_exist?(*p)
242
- p = p.flatten
243
-
244
- return nil if p.length == 0
245
-
246
- count = p.length
247
- (rh_lexist?(*p) == count)
248
37
  end
249
-
250
- # Recursive Hash Get
251
- # This function will returns the level of recursive hash was found.
252
- # * *Args* :
253
- # - +p+ : Array of string or symbols. keys tree to follow and check
254
- # existence in yVal.
255
- #
256
- # * *Returns* :
257
- # - +value+ : Represents the data found in the tree. Can be of any type.
258
- #
259
- # * *Raises* :
260
- # No exceptions
261
- #
262
- # Example:(implemented in spec)
263
- #
264
- # yVal = { :test => {:test2 => 'value1', :test3 => 'value2'},
265
- # :test4 => 'value3'}
266
- #
267
- # yVal can be represented like:
268
- #
269
- # yVal:
270
- # test:
271
- # test2 = 'value1'
272
- # test3 = 'value2'
273
- # test4 = 'value3'
274
- #
275
- # so:
276
- # yVal.rh_get(:test) => {:test2 => 'value1', :test3 => 'value2'}
277
- # yVal.rh_get(:test5) => nil
278
- # yVal.rh_get(:test, :test2) => 'value1'
279
- # yVal.rh_get(:test, :test2, :test5) => nil
280
- # yVal.rh_get(:test, :test5 ) => nil
281
- # yVal.rh_get => { :test => {:test2 => 'value1', :test3 => 'value2'},
282
- # :test4 => 'value3'}
283
- def rh_get(*p)
284
- p = p.flatten
285
- return self if p.length == 0
286
-
287
- if p.length == 1
288
- return self[p[0]] if self.key?(p[0])
289
- return nil
290
- end
291
- return self[p[0]].rh_get(p.drop(1)) if self[p[0]].is_a?(Hash)
292
- nil
293
- end
294
-
295
- # Recursive Hash Set
296
- # This function will build a recursive hash according to the '*p' key tree.
297
- # if yVal is not nil, it will be updated.
298
- #
299
- # * *Args* :
300
- # - +p+ : Array of string or symbols. keys tree to follow and check
301
- # existence in yVal.
302
- #
303
- # * *Returns* :
304
- # - +value+ : the value set.
305
- #
306
- # * *Raises* :
307
- # No exceptions
308
- #
309
- # Example:(implemented in spec)
310
- #
311
- # yVal = {}
312
- #
313
- # yVal.rh_set(:test) => nil
314
- # # yVal = {}
315
- #
316
- # yVal.rh_set(:test5) => nil
317
- # # yVal = {}
318
- #
319
- # yVal.rh_set(:test, :test2) => :test
320
- # # yVal = {:test2 => :test}
321
- #
322
- # yVal.rh_set(:test, :test2, :test5) => :test
323
- # # yVal = {:test2 => {:test5 => :test} }
324
- #
325
- # yVal.rh_set(:test, :test5 ) => :test
326
- # # yVal = {:test2 => {:test5 => :test}, :test5 => :test }
327
- #
328
- # yVal.rh_set('blabla', :test2, 'text') => :test
329
- # # yVal = {:test2 => {:test5 => :test, 'text' => 'blabla'},
330
- # :test5 => :test }
331
- def rh_set(value, *p)
332
- p = p.flatten
333
- return nil if p.length == 0
334
-
335
- if p.length == 1
336
- self[p[0]] = value
337
- return value
338
- end
339
-
340
- self[p[0]] = {} unless self[p[0]].is_a?(Hash)
341
- self[p[0]].rh_set(value, p.drop(1))
342
- end
343
-
344
- # Recursive Hash delete
345
- # This function will remove the last key defined by the key tree
346
- #
347
- # * *Args* :
348
- # - +p+ : Array of string or symbols. keys tree to follow and check
349
- # existence in yVal.
350
- #
351
- # * *Returns* :
352
- # - +value+ : The Hash updated.
353
- #
354
- # * *Raises* :
355
- # No exceptions
356
- #
357
- # Example:(implemented in spec)
358
- #
359
- # yVal = {{:test2 => { :test5 => :test,
360
- # 'text' => 'blabla' },
361
- # :test5 => :test}}
362
- #
363
- #
364
- # yVal.rh_del(:test) => nil
365
- # # yVal = no change
366
- #
367
- # yVal.rh_del(:test, :test2) => nil
368
- # # yVal = no change
369
- #
370
- # yVal.rh_del(:test2, :test5) => {:test5 => :test}
371
- # # yVal = {:test2 => {:test5 => :test} }
372
- #
373
- # yVal.rh_del(:test, :test2)
374
- # # yVal = {:test2 => {:test5 => :test} }
375
- #
376
- # yVal.rh_del(:test, :test5)
377
- # # yVal = {:test2 => {} }
378
- #
379
- def rh_del(*p)
380
- p = p.flatten
381
-
382
- return nil if p.length == 0
383
-
384
- return delete(p[0]) if p.length == 1
385
-
386
- return nil if self[p[0]].nil?
387
- self[p[0]].rh_del(p.drop(1))
388
- end
389
-
390
- # Move levels (default level 1) of tree keys to become symbol.
391
- #
392
- # * *Args* :
393
- # - +levels+: level of key tree to update.
394
- # * *Returns* :
395
- # - a new hash of hashes updated. Original Hash is not updated anymore.
396
- #
397
- # examples:
398
- # With hdata = { :test => { :test2 => { :test5 => :test,
399
- # 'text' => 'blabla' },
400
- # 'test5' => 'test' }}
401
- #
402
- # hdata.rh_key_to_symbol(1) return no diff
403
- # hdata.rh_key_to_symbol(2) return "test5" is replaced by :test5
404
- # # hdata = { :test => { :test2 => { :test5 => :test,
405
- # # 'text' => 'blabla' },
406
- # # :test5 => 'test' }}
407
- # rh_key_to_symbol(3) return "test5" replaced by :test5, and "text" to :text
408
- # # hdata = { :test => { :test2 => { :test5 => :test,
409
- # # :text => 'blabla' },
410
- # # :test5 => 'test' }}
411
- # rh_key_to_symbol(4) same like rh_key_to_symbol(3)
412
-
413
- def rh_key_to_symbol(levels = 1)
414
- result = {}
415
- each do |key, value|
416
- new_key = key
417
- new_key = key.to_sym if key.is_a?(String)
418
- if value.is_a?(Hash) && levels > 1
419
- value = value.rh_key_to_symbol(levels - 1)
420
- end
421
- result[new_key] = value
422
- end
423
- result
424
- end
425
-
426
- # Check if levels of tree keys are all symbols.
427
- #
428
- # * *Args* :
429
- # - +levels+: level of key tree to update.
430
- # * *Returns* :
431
- # - true : one key path is not symbol.
432
- # - false : all key path are symbols.
433
- # * *Raises* :
434
- # Nothing
435
- #
436
- # examples:
437
- # With hdata = { :test => { :test2 => { :test5 => :test,
438
- # 'text' => 'blabla' },
439
- # 'test5' => 'test' }}
440
- #
441
- # hdata.rh_key_to_symbol?(1) return false
442
- # hdata.rh_key_to_symbol?(2) return true
443
- # hdata.rh_key_to_symbol?(3) return true
444
- # hdata.rh_key_to_symbol?(4) return true
445
- def rh_key_to_symbol?(levels = 1)
446
- each do |key, value|
447
- return true if key.is_a?(String)
448
-
449
- res = false
450
- if levels > 1 && value.is_a?(Hash)
451
- res = value.rh_key_to_symbol?(levels - 1)
452
- end
453
- return true if res
454
- end
455
- false
456
- end
457
-
458
- # return an exact clone of the recursive Array and Hash contents.
459
- #
460
- # * *Args* :
461
- #
462
- # * *Returns* :
463
- # - Recursive Array/Hash cloned. Other kind of objects are kept referenced.
464
- # * *Raises* :
465
- # Nothing
466
- #
467
- # examples:
468
- # hdata = { :test => { :test2 => { :test5 => :test,
469
- # 'text' => 'blabla' },
470
- # 'test5' => 'test' },
471
- # :array => [{ :test => :value1 }, 2, { :test => :value3 }]}
472
- #
473
- # hclone = hdata.rh_clone
474
- # hclone[:test] = "test"
475
- # hdata[:test] == { :test2 => { :test5 => :test,'text' => 'blabla' }
476
- # # => true
477
- # hclone[:array].pop
478
- # hdata[:array].length != hclone[:array].length
479
- # # => true
480
- # hclone[:array][0][:test] = "value2"
481
- # hdata[:array][0][:test] != hclone[:array][0][:test]
482
- # # => true
483
- def rh_clone
484
- result = {}
485
- each do |key, value|
486
- if [Array, Hash].include?(value.class)
487
- result[key] = value.rh_clone
488
- else
489
- result[key] = value
490
- end
491
- end
492
- result
493
- end
494
-
495
- # Merge the current Hash object (self) cloned with a Hash/Array tree contents
496
- # (data).
497
- #
498
- # 'self' is used as original data to merge to.
499
- # 'data' is used as data to merged to clone of 'self'. If you want to update
500
- # 'self', use rh_merge!
501
- #
502
- # if 'self' or 'data' contains a Hash tree, the merge will be executed
503
- # recursively.
504
- #
505
- # The current function will execute the merge of the 'self' keys with the top
506
- # keys in 'data'
507
- #
508
- # The merge can be controlled by an additionnal Hash key '__*' in each
509
- # 'self' key.
510
- # If both a <key> exist in 'self' and 'data', the following decision is made:
511
- # - if both 'self' and 'data' key contains an Hash or and Array, a recursive
512
- # merge if Hash or update if Array, is started.
513
- #
514
- # - if 'self' <key> contains an Hash or an Array, but not 'data' <key>, then
515
- # 'self' <key> will be set to the 'data' <key> except if 'self' <Key> has
516
- # :__struct_changing: true
517
- # data <key> value can set :unset value
518
- #
519
- # - if 'self' <key> is :unset and 'data' <key> is any value
520
- # 'self' <key> value is set with 'data' <key> value.
521
- # 'data' <key> value can contains a Hash with :__no_unset: true to
522
- # protect this key against the next merge. (next config layer merge)
523
- #
524
- # - if 'data' <key> exist but not in 'self', 'data' <key> is just added.
525
- #
526
- # - if 'data' & 'self' <key> exist, 'self'<key> is updated except if key is in
527
- # :__protected array list.
528
- #
529
- # * *Args* :
530
- # - hash : Hash data to merge.
531
- #
532
- # * *Returns* :
533
- # - Recursive Array/Hash merged.
534
- #
535
- # * *Raises* :
536
- # Nothing
537
- #
538
- # examples:
539
- #
540
- def rh_merge(data)
541
- _rh_merge(clone, data)
542
- end
543
-
544
- # Merge the current Hash object (self) with a Hash/Array tree contents (data).
545
- #
546
- # For details on this functions, see #rh_merge
547
- #
548
- def rh_merge!(data)
549
- _rh_merge(self, data)
550
- end
551
- end
552
-
553
- # Recursive Hash added to the Hash class
554
- class Hash
555
- private
556
-
557
- # Internal function which do the real merge task by #rh_merge and #rh_merge!
558
- #
559
- # See #rh_merge for details
560
- #
561
- def _rh_merge(result, data)
562
- return _rh_merge_choose_data(result, data) unless data.is_a?(Hash)
563
-
564
- data.each do |key, _value|
565
- next if [:__struct_changing, :__protected].include?(key)
566
-
567
- _do_rh_merge(result, key, data)
568
- end
569
- [:__struct_changing, :__protected].each do |key|
570
- # Refuse merge by default if key data type are different.
571
- # This assume that the first layer merge has set
572
- # :__unset as a Hash, and :__protected as an Array.
573
-
574
- _do_rh_merge(result, key, data, true) if data.key?(key)
575
-
576
- # Remove all control element in arrays
577
- _rh_remove_control(result[key]) if result.key?(key)
578
- end
579
-
580
- result
581
- end
582
-
583
- def _rh_merge_choose_data(result, data)
584
- # return result as first one impose the type between Hash/Array.
585
- return result if [Hash, Array].include?(result.class) ||
586
- [Hash, Array].include?(data.class)
587
-
588
- data
589
- end
590
- # Internal function to execute the merge on one key provided by #_rh_merge
591
- #
592
- # if refuse_discordance is true, then result[key] can't be updated if
593
- # stricly not of same type.
594
- def _do_rh_merge(result, key, data, refuse_discordance = false)
595
- value = data[key]
596
-
597
- return if _rh_merge_do_add_key(result, key, value)
598
-
599
- return if _rh_merge_recursive(result, key, data)
600
-
601
- return if refuse_discordance
602
-
603
- return unless _rh_struct_changing_ok?(result, key, data)
604
-
605
- return unless _rh_merge_ok?(result, key)
606
-
607
- _rh_merge_do_upd_key(result, key, value)
608
- end
609
-
610
- def _rh_merge_do_add_key(result, key, value)
611
- unless result.key?(key) || value == :unset
612
- result[key] = value # New key added
613
- return true
614
- end
615
- false
616
- end
617
-
618
- def _rh_merge_do_upd_key(result, key, value)
619
- if value == :unset
620
- result.delete(key) if result.key?(key)
621
- return
622
- end
623
-
624
- result[key] = value # Key updated
625
- end
626
-
627
- include Rh
628
- end
629
-
630
- # Defines rh_clone for Array
631
- class Array
632
- # return an exact clone of the recursive Array and Hash contents.
633
- #
634
- # * *Args* :
635
- #
636
- # * *Returns* :
637
- # - Recursive Array/Hash cloned.
638
- # * *Raises* :
639
- # Nothing
640
- #
641
- # examples:
642
- # hdata = { :test => { :test2 => { :test5 => :test,
643
- # 'text' => 'blabla' },
644
- # 'test5' => 'test' },
645
- # :array => [{ :test => :value1 }, 2, { :test => :value3 }]}
646
- #
647
- # hclone = hdata.rh_clone
648
- # hclone[:test] = "test"
649
- # hdata[:test] == { :test2 => { :test5 => :test,'text' => 'blabla' }
650
- # # => true
651
- # hclone[:array].pop
652
- # hdata[:array].length != hclone[:array].length
653
- # # => true
654
- # hclone[:array][0][:test] = "value2"
655
- # hdata[:array][0][:test] != hclone[:array][0][:test]
656
- # # => true
657
- def rh_clone
658
- result = []
659
- each do |value|
660
- begin
661
- result << value.rh_clone
662
- rescue
663
- result << value
664
- end
665
- end
666
- result
667
- end
668
-
669
- # This function is part of the rh_merge functionnality adapted for Array.
670
- #
671
- # To provide Array recursivity, we uses the element index.
672
- #
673
- # **Warning!** If the Array order has changed (sort/random) the index changed
674
- # and can generate unwanted result.
675
- #
676
- # To implement recursivity, and some specific Array management (add/remove)
677
- # you have to create an Hash and insert it at position 0 in the 'self' Array.
678
- #
679
- # **Warning!** If you create an Array, where index 0 contains a Hash, this
680
- # Hash will be considered as the Array control element.
681
- # If the first index of your Array is not a Hash, an empty Hash will be
682
- # inserted at position 0.
683
- #
684
- # 'data' has the same restriction then 'self' about the first element.
685
- # 'data' can influence the rh_merge Array behavior, by updating the first
686
- # element.
687
- #
688
- # The first Hash element has the following attributes:
689
- #
690
- # - :__struct_changing: Array of index which accepts to move from a structured
691
- # data (Hash/Array) to another structure or type.
692
- #
693
- # Ex: Hash => Array, Array => Integer
694
- #
695
- # - :__protected: Array of index which protects against update from 'data'
696
- #
697
- # - :__remove: Array of elements to remove. each element are remove with
698
- # Array.delete function. See Array delete function for details.
699
- #
700
- # - :__remove_index: Array of indexes to remove.
701
- # Each element are removed with Array.delete_at function.
702
- # It starts from the highest index until the lowest.
703
- # See Array delete function for details.
704
- #
705
- # **NOTE**: __remove and __remove_index cannot be used together.
706
- # if both are set, __remove is choosen
707
- #
708
- # **NOTE** : __remove* is executed before __add*
709
- #
710
- # - :__add: Array of elements to add. Those elements are systematically added
711
- # at the end of the Array. See Array.<< for details.
712
- #
713
- # - :__add_index: Hash of index(key) + Array of data(value) to add.
714
- # The index listed refer to merged 'self' Array. several elements with same
715
- # index are grouply inserted in the index.
716
- # ex:
717
- # [:data3].rh_merge({:__add_index => [0 => [:data1, :data2]]})
718
- # => [{}, :data1, :data2, :data3]
719
- #
720
- # **NOTE**: __add and __add_index cannot be used together.
721
- # if both are set, __add is choosen
722
- #
723
- # How merge is executed:
724
- #
725
- # Starting at index 0, each index of 'data' and 'self' are used to compare
726
- # indexed data.
727
- # - If 'data' index 0 has not an Hash, the 'self' index 0 is just skipped.
728
- # - If 'data' index 0 has the 'control' Hash, the array will be updated
729
- # according to :__add and :__remove arrays.
730
- # when done, those attributes are removed
731
- #
732
- # - For all next index (1 => 'data'.length), data are compared
733
- #
734
- # - If the 'data' length is > than 'self' length
735
- # addtionnal indexed data are added to 'self'
736
- #
737
- # - If index element exist in both 'data' and 'self',
738
- # 'self' indexed data is updated/merged according to control.
739
- # 'data' indexed data can use :unset to remove the data at this index
740
- # nil is also supported. But the index won't be removed. data will just
741
- # be set to nil
742
- #
743
- # when all Arrays elements are merged, rh_merge will:
744
- # - remove 'self' elements containing ':unset'
745
- #
746
- # - merge 'self' data at index 0 with 'data' found index 0
747
- #
748
- def rh_merge(data)
749
- _rh_merge(clone, data)
750
- end
751
-
752
- def rh_merge!(data)
753
- _rh_merge(self, data)
754
- end
755
-
756
- private
757
-
758
- def _rh_merge(result, data)
759
- data = data.clone
760
- data_control = _rh_merge_control(data)
761
- result_control = _rh_merge_control(result)
762
-
763
- _rh_do_control_merge(result_control, result, data_control, data)
764
-
765
- (1..(data.length - 1)).each do |index|
766
- _rh_do_array_merge(result, index, data)
767
- end
768
-
769
- (-(result.length - 1)..-1).each do |index|
770
- result.delete_at(index.abs) if result[index.abs] == :unset
771
- end
772
-
773
- _rh_do_array_merge(result, 0, [data_control])
774
- # Remove all control elements in tree of arrays
775
- _rh_remove_control(result[0])
776
-
777
- result
778
- end
779
-
780
- def _rh_do_array_merge(result, index, data)
781
- return if _rh_merge_recursive(result, index, data)
782
-
783
- return unless _rh_struct_changing_ok?(result, index, data)
784
-
785
- return unless _rh_merge_ok?(result, index)
786
-
787
- result[index] = data[index] unless data[index] == :kept
788
- end
789
-
790
- # Get the control element. or create it if missing.
791
- def _rh_merge_control(array)
792
- unless array[0].is_a?(Hash)
793
- array.insert(0, :__control => true)
794
- return array[0]
795
- end
796
-
797
- _rh_control_tags.each do |prop|
798
- if array[0].key?(prop)
799
- array[0][:__control] = true
800
- return array[0]
801
- end
802
- end
803
-
804
- array.insert(0, :__control => true)
805
-
806
- array[0]
807
- end
808
-
809
- # Do the merge according to :__add and :__remove
810
- def _rh_do_control_merge(_result_control, result, data_control, _data)
811
- if data_control[:__remove].is_a?(Array)
812
- _rh_do_control_remove(result, data_control[:__remove])
813
- elsif data_control[:__remove_index].is_a?(Array)
814
- index_to_remove = data_control[:__remove_index].uniq.sort.reverse
815
- _rh_do_control_remove_index(result, index_to_remove)
816
- end
817
-
818
- data_control.delete(:__remove)
819
- data_control.delete(:__remove_index)
820
-
821
- if data_control[:__add].is_a?(Array)
822
- data_control[:__add].each { |element| result << element }
823
- elsif data_control[:__add_index].is_a?(Hash)
824
- _rh_do_control_add_index(result, data_control[:__add_index].sort)
825
- end
826
-
827
- data_control.delete(:__add)
828
- data_control.delete(:__add_index)
829
- end
830
-
831
- def _rh_do_control_add_index(result, add_index)
832
- add_index.reverse_each do |elements_to_insert|
833
- next unless elements_to_insert.is_a?(Array) &&
834
- elements_to_insert[0].is_a?(Fixnum) &&
835
- elements_to_insert[1].is_a?(Array)
836
-
837
- index = elements_to_insert[0] + 1
838
- elements = elements_to_insert[1]
839
-
840
- elements.reverse_each { |element| result.insert(index, element) }
841
- end
842
- end
843
-
844
- # do the element removal.
845
- def _rh_do_control_remove(result, remove)
846
- remove.each { |element| result.delete(element) }
847
- end
848
-
849
- def _rh_do_control_remove_index(result, index_to_remove)
850
- index_to_remove.each { |index| result.delete_at(index + 1) }
851
- end
852
-
853
- include Rh
854
38
  end