hash-hooked 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +8 -0
- data/README.md +58 -0
- data/lib/hash-hooked.rb +2 -0
- data/lib/hash/hooked.rb +29 -0
- data/lib/hash/hooked/hash_interface.rb +527 -0
- data/lib/hash/namespaces.rb +6 -0
- data/lib/hash/requires.rb +26 -0
- data/lib/hooked/hash.rb +2 -0
- data/lib/hooked_hash.rb +6 -0
- data/lib/hooked_hash/hash_interface.rb +29 -0
- data/spec/hash/hooked_spec.rb +368 -0
- metadata +73 -0
data/CHANGELOG.rdoc
ADDED
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Hooked Hash #
|
2
|
+
|
3
|
+
http://rubygems.org/gems/hash-hooked
|
4
|
+
|
5
|
+
# Description #
|
6
|
+
|
7
|
+
Provides ::Hash::Hooked and ::HookedHash.
|
8
|
+
|
9
|
+
# Summary #
|
10
|
+
|
11
|
+
A subclass of Hash that offers event hooks for pre-set/pre-delete, set/delete. ::HookedHash offers implicit reference to a configuration instance.
|
12
|
+
|
13
|
+
# Install #
|
14
|
+
|
15
|
+
* sudo gem install hash-hooked
|
16
|
+
|
17
|
+
# Usage #
|
18
|
+
|
19
|
+
Provides methods that can be overridden that will be called during every corresponding event:
|
20
|
+
|
21
|
+
* pre_set_hook
|
22
|
+
* post_set_hook
|
23
|
+
* pre_get_hook
|
24
|
+
* post_get_hook
|
25
|
+
* pre_delete_hook
|
26
|
+
* post_delete_hook
|
27
|
+
|
28
|
+
As a result, several internal perform methods have been created:
|
29
|
+
|
30
|
+
* perform_set_between_hooks
|
31
|
+
* perform_delete_between_hooks
|
32
|
+
|
33
|
+
Also provides methods corresponding to each already-existing method + '_without_hooks', which causes methods to bypass hooks.
|
34
|
+
|
35
|
+
# License #
|
36
|
+
|
37
|
+
(The MIT License)
|
38
|
+
|
39
|
+
Copyright (c) Asher
|
40
|
+
|
41
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
42
|
+
a copy of this software and associated documentation files (the
|
43
|
+
'Software'), to deal in the Software without restriction, including
|
44
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
45
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
46
|
+
permit persons to whom the Software is furnished to do so, subject to
|
47
|
+
the following conditions:
|
48
|
+
|
49
|
+
The above copyright notice and this permission notice shall be
|
50
|
+
included in all copies or substantial portions of the Software.
|
51
|
+
|
52
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
53
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
54
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
55
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
56
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
57
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
58
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/hash-hooked.rb
ADDED
data/lib/hash/hooked.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
require 'identifies_as'
|
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 ::Hash::Hooked < ::Hash
|
11
|
+
|
12
|
+
# Alias to original :[]= method. Used to perform actual set between hooks.
|
13
|
+
# @param [Object] key Key where object is to be stored.
|
14
|
+
# @param [Object] object Element being stored.
|
15
|
+
# @return [Object] Element returned.
|
16
|
+
alias_method :perform_set_between_hooks, :store
|
17
|
+
|
18
|
+
# Alias to original :delete method. Used to perform actual delete between hooks.
|
19
|
+
# @param [Object] key Key where object is to be stored.
|
20
|
+
# @return [Object] Element returned.
|
21
|
+
alias_method :perform_delete_between_hooks, :delete
|
22
|
+
|
23
|
+
# Alias to original :merge method. Used to perform actual merge between hooks.
|
24
|
+
# @return [Object] Self.
|
25
|
+
alias_method :perform_merge_between_hooks!, :merge!
|
26
|
+
|
27
|
+
include ::Hash::Hooked::HashInterface
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,527 @@
|
|
1
|
+
|
2
|
+
module ::Hash::Hooked::HashInterface
|
3
|
+
|
4
|
+
instances_identify_as!( ::Hash::Hooked )
|
5
|
+
|
6
|
+
###################################### Subclass Hooks ##########################################
|
7
|
+
|
8
|
+
##################
|
9
|
+
# pre_set_hook #
|
10
|
+
##################
|
11
|
+
|
12
|
+
# A hook that is called before setting a value; return value is used in place of object.
|
13
|
+
# @param [Object] key Key where object is to be stored.
|
14
|
+
# @param [Object] object Element being stored.
|
15
|
+
# @return [true,false] Return value is used in place of object.
|
16
|
+
def pre_set_hook( key, object )
|
17
|
+
|
18
|
+
return object
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
###################
|
23
|
+
# post_set_hook #
|
24
|
+
###################
|
25
|
+
|
26
|
+
# A hook that is called after setting a value.
|
27
|
+
# @param [Object] key Key where object is to be stored.
|
28
|
+
# @param [Object] object Element being stored.
|
29
|
+
# @return [Object] Ignored.
|
30
|
+
def post_set_hook( key, object )
|
31
|
+
|
32
|
+
return object
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
##################
|
37
|
+
# pre_get_hook #
|
38
|
+
##################
|
39
|
+
|
40
|
+
# A hook that is called before getting a value; if return value is false, get does not occur.
|
41
|
+
# @param [Object] key Key where object is to be stored.
|
42
|
+
# @return [true,false] If return value is false, get does not occur.
|
43
|
+
def pre_get_hook( key )
|
44
|
+
|
45
|
+
return true
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
###################
|
50
|
+
# post_get_hook #
|
51
|
+
###################
|
52
|
+
|
53
|
+
# A hook that is called after getting a value.
|
54
|
+
# @param [Object] key Key where object is to be stored.
|
55
|
+
# @param [Object] object Element being stored.
|
56
|
+
# @return [Object] Object returned in place of get result.
|
57
|
+
def post_get_hook( key, object )
|
58
|
+
|
59
|
+
return object
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
#####################
|
64
|
+
# pre_delete_hook #
|
65
|
+
#####################
|
66
|
+
|
67
|
+
# A hook that is called before deleting a value; if return value is false, delete does not occur.
|
68
|
+
# @param [Object] key Key where object is to be stored.
|
69
|
+
# @return [true,false] If return value is false, delete does not occur.
|
70
|
+
def pre_delete_hook( key )
|
71
|
+
|
72
|
+
return true
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
######################
|
77
|
+
# post_delete_hook #
|
78
|
+
######################
|
79
|
+
|
80
|
+
# A hook that is called after deleting a value.
|
81
|
+
# @param [Object] key Key where object is to be stored.
|
82
|
+
# @param [Object] object Element deleted.
|
83
|
+
# @return [Object] Object returned in place of delete result.
|
84
|
+
def post_delete_hook( key, object )
|
85
|
+
|
86
|
+
return object
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
##################################### Self Management ##########################################
|
91
|
+
|
92
|
+
########
|
93
|
+
# [] #
|
94
|
+
########
|
95
|
+
|
96
|
+
def []( key )
|
97
|
+
|
98
|
+
object = nil
|
99
|
+
|
100
|
+
if @without_hooks
|
101
|
+
pre_get_hook_result = true
|
102
|
+
else
|
103
|
+
pre_get_hook_result = pre_get_hook( key )
|
104
|
+
end
|
105
|
+
|
106
|
+
if pre_get_hook_result
|
107
|
+
|
108
|
+
object = super( key )
|
109
|
+
|
110
|
+
unless @without_hooks
|
111
|
+
object = post_get_hook( key, object )
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
return object
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
#######################
|
121
|
+
# get_without_hooks #
|
122
|
+
#######################
|
123
|
+
|
124
|
+
# Alias to :[] that bypasses hooks.
|
125
|
+
# @param [Object] key Key where object is to be stored.
|
126
|
+
# @return [Object] Element returned.
|
127
|
+
def get_without_hooks( key )
|
128
|
+
|
129
|
+
@without_hooks = true
|
130
|
+
|
131
|
+
object = self[ key ]
|
132
|
+
|
133
|
+
@without_hooks = false
|
134
|
+
|
135
|
+
return object
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
#########
|
140
|
+
# []= #
|
141
|
+
#########
|
142
|
+
|
143
|
+
def []=( key, object )
|
144
|
+
|
145
|
+
unless @without_hooks
|
146
|
+
object = pre_set_hook( key, object )
|
147
|
+
end
|
148
|
+
|
149
|
+
perform_set_between_hooks( key, object )
|
150
|
+
|
151
|
+
unless @without_hooks
|
152
|
+
object = post_set_hook( key, object )
|
153
|
+
end
|
154
|
+
|
155
|
+
return object
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
alias_method :store, :[]=
|
160
|
+
|
161
|
+
#########################
|
162
|
+
# store_without_hooks #
|
163
|
+
#########################
|
164
|
+
|
165
|
+
# Alias to :[]= that bypasses hooks.
|
166
|
+
# @param [Object] key Key where object is to be stored.
|
167
|
+
# @param [Object] object Element being set.
|
168
|
+
# @return [Object] Element returned.
|
169
|
+
def store_without_hooks( key, object )
|
170
|
+
|
171
|
+
@without_hooks = true
|
172
|
+
|
173
|
+
self[ key ] = object
|
174
|
+
|
175
|
+
@without_hooks = false
|
176
|
+
|
177
|
+
return object
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
############
|
182
|
+
# delete #
|
183
|
+
############
|
184
|
+
|
185
|
+
def delete( key )
|
186
|
+
|
187
|
+
object = nil
|
188
|
+
|
189
|
+
if @without_hooks
|
190
|
+
pre_delete_result = true
|
191
|
+
else
|
192
|
+
pre_delete_result = pre_delete_hook( key )
|
193
|
+
end
|
194
|
+
|
195
|
+
if pre_delete_result
|
196
|
+
|
197
|
+
object = perform_delete_between_hooks( key )
|
198
|
+
|
199
|
+
unless @without_hooks
|
200
|
+
object = post_delete_hook( key, object )
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
return object
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
##########################
|
210
|
+
# delete_without_hooks #
|
211
|
+
##########################
|
212
|
+
|
213
|
+
# Alias to :delete that bypasses hooks.
|
214
|
+
# @param [Object] object Element being deleted.
|
215
|
+
# @return [Object] Element returned.
|
216
|
+
def delete_without_hooks( key )
|
217
|
+
|
218
|
+
@without_hooks = true
|
219
|
+
|
220
|
+
object = delete( key )
|
221
|
+
|
222
|
+
@without_hooks = false
|
223
|
+
|
224
|
+
return object
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
###############
|
229
|
+
# delete_if #
|
230
|
+
###############
|
231
|
+
|
232
|
+
def delete_if
|
233
|
+
|
234
|
+
return to_enum unless block_given?
|
235
|
+
|
236
|
+
indexes = [ ]
|
237
|
+
|
238
|
+
self.each do |this_key, this_object|
|
239
|
+
if yield( this_key, this_object )
|
240
|
+
delete( this_key )
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
return self
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
#############################
|
249
|
+
# delete_if_without_hooks #
|
250
|
+
#############################
|
251
|
+
|
252
|
+
# Alias to :delete_if that bypasses hooks.
|
253
|
+
# @yield Block passed to :delete_if.
|
254
|
+
# @return [Object] Deleted element.
|
255
|
+
def delete_if_without_hooks( & block )
|
256
|
+
|
257
|
+
@without_hooks = true
|
258
|
+
|
259
|
+
delete_if( & block )
|
260
|
+
|
261
|
+
@without_hooks = false
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
#############
|
266
|
+
# reject! #
|
267
|
+
#############
|
268
|
+
|
269
|
+
def reject!
|
270
|
+
|
271
|
+
return to_enum unless block_given?
|
272
|
+
|
273
|
+
return_value = nil
|
274
|
+
|
275
|
+
self.each do |this_key, this_object|
|
276
|
+
if yield( this_key, this_object )
|
277
|
+
delete( this_key )
|
278
|
+
return_value = self
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
return return_value
|
283
|
+
|
284
|
+
end
|
285
|
+
|
286
|
+
###########################
|
287
|
+
# reject_without_hooks! #
|
288
|
+
###########################
|
289
|
+
|
290
|
+
# Alias to :reject that bypasses hooks.
|
291
|
+
# @yield Block passed to :keep_if.
|
292
|
+
# @return [Object] Self.
|
293
|
+
def reject_without_hooks!
|
294
|
+
|
295
|
+
@without_hooks = true
|
296
|
+
|
297
|
+
return_value = reject!( & block )
|
298
|
+
|
299
|
+
@without_hooks = false
|
300
|
+
|
301
|
+
return return_value
|
302
|
+
|
303
|
+
end
|
304
|
+
|
305
|
+
#############
|
306
|
+
# keep_if #
|
307
|
+
#############
|
308
|
+
|
309
|
+
def keep_if
|
310
|
+
|
311
|
+
return to_enum unless block_given?
|
312
|
+
|
313
|
+
indexes = [ ]
|
314
|
+
|
315
|
+
self.each do |this_key, this_object|
|
316
|
+
unless yield( this_key, this_object )
|
317
|
+
delete( this_key )
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
return self
|
322
|
+
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
###########################
|
327
|
+
# keep_if_without_hooks #
|
328
|
+
###########################
|
329
|
+
|
330
|
+
# Alias to :keep_if that bypasses hooks.
|
331
|
+
# @yield Block passed to :keep_if.
|
332
|
+
# @return [Object] Deleted element.
|
333
|
+
def keep_if_without_hooks( & block )
|
334
|
+
|
335
|
+
@without_hooks = true
|
336
|
+
|
337
|
+
keep_if( & block )
|
338
|
+
|
339
|
+
@without_hooks = false
|
340
|
+
|
341
|
+
return self
|
342
|
+
|
343
|
+
end
|
344
|
+
|
345
|
+
#############
|
346
|
+
# select! #
|
347
|
+
#############
|
348
|
+
|
349
|
+
def select!
|
350
|
+
|
351
|
+
return to_enum unless block_given?
|
352
|
+
|
353
|
+
return_value = nil
|
354
|
+
|
355
|
+
self.each do |this_key, this_object|
|
356
|
+
unless yield( this_key, this_object )
|
357
|
+
delete( this_key )
|
358
|
+
return_value = self
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
return return_value
|
363
|
+
|
364
|
+
end
|
365
|
+
|
366
|
+
###########################
|
367
|
+
# select_without_hooks! #
|
368
|
+
###########################
|
369
|
+
|
370
|
+
# Alias to :select that bypasses hooks.
|
371
|
+
# @yield Block passed to :select!.
|
372
|
+
# @return [Object] Self.
|
373
|
+
def select_without_hooks!( & block )
|
374
|
+
|
375
|
+
@without_hooks = true
|
376
|
+
|
377
|
+
return_value = select!( & block )
|
378
|
+
|
379
|
+
@without_hooks = false
|
380
|
+
|
381
|
+
return return_value
|
382
|
+
|
383
|
+
end
|
384
|
+
|
385
|
+
############
|
386
|
+
# merge! #
|
387
|
+
# update #
|
388
|
+
############
|
389
|
+
|
390
|
+
def merge!( other_hash )
|
391
|
+
|
392
|
+
other_hash.each do |this_key, this_object|
|
393
|
+
if @compositing_proc
|
394
|
+
self[ this_key ] = @compositing_proc.call( self, this_key, this_object )
|
395
|
+
else
|
396
|
+
self[ this_key ] = this_object
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
return self
|
401
|
+
|
402
|
+
end
|
403
|
+
|
404
|
+
alias_method :update, :merge!
|
405
|
+
|
406
|
+
##########################
|
407
|
+
# merge_without_hooks! #
|
408
|
+
# update_without_hooks #
|
409
|
+
##########################
|
410
|
+
|
411
|
+
# Alias to :merge! that bypasses hooks.
|
412
|
+
# @return [Object] Self.
|
413
|
+
def merge_without_hooks!
|
414
|
+
|
415
|
+
@without_hooks = true
|
416
|
+
|
417
|
+
merge!( other_hash )
|
418
|
+
|
419
|
+
@without_hooks = false
|
420
|
+
|
421
|
+
return self
|
422
|
+
|
423
|
+
end
|
424
|
+
|
425
|
+
alias_method :update_without_hooks, :merge_without_hooks!
|
426
|
+
|
427
|
+
#############
|
428
|
+
# replace #
|
429
|
+
#############
|
430
|
+
|
431
|
+
def replace( other_hash )
|
432
|
+
|
433
|
+
# clear current values
|
434
|
+
clear
|
435
|
+
|
436
|
+
# merge replacement settings
|
437
|
+
merge!( other_hash )
|
438
|
+
|
439
|
+
return self
|
440
|
+
|
441
|
+
end
|
442
|
+
|
443
|
+
###########################
|
444
|
+
# replace_without_hooks #
|
445
|
+
###########################
|
446
|
+
|
447
|
+
# Alias to :replace that bypasses hooks.
|
448
|
+
# @param [Array] other_array Other array to replace self with.
|
449
|
+
# @return [Object] Self.
|
450
|
+
def replace_without_hooks( other_hash )
|
451
|
+
|
452
|
+
@without_hooks = true
|
453
|
+
|
454
|
+
replace( other_hash )
|
455
|
+
|
456
|
+
@without_hooks = false
|
457
|
+
|
458
|
+
end
|
459
|
+
|
460
|
+
###########
|
461
|
+
# shift #
|
462
|
+
###########
|
463
|
+
|
464
|
+
def shift
|
465
|
+
|
466
|
+
object = nil
|
467
|
+
|
468
|
+
unless empty?
|
469
|
+
last_key = first[ 0 ]
|
470
|
+
object = delete( last_key )
|
471
|
+
end
|
472
|
+
|
473
|
+
return [ last_key, object ]
|
474
|
+
|
475
|
+
end
|
476
|
+
|
477
|
+
#########################
|
478
|
+
# shift_without_hooks #
|
479
|
+
#########################
|
480
|
+
|
481
|
+
# Alias to :shift that bypasses hooks.
|
482
|
+
# @return [Object] Self.
|
483
|
+
def shift_without_hooks
|
484
|
+
|
485
|
+
@without_hooks = true
|
486
|
+
|
487
|
+
object = shift
|
488
|
+
|
489
|
+
@without_hooks = false
|
490
|
+
|
491
|
+
return object
|
492
|
+
|
493
|
+
end
|
494
|
+
|
495
|
+
###########
|
496
|
+
# clear #
|
497
|
+
###########
|
498
|
+
|
499
|
+
def clear
|
500
|
+
|
501
|
+
keys.each do |this_key|
|
502
|
+
delete( this_key )
|
503
|
+
end
|
504
|
+
|
505
|
+
return self
|
506
|
+
|
507
|
+
end
|
508
|
+
|
509
|
+
#########################
|
510
|
+
# clear_without_hooks #
|
511
|
+
#########################
|
512
|
+
|
513
|
+
# Alias to :clear that bypasses hooks.
|
514
|
+
# @return [Object] Self.
|
515
|
+
def clear_without_hooks
|
516
|
+
|
517
|
+
@without_hooks = true
|
518
|
+
|
519
|
+
clear
|
520
|
+
|
521
|
+
@without_hooks = false
|
522
|
+
|
523
|
+
return self
|
524
|
+
|
525
|
+
end
|
526
|
+
|
527
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
basepath = 'hooked'
|
3
|
+
|
4
|
+
files = [
|
5
|
+
|
6
|
+
'hash_interface'
|
7
|
+
|
8
|
+
]
|
9
|
+
|
10
|
+
second_basepath = '../hooked_hash'
|
11
|
+
|
12
|
+
second_files = [
|
13
|
+
|
14
|
+
'hash_interface'
|
15
|
+
|
16
|
+
]
|
17
|
+
|
18
|
+
files.each do |this_file|
|
19
|
+
require_relative( File.join( basepath, this_file ) + '.rb' )
|
20
|
+
end
|
21
|
+
second_files.each do |this_file|
|
22
|
+
require_relative( File.join( second_basepath, this_file ) + '.rb' )
|
23
|
+
end
|
24
|
+
|
25
|
+
require_relative( basepath + '.rb' )
|
26
|
+
require_relative( second_basepath + '.rb' )
|
data/lib/hooked/hash.rb
ADDED
data/lib/hooked_hash.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module ::HookedHash::Interface
|
3
|
+
|
4
|
+
instances_identify_as!( ::HookedHash )
|
5
|
+
|
6
|
+
################
|
7
|
+
# initialize #
|
8
|
+
################
|
9
|
+
|
10
|
+
# Initialize with reference a configuration instance.
|
11
|
+
# @param [Object] object Object that HookedHash instance is attached to, primarily useful for
|
12
|
+
# reference from hooks.
|
13
|
+
# @param [Hash<Object>] args Parameters passed through super to Hash#initialize.
|
14
|
+
# @return [true,false] Whether receiver identifies as object.
|
15
|
+
def initialize( configuration_instance = nil, *args )
|
16
|
+
|
17
|
+
@configuration_instance = configuration_instance
|
18
|
+
|
19
|
+
super( *args )
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
############################
|
24
|
+
# configuration_instance #
|
25
|
+
############################
|
26
|
+
|
27
|
+
attr_accessor :configuration_instance
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,368 @@
|
|
1
|
+
|
2
|
+
require_relative '../../lib/hash-hooked.rb'
|
3
|
+
|
4
|
+
describe ::Hash::Hooked do
|
5
|
+
|
6
|
+
################
|
7
|
+
# initialize #
|
8
|
+
################
|
9
|
+
|
10
|
+
it 'can add initialize with an ancestor, inheriting its values and linking to it as a child' do
|
11
|
+
|
12
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
13
|
+
|
14
|
+
cascading_composite_hash.instance_variable_get( :@parent_composite_object ).should == nil
|
15
|
+
cascading_composite_hash.should == {}
|
16
|
+
cascading_composite_hash[ :A ] = 1
|
17
|
+
cascading_composite_hash[ :B ] = 2
|
18
|
+
cascading_composite_hash[ :C ] = 3
|
19
|
+
cascading_composite_hash[ :D ] = 4
|
20
|
+
cascading_composite_hash.should == { :A => 1,
|
21
|
+
:B => 2,
|
22
|
+
:C => 3,
|
23
|
+
:D => 4 }
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
#########
|
28
|
+
# []= #
|
29
|
+
#########
|
30
|
+
|
31
|
+
it 'can add elements' do
|
32
|
+
|
33
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
34
|
+
|
35
|
+
cascading_composite_hash[ :some_setting ] = :some_value
|
36
|
+
cascading_composite_hash.should == { :some_setting => :some_value }
|
37
|
+
|
38
|
+
cascading_composite_hash[ :other_setting ] = :some_value
|
39
|
+
cascading_composite_hash.should == { :some_setting => :some_value,
|
40
|
+
:other_setting => :some_value }
|
41
|
+
|
42
|
+
cascading_composite_hash.method( :[]= ).should == cascading_composite_hash.method( :store )
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
############
|
47
|
+
# delete #
|
48
|
+
############
|
49
|
+
|
50
|
+
it 'can delete elements' do
|
51
|
+
|
52
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
53
|
+
|
54
|
+
cascading_composite_hash.store( :some_setting, :some_value )
|
55
|
+
cascading_composite_hash.should == { :some_setting => :some_value }
|
56
|
+
|
57
|
+
cascading_composite_hash.store( :other_setting, :some_value )
|
58
|
+
cascading_composite_hash.should == { :some_setting => :some_value,
|
59
|
+
:other_setting => :some_value }
|
60
|
+
|
61
|
+
cascading_composite_hash.delete( :some_setting )
|
62
|
+
cascading_composite_hash.should == { :other_setting => :some_value }
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
###############
|
67
|
+
# delete_if #
|
68
|
+
###############
|
69
|
+
|
70
|
+
it 'can delete elements with a block' do
|
71
|
+
|
72
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
73
|
+
|
74
|
+
cascading_composite_hash.store( :some_setting, :some_value )
|
75
|
+
cascading_composite_hash.should == { :some_setting => :some_value }
|
76
|
+
|
77
|
+
cascading_composite_hash.store( :other_setting, :some_value )
|
78
|
+
cascading_composite_hash.should == { :some_setting => :some_value,
|
79
|
+
:other_setting => :some_value }
|
80
|
+
cascading_composite_hash.delete_if do |key, value|
|
81
|
+
key == :some_setting
|
82
|
+
end
|
83
|
+
cascading_composite_hash.should == { :other_setting => :some_value }
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
#############
|
88
|
+
# reject! #
|
89
|
+
#############
|
90
|
+
|
91
|
+
it 'can delete elements with a block' do
|
92
|
+
|
93
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
94
|
+
|
95
|
+
cascading_composite_hash.store( :some_setting, :some_value )
|
96
|
+
cascading_composite_hash.should == { :some_setting => :some_value }
|
97
|
+
|
98
|
+
cascading_composite_hash.store( :other_setting, :some_value )
|
99
|
+
cascading_composite_hash.should == { :some_setting => :some_value,
|
100
|
+
:other_setting => :some_value }
|
101
|
+
cascading_composite_hash.reject! do |key, value|
|
102
|
+
key == :some_setting
|
103
|
+
end
|
104
|
+
cascading_composite_hash.should == { :other_setting => :some_value }
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
#############
|
109
|
+
# keep_if #
|
110
|
+
#############
|
111
|
+
|
112
|
+
it 'can keep elements with a block' do
|
113
|
+
|
114
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
115
|
+
|
116
|
+
cascading_composite_hash.store( :some_setting, :some_value )
|
117
|
+
cascading_composite_hash.should == { :some_setting => :some_value }
|
118
|
+
|
119
|
+
cascading_composite_hash.store( :other_setting, :some_value )
|
120
|
+
cascading_composite_hash.should == { :some_setting => :some_value,
|
121
|
+
:other_setting => :some_value }
|
122
|
+
cascading_composite_hash.keep_if do |key, value|
|
123
|
+
key != :some_setting
|
124
|
+
end
|
125
|
+
cascading_composite_hash.should == { :other_setting => :some_value }
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
#############
|
130
|
+
# select! #
|
131
|
+
#############
|
132
|
+
|
133
|
+
it 'can keep elements with a block' do
|
134
|
+
|
135
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
136
|
+
|
137
|
+
cascading_composite_hash.store( :some_setting, :some_value )
|
138
|
+
cascading_composite_hash.should == { :some_setting => :some_value }
|
139
|
+
|
140
|
+
cascading_composite_hash.store( :other_setting, :some_value )
|
141
|
+
cascading_composite_hash.should == { :some_setting => :some_value,
|
142
|
+
:other_setting => :some_value }
|
143
|
+
cascading_composite_hash.select! do |key, value|
|
144
|
+
key != :some_setting
|
145
|
+
end
|
146
|
+
cascading_composite_hash.should == { :other_setting => :some_value }
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
############
|
151
|
+
# merge! #
|
152
|
+
# update #
|
153
|
+
############
|
154
|
+
|
155
|
+
it 'can merge from another hash' do
|
156
|
+
|
157
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
158
|
+
|
159
|
+
cascading_composite_hash.merge!( :some_setting => :some_value )
|
160
|
+
cascading_composite_hash.should == { :some_setting => :some_value }
|
161
|
+
cascading_composite_hash.merge!( :other_setting => :some_value )
|
162
|
+
cascading_composite_hash.should == { :some_setting => :some_value,
|
163
|
+
:other_setting => :some_value }
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
#############
|
168
|
+
# replace #
|
169
|
+
#############
|
170
|
+
|
171
|
+
it 'can replace existing elements with others' do
|
172
|
+
|
173
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
174
|
+
|
175
|
+
cascading_composite_hash.replace( :some_setting => :some_value )
|
176
|
+
cascading_composite_hash.should == { :some_setting => :some_value }
|
177
|
+
cascading_composite_hash.replace( :other_setting => :some_value )
|
178
|
+
cascading_composite_hash.should == { :other_setting => :some_value }
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
###########
|
183
|
+
# shift #
|
184
|
+
###########
|
185
|
+
|
186
|
+
it 'can shift the first element' do
|
187
|
+
|
188
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
189
|
+
|
190
|
+
cascading_composite_hash.store( :some_setting, :some_value )
|
191
|
+
cascading_composite_hash.should == { :some_setting => :some_value }
|
192
|
+
cascading_composite_hash.store( :other_setting, :some_value )
|
193
|
+
cascading_composite_hash.should == { :some_setting => :some_value,
|
194
|
+
:other_setting => :some_value }
|
195
|
+
cascading_composite_hash.shift
|
196
|
+
cascading_composite_hash.should == { :other_setting => :some_value }
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
###########
|
201
|
+
# clear #
|
202
|
+
###########
|
203
|
+
|
204
|
+
it 'can clear, causing present elements to be excluded' do
|
205
|
+
|
206
|
+
cascading_composite_hash = ::Hash::Hooked.new
|
207
|
+
|
208
|
+
cascading_composite_hash.store( :some_setting, :some_value )
|
209
|
+
cascading_composite_hash.should == { :some_setting => :some_value }
|
210
|
+
cascading_composite_hash.store( :other_setting, :some_value )
|
211
|
+
cascading_composite_hash.should == { :some_setting => :some_value,
|
212
|
+
:other_setting => :some_value }
|
213
|
+
cascading_composite_hash.clear
|
214
|
+
cascading_composite_hash.should == { }
|
215
|
+
cascading_composite_hash.store( :other_setting, :some_value )
|
216
|
+
cascading_composite_hash.should == { :other_setting => :some_value }
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
##################
|
221
|
+
# pre_set_hook #
|
222
|
+
##################
|
223
|
+
|
224
|
+
it 'has a hook that is called before setting a value; return value is used in place of object' do
|
225
|
+
|
226
|
+
class ::Hash::Hooked::SubMockPreSet < ::Hash::Hooked
|
227
|
+
|
228
|
+
def pre_set_hook( key, object, is_insert = false )
|
229
|
+
return :some_other_value
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
cascading_composite_hash = ::Hash::Hooked::SubMockPreSet.new
|
235
|
+
|
236
|
+
cascading_composite_hash[ :some_key ] = :some_value
|
237
|
+
|
238
|
+
cascading_composite_hash.should == { :some_key => :some_other_value }
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
###################
|
243
|
+
# post_set_hook #
|
244
|
+
###################
|
245
|
+
|
246
|
+
it 'has a hook that is called after setting a value' do
|
247
|
+
|
248
|
+
class ::Hash::Hooked::SubMockPostSet < ::Hash::Hooked
|
249
|
+
|
250
|
+
def post_set_hook( key, object, is_insert = false )
|
251
|
+
unless key == :some_other_key
|
252
|
+
self[ :some_other_key ] = :some_other_value
|
253
|
+
end
|
254
|
+
return object
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
|
259
|
+
cascading_composite_hash = ::Hash::Hooked::SubMockPostSet.new
|
260
|
+
|
261
|
+
cascading_composite_hash[ :some_key ] = :some_value
|
262
|
+
|
263
|
+
cascading_composite_hash.should == { :some_key => :some_value,
|
264
|
+
:some_other_key => :some_other_value }
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
##################
|
269
|
+
# pre_get_hook #
|
270
|
+
##################
|
271
|
+
|
272
|
+
it 'has a hook that is called before getting a value; if return value is false, get does not occur' do
|
273
|
+
|
274
|
+
class ::Hash::Hooked::SubMockPreGet < ::Hash::Hooked
|
275
|
+
|
276
|
+
def pre_get_hook( key )
|
277
|
+
return false
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
|
282
|
+
cascading_composite_hash = ::Hash::Hooked::SubMockPreGet.new
|
283
|
+
|
284
|
+
cascading_composite_hash[ :some_key ] = :some_value
|
285
|
+
cascading_composite_hash[ :some_key ].should == nil
|
286
|
+
|
287
|
+
cascading_composite_hash.should == { :some_key => :some_value }
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
###################
|
292
|
+
# post_get_hook #
|
293
|
+
###################
|
294
|
+
|
295
|
+
it 'has a hook that is called after getting a value' do
|
296
|
+
|
297
|
+
class ::Hash::Hooked::SubMockPostGet < ::Hash::Hooked
|
298
|
+
|
299
|
+
def post_get_hook( key, object )
|
300
|
+
self[ :some_other_key ] = :some_other_value
|
301
|
+
return object
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
305
|
+
|
306
|
+
cascading_composite_hash = ::Hash::Hooked::SubMockPostGet.new
|
307
|
+
|
308
|
+
cascading_composite_hash[ :some_key ] = :some_value
|
309
|
+
|
310
|
+
cascading_composite_hash.should == { :some_key => :some_value }
|
311
|
+
|
312
|
+
cascading_composite_hash[ :some_key ].should == :some_value
|
313
|
+
|
314
|
+
cascading_composite_hash.should == { :some_key => :some_value,
|
315
|
+
:some_other_key => :some_other_value }
|
316
|
+
|
317
|
+
end
|
318
|
+
|
319
|
+
#####################
|
320
|
+
# pre_delete_hook #
|
321
|
+
#####################
|
322
|
+
|
323
|
+
it 'has a hook that is called before deleting an key; if return value is false, delete does not occur' do
|
324
|
+
|
325
|
+
class ::Hash::Hooked::SubMockPreDelete < ::Hash::Hooked
|
326
|
+
|
327
|
+
def pre_delete_hook( key )
|
328
|
+
return false
|
329
|
+
end
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
cascading_composite_hash = ::Hash::Hooked::SubMockPreDelete.new
|
334
|
+
|
335
|
+
cascading_composite_hash[ :some_key ] = :some_value
|
336
|
+
cascading_composite_hash.delete( :some_key )
|
337
|
+
|
338
|
+
cascading_composite_hash.should == { :some_key => :some_value }
|
339
|
+
|
340
|
+
end
|
341
|
+
|
342
|
+
######################
|
343
|
+
# post_delete_hook #
|
344
|
+
######################
|
345
|
+
|
346
|
+
it 'has a hook that is called after deleting an key' do
|
347
|
+
|
348
|
+
class ::Hash::Hooked::SubMockPostDelete < ::Hash::Hooked
|
349
|
+
|
350
|
+
def post_delete_hook( key, object )
|
351
|
+
unless key == :some_other_key
|
352
|
+
delete( :some_other_key )
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
cascading_composite_hash = ::Hash::Hooked::SubMockPostDelete.new
|
359
|
+
|
360
|
+
cascading_composite_hash[ :some_key ] = :some_value
|
361
|
+
cascading_composite_hash[ :some_other_key ] = :some_other_value
|
362
|
+
cascading_composite_hash.delete( :some_key )
|
363
|
+
|
364
|
+
cascading_composite_hash.should == { }
|
365
|
+
|
366
|
+
end
|
367
|
+
|
368
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hash-hooked
|
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-06-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: identifies_as
|
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: A subclass of Hash that offers event hooks for pre-set/pre-delete, set/delete.
|
31
|
+
::HookedHash offers implicit reference to a configuration instance.
|
32
|
+
email: asher@ridiculouspower.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- lib/hash/hooked/hash_interface.rb
|
38
|
+
- lib/hash/hooked.rb
|
39
|
+
- lib/hash/namespaces.rb
|
40
|
+
- lib/hash/requires.rb
|
41
|
+
- lib/hash-hooked.rb
|
42
|
+
- lib/hooked/hash.rb
|
43
|
+
- lib/hooked_hash/hash_interface.rb
|
44
|
+
- lib/hooked_hash.rb
|
45
|
+
- spec/hash/hooked_spec.rb
|
46
|
+
- README.md
|
47
|
+
- CHANGELOG.rdoc
|
48
|
+
homepage: http://rubygems.org/gems/hash-hooked
|
49
|
+
licenses: []
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project: hash-hooked
|
68
|
+
rubygems_version: 1.8.23
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Provides ::Hash::Hooked and ::HookedHash.
|
72
|
+
test_files: []
|
73
|
+
has_rdoc:
|