compositing-array 1.0.13

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,43 @@
1
+
2
+ == 3/17/12
3
+
4
+ Initial release.
5
+
6
+ == 3/18/12
7
+
8
+ Added hooks for subclassing.
9
+
10
+ == 3/19/12
11
+
12
+ Moved parent initialization to separate method (with call to initialize so existing behavior remains).
13
+ Now parent can be initialized after object initialization.
14
+
15
+ == 3/24/12
16
+
17
+ Added _without_hook methods to perform actions without calling hooks.
18
+
19
+ == 3/26/12
20
+
21
+ Fixed typo that broke set without hooks.
22
+
23
+ == 5/27/12
24
+
25
+ Added common CompositingObject support.
26
+
27
+ == 5/31/12
28
+
29
+ Added :parent_composite_object and changed :parent_composite_array to alias :parent_composite_object.
30
+
31
+ == 6/1/12
32
+
33
+ Added :configuration_instance parameter to :initialize.
34
+
35
+ == 6/15/12
36
+
37
+ Moved hooks out to hooked-array and utilized hooked-array as foundation.
38
+ Fixed dependency in gemspec.
39
+
40
+ == 6/18/12
41
+
42
+ Fixed index miscount.
43
+ Fixed index miscount fix for proper count.
@@ -0,0 +1,65 @@
1
+ # Compositing Array #
2
+
3
+ http://rubygems.org/gems/compositing-array
4
+
5
+ # Description #
6
+
7
+ Provides CompositingArray.
8
+
9
+ # Summary #
10
+
11
+ An implementation of Array that permits chaining, where children inherit changes to parent and where parent settings can be overridden in children.
12
+
13
+ # Install #
14
+
15
+ * sudo gem install compositing-array
16
+
17
+ # Usage #
18
+
19
+ ```ruby
20
+ compositing_array = CompositingArray.new
21
+ sub_compositing_array = CompositingArray.new( compositing_array )
22
+
23
+ compositing_array.push( :some_value )
24
+ # compositing_array
25
+ # => [ :A ]
26
+ # sub_compositing_array
27
+ # => [ :A ]
28
+
29
+ compositing_array.delete_at( 0 )
30
+ # compositing_array
31
+ # => [ ]
32
+ # sub_compositing_array
33
+ # => [ ]
34
+
35
+ sub_compositing_array.push( :some_value )
36
+ # compositing_array
37
+ # => [ ]
38
+ # sub_compositing_array
39
+ # => [ :A ]
40
+ ```
41
+
42
+ # License #
43
+
44
+ (The MIT License)
45
+
46
+ Copyright (c) Asher
47
+
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of this software and associated documentation files (the
50
+ 'Software'), to deal in the Software without restriction, including
51
+ without limitation the rights to use, copy, modify, merge, publish,
52
+ distribute, sublicense, and/or sell copies of the Software, and to
53
+ permit persons to whom the Software is furnished to do so, subject to
54
+ the following conditions:
55
+
56
+ The above copyright notice and this permission notice shall be
57
+ included in all copies or substantial portions of the Software.
58
+
59
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
60
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
61
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
62
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
63
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
64
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
65
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
File without changes
@@ -0,0 +1,9 @@
1
+
2
+ require 'hooked-array'
3
+
4
+ class ::CompositingArray < ::HookedArray
5
+ end
6
+
7
+ require_relative 'compositing-array/CompositingArray/Interface.rb'
8
+ require_relative 'compositing-array/CompositingArray.rb'
9
+
@@ -0,0 +1,6 @@
1
+
2
+ class ::CompositingArray < ::HookedArray
3
+
4
+ include ::CompositingArray::Interface
5
+
6
+ end
@@ -0,0 +1,478 @@
1
+
2
+ module ::CompositingArray::Interface
3
+
4
+ instances_identify_as!( ::CompositingArray )
5
+
6
+ ################
7
+ # initialize #
8
+ ################
9
+
10
+ def initialize( parent_composite_array = nil, configuration_instance = nil, *args )
11
+
12
+ super( configuration_instance, *args )
13
+
14
+ # arrays that inherit from us
15
+ @sub_composite_arrays = [ ]
16
+
17
+ # hash tracking index in self corresponding to index in parent
18
+ # this is since objects can be inserted before/between parent objects
19
+ @replaced_parents = { }
20
+
21
+ # initialize corresponding indexes in self to indexes in parent
22
+ @local_index_for_parent_index = { }
23
+
24
+ # we keep track of how many objects are interpolated between parent objects
25
+ # plus number of parent objects
26
+ @parent_and_interpolated_object_count = 0
27
+
28
+ initialize_for_parent( parent_composite_array )
29
+
30
+ end
31
+
32
+ ################################### Sub-Array Management #######################################
33
+
34
+ ###########################
35
+ # initialize_for_parent #
36
+ ###########################
37
+
38
+ def initialize_for_parent( parent_composite_array )
39
+
40
+ if @parent_composite_object = parent_composite_array
41
+
42
+ # initialize contents of self from parent contents
43
+ unless @parent_composite_object.empty?
44
+ update_as_sub_array_for_parent_insert( 0, *@parent_composite_object )
45
+ end
46
+
47
+ @parent_composite_object.register_sub_composite_array( self )
48
+
49
+ end
50
+
51
+ end
52
+
53
+ #############################
54
+ # parent_composite_object #
55
+ # parent_composite_array #
56
+ #############################
57
+
58
+ attr_accessor :parent_composite_object
59
+
60
+ alias_method :parent_composite_array, :parent_composite_object
61
+
62
+ ##################################
63
+ # register_sub_composite_array #
64
+ ##################################
65
+
66
+ def register_sub_composite_array( sub_composite_array )
67
+
68
+ @sub_composite_arrays.push( sub_composite_array )
69
+
70
+ return self
71
+
72
+ end
73
+
74
+ ####################################
75
+ # unregister_sub_composite_array #
76
+ ####################################
77
+
78
+ def unregister_sub_composite_array( sub_composite_array )
79
+
80
+ @sub_composite_arrays.delete( sub_composite_array )
81
+
82
+ return self
83
+
84
+ end
85
+
86
+ ###################################### Subclass Hooks ##########################################
87
+
88
+ ########################
89
+ # child_pre_set_hook #
90
+ ########################
91
+
92
+ def child_pre_set_hook( index, object, is_insert = false )
93
+
94
+ return object
95
+
96
+ end
97
+
98
+ #########################
99
+ # child_post_set_hook #
100
+ #########################
101
+
102
+ def child_post_set_hook( index, object, is_insert = false )
103
+
104
+ return object
105
+
106
+ end
107
+
108
+ ###########################
109
+ # child_pre_delete_hook #
110
+ ###########################
111
+
112
+ def child_pre_delete_hook( index )
113
+
114
+ # false means delete does not take place
115
+ return true
116
+
117
+ end
118
+
119
+ ############################
120
+ # child_post_delete_hook #
121
+ ############################
122
+
123
+ def child_post_delete_hook( index, object )
124
+
125
+ return object
126
+
127
+ end
128
+
129
+ ##################################### Self Management ##########################################
130
+
131
+ #########
132
+ # []= #
133
+ #########
134
+
135
+ def []=( index, object )
136
+
137
+ super
138
+
139
+ if index_inside_parent_objects?( index )
140
+ @replaced_parents[ index ] = true
141
+ end
142
+
143
+ @sub_composite_arrays.each do |this_sub_array|
144
+ this_sub_array.instance_eval do
145
+ update_as_sub_array_for_parent_set( index, object )
146
+ end
147
+ end
148
+
149
+ return object
150
+
151
+ end
152
+
153
+ ############
154
+ # insert #
155
+ ############
156
+
157
+ def insert( index, *objects )
158
+
159
+ unless objects.empty?
160
+
161
+ if inserted_objects = super
162
+
163
+ # super may insert more (nil generally) or remove some (ie for duplicates)
164
+ count_changed = objects.count - inserted_objects.count
165
+
166
+ if count_changed < 0
167
+ index += count_changed
168
+ end
169
+
170
+ objects = inserted_objects
171
+
172
+ if index_inside_parent_objects?( index )
173
+ update_corresponding_index_for_local_change( index, objects.count )
174
+ end
175
+
176
+ @sub_composite_arrays.each do |this_sub_array|
177
+ this_sub_array.instance_eval do
178
+ update_as_sub_array_for_parent_insert( index, *objects )
179
+ end
180
+ end
181
+
182
+ end
183
+
184
+ end
185
+
186
+ return objects
187
+
188
+ end
189
+
190
+ ###############
191
+ # delete_at #
192
+ ###############
193
+
194
+ def delete_at( index )
195
+
196
+ deleted_object = super( index )
197
+
198
+ @replaced_parents.delete( index )
199
+
200
+ if index_inside_parent_objects?( index )
201
+ update_corresponding_index_for_local_change( index, -1 )
202
+ end
203
+
204
+ @sub_composite_arrays.each do |this_sub_array|
205
+ this_sub_array.instance_eval do
206
+ update_as_sub_array_for_parent_delete( index )
207
+ end
208
+ end
209
+
210
+ return deleted_object
211
+
212
+ end
213
+
214
+ #############
215
+ # freeze! #
216
+ #############
217
+
218
+ # freezes configuration and prevents ancestors from changing this configuration in the future
219
+ def freeze!
220
+
221
+ # unregister with parent composite so we don't get future updates from it
222
+ if @parent_composite_object
223
+ @parent_composite_object.unregister_sub_composite_array( self )
224
+ end
225
+
226
+ return self
227
+
228
+ end
229
+
230
+ ##################################################################################################
231
+ private ######################################################################################
232
+ ##################################################################################################
233
+
234
+ ################ Self Management for Inserts between Parent-Provided Elements ##################
235
+
236
+ ##################################
237
+ # index_inside_parent_objects? #
238
+ ##################################
239
+
240
+ def index_inside_parent_objects?( index )
241
+
242
+ index_inside_parent_objects = false
243
+
244
+ if index < @parent_and_interpolated_object_count
245
+
246
+ index_inside_parent_objects = true
247
+
248
+ end
249
+
250
+ return index_inside_parent_objects
251
+
252
+ end
253
+
254
+ #################################################
255
+ # update_corresponding_index_for_local_change #
256
+ #################################################
257
+
258
+ def update_corresponding_index_for_local_change( index, step_value )
259
+
260
+ # update corresponding indexes for changes in self
261
+
262
+ indexes_to_delete = [ ]
263
+
264
+ @local_index_for_parent_index.each do |this_parent_index, this_local_index|
265
+ if this_parent_index >= index
266
+ existing_corresponding_value = @local_index_for_parent_index[ this_parent_index ]
267
+ new_corresponding_value = existing_corresponding_value + step_value
268
+ if new_corresponding_value >= 0
269
+ @local_index_for_parent_index[ this_parent_index ] = new_corresponding_value
270
+ else
271
+ indexes_to_delete.push( this_parent_index )
272
+ end
273
+ end
274
+ end
275
+
276
+ step_parent_and_interpolated_object_count( step_value )
277
+
278
+ end
279
+
280
+ ######################### Self-as-Sub Management for Parent Updates ############################
281
+
282
+ ########################################
283
+ # update_as_sub_array_for_parent_set #
284
+ ########################################
285
+
286
+ def update_as_sub_array_for_parent_set( index, object )
287
+
288
+ # if our index is bigger than current parent set we are inserting
289
+ if index >= @parent_and_interpolated_object_count
290
+
291
+ update_as_sub_array_for_parent_insert( index, object )
292
+
293
+ # otherwise we are replacing and have a corresponding element defined already
294
+ else
295
+
296
+ unless @replaced_parents[ index ]
297
+
298
+ corresponding_index = @local_index_for_parent_index[ index ]
299
+
300
+ update_parent_element_in_self( corresponding_index, object )
301
+
302
+ @sub_composite_arrays.each do |this_array|
303
+ this_array.instance_eval do
304
+ update_as_sub_array_for_parent_set( corresponding_index, object )
305
+ end
306
+ end
307
+
308
+ end
309
+
310
+ end
311
+
312
+ end
313
+
314
+ ###################################
315
+ # update_parent_element_in_self #
316
+ ###################################
317
+
318
+ def update_parent_element_in_self( corresponding_index, object )
319
+
320
+ unless @without_hooks
321
+
322
+ object = pre_set_hook( corresponding_index, object, false )
323
+
324
+ object = child_pre_set_hook( corresponding_index, object, false )
325
+
326
+ end
327
+
328
+ perform_set_between_hooks( corresponding_index, object )
329
+
330
+ unless @without_hooks
331
+ child_post_set_hook( corresponding_index, object, false )
332
+ end
333
+
334
+ end
335
+
336
+ ###########################################
337
+ # update_as_sub_array_for_parent_insert #
338
+ ###########################################
339
+
340
+ def update_as_sub_array_for_parent_insert( index, *objects )
341
+
342
+ # new parent indexes have been inserted at index in parent
343
+
344
+ # we need the corresponding index in self where parallel insert will occur
345
+ if corresponding_index = @local_index_for_parent_index[ index ]
346
+
347
+ if corresponding_index < 0
348
+ corresponding_index = 0
349
+ else
350
+ update_corresponding_index_for_parent_change( index, objects.count )
351
+ end
352
+
353
+ else
354
+
355
+ corresponding_index = @parent_and_interpolated_object_count
356
+ @parent_and_interpolated_object_count += objects.count
357
+
358
+ end
359
+
360
+ # then we're going to increment existing correspondences
361
+ # now since we added a space for the new elements we can add their new correspondences
362
+ objects.count.times do |this_time|
363
+ new_parent_index = index + this_time
364
+ new_corresponding_index = corresponding_index + this_time
365
+ @local_index_for_parent_index[ new_parent_index ] = new_corresponding_index
366
+ end
367
+
368
+ insert_parent_elements_in_self( corresponding_index, *objects )
369
+
370
+ @sub_composite_arrays.each do |this_array|
371
+ this_array.instance_eval do
372
+ update_as_sub_array_for_parent_insert( corresponding_index, *objects )
373
+ end
374
+ end
375
+
376
+ end
377
+
378
+ ####################################
379
+ # insert_parent_elements_in_self #
380
+ ####################################
381
+
382
+ def insert_parent_elements_in_self( corresponding_index, *objects )
383
+
384
+ objects_to_insert = [ ]
385
+ objects.each_with_index do |this_object, this_index|
386
+ unless @without_hooks
387
+ this_object = pre_set_hook( corresponding_index + this_index, this_object, true )
388
+ this_object = child_pre_set_hook( corresponding_index + this_index, this_object, true )
389
+ end
390
+ # only keep objects the pre-set hook says to keep
391
+ objects_to_insert.push( this_object )
392
+ end
393
+ objects = objects_to_insert
394
+
395
+ perform_insert_between_hooks( corresponding_index, *objects )
396
+
397
+ unless @without_hooks
398
+ objects.each_with_index do |this_object, this_index|
399
+ post_set_hook( corresponding_index + this_index, this_object, true )
400
+ child_post_set_hook( corresponding_index + this_index, this_object, true )
401
+ end
402
+ end
403
+
404
+ end
405
+
406
+ ###########################################
407
+ # update_as_sub_array_for_parent_delete #
408
+ ###########################################
409
+
410
+ def update_as_sub_array_for_parent_delete( index )
411
+
412
+ corresponding_index = @local_index_for_parent_index[ index ]
413
+
414
+ if @without_hooks
415
+ child_pre_delete_hook_result = true
416
+ else
417
+ child_pre_delete_hook_result = child_pre_delete_hook( index )
418
+ end
419
+
420
+ if child_pre_delete_hook_result
421
+ object = perform_delete_between_hooks( corresponding_index )
422
+ end
423
+
424
+ @parent_and_interpolated_object_count -= 1
425
+
426
+ unless @without_hooks
427
+ child_post_delete_hook( index, object )
428
+ end
429
+
430
+ @sub_composite_arrays.each do |this_array|
431
+ this_array.instance_eval do
432
+ update_as_sub_array_for_parent_delete( corresponding_index )
433
+ end
434
+ end
435
+
436
+ end
437
+
438
+ ##################################################
439
+ # update_corresponding_index_for_parent_change #
440
+ ##################################################
441
+
442
+ def update_corresponding_index_for_parent_change( parent_index, step_value )
443
+
444
+ # update corresponding indexes for changes in parent
445
+
446
+ stepped_indices = { }
447
+
448
+ # iterate the hash with all indices included and increment/decrement any >= parent_index
449
+ @local_index_for_parent_index.each do |this_parent_index, this_local_index|
450
+ if this_parent_index >= parent_index
451
+ new_index = this_parent_index + step_value
452
+ new_local_index = @local_index_for_parent_index.delete( this_parent_index ) + step_value
453
+ stepped_indices[ new_index ] = new_local_index
454
+ end
455
+ end
456
+
457
+ # merge stepped indices back in
458
+ @local_index_for_parent_index.merge!( stepped_indices )
459
+
460
+ step_parent_and_interpolated_object_count( step_value )
461
+
462
+ end
463
+
464
+ ###############################################
465
+ # step_parent_and_interpolated_object_count #
466
+ ###############################################
467
+
468
+ def step_parent_and_interpolated_object_count( step_value )
469
+
470
+ @parent_and_interpolated_object_count += step_value
471
+
472
+ if @parent_and_interpolated_object_count < 0
473
+ @parent_and_interpolated_object_count = 0
474
+ end
475
+
476
+ end
477
+
478
+ end