compositing-array 1.0.13

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