hash-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.
@@ -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: