compositing-hash 1.0.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
1
+ # CompositingHash #
2
+
3
+ http://rubygems.org/gems/compositing-hash
4
+
5
+ # Description #
6
+
7
+ Provides CompositingHash.
8
+
9
+ # Summary #
10
+
11
+ An implementation of Hash 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-hash
16
+
17
+ # Usage #
18
+
19
+ ```ruby
20
+ compositing_hash = CompositingHash.new
21
+ sub_compositing_hash = CompositingHash.new( compositing_hash )
22
+
23
+ compositing_hash[ :some_key ] = :some_value
24
+ # compositing_hash
25
+ # => { :some_key => :some_value }
26
+ # sub_compositing_hash
27
+ # => { :some_key => :some_value }
28
+
29
+ compositing_hash.delete( :some_key )
30
+ # compositing_hash
31
+ # => { }
32
+ # sub_compositing_hash
33
+ # => { }
34
+
35
+ sub_compositing_hash[ :some_key ] = :some_value
36
+ # compositing_hash
37
+ # => { }
38
+ # sub_compositing_hash
39
+ # => { :some_key => :some_value }
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,5 @@
1
+
2
+ require 'hooked-hash'
3
+
4
+ require_relative 'compositing-hash/CompositingHash.rb'
5
+
@@ -0,0 +1,374 @@
1
+
2
+ class ::CompositingHash < ::HookedHash
3
+
4
+ ################
5
+ # initialize #
6
+ ################
7
+
8
+ def initialize( parent_composite_hash = nil, configuration_instance = nil )
9
+
10
+ super( configuration_instance )
11
+
12
+ @replaced_parents = { }
13
+ @parent_key_lookup = { }
14
+
15
+ # we may later have our own child composites that register with us
16
+ @sub_composite_hashes = [ ]
17
+
18
+ initialize_for_parent( parent_composite_hash )
19
+
20
+ end
21
+
22
+ #############################
23
+ # parent_composite_object #
24
+ # parent_composite_hash #
25
+ #############################
26
+
27
+ attr_accessor :parent_composite_object
28
+
29
+ alias_method :parent_composite_hash, :parent_composite_object
30
+
31
+ ################################### Sub-Hash Management #######################################
32
+
33
+ ###########################
34
+ # initialize_for_parent #
35
+ ###########################
36
+
37
+ def initialize_for_parent( parent_composite_hash )
38
+
39
+ if @parent_composite_object = parent_composite_hash
40
+
41
+ @parent_composite_object.register_sub_composite_hash( self )
42
+
43
+ # @parent_key_lookup tracks keys that we have not yet received from parent
44
+ @parent_composite_object.each do |this_key, this_object|
45
+ @parent_key_lookup[ this_key ] = true
46
+ non_cascading_store( this_key, nil )
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ #################################
54
+ # register_sub_composite_hash #
55
+ #################################
56
+
57
+ def register_sub_composite_hash( sub_composite_hash )
58
+
59
+ @sub_composite_hashes.push( sub_composite_hash )
60
+
61
+ return self
62
+
63
+ end
64
+
65
+ ###################################
66
+ # unregister_sub_composite_hash #
67
+ ###################################
68
+
69
+ def unregister_sub_composite_hash( sub_composite_hash )
70
+
71
+ @sub_composite_hashes.delete( sub_composite_hash )
72
+
73
+ return self
74
+
75
+ end
76
+
77
+ ###################################### Subclass Hooks ##########################################
78
+
79
+ ########################
80
+ # child_pre_set_hook #
81
+ ########################
82
+
83
+ def child_pre_set_hook( key, object )
84
+
85
+ return object
86
+
87
+ end
88
+
89
+ #########################
90
+ # child_post_set_hook #
91
+ #########################
92
+
93
+ def child_post_set_hook( key, object )
94
+
95
+ return object
96
+
97
+ end
98
+
99
+ ###########################
100
+ # child_pre_delete_hook #
101
+ ###########################
102
+
103
+ def child_pre_delete_hook( key )
104
+
105
+ # false means delete does not take place
106
+ return true
107
+
108
+ end
109
+
110
+ ############################
111
+ # child_post_delete_hook #
112
+ ############################
113
+
114
+ def child_post_delete_hook( key, object )
115
+
116
+ return object
117
+
118
+ end
119
+
120
+ ##################################### Self Management ##########################################
121
+
122
+ ########
123
+ # == #
124
+ ########
125
+
126
+ def ==( object )
127
+
128
+ @parent_key_lookup.each do |this_key, true_value|
129
+ self[ this_key ]
130
+ end
131
+
132
+ super
133
+
134
+ end
135
+
136
+ ##########
137
+ # each #
138
+ ##########
139
+
140
+ def each( *args, & block )
141
+
142
+ @parent_key_lookup.each do |this_key, true_value|
143
+ self[ this_key ]
144
+ end
145
+
146
+ super
147
+
148
+ end
149
+
150
+ ##########
151
+ # to_s #
152
+ ##########
153
+
154
+ def to_s
155
+
156
+ @parent_key_lookup.each do |this_key, true_value|
157
+ self[ this_key ]
158
+ end
159
+
160
+ super
161
+
162
+ end
163
+
164
+ #############
165
+ # inspect #
166
+ #############
167
+
168
+ def inspect
169
+
170
+ @parent_key_lookup.each do |this_key, true_value|
171
+ self[ this_key ]
172
+ end
173
+
174
+ super
175
+
176
+ end
177
+
178
+ ########
179
+ # [] #
180
+ ########
181
+
182
+ def []( key )
183
+
184
+ return_value = nil
185
+
186
+ if @parent_key_lookup.has_key?( key )
187
+ return_value = set_parent_element_in_self( key, @parent_composite_object[ key ] )
188
+ @parent_key_lookup.delete( key )
189
+ else
190
+ return_value = super
191
+ end
192
+
193
+ return return_value
194
+
195
+ end
196
+
197
+ #########
198
+ # []= #
199
+ #########
200
+
201
+ private
202
+ alias_method :non_cascading_store, :store
203
+ public
204
+
205
+ def []=( key, object )
206
+
207
+ @replaced_parents[ key ] = true
208
+
209
+ @parent_key_lookup.delete( key )
210
+
211
+ super
212
+
213
+ @sub_composite_hashes.each do |this_sub_hash|
214
+ this_sub_hash.instance_eval do
215
+ update_as_sub_hash_for_parent_store( key )
216
+ end
217
+ end
218
+
219
+ return object
220
+
221
+ end
222
+ alias_method :store, :[]=
223
+
224
+ ############
225
+ # delete #
226
+ ############
227
+
228
+ private
229
+ alias_method :non_cascading_delete, :delete
230
+ public
231
+
232
+ def delete( key )
233
+
234
+ @replaced_parents.delete( key )
235
+
236
+ @parent_key_lookup.delete( key )
237
+
238
+ object = super
239
+
240
+ @sub_composite_hashes.each do |this_sub_hash|
241
+ this_sub_hash.instance_eval do
242
+ update_as_sub_hash_for_parent_delete( key, object )
243
+ end
244
+ end
245
+
246
+ return object
247
+
248
+ end
249
+
250
+ #############
251
+ # freeze! #
252
+ #############
253
+
254
+ # freezes configuration and prevents ancestors from changing this configuration in the future
255
+ def freeze!
256
+
257
+ # unregister with parent composite so we don't get future updates from it
258
+ if @parent_composite_object
259
+ @parent_composite_object.unregister_sub_composite_hash( self )
260
+ end
261
+
262
+ return self
263
+
264
+ end
265
+
266
+ ##################################################################################################
267
+ private ######################################################################################
268
+ ##################################################################################################
269
+
270
+ ######################### Self-as-Sub Management for Parent Updates ############################
271
+
272
+ ################################
273
+ # set_parent_element_in_self #
274
+ ################################
275
+
276
+ def set_parent_element_in_self( key, object )
277
+
278
+ unless @without_child_hooks
279
+ object = child_pre_set_hook( key, object )
280
+ end
281
+
282
+ unless @without_hooks
283
+ object = pre_set_hook( key, object )
284
+ end
285
+
286
+ non_cascading_store( key, object )
287
+
288
+ unless @without_hooks
289
+ object = post_set_hook( key, object )
290
+ end
291
+
292
+ unless @without_child_hooks
293
+ object = child_post_set_hook( key, object )
294
+ end
295
+
296
+ return object
297
+
298
+ end
299
+
300
+ #########################################
301
+ # update_as_sub_hash_for_parent_store #
302
+ #########################################
303
+
304
+ def update_as_sub_hash_for_parent_store( key )
305
+
306
+ unless @replaced_parents[ key ]
307
+
308
+ @parent_key_lookup[ key ] = true
309
+
310
+ @sub_composite_hashes.each do |this_hash|
311
+ this_hash.instance_eval do
312
+ update_as_sub_hash_for_parent_store( key )
313
+ end
314
+ end
315
+
316
+ end
317
+
318
+ end
319
+
320
+ ##########################################
321
+ # update_as_sub_hash_for_parent_delete #
322
+ ##########################################
323
+
324
+ def update_as_sub_hash_for_parent_delete( key, object )
325
+
326
+ unless @replaced_parents[ key ]
327
+
328
+ if @without_child_hooks
329
+ child_pre_delete_hook_result = true
330
+ else
331
+ child_pre_delete_hook_result = child_pre_delete_hook( key )
332
+ end
333
+
334
+ if @without_hooks
335
+ pre_delete_hook_result = true
336
+ else
337
+ pre_delete_hook_result = pre_delete_hook( key )
338
+ end
339
+
340
+ if child_pre_delete_hook_result and pre_delete_hook_result
341
+
342
+ @parent_key_lookup.delete( key )
343
+ object = non_cascading_delete( key )
344
+
345
+ unless @without_hooks
346
+ post_delete_hook( key, object )
347
+ end
348
+
349
+ unless @without_child_hooks
350
+ child_post_delete_hook( key, object )
351
+ end
352
+
353
+ else
354
+
355
+ # if we were told not to delete in child when parent delete
356
+ # and the child does not yet have its parent value
357
+ # then we need to get it now
358
+ if @parent_key_lookup.delete( key )
359
+ self[ key ] = object
360
+ end
361
+
362
+ end
363
+
364
+ @sub_composite_hashes.each do |this_hash|
365
+ this_hash.instance_eval do
366
+ update_as_sub_hash_for_parent_delete( key, object )
367
+ end
368
+ end
369
+
370
+ end
371
+
372
+ end
373
+
374
+ end
@@ -0,0 +1,648 @@
1
+
2
+ require_relative '../lib/compositing-hash.rb'
3
+
4
+ describe ::CompositingHash do
5
+
6
+ before :all do
7
+
8
+ module ::CompositingHash::MockA
9
+ # needed for ccv ancestor determination
10
+ def self.some_configuration
11
+ end
12
+ end
13
+ module ::CompositingHash::MockB
14
+ end
15
+
16
+ @configuration_instance = ::CompositingHash::MockA
17
+ @sub_configuration_instance = ::CompositingHash::MockB
18
+
19
+ end
20
+
21
+ ################
22
+ # initialize #
23
+ ################
24
+
25
+ it 'can add initialize with an ancestor, inheriting its values and linking to it as a child' do
26
+
27
+ cascading_composite_hash = ::CompositingHash.new
28
+
29
+ cascading_composite_hash.instance_variable_get( :@parent_composite_object ).should == nil
30
+ cascading_composite_hash.should == {}
31
+ cascading_composite_hash[ :A ] = 1
32
+ cascading_composite_hash[ :B ] = 2
33
+ cascading_composite_hash[ :C ] = 3
34
+ cascading_composite_hash[ :D ] = 4
35
+ cascading_composite_hash.should == { :A => 1,
36
+ :B => 2,
37
+ :C => 3,
38
+ :D => 4 }
39
+
40
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
41
+ sub_cascading_composite_hash.instance_variable_get( :@parent_composite_object ).should == cascading_composite_hash
42
+ sub_cascading_composite_hash.should == { :A => 1,
43
+ :B => 2,
44
+ :C => 3,
45
+ :D => 4 }
46
+
47
+ end
48
+
49
+ ##################################################################################################
50
+ # private #####################################################################################
51
+ ##################################################################################################
52
+
53
+ #########################################
54
+ # update_as_sub_hash_for_parent_store #
55
+ #########################################
56
+
57
+ it 'can update for a parent store' do
58
+
59
+ cascading_composite_hash = ::CompositingHash.new
60
+ cascading_composite_hash[ :A ] = 1
61
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
62
+
63
+ sub_cascading_composite_hash.instance_eval do
64
+ update_as_sub_hash_for_parent_store( :A )
65
+ self.should == { :A => 1 }
66
+ end
67
+
68
+ end
69
+
70
+ ##########################################
71
+ # update_as_sub_hash_for_parent_delete #
72
+ ##########################################
73
+
74
+ it 'can update for a parent delete' do
75
+
76
+ cascading_composite_hash = ::CompositingHash.new
77
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
78
+
79
+ cascading_composite_hash[ :A ] = 1
80
+ cascading_composite_hash.should == { :A => 1 }
81
+ sub_cascading_composite_hash.should == { :A => 1 }
82
+
83
+ sub_cascading_composite_hash.instance_eval do
84
+ update_as_sub_hash_for_parent_delete( :A, 1 )
85
+ self.should == { }
86
+ end
87
+
88
+ end
89
+
90
+ ##################################################################################################
91
+ # public ######################################################################################
92
+ ##################################################################################################
93
+
94
+ #########
95
+ # []= #
96
+ #########
97
+
98
+ it 'can add elements' do
99
+
100
+ cascading_composite_hash = ::CompositingHash.new
101
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
102
+
103
+ cascading_composite_hash[ :some_setting ] = :some_value
104
+ cascading_composite_hash.should == { :some_setting => :some_value }
105
+ sub_cascading_composite_hash.should == { :some_setting => :some_value }
106
+
107
+ cascading_composite_hash[ :other_setting ] = :some_value
108
+ cascading_composite_hash.should == { :some_setting => :some_value,
109
+ :other_setting => :some_value }
110
+ sub_cascading_composite_hash.should == { :some_setting => :some_value,
111
+ :other_setting => :some_value }
112
+
113
+ sub_cascading_composite_hash[ :yet_another_setting ] = :some_value
114
+ cascading_composite_hash.should == { :some_setting => :some_value,
115
+ :other_setting => :some_value }
116
+ sub_cascading_composite_hash.should == { :some_setting => :some_value,
117
+ :other_setting => :some_value,
118
+ :yet_another_setting => :some_value }
119
+
120
+ cascading_composite_hash.method( :[]= ).should == cascading_composite_hash.method( :store )
121
+
122
+ end
123
+
124
+ ############
125
+ # delete #
126
+ ############
127
+
128
+ it 'can delete elements' do
129
+
130
+ cascading_composite_hash = ::CompositingHash.new
131
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
132
+
133
+ cascading_composite_hash.store( :some_setting, :some_value )
134
+ cascading_composite_hash.should == { :some_setting => :some_value }
135
+ sub_cascading_composite_hash.should == { :some_setting => :some_value }
136
+
137
+ cascading_composite_hash.store( :other_setting, :some_value )
138
+ cascading_composite_hash.should == { :some_setting => :some_value,
139
+ :other_setting => :some_value }
140
+ sub_cascading_composite_hash.should == { :some_setting => :some_value,
141
+ :other_setting => :some_value }
142
+
143
+ cascading_composite_hash.delete( :some_setting )
144
+ cascading_composite_hash.should == { :other_setting => :some_value }
145
+ sub_cascading_composite_hash.should == { :other_setting => :some_value }
146
+
147
+ sub_cascading_composite_hash.store( :yet_another_setting, :some_value )
148
+ cascading_composite_hash.should == { :other_setting => :some_value }
149
+ sub_cascading_composite_hash.should == { :other_setting => :some_value,
150
+ :yet_another_setting => :some_value }
151
+
152
+ sub_cascading_composite_hash.delete( :other_setting )
153
+ sub_cascading_composite_hash.should == { :yet_another_setting => :some_value }
154
+ cascading_composite_hash.should == { :other_setting => :some_value }
155
+
156
+ end
157
+
158
+ ###############
159
+ # delete_if #
160
+ ###############
161
+
162
+ it 'can delete elements with a block' do
163
+
164
+ cascading_composite_hash = ::CompositingHash.new
165
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
166
+
167
+ cascading_composite_hash.store( :some_setting, :some_value )
168
+ cascading_composite_hash.should == { :some_setting => :some_value }
169
+ sub_cascading_composite_hash.should == { :some_setting => :some_value }
170
+
171
+ cascading_composite_hash.store( :other_setting, :some_value )
172
+ cascading_composite_hash.should == { :some_setting => :some_value,
173
+ :other_setting => :some_value }
174
+ sub_cascading_composite_hash.should == { :some_setting => :some_value,
175
+ :other_setting => :some_value }
176
+ cascading_composite_hash.delete_if do |key, value|
177
+ key == :some_setting
178
+ end
179
+ cascading_composite_hash.should == { :other_setting => :some_value }
180
+ sub_cascading_composite_hash.should == { :other_setting => :some_value }
181
+
182
+ sub_cascading_composite_hash.store( :yet_another_setting, :some_value )
183
+ cascading_composite_hash.should == { :other_setting => :some_value }
184
+ sub_cascading_composite_hash.should == { :other_setting => :some_value,
185
+ :yet_another_setting => :some_value }
186
+ sub_cascading_composite_hash.delete_if do |key, value|
187
+ key == :other_setting
188
+ end
189
+ cascading_composite_hash.should == { :other_setting => :some_value }
190
+ sub_cascading_composite_hash.should == { :yet_another_setting => :some_value }
191
+
192
+ end
193
+
194
+ #############
195
+ # reject! #
196
+ #############
197
+
198
+ it 'can delete elements with a block' do
199
+
200
+ cascading_composite_hash = ::CompositingHash.new
201
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
202
+
203
+ cascading_composite_hash.store( :some_setting, :some_value )
204
+ cascading_composite_hash.should == { :some_setting => :some_value }
205
+ sub_cascading_composite_hash.should == { :some_setting => :some_value }
206
+
207
+ cascading_composite_hash.store( :other_setting, :some_value )
208
+ cascading_composite_hash.should == { :some_setting => :some_value,
209
+ :other_setting => :some_value }
210
+ sub_cascading_composite_hash.should == { :some_setting => :some_value,
211
+ :other_setting => :some_value }
212
+ cascading_composite_hash.reject! do |key, value|
213
+ key == :some_setting
214
+ end
215
+ cascading_composite_hash.should == { :other_setting => :some_value }
216
+ sub_cascading_composite_hash.should == { :other_setting => :some_value }
217
+
218
+ sub_cascading_composite_hash.store( :yet_another_setting, :some_value )
219
+ cascading_composite_hash.should == { :other_setting => :some_value }
220
+ sub_cascading_composite_hash.should == { :other_setting => :some_value,
221
+ :yet_another_setting => :some_value }
222
+ sub_cascading_composite_hash.reject! do |key, value|
223
+ key == :other_setting
224
+ end
225
+ cascading_composite_hash.should == { :other_setting => :some_value }
226
+ sub_cascading_composite_hash.should == { :yet_another_setting => :some_value }
227
+
228
+ end
229
+
230
+ #############
231
+ # keep_if #
232
+ #############
233
+
234
+ it 'can keep elements with a block' do
235
+
236
+ cascading_composite_hash = ::CompositingHash.new
237
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
238
+
239
+ cascading_composite_hash.store( :some_setting, :some_value )
240
+ cascading_composite_hash.should == { :some_setting => :some_value }
241
+ sub_cascading_composite_hash.should == { :some_setting => :some_value }
242
+
243
+ cascading_composite_hash.store( :other_setting, :some_value )
244
+ cascading_composite_hash.should == { :some_setting => :some_value,
245
+ :other_setting => :some_value }
246
+ sub_cascading_composite_hash.should == { :some_setting => :some_value,
247
+ :other_setting => :some_value }
248
+ cascading_composite_hash.keep_if do |key, value|
249
+ key != :some_setting
250
+ end
251
+ cascading_composite_hash.should == { :other_setting => :some_value }
252
+ sub_cascading_composite_hash.should == { :other_setting => :some_value }
253
+
254
+ sub_cascading_composite_hash.store( :yet_another_setting, :some_value )
255
+ cascading_composite_hash.should == { :other_setting => :some_value }
256
+ sub_cascading_composite_hash.should == { :other_setting => :some_value,
257
+ :yet_another_setting => :some_value }
258
+ sub_cascading_composite_hash.keep_if do |key, value|
259
+ key != :other_setting
260
+ end
261
+ cascading_composite_hash.should == { :other_setting => :some_value }
262
+ sub_cascading_composite_hash.should == { :yet_another_setting => :some_value }
263
+
264
+ end
265
+
266
+ #############
267
+ # select! #
268
+ #############
269
+
270
+ it 'can keep elements with a block' do
271
+
272
+ cascading_composite_hash = ::CompositingHash.new
273
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
274
+
275
+ cascading_composite_hash.store( :some_setting, :some_value )
276
+ cascading_composite_hash.should == { :some_setting => :some_value }
277
+ sub_cascading_composite_hash.should == { :some_setting => :some_value }
278
+
279
+ cascading_composite_hash.store( :other_setting, :some_value )
280
+ cascading_composite_hash.should == { :some_setting => :some_value,
281
+ :other_setting => :some_value }
282
+ sub_cascading_composite_hash.should == { :some_setting => :some_value,
283
+ :other_setting => :some_value }
284
+ cascading_composite_hash.select! do |key, value|
285
+ key != :some_setting
286
+ end
287
+ cascading_composite_hash.should == { :other_setting => :some_value }
288
+ sub_cascading_composite_hash.should == { :other_setting => :some_value }
289
+
290
+ sub_cascading_composite_hash.store( :yet_another_setting, :some_value )
291
+ cascading_composite_hash.should == { :other_setting => :some_value }
292
+ sub_cascading_composite_hash.should == { :other_setting => :some_value,
293
+ :yet_another_setting => :some_value }
294
+ sub_cascading_composite_hash.select! do |key, value|
295
+ key != :other_setting
296
+ end
297
+ cascading_composite_hash.should == { :other_setting => :some_value }
298
+ sub_cascading_composite_hash.should == { :yet_another_setting => :some_value }
299
+
300
+ end
301
+
302
+ ############
303
+ # merge! #
304
+ # update #
305
+ ############
306
+
307
+ it 'can merge from another hash' do
308
+
309
+ cascading_composite_hash = ::CompositingHash.new
310
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
311
+
312
+ cascading_composite_hash.merge!( :some_setting => :some_value )
313
+ cascading_composite_hash.should == { :some_setting => :some_value }
314
+ cascading_composite_hash.merge!( :other_setting => :some_value )
315
+ cascading_composite_hash.should == { :some_setting => :some_value,
316
+ :other_setting => :some_value }
317
+
318
+ sub_cascading_composite_hash.should == { :some_setting => :some_value,
319
+ :other_setting => :some_value }
320
+ sub_cascading_composite_hash.merge!( :yet_another_setting => :some_value )
321
+ sub_cascading_composite_hash.should == { :some_setting => :some_value,
322
+ :other_setting => :some_value,
323
+ :yet_another_setting => :some_value }
324
+
325
+ end
326
+
327
+ #############
328
+ # replace #
329
+ #############
330
+
331
+ it 'can replace existing elements with others' do
332
+
333
+ cascading_composite_hash = ::CompositingHash.new
334
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
335
+
336
+ cascading_composite_hash.replace( :some_setting => :some_value )
337
+ cascading_composite_hash.should == { :some_setting => :some_value }
338
+ cascading_composite_hash.replace( :other_setting => :some_value )
339
+ cascading_composite_hash.should == { :other_setting => :some_value }
340
+
341
+ sub_cascading_composite_hash.should == { :other_setting => :some_value }
342
+ sub_cascading_composite_hash.replace( :yet_another_setting => :some_value )
343
+ sub_cascading_composite_hash.should == { :yet_another_setting => :some_value }
344
+
345
+ end
346
+
347
+ ###########
348
+ # shift #
349
+ ###########
350
+
351
+ it 'can shift the first element' do
352
+
353
+ cascading_composite_hash = ::CompositingHash.new
354
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
355
+
356
+ cascading_composite_hash.store( :some_setting, :some_value )
357
+ cascading_composite_hash.should == { :some_setting => :some_value }
358
+ cascading_composite_hash.store( :other_setting, :some_value )
359
+ cascading_composite_hash.should == { :some_setting => :some_value,
360
+ :other_setting => :some_value }
361
+ cascading_composite_hash.shift
362
+ cascading_composite_hash.should == { :other_setting => :some_value }
363
+
364
+ sub_cascading_composite_hash.should == { :other_setting => :some_value }
365
+ sub_cascading_composite_hash.store( :yet_another_setting, :some_value )
366
+ sub_cascading_composite_hash.should == { :other_setting => :some_value,
367
+ :yet_another_setting => :some_value }
368
+ sub_cascading_composite_hash.shift
369
+ sub_cascading_composite_hash.should == { :yet_another_setting => :some_value }
370
+ cascading_composite_hash.should == { :other_setting => :some_value }
371
+
372
+ end
373
+
374
+ ###########
375
+ # clear #
376
+ ###########
377
+
378
+ it 'can clear, causing present elements to be excluded' do
379
+
380
+ cascading_composite_hash = ::CompositingHash.new
381
+ sub_cascading_composite_hash = ::CompositingHash.new( cascading_composite_hash )
382
+
383
+ cascading_composite_hash.store( :some_setting, :some_value )
384
+ cascading_composite_hash.should == { :some_setting => :some_value }
385
+ cascading_composite_hash.store( :other_setting, :some_value )
386
+ cascading_composite_hash.should == { :some_setting => :some_value,
387
+ :other_setting => :some_value }
388
+ cascading_composite_hash.clear
389
+ cascading_composite_hash.should == { }
390
+ cascading_composite_hash.store( :other_setting, :some_value )
391
+ cascading_composite_hash.should == { :other_setting => :some_value }
392
+
393
+ sub_cascading_composite_hash.should == { :other_setting => :some_value }
394
+ sub_cascading_composite_hash.store( :yet_another_setting, :some_value )
395
+ sub_cascading_composite_hash.should == { :other_setting => :some_value,
396
+ :yet_another_setting => :some_value }
397
+ sub_cascading_composite_hash.clear
398
+ sub_cascading_composite_hash.should == { }
399
+ cascading_composite_hash.should == { :other_setting => :some_value }
400
+
401
+ end
402
+
403
+ ##################
404
+ # pre_set_hook #
405
+ ##################
406
+
407
+ it 'has a hook that is called before setting a value; return value is used in place of object' do
408
+
409
+ class ::CompositingHash::SubMockPreSet < ::CompositingHash
410
+
411
+ def pre_set_hook( key, object, is_insert = false )
412
+ return :some_other_value
413
+ end
414
+
415
+ end
416
+
417
+ cascading_composite_hash = ::CompositingHash::SubMockPreSet.new
418
+
419
+ cascading_composite_hash[ :some_key ] = :some_value
420
+
421
+ cascading_composite_hash.should == { :some_key => :some_other_value }
422
+
423
+ end
424
+
425
+ ###################
426
+ # post_set_hook #
427
+ ###################
428
+
429
+ it 'has a hook that is called after setting a value' do
430
+
431
+ class ::CompositingHash::SubMockPostSet < ::CompositingHash
432
+
433
+ def post_set_hook( key, object, is_insert = false )
434
+ unless key == :some_other_key
435
+ self[ :some_other_key ] = :some_other_value
436
+ end
437
+ return object
438
+ end
439
+
440
+ end
441
+
442
+ cascading_composite_hash = ::CompositingHash::SubMockPostSet.new
443
+
444
+ cascading_composite_hash[ :some_key ] = :some_value
445
+
446
+ cascading_composite_hash.should == { :some_key => :some_value,
447
+ :some_other_key => :some_other_value }
448
+
449
+ end
450
+
451
+ ##################
452
+ # pre_get_hook #
453
+ ##################
454
+
455
+ it 'has a hook that is called before getting a value; if return value is false, get does not occur' do
456
+
457
+ class ::CompositingHash::SubMockPreGet < ::CompositingHash
458
+
459
+ def pre_get_hook( key )
460
+ return false
461
+ end
462
+
463
+ end
464
+
465
+ cascading_composite_hash = ::CompositingHash::SubMockPreGet.new
466
+
467
+ cascading_composite_hash[ :some_key ] = :some_value
468
+ cascading_composite_hash[ :some_key ].should == nil
469
+
470
+ cascading_composite_hash.should == { :some_key => :some_value }
471
+
472
+ end
473
+
474
+ ###################
475
+ # post_get_hook #
476
+ ###################
477
+
478
+ it 'has a hook that is called after getting a value' do
479
+
480
+ class ::CompositingHash::SubMockPostGet < ::CompositingHash
481
+
482
+ def post_get_hook( key, object )
483
+ self[ :some_other_key ] = :some_other_value
484
+ return object
485
+ end
486
+
487
+ end
488
+
489
+ cascading_composite_hash = ::CompositingHash::SubMockPostGet.new
490
+
491
+ cascading_composite_hash[ :some_key ] = :some_value
492
+
493
+ cascading_composite_hash.should == { :some_key => :some_value }
494
+
495
+ cascading_composite_hash[ :some_key ].should == :some_value
496
+
497
+ cascading_composite_hash.should == { :some_key => :some_value,
498
+ :some_other_key => :some_other_value }
499
+
500
+ end
501
+
502
+ #####################
503
+ # pre_delete_hook #
504
+ #####################
505
+
506
+ it 'has a hook that is called before deleting an key; if return value is false, delete does not occur' do
507
+
508
+ class ::CompositingHash::SubMockPreDelete < ::CompositingHash
509
+
510
+ def pre_delete_hook( key )
511
+ return false
512
+ end
513
+
514
+ end
515
+
516
+ cascading_composite_hash = ::CompositingHash::SubMockPreDelete.new
517
+
518
+ cascading_composite_hash[ :some_key ] = :some_value
519
+ cascading_composite_hash.delete( :some_key )
520
+
521
+ cascading_composite_hash.should == { :some_key => :some_value }
522
+
523
+ end
524
+
525
+ ######################
526
+ # post_delete_hook #
527
+ ######################
528
+
529
+ it 'has a hook that is called after deleting an key' do
530
+
531
+ class ::CompositingHash::SubMockPostDelete < ::CompositingHash
532
+
533
+ def post_delete_hook( key, object )
534
+ unless key == :some_other_key
535
+ delete( :some_other_key )
536
+ end
537
+ end
538
+
539
+ end
540
+
541
+ cascading_composite_hash = ::CompositingHash::SubMockPostDelete.new
542
+
543
+ cascading_composite_hash[ :some_key ] = :some_value
544
+ cascading_composite_hash[ :some_other_key ] = :some_other_value
545
+ cascading_composite_hash.delete( :some_key )
546
+
547
+ cascading_composite_hash.should == { }
548
+
549
+ end
550
+
551
+ ########################
552
+ # child_pre_set_hook #
553
+ ########################
554
+
555
+ it 'has a hook that is called before setting a value that has been passed by a parent; return value is used in place of object' do
556
+
557
+ class ::CompositingHash::SubMockChildPreSet < ::CompositingHash
558
+
559
+ def child_pre_set_hook( key, object, is_insert = false )
560
+ return :some_other_value
561
+ end
562
+
563
+ end
564
+
565
+ cascading_composite_hash = ::CompositingHash::SubMockChildPreSet.new
566
+ sub_cascading_composite_hash = ::CompositingHash::SubMockChildPreSet.new( cascading_composite_hash )
567
+
568
+ cascading_composite_hash[ :some_key ] = :some_value
569
+
570
+ cascading_composite_hash.should == { :some_key => :some_value }
571
+ sub_cascading_composite_hash.should == { :some_key => :some_other_value }
572
+
573
+ end
574
+
575
+ #########################
576
+ # child_post_set_hook #
577
+ #########################
578
+
579
+ it 'has a hook that is called after setting a value passed by a parent' do
580
+
581
+ class ::CompositingHash::SubMockChildPostSet < ::CompositingHash
582
+
583
+ def child_post_set_hook( key, object, is_insert = false )
584
+ self[ :some_other_key ] = :some_other_value
585
+ end
586
+
587
+ end
588
+
589
+ cascading_composite_hash = ::CompositingHash::SubMockChildPostSet.new
590
+ sub_cascading_composite_hash = ::CompositingHash::SubMockChildPostSet.new( cascading_composite_hash )
591
+ cascading_composite_hash[ :some_key ] = :some_value
592
+
593
+ cascading_composite_hash.should == { :some_key => :some_value }
594
+ sub_cascading_composite_hash.should == { :some_key => :some_value,
595
+ :some_other_key => :some_other_value }
596
+
597
+ end
598
+
599
+ ###########################
600
+ # child_pre_delete_hook #
601
+ ###########################
602
+
603
+ it 'has a hook that is called before deleting an key that has been passed by a parent; if return value is false, delete does not occur' do
604
+
605
+ class ::CompositingHash::SubMockChildPreDelete < ::CompositingHash
606
+
607
+ def child_pre_delete_hook( key )
608
+ false
609
+ end
610
+
611
+ end
612
+
613
+ cascading_composite_hash = ::CompositingHash::SubMockChildPreDelete.new
614
+ sub_cascading_composite_hash = ::CompositingHash::SubMockChildPreDelete.new( cascading_composite_hash )
615
+ cascading_composite_hash[ :some_key ] = :some_value
616
+ cascading_composite_hash.delete( :some_key )
617
+
618
+ cascading_composite_hash.should == { }
619
+ sub_cascading_composite_hash.should == { :some_key => :some_value }
620
+
621
+ end
622
+
623
+ ############################
624
+ # child_post_delete_hook #
625
+ ############################
626
+
627
+ it 'has a hook that is called after deleting an key passed by a parent' do
628
+
629
+ class ::CompositingHash::SubMockChildPostDelete < ::CompositingHash
630
+
631
+ def child_post_delete_hook( key, object )
632
+ delete( :some_other_key )
633
+ end
634
+
635
+ end
636
+
637
+ cascading_composite_hash = ::CompositingHash::SubMockChildPostDelete.new
638
+ sub_cascading_composite_hash = ::CompositingHash::SubMockChildPostDelete.new( cascading_composite_hash )
639
+ cascading_composite_hash[ :some_key ] = :some_value
640
+ sub_cascading_composite_hash[ :some_other_key ] = :some_other_value
641
+ cascading_composite_hash.delete( :some_key )
642
+
643
+ cascading_composite_hash.should == { }
644
+ sub_cascading_composite_hash.should == { }
645
+
646
+ end
647
+
648
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: compositing-hash
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.15
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Asher
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: hooked-hash
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: An implementation of Hash that permits chaining, where children inherit
31
+ changes to parent and where parent settings can be overridden in children.
32
+ email: asher@ridiculouspower.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/compositing-hash/CompositingHash.rb
38
+ - lib/compositing-hash.rb
39
+ - spec/CompositingHash_spec.rb
40
+ - README.md
41
+ - README.rdoc
42
+ homepage: http://rubygems.org/gems/compositing-hash
43
+ licenses: []
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project: compositing-hash
62
+ rubygems_version: 1.8.23
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: Provides CompositingHash.
66
+ test_files: []
67
+ has_rdoc: