hash-compositing 1.0.0

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/hash-compositing
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 hash-compositing
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,2 @@
1
+
2
+ require_relative 'hash/compositing.rb'
@@ -0,0 +1,14 @@
1
+
2
+ require 'hash/hooked'
3
+
4
+ # namespaces that have to be declared ahead of time for proper load order
5
+ require_relative './namespaces'
6
+
7
+ # source file requires
8
+ require_relative './requires.rb'
9
+
10
+ class ::CompositingHash < ::HookedHash
11
+
12
+ include ::CompositingHash::HashInterface
13
+
14
+ end
@@ -0,0 +1,405 @@
1
+
2
+ module ::CompositingHash::HashInterface
3
+
4
+ ##########################
5
+ # self.append_features #
6
+ ##########################
7
+
8
+ def self.append_features( instance )
9
+
10
+ instance.module_eval do
11
+
12
+ private
13
+
14
+ alias_method :non_cascading_store, :[]=
15
+
16
+ alias_method :non_cascading_delete, :delete
17
+
18
+ end
19
+
20
+ super
21
+
22
+ end
23
+
24
+ ###################
25
+ # self.included #
26
+ ###################
27
+
28
+ def self.included( instance )
29
+
30
+ instance.module_eval do
31
+
32
+ alias_method :store, :[]=
33
+
34
+ end
35
+
36
+ super
37
+
38
+ end
39
+
40
+ ################
41
+ # initialize #
42
+ ################
43
+
44
+ def initialize( parent_composite_hash = nil, configuration_instance = nil )
45
+
46
+ super( configuration_instance )
47
+
48
+ @replaced_parents = { }
49
+ @parent_key_lookup = { }
50
+
51
+ # we may later have our own child composites that register with us
52
+ @sub_composite_hashes = [ ]
53
+
54
+ initialize_for_parent( parent_composite_hash )
55
+
56
+ end
57
+
58
+ #############################
59
+ # parent_composite_object #
60
+ # parent_composite_hash #
61
+ #############################
62
+
63
+ attr_accessor :parent_composite_object
64
+
65
+ alias_method :parent_composite_hash, :parent_composite_object
66
+
67
+ ################################### Sub-Hash Management #######################################
68
+
69
+ ###########################
70
+ # initialize_for_parent #
71
+ ###########################
72
+
73
+ def initialize_for_parent( parent_composite_hash )
74
+
75
+ if @parent_composite_object = parent_composite_hash
76
+
77
+ @parent_composite_object.register_sub_composite_hash( self )
78
+
79
+ # @parent_key_lookup tracks keys that we have not yet received from parent
80
+ @parent_composite_object.each do |this_key, this_object|
81
+ @parent_key_lookup[ this_key ] = true
82
+ non_cascading_store( this_key, nil )
83
+ end
84
+
85
+ end
86
+
87
+ end
88
+
89
+ #################################
90
+ # register_sub_composite_hash #
91
+ #################################
92
+
93
+ def register_sub_composite_hash( sub_composite_hash )
94
+
95
+ @sub_composite_hashes.push( sub_composite_hash )
96
+
97
+ return self
98
+
99
+ end
100
+
101
+ ###################################
102
+ # unregister_sub_composite_hash #
103
+ ###################################
104
+
105
+ def unregister_sub_composite_hash( sub_composite_hash )
106
+
107
+ @sub_composite_hashes.delete( sub_composite_hash )
108
+
109
+ return self
110
+
111
+ end
112
+
113
+ ###################################### Subclass Hooks ##########################################
114
+
115
+ ########################
116
+ # child_pre_set_hook #
117
+ ########################
118
+
119
+ def child_pre_set_hook( key, object )
120
+
121
+ return object
122
+
123
+ end
124
+
125
+ #########################
126
+ # child_post_set_hook #
127
+ #########################
128
+
129
+ def child_post_set_hook( key, object )
130
+
131
+ return object
132
+
133
+ end
134
+
135
+ ###########################
136
+ # child_pre_delete_hook #
137
+ ###########################
138
+
139
+ def child_pre_delete_hook( key )
140
+
141
+ # false means delete does not take place
142
+ return true
143
+
144
+ end
145
+
146
+ ############################
147
+ # child_post_delete_hook #
148
+ ############################
149
+
150
+ def child_post_delete_hook( key, object )
151
+
152
+ return object
153
+
154
+ end
155
+
156
+ ##################################### Self Management ##########################################
157
+
158
+ ########
159
+ # == #
160
+ ########
161
+
162
+ def ==( object )
163
+
164
+ load_parent_state
165
+
166
+ super
167
+
168
+ end
169
+
170
+ ##########
171
+ # each #
172
+ ##########
173
+
174
+ def each( *args, & block )
175
+
176
+ load_parent_state
177
+
178
+ super
179
+
180
+ end
181
+
182
+ ##########
183
+ # to_s #
184
+ ##########
185
+
186
+ def to_s
187
+
188
+ load_parent_state
189
+
190
+ super
191
+
192
+ end
193
+
194
+ #############
195
+ # inspect #
196
+ #############
197
+
198
+ def inspect
199
+
200
+ load_parent_state
201
+
202
+ super
203
+
204
+ end
205
+
206
+ ########
207
+ # [] #
208
+ ########
209
+
210
+ def []( key )
211
+
212
+ return_value = nil
213
+
214
+ if @parent_key_lookup[ key ]
215
+ return_value = lazy_set_parent_element_in_self( key, @parent_composite_object[ key ] )
216
+ else
217
+ return_value = super
218
+ end
219
+
220
+ return return_value
221
+
222
+ end
223
+
224
+ #########
225
+ # []= #
226
+ #########
227
+
228
+ def []=( key, object )
229
+
230
+ @replaced_parents[ key ] = true
231
+
232
+ @parent_key_lookup.delete( key )
233
+
234
+ super
235
+
236
+ @sub_composite_hashes.each do |this_sub_hash|
237
+ this_sub_hash.instance_eval do
238
+ update_as_sub_hash_for_parent_store( key )
239
+ end
240
+ end
241
+
242
+ return object
243
+
244
+ end
245
+
246
+ ############
247
+ # delete #
248
+ ############
249
+
250
+ def delete( key )
251
+
252
+ @replaced_parents.delete( key )
253
+
254
+ @parent_key_lookup.delete( key )
255
+
256
+ object = super
257
+
258
+ @sub_composite_hashes.each do |this_sub_hash|
259
+ this_sub_hash.instance_eval do
260
+ update_as_sub_hash_for_parent_delete( key, object )
261
+ end
262
+ end
263
+
264
+ return object
265
+
266
+ end
267
+
268
+ #############
269
+ # freeze! #
270
+ #############
271
+
272
+ # freezes configuration and prevents ancestors from changing this configuration in the future
273
+ def freeze!
274
+
275
+ # unregister with parent composite so we don't get future updates from it
276
+ if @parent_composite_object
277
+ @parent_composite_object.unregister_sub_composite_hash( self )
278
+ end
279
+
280
+ return self
281
+
282
+ end
283
+
284
+ ##################################################################################################
285
+ private ######################################################################################
286
+ ##################################################################################################
287
+
288
+ ######################### Self-as-Sub Management for Parent Updates ############################
289
+
290
+ ################################
291
+ # lazy_set_parent_element_in_self #
292
+ ################################
293
+
294
+ def lazy_set_parent_element_in_self( key, object )
295
+
296
+ unless @without_child_hooks
297
+ object = child_pre_set_hook( key, object )
298
+ end
299
+
300
+ unless @without_hooks
301
+ object = pre_set_hook( key, object )
302
+ end
303
+
304
+ non_cascading_store( key, object )
305
+
306
+ unless @without_hooks
307
+ object = post_set_hook( key, object )
308
+ end
309
+
310
+ unless @without_child_hooks
311
+ object = child_post_set_hook( key, object )
312
+ end
313
+
314
+ return object
315
+
316
+ end
317
+
318
+ #########################################
319
+ # update_as_sub_hash_for_parent_store #
320
+ #########################################
321
+
322
+ def update_as_sub_hash_for_parent_store( key )
323
+
324
+ unless @replaced_parents[ key ]
325
+
326
+ @parent_key_lookup[ key ] = true
327
+
328
+ @sub_composite_hashes.each do |this_hash|
329
+ this_hash.instance_eval do
330
+ update_as_sub_hash_for_parent_store( key )
331
+ end
332
+ end
333
+
334
+ end
335
+
336
+ end
337
+
338
+ ##########################################
339
+ # update_as_sub_hash_for_parent_delete #
340
+ ##########################################
341
+
342
+ def update_as_sub_hash_for_parent_delete( key, object )
343
+
344
+ unless @replaced_parents[ key ]
345
+
346
+ if @without_child_hooks
347
+ child_pre_delete_hook_result = true
348
+ else
349
+ child_pre_delete_hook_result = child_pre_delete_hook( key )
350
+ end
351
+
352
+ if @without_hooks
353
+ pre_delete_hook_result = true
354
+ else
355
+ pre_delete_hook_result = pre_delete_hook( key )
356
+ end
357
+
358
+ if child_pre_delete_hook_result and pre_delete_hook_result
359
+
360
+ @parent_key_lookup.delete( key )
361
+
362
+ object = non_cascading_delete( key )
363
+
364
+ unless @without_hooks
365
+ post_delete_hook( key, object )
366
+ end
367
+
368
+ unless @without_child_hooks
369
+ child_post_delete_hook( key, object )
370
+ end
371
+
372
+ else
373
+
374
+ # if we were told not to delete in child when parent delete
375
+ # and the child does not yet have its parent value
376
+ # then we need to get it now
377
+ if @parent_key_lookup.delete( key )
378
+ lazy_set_parent_element_in_self( key, object )
379
+ end
380
+
381
+ end
382
+
383
+ @sub_composite_hashes.each do |this_hash|
384
+ this_hash.instance_eval do
385
+ update_as_sub_hash_for_parent_delete( key, object )
386
+ end
387
+ end
388
+
389
+ end
390
+
391
+ end
392
+
393
+ #######################
394
+ # load_parent_state #
395
+ #######################
396
+
397
+ def load_parent_state
398
+
399
+ @parent_key_lookup.each do |this_key, true_value|
400
+ self[ this_key ]
401
+ end
402
+
403
+ end
404
+
405
+ end
@@ -0,0 +1,2 @@
1
+
2
+ require_relative '../compositing.rb'
@@ -0,0 +1,3 @@
1
+
2
+ class ::CompositingHash < ::HookedHash
3
+ end
@@ -0,0 +1,3 @@
1
+
2
+ require_relative 'compositing/hash_interface.rb'
3
+ require_relative 'compositing.rb'
@@ -0,0 +1,648 @@
1
+
2
+ require_relative '../../lib/hash-compositing.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,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hash-compositing
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Asher
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: hash-hooked
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/hash/compositing/hash_interface.rb
38
+ - lib/hash/compositing.rb
39
+ - lib/hash/hooked/compositing.rb
40
+ - lib/hash/namespaces.rb
41
+ - lib/hash/requires.rb
42
+ - lib/hash-compositing.rb
43
+ - spec/hash/compositing_spec.rb
44
+ - README.md
45
+ - README.rdoc
46
+ homepage: http://rubygems.org/gems/hash-compositing
47
+ licenses: []
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project: hash-compositing
66
+ rubygems_version: 1.8.23
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Provides Hash::Compositing.
70
+ test_files: []
71
+ has_rdoc: