array-compositing 1.0.0

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.
data/CHANGELOG.rdoc ADDED
@@ -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.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # Compositing Array #
2
+
3
+ http://rubygems.org/gems/array-compositing
4
+
5
+ # Description #
6
+
7
+ Provides Array::Compositing.
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 array-compositing
16
+
17
+ # Usage #
18
+
19
+ ```ruby
20
+ compositing_array = Array::Compositing.new
21
+ sub_compositing_array = Array::Compositing.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.
data/README.rdoc ADDED
File without changes
@@ -0,0 +1,2 @@
1
+
2
+ require_relative 'array/compositing.rb'
@@ -0,0 +1,15 @@
1
+
2
+ require 'array/hooked'
3
+ #require_relative '../../../../hooked_objects/array-hooked/lib/array-hooked.rb'
4
+
5
+ # namespaces that have to be declared ahead of time for proper load order
6
+ require_relative './namespaces'
7
+
8
+ # source file requires
9
+ require_relative './requires.rb'
10
+
11
+ class ::Array::Compositing < ::Array::Hooked
12
+
13
+ include ::Array::Compositing::ArrayInterface
14
+
15
+ end
@@ -0,0 +1,486 @@
1
+
2
+ module ::Array::Compositing::ArrayInterface
3
+
4
+ include ::Array::Hooked::ArrayInterface
5
+
6
+ instances_identify_as!( ::Array::Compositing )
7
+
8
+ ParentIndexStruct = ::Struct.new( :local_index, :replaced )
9
+
10
+ extend ::Module::Cluster
11
+
12
+ cluster( :compositing_array_interface ).before_include.cascade_to( :class ) do |hooked_instance|
13
+
14
+ hooked_instance.class_eval do
15
+
16
+ unless method_defined?( :non_cascading_set )
17
+ alias_method :non_cascading_set, :[]=
18
+ end
19
+
20
+ unless method_defined?( :non_cascading_insert )
21
+ alias_method :non_cascading_insert, :insert
22
+ end
23
+
24
+ unless method_defined?( :non_cascading_delete_at )
25
+ alias_method :non_cascading_delete_at, :delete_at
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
32
+ ################
33
+ # initialize #
34
+ ################
35
+
36
+ def initialize( parent_composite_array = nil, configuration_instance = nil, *args )
37
+
38
+ super( configuration_instance, *args )
39
+
40
+ # arrays that inherit from us
41
+ @sub_composite_arrays = [ ]
42
+
43
+ @parent_index_map = ::Array::Compositing::ParentIndexMap.new
44
+
45
+ initialize_for_parent( parent_composite_array )
46
+
47
+ end
48
+
49
+ ################################### Sub-Array Management #######################################
50
+
51
+ ###########################
52
+ # initialize_for_parent #
53
+ ###########################
54
+
55
+ def initialize_for_parent( parent_composite_array )
56
+
57
+ if @parent_composite_object = parent_composite_array
58
+
59
+ @parent_composite_object.register_sub_composite_array( self )
60
+
61
+ # record in our parent index map that parent has elements that have been inserted
62
+ parent_element_count = @parent_composite_object.count
63
+ @parent_index_map.parent_insert( 0, parent_element_count )
64
+
65
+ # initialize contents of self from parent contents
66
+ parent_element_count.times do |this_time|
67
+ # placeholders so we don't have to stub :count, etc.
68
+ undecorated_insert( 0, nil )
69
+ end
70
+
71
+ end
72
+
73
+ end
74
+
75
+ #############################
76
+ # parent_composite_object #
77
+ # parent_composite_array #
78
+ #############################
79
+
80
+ attr_accessor :parent_composite_object
81
+
82
+ alias_method :parent_composite_array, :parent_composite_object
83
+
84
+ ##################################
85
+ # register_sub_composite_array #
86
+ ##################################
87
+
88
+ def register_sub_composite_array( sub_composite_array )
89
+
90
+ @sub_composite_arrays.push( sub_composite_array )
91
+
92
+ return self
93
+
94
+ end
95
+
96
+ ####################################
97
+ # unregister_sub_composite_array #
98
+ ####################################
99
+
100
+ def unregister_sub_composite_array( sub_composite_array )
101
+
102
+ @sub_composite_arrays.delete( sub_composite_array )
103
+
104
+ return self
105
+
106
+ end
107
+
108
+ ###################################### Subclass Hooks ##########################################
109
+
110
+ ########################
111
+ # child_pre_set_hook #
112
+ ########################
113
+
114
+ def child_pre_set_hook( index, object, is_insert = false )
115
+
116
+ return object
117
+
118
+ end
119
+
120
+ #########################
121
+ # child_post_set_hook #
122
+ #########################
123
+
124
+ def child_post_set_hook( index, object, is_insert = false )
125
+
126
+ return object
127
+
128
+ end
129
+
130
+ ###########################
131
+ # child_pre_delete_hook #
132
+ ###########################
133
+
134
+ def child_pre_delete_hook( index )
135
+
136
+ # false means delete does not take place
137
+ return true
138
+
139
+ end
140
+
141
+ ############################
142
+ # child_post_delete_hook #
143
+ ############################
144
+
145
+ def child_post_delete_hook( index, object )
146
+
147
+ return object
148
+
149
+ end
150
+
151
+ ##################################### Self Management ##########################################
152
+
153
+ ########
154
+ # == #
155
+ ########
156
+
157
+ def ==( object )
158
+
159
+ load_parent_state
160
+
161
+ super
162
+
163
+ end
164
+
165
+ ##########
166
+ # to_s #
167
+ ##########
168
+
169
+ def to_s
170
+
171
+ #load_parent_state
172
+
173
+ super
174
+
175
+ end
176
+
177
+ #############
178
+ # inspect #
179
+ #############
180
+
181
+ def inspect
182
+
183
+ load_parent_state
184
+
185
+ super
186
+
187
+ end
188
+
189
+ ########
190
+ # [] #
191
+ ########
192
+
193
+ def []( local_index )
194
+
195
+ return_value = nil
196
+
197
+ if @parent_index_map.requires_lookup?( local_index )
198
+ return_value = lazy_set_parent_element_in_self( local_index )
199
+ else
200
+ return_value = super
201
+ end
202
+
203
+ return return_value
204
+
205
+ end
206
+
207
+ #########
208
+ # []= #
209
+ #########
210
+
211
+ def []=( local_index, object )
212
+
213
+ super
214
+
215
+ @sub_composite_arrays.each do |this_sub_array|
216
+ this_sub_array.instance_eval do
217
+ update_for_parent_set( local_index, object )
218
+ end
219
+ end
220
+
221
+ return object
222
+
223
+ end
224
+
225
+ alias_method :store, :[]=
226
+
227
+ ###############
228
+ # delete_at #
229
+ ###############
230
+
231
+ def delete_at( local_index )
232
+
233
+ @parent_index_map.local_delete_at( local_index )
234
+
235
+ deleted_object = non_cascading_delete_at( local_index )
236
+
237
+ @sub_composite_arrays.each do |this_sub_array|
238
+ this_sub_array.instance_eval do
239
+ update_for_parent_delete_at( local_index, deleted_object )
240
+ end
241
+ end
242
+
243
+ return deleted_object
244
+
245
+ end
246
+
247
+ #############
248
+ # freeze! #
249
+ #############
250
+
251
+ # freezes configuration and prevents ancestors from changing this configuration in the future
252
+ def freeze!
253
+
254
+ # unregister with parent composite so we don't get future updates from it
255
+ if @parent_composite_object
256
+ @parent_composite_object.unregister_sub_composite_array( self )
257
+ end
258
+
259
+ return self
260
+
261
+ end
262
+
263
+ ######################################################################################################################
264
+ private ##########################################################################################################
265
+ ######################################################################################################################
266
+
267
+ ###############################
268
+ # perform_set_between_hooks #
269
+ ###############################
270
+
271
+ def perform_set_between_hooks( local_index, object )
272
+
273
+ did_set = false
274
+
275
+ if did_set = super
276
+ @parent_index_map.local_set( local_index )
277
+ end
278
+
279
+ return did_set
280
+
281
+ end
282
+
283
+ ################################################
284
+ # perform_single_object_insert_between_hooks #
285
+ ################################################
286
+
287
+ def perform_single_object_insert_between_hooks( local_index, object )
288
+
289
+ if local_index = super
290
+
291
+ @parent_index_map.local_insert( local_index, 1 )
292
+
293
+ @sub_composite_arrays.each do |this_sub_array|
294
+ this_sub_array.instance_eval do
295
+ update_for_parent_insert( local_index, object )
296
+ end
297
+ end
298
+
299
+ end
300
+
301
+ return local_index
302
+
303
+ end
304
+
305
+ #####################################
306
+ # lazy_set_parent_element_in_self #
307
+ #####################################
308
+
309
+ def lazy_set_parent_element_in_self( local_index, *optional_object )
310
+
311
+ object = nil
312
+
313
+ case optional_object.count
314
+ when 0
315
+ parent_index = @parent_index_map.parent_index( local_index )
316
+ object = @parent_composite_object[ parent_index ]
317
+ when 1
318
+ object = optional_object[ 0 ]
319
+ end
320
+
321
+ # We call hooks manually so that we can do a direct undecorated set.
322
+ # This is because we already have an object we loaded as a place-holder that we are now updating.
323
+ # So we don't want to sort/test uniqueness/etc. We just want to insert at the actual index.
324
+
325
+ unless @without_child_hooks
326
+ object = child_pre_set_hook( local_index, object, false )
327
+ end
328
+
329
+ unless @without_hooks
330
+ object = pre_set_hook( local_index, object, false )
331
+ end
332
+
333
+ undecorated_set( local_index, object )
334
+
335
+ @parent_index_map.looked_up!( local_index )
336
+
337
+ unless @without_hooks
338
+ post_set_hook( local_index, object, false )
339
+ end
340
+
341
+ unless @without_child_hooks
342
+ child_post_set_hook( local_index, object, false )
343
+ end
344
+
345
+ return object
346
+
347
+ end
348
+
349
+ ###########################
350
+ # update_for_parent_set #
351
+ ###########################
352
+
353
+ def update_for_parent_set( parent_index, object )
354
+
355
+ unless @parent_index_map.replaced_parent_element_with_parent_index?( parent_index )
356
+
357
+ local_index = @parent_index_map.parent_set( parent_index )
358
+
359
+ if @parent_index_map.requires_lookup?( local_index )
360
+
361
+ @sub_composite_arrays.each do |this_array|
362
+ this_array.instance_eval do
363
+ update_for_parent_set( local_index, object )
364
+ end
365
+ end
366
+
367
+ end
368
+
369
+ end
370
+
371
+ end
372
+
373
+ ##############################
374
+ # update_for_parent_insert #
375
+ ##############################
376
+
377
+ def update_for_parent_insert( parent_insert_index, object )
378
+
379
+ unless @parent_index_map.replaced_parent_element_with_parent_index?( parent_insert_index )
380
+
381
+ local_index = @parent_index_map.parent_insert( parent_insert_index, 1 )
382
+
383
+ undecorated_insert( local_index, nil )
384
+
385
+ @sub_composite_arrays.each do |this_array|
386
+ this_array.instance_eval do
387
+ update_for_parent_insert( local_index, object )
388
+ end
389
+ end
390
+
391
+ end
392
+
393
+ end
394
+
395
+ #################################
396
+ # update_for_parent_delete_at #
397
+ #################################
398
+
399
+ def update_for_parent_delete_at( parent_index, object )
400
+
401
+ did_delete = false
402
+
403
+ unless @parent_index_map.replaced_parent_element_with_parent_index?( parent_index )
404
+
405
+ local_index = @parent_index_map.local_index( parent_index )
406
+
407
+ if @without_child_hooks
408
+ child_pre_delete_hook_result = true
409
+ else
410
+ child_pre_delete_hook_result = child_pre_delete_hook( local_index )
411
+ end
412
+
413
+ if child_pre_delete_hook_result
414
+
415
+ @parent_index_map.parent_delete_at( parent_index )
416
+
417
+ # I'm unclear why if we call perform_delete_between_hooks (including through non_cascading_delete_at)
418
+ # we end up smashing the last index's lazy lookup value, turning it false
419
+ # for now simply adding hooks manually here works; the only loss is a little duplicate code
420
+ # to call the local (non-child) hooks
421
+ if @without_hooks
422
+ pre_delete_hook_result = true
423
+ else
424
+ pre_delete_hook_result = pre_delete_hook( local_index )
425
+ end
426
+
427
+ if pre_delete_hook_result
428
+
429
+ object = undecorated_delete_at( local_index )
430
+
431
+ did_delete = true
432
+
433
+ unless @without_hooks
434
+ object = post_delete_hook( local_index, object )
435
+ end
436
+
437
+ unless @without_child_hooks
438
+ child_post_delete_hook( local_index, object )
439
+ end
440
+
441
+ @sub_composite_arrays.each do |this_array|
442
+ this_array.instance_eval do
443
+ update_for_parent_delete_at( local_index, object )
444
+ end
445
+ end
446
+
447
+ end
448
+
449
+ else
450
+
451
+ if @parent_index_map.requires_lookup?( local_index )
452
+ lazy_set_parent_element_in_self( local_index, object )
453
+ end
454
+
455
+ end
456
+
457
+ end
458
+
459
+ return did_delete
460
+
461
+ end
462
+
463
+ ######################
464
+ # parent_reversed! #
465
+ ######################
466
+
467
+ def parent_reversed!
468
+
469
+ @sort_order_reversed = ! @sort_order_reversed
470
+
471
+ end
472
+
473
+ #######################
474
+ # load_parent_state #
475
+ #######################
476
+
477
+ def load_parent_state
478
+
479
+ # if is used for case where duplicate is created (like :uniq) and initialization not called during dupe process
480
+ @parent_index_map.indexes_requiring_lookup.each do |this_local_index|
481
+ lazy_set_parent_element_in_self( this_local_index )
482
+ end
483
+
484
+ end
485
+
486
+ end