gorillib 0.1.4 → 0.1.5
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/.gitignore +4 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/gorillib.gemspec +9 -8
- data/lib/gorillib/hashlike/deep_hash.rb +156 -0
- data/lib/gorillib/hashlike/deep_merge.rb +4 -3
- data/lib/gorillib/receiver.rb +21 -6
- data/spec/hashlike/deep_hash_spec.rb +579 -0
- metadata +23 -102
- data/Gemfile.lock +0 -36
data/.gitignore
CHANGED
data/Rakefile
CHANGED
@@ -32,7 +32,7 @@ Jeweler::Tasks.new do |gem|
|
|
32
32
|
gem.files = dotfiles + Dir["**/*"].
|
33
33
|
reject{|f| f =~ %r{^(vendor|coverage)/} }.
|
34
34
|
reject{|f| File.directory?(f) }.
|
35
|
-
reject{|f| ignores.any?{|i| File.fnmatch(i, f) || File.fnmatch(i+'/**/*', f) } }
|
35
|
+
reject{|f| ignores.any?{|i| File.fnmatch(i, f) || File.fnmatch(i+'/**/*', f) || File.fnmatch(i+'/*', f) } }
|
36
36
|
gem.test_files = gem.files.grep(/^spec\//)
|
37
37
|
gem.require_paths = ['lib']
|
38
38
|
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.5
|
data/gorillib.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{gorillib}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date = %q{2011-
|
11
|
+
s.authors = [%q{Infochimps}]
|
12
|
+
s.date = %q{2011-08-02}
|
13
13
|
s.description = %q{Gorillib: infochimps lightweight subset of ruby convenience methods}
|
14
14
|
s.email = %q{coders@infochimps.org}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -21,7 +21,6 @@ Gem::Specification.new do |s|
|
|
21
21
|
".rspec",
|
22
22
|
"CHANGELOG.textile",
|
23
23
|
"Gemfile",
|
24
|
-
"Gemfile.lock",
|
25
24
|
"LICENSE.textile",
|
26
25
|
"README.textile",
|
27
26
|
"Rakefile",
|
@@ -49,6 +48,7 @@ Gem::Specification.new do |s|
|
|
49
48
|
"lib/gorillib/hashlike/compact.rb",
|
50
49
|
"lib/gorillib/hashlike/deep_compact.rb",
|
51
50
|
"lib/gorillib/hashlike/deep_dup.rb",
|
51
|
+
"lib/gorillib/hashlike/deep_hash.rb",
|
52
52
|
"lib/gorillib/hashlike/deep_merge.rb",
|
53
53
|
"lib/gorillib/hashlike/hashlike_via_accessors.rb",
|
54
54
|
"lib/gorillib/hashlike/keys.rb",
|
@@ -98,6 +98,7 @@ Gem::Specification.new do |s|
|
|
98
98
|
"spec/hash/slice_spec.rb",
|
99
99
|
"spec/hash/zip_spec.rb",
|
100
100
|
"spec/hashlike/behave_same_as_hash_spec.rb",
|
101
|
+
"spec/hashlike/deep_hash_spec.rb",
|
101
102
|
"spec/hashlike/hashlike_behavior_spec.rb",
|
102
103
|
"spec/hashlike/hashlike_via_accessors_spec.rb",
|
103
104
|
"spec/hashlike_spec.rb",
|
@@ -133,9 +134,9 @@ Gem::Specification.new do |s|
|
|
133
134
|
"spec/support/matchers/evaluate_to_true.rb"
|
134
135
|
]
|
135
136
|
s.homepage = %q{http://infochimps.com/labs}
|
136
|
-
s.licenses = [
|
137
|
-
s.require_paths = [
|
138
|
-
s.rubygems_version = %q{1.
|
137
|
+
s.licenses = [%q{MIT}]
|
138
|
+
s.require_paths = [%q{lib}]
|
139
|
+
s.rubygems_version = %q{1.8.6}
|
139
140
|
s.summary = %q{include only what you need. No dependencies, no creep}
|
140
141
|
s.test_files = [
|
141
142
|
"spec/array/compact_blank_spec.rb",
|
@@ -152,6 +153,7 @@ Gem::Specification.new do |s|
|
|
152
153
|
"spec/hash/slice_spec.rb",
|
153
154
|
"spec/hash/zip_spec.rb",
|
154
155
|
"spec/hashlike/behave_same_as_hash_spec.rb",
|
156
|
+
"spec/hashlike/deep_hash_spec.rb",
|
155
157
|
"spec/hashlike/hashlike_behavior_spec.rb",
|
156
158
|
"spec/hashlike/hashlike_via_accessors_spec.rb",
|
157
159
|
"spec/hashlike_spec.rb",
|
@@ -188,7 +190,6 @@ Gem::Specification.new do |s|
|
|
188
190
|
]
|
189
191
|
|
190
192
|
if s.respond_to? :specification_version then
|
191
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
192
193
|
s.specification_version = 3
|
193
194
|
|
194
195
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'gorillib/hashlike/deep_merge'
|
2
|
+
require 'gorillib/hashlike/deep_compact'
|
3
|
+
require 'gorillib/hashlike/deep_dup'
|
4
|
+
|
5
|
+
module Gorillib
|
6
|
+
module Hashlike
|
7
|
+
module DeepHash
|
8
|
+
|
9
|
+
include Gorillib::Hashlike::DeepMerge
|
10
|
+
include Gorillib::Hashlike::DeepCompact
|
11
|
+
include Gorillib::Hashlike::DeepDup
|
12
|
+
|
13
|
+
# @param constructor<Object>
|
14
|
+
# The default value for the DeepHash. Defaults to an empty hash.
|
15
|
+
# If constructor is a Hash, adopt its values.
|
16
|
+
def initialize(constructor = {})
|
17
|
+
if constructor.is_a?(Hash)
|
18
|
+
super()
|
19
|
+
deep_merge!(constructor) unless constructor.empty?
|
20
|
+
else
|
21
|
+
super(constructor)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.included(base)
|
26
|
+
base.class_eval do
|
27
|
+
unless method_defined?(:regular_writer) then alias_method :regular_writer, :[]= ; end
|
28
|
+
unless method_defined?(:regular_update) then alias_method :regular_update, :update ; end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sets a member value.
|
33
|
+
#
|
34
|
+
# Given a deep key (one that contains '.'), uses it as a chain of hash
|
35
|
+
# memberships. Otherwise calls the normal hash member setter
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# foo = DeepHash.new :hi => 'there'
|
39
|
+
# foo['howdy.doody'] = 3
|
40
|
+
# foo # => { :hi => 'there', :howdy => { :doody => 3 } }
|
41
|
+
#
|
42
|
+
def []= attr, val
|
43
|
+
attr = convert_key(attr)
|
44
|
+
val = convert_value(val)
|
45
|
+
attr.is_a?(Array) ? deep_set(*(attr | [val])) : super(attr, val)
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# Gets a member value.
|
50
|
+
#
|
51
|
+
# Given a deep key (one that contains '.'), uses it as a chain of hash
|
52
|
+
# memberships. Otherwise calls the normal hash member getter
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# foo = DeepHash.new({ :hi => 'there', :howdy => { :doody => 3 } })
|
56
|
+
# foo['howdy.doody'] # => 3
|
57
|
+
# foo['hi'] # => 'there'
|
58
|
+
# foo[:hi] # => 'there'
|
59
|
+
#
|
60
|
+
def [] attr
|
61
|
+
attr = convert_key(attr)
|
62
|
+
attr.is_a?(Array) ? deep_get(*attr) : super(attr)
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Treat hash as tree of hashes:
|
67
|
+
#
|
68
|
+
# x = { :a => :val, :subhash => { :b => :val_b } }
|
69
|
+
# x.deep_set(:subhash, :cat, :hat)
|
70
|
+
# # => { :a => :val, :subhash => { :b => :val_b, :cat => :hat } }
|
71
|
+
# x.deep_set(:subhash, :b, :newval)
|
72
|
+
# # => { :a => :val, :subhash => { :b => :newval, :cat => :hat } }
|
73
|
+
#
|
74
|
+
#
|
75
|
+
def deep_set *args
|
76
|
+
val = args.pop
|
77
|
+
last_key = args.pop
|
78
|
+
# dig down to last subtree (building out if necessary)
|
79
|
+
hsh = self
|
80
|
+
args.each do |key|
|
81
|
+
hsh.regular_writer(key, self.class.new) unless hsh.has_key?(key)
|
82
|
+
hsh = hsh[key]
|
83
|
+
end
|
84
|
+
# set leaf value
|
85
|
+
hsh[last_key] = val
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Treat hash as tree of hashes:
|
90
|
+
#
|
91
|
+
# x = { :a => :val_a, :subhash => { :b => :val_b } }
|
92
|
+
# x.deep_get(:a)
|
93
|
+
# # => :val_a
|
94
|
+
# x.deep_get(:subhash, :c)
|
95
|
+
# # => nil
|
96
|
+
# x.deep_get(:subhash, :c, :f)
|
97
|
+
# # => nil
|
98
|
+
# x.deep_get(:subhash, :b)
|
99
|
+
# # => nil
|
100
|
+
#
|
101
|
+
def deep_get *args
|
102
|
+
last_key = args.pop
|
103
|
+
# dig down to last subtree (building out if necessary)
|
104
|
+
hsh = args.inject(self){|h, k| h[k] || self.class.new }
|
105
|
+
# get leaf value
|
106
|
+
hsh[last_key]
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Treat hash as tree of hashes:
|
111
|
+
#
|
112
|
+
# x = { :a => :val, :subhash => { :a => :val1, :b => :val2 } }
|
113
|
+
# x.deep_delete(:subhash, :a)
|
114
|
+
# #=> :val
|
115
|
+
# x
|
116
|
+
# #=> { :a => :val, :subhash => { :b => :val2 } }
|
117
|
+
#
|
118
|
+
def deep_delete *args
|
119
|
+
last_key = args.pop # key to delete
|
120
|
+
last_hsh = args.empty? ? self : (deep_get(*args)||{}) # hsh containing that key
|
121
|
+
last_hsh.delete(last_key)
|
122
|
+
end
|
123
|
+
|
124
|
+
protected
|
125
|
+
# @attr key<Object> The key to convert.
|
126
|
+
#
|
127
|
+
# @attr [Object]
|
128
|
+
# The converted key. A dotted attr ('moon.cheese.type') becomes
|
129
|
+
# an array of sequential keys for deep_set and deep_get
|
130
|
+
#
|
131
|
+
# @private
|
132
|
+
def convert_key(attr)
|
133
|
+
case
|
134
|
+
when attr.to_s.include?('.') then attr.to_s.split(".").map{|sub_attr| sub_attr.to_sym }
|
135
|
+
when attr.is_a?(Array) then attr.map{|sub_attr| sub_attr.to_sym }
|
136
|
+
else attr.to_sym
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# @param value<Object> The value to convert.
|
141
|
+
#
|
142
|
+
# @return [Object]
|
143
|
+
# The converted value. A Hash or an Array of hashes, will be converted to
|
144
|
+
# their DeepHash equivalents.
|
145
|
+
#
|
146
|
+
# @private
|
147
|
+
def convert_value(value)
|
148
|
+
if value.class == Hash then self.class.new(value)
|
149
|
+
elsif value.is_a?(Array) then value.collect{|e| convert_value(e) }
|
150
|
+
else value
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
@@ -9,9 +9,10 @@ module Gorillib
|
|
9
9
|
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
10
10
|
# Modifies the receiver in place.
|
11
11
|
def deep_merge!(other_hash)
|
12
|
-
other_hash.each_pair do |
|
13
|
-
|
14
|
-
self[
|
12
|
+
other_hash.each_pair do |ok, ov|
|
13
|
+
ov = convert_value(ov) if respond_to?(:convert_value)
|
14
|
+
sv = self[ok]
|
15
|
+
self[ok] = sv.is_a?(Hash) && ov.is_a?(Hash) ? sv.deep_merge(ov) : ov
|
15
16
|
end
|
16
17
|
self
|
17
18
|
end unless method_defined?(:deep_merge!)
|
data/lib/gorillib/receiver.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'gorillib/object/blank'
|
2
2
|
require 'gorillib/object/try'
|
3
3
|
require 'gorillib/array/extract_options'
|
4
|
+
require 'gorillib/metaprogramming/class_attribute'
|
4
5
|
|
5
6
|
# dummy type for receiving True or False
|
6
7
|
class Boolean ; end unless defined?(Boolean)
|
@@ -126,7 +127,7 @@ module Receiver
|
|
126
127
|
#
|
127
128
|
def receive! hsh={}
|
128
129
|
raise ArgumentError, "Can't receive (it isn't hashlike): {#{hsh.inspect}}" unless hsh.respond_to?(:[]) && hsh.respond_to?(:has_key?)
|
129
|
-
|
130
|
+
_receiver_fields.each do |attr|
|
130
131
|
if hsh.has_key?(attr.to_sym) then val = hsh[attr.to_sym]
|
131
132
|
elsif hsh.has_key?(attr.to_s) then val = hsh[attr.to_s]
|
132
133
|
else next ; end
|
@@ -153,8 +154,20 @@ protected
|
|
153
154
|
self.send("receive_#{attr}", val)
|
154
155
|
end
|
155
156
|
|
157
|
+
def _receiver_fields
|
158
|
+
self.class.receiver_attr_names
|
159
|
+
end
|
160
|
+
|
161
|
+
def _receiver_defaults
|
162
|
+
self.class.receiver_defaults
|
163
|
+
end
|
164
|
+
|
165
|
+
def _after_receivers
|
166
|
+
self.class.after_receivers
|
167
|
+
end
|
168
|
+
|
156
169
|
def impose_defaults!(hsh)
|
157
|
-
|
170
|
+
_receiver_defaults.each do |attr, val|
|
158
171
|
next if attr_set?(attr)
|
159
172
|
self.instance_variable_set "@#{attr}", val
|
160
173
|
end
|
@@ -180,7 +193,7 @@ protected
|
|
180
193
|
|
181
194
|
|
182
195
|
def run_after_receivers(hsh)
|
183
|
-
|
196
|
+
_after_receivers.each do |after_receiver|
|
184
197
|
self.instance_exec(hsh, &after_receiver)
|
185
198
|
end
|
186
199
|
end
|
@@ -192,13 +205,14 @@ public
|
|
192
205
|
#
|
193
206
|
# Returns a new instance with the given hash used to set all rcvrs.
|
194
207
|
#
|
195
|
-
# All args
|
208
|
+
# All args up to the last one are passed to the initializer.
|
209
|
+
# The last arg must be a hash -- its attributes are set on the newly-created object
|
196
210
|
#
|
197
|
-
# @param hsh [Hash] attr-value pairs to set on the newly created object
|
211
|
+
# @param hsh [Hash] attr-value pairs to set on the newly created object.
|
198
212
|
# @param *args [Array] arguments to pass to the constructor
|
199
213
|
# @return [Object] a new instance
|
200
214
|
def receive *args
|
201
|
-
hsh = args.
|
215
|
+
hsh = args.pop
|
202
216
|
raise ArgumentError, "Can't receive (it isn't hashlike): {#{hsh.inspect}} -- the hsh should be the *last* arg" unless hsh.respond_to?(:[]) && hsh.respond_to?(:has_key?)
|
203
217
|
obj = self.new(*args)
|
204
218
|
obj.receive!(hsh)
|
@@ -384,4 +398,5 @@ public
|
|
384
398
|
end
|
385
399
|
end
|
386
400
|
end
|
401
|
+
|
387
402
|
end
|
@@ -0,0 +1,579 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'gorillib/receiver_model'
|
3
|
+
require 'gorillib/hashlike/deep_hash'
|
4
|
+
|
5
|
+
|
6
|
+
class AwesomeHash < Hash
|
7
|
+
# include Gorillib::ReceiverModel
|
8
|
+
include Gorillib::Hashlike::DeepHash
|
9
|
+
end
|
10
|
+
class AwesomeHashSubclass < AwesomeHash ; end
|
11
|
+
|
12
|
+
describe Gorillib::Hashlike::DeepHash do
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
@deep_hash = AwesomeHash.new({ :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b' })
|
16
|
+
@hash = { "str_key" => "strk_val", :sym_key => "symk_val"}
|
17
|
+
@sub = AwesomeHashSubclass.new("str_key" => "strk_val", :sym_key => "symk_val")
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "examples" do
|
21
|
+
it 'merges with a nested dotted nested hash' do
|
22
|
+
y = {"a.b.c"=>{"d"=>3, "f.g"=>7}, "p"=>9}
|
23
|
+
x = AwesomeHash.new({ :a => { :b => { :c => { :d => 7, :e => 99 } } }, :p => 5, 'z' => 7, :z => 7 })
|
24
|
+
x.deep_merge(y).should == {:a=>{:b=>{:c=>{:d=>3, :e=>99, :f=>{:g=>7}}}}, :p=>9, :z=>7}
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'breaks if the structures are incommensurate, so do not do that' do
|
28
|
+
y = { 'a.b.c' => 7 }
|
29
|
+
x = AwesomeHash.new({ :a => { :b => 69 }})
|
30
|
+
lambda{ x.deep_merge(y) }.should raise_error(TypeError, /Symbol into Integer/)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# describe "#initialize" do
|
35
|
+
# it 'adopts a Hash when given' do
|
36
|
+
# deep_hash = AwesomeHash.new(@hash)
|
37
|
+
# @hash.each{|k,v| deep_hash[k].should == v }
|
38
|
+
# deep_hash.keys.any?{|key| key.is_a?(String) }.should be_false
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# it 'converts all pure Hash values into AwesomeHashes if param is a Hash' do
|
42
|
+
# deep_hash = AwesomeHash.new :sym_key => @hash
|
43
|
+
# deep_hash[:sym_key].should be_an_instance_of(AwesomeHash)
|
44
|
+
# # sanity check
|
45
|
+
# deep_hash[:sym_key][:sym_key].should == "symk_val"
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# it 'does not convert Hash subclass values into AwesomeHashes' do
|
49
|
+
# deep_hash = AwesomeHash.new :sub => @sub
|
50
|
+
# deep_hash[:sub].should be_an_instance_of(AwesomeHashSubclass)
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# it 'converts all value items if value is an Array' do
|
54
|
+
# deep_hash = AwesomeHash.new :arry => { :sym_key => [@hash] }
|
55
|
+
# deep_hash[:arry].should be_an_instance_of(AwesomeHash)
|
56
|
+
# # sanity check
|
57
|
+
# deep_hash[:arry][:sym_key].first[:sym_key].should == "symk_val"
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# it 'delegates to superclass constructor if param is not a Hash' do
|
61
|
+
# deep_hash = AwesomeHash.new("dash berlin")
|
62
|
+
#
|
63
|
+
# deep_hash["unexisting key"].should == "dash berlin"
|
64
|
+
# end
|
65
|
+
# end # describe "#initialize"
|
66
|
+
#
|
67
|
+
# describe "#update" do
|
68
|
+
# it 'converts all keys into symbols when param is a Hash' do
|
69
|
+
# deep_hash = AwesomeHash.new(@hash)
|
70
|
+
# deep_hash.update("starry" => "night")
|
71
|
+
#
|
72
|
+
# deep_hash.keys.any?{|key| key.is_a?(String) }.should be_false
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# it 'converts all Hash values into AwesomeHashes if param is a Hash' do
|
76
|
+
# deep_hash = AwesomeHash.new :hash => @hash
|
77
|
+
# deep_hash.update(:hash => { :sym_key => "is buggy in Ruby 1.8.6" })
|
78
|
+
#
|
79
|
+
# deep_hash[:hash].should be_an_instance_of(AwesomeHash)
|
80
|
+
# end
|
81
|
+
# end # describe "#update"
|
82
|
+
#
|
83
|
+
# describe '#[]=' do
|
84
|
+
# it 'symbolizes keys' do
|
85
|
+
# @deep_hash['leaf_at_top'] = :fedora
|
86
|
+
# @deep_hash['new'] = :unseen
|
87
|
+
# @deep_hash.should == {:nested_1 => {:nested_2 => {:leaf_3 => "val3"}, :leaf_2 => ['arr']}, :leaf_at_top => :fedora, :new => :unseen}
|
88
|
+
# end
|
89
|
+
# it 'deep-sets dotted vals, replacing values' do
|
90
|
+
# @deep_hash['moon.man'] = :cheesy
|
91
|
+
# @deep_hash[:moon][:man].should == :cheesy
|
92
|
+
# end
|
93
|
+
# it 'deep-sets dotted vals, creating new values' do
|
94
|
+
# @deep_hash['moon.cheese.type'] = :tilsit
|
95
|
+
# @deep_hash[:moon][:cheese][:type].should == :tilsit
|
96
|
+
# end
|
97
|
+
# it 'deep-sets dotted vals, auto-vivifying intermediate hashes' do
|
98
|
+
# @deep_hash['this.that.the_other'] = :fuhgeddaboudit
|
99
|
+
# @deep_hash[:this][:that][:the_other].should == :fuhgeddaboudit
|
100
|
+
# end
|
101
|
+
# it 'converts all Hash value into AwesomeHash' do
|
102
|
+
# deep_hash = AwesomeHash.new :hash => @hash
|
103
|
+
# deep_hash[:hash] = { :sym_key => "is buggy in Ruby 1.8.6" }
|
104
|
+
# deep_hash[:hash].should be_an_instance_of(AwesomeHash)
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# it "only accepts #to_sym'bolizable things as keys" do
|
108
|
+
# lambda{ @deep_hash[1] = 'hi' }.should raise_error(NoMethodError, /undefined method `to_sym'/)
|
109
|
+
# lambda{ @deep_hash[{ :a => :b }] = 'hi' }.should raise_error(NoMethodError, /undefined method `to_sym'/)
|
110
|
+
# lambda{ @deep_hash[Object.new] = 'hi' }.should raise_error(NoMethodError, /undefined method `to_sym'/)
|
111
|
+
# lambda{ @deep_hash[:a] = 'hi' }.should_not raise_error
|
112
|
+
# end
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# describe '#[]' do
|
116
|
+
# it 'deep-gets dotted vals' do
|
117
|
+
# hsh = { :hat => :cat, :basket => :lotion, :moon => { :man => :smiling, :cheese => {:type => :tilsit} } }
|
118
|
+
# @deep_hash = Configliere::Param.new hsh.dup
|
119
|
+
# @deep_hash['moon.man'].should == :smiling
|
120
|
+
# @deep_hash['moon.cheese.type'].should == :tilsit
|
121
|
+
# @deep_hash['moon.cheese.smell'].should be_nil
|
122
|
+
# @deep_hash['moon.non.existent.interim.values'].should be_nil
|
123
|
+
# @deep_hash['moon.non'].should be_nil
|
124
|
+
# if (RUBY_VERSION >= '1.9') then lambda{ @deep_hash['hat.cat'] }.should raise_error(TypeError)
|
125
|
+
# else lambda{ @deep_hash['hat.cat'] }.should raise_error(NoMethodError, 'undefined method `[]\' for :cat:Symbol') end
|
126
|
+
# @deep_hash.should == hsh # shouldn't change from reading (specifically, shouldn't autovivify)
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# it "only accepts #to_sym'bolizable things as keys" do
|
130
|
+
# lambda{ @deep_hash[1] }.should raise_error(NoMethodError, /undefined method `to_sym'/)
|
131
|
+
# lambda{ @deep_hash[{ :a => :b }] }.should raise_error(NoMethodError, /undefined method `to_sym'/)
|
132
|
+
# lambda{ @deep_hash[Object.new] }.should raise_error(NoMethodError, /undefined method `to_sym'/)
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# def arrays_should_be_equal arr1, arr2
|
137
|
+
# arr1.sort_by{|s| s.to_s }.should == arr2.sort_by{|s| s.to_s }
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# describe "#to_hash" do
|
141
|
+
# it 'returns instance of Hash' do
|
142
|
+
# AwesomeHash.new(@hash).to_hash.should be_an_instance_of(Hash)
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# it 'preserves keys' do
|
146
|
+
# deep_hash = AwesomeHash.new(@hash)
|
147
|
+
# converted = deep_hash.to_hash
|
148
|
+
# arrays_should_be_equal deep_hash.keys, converted.keys
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# it 'preserves value' do
|
152
|
+
# deep_hash = AwesomeHash.new(@hash)
|
153
|
+
# converted = deep_hash.to_hash
|
154
|
+
# arrays_should_be_equal deep_hash.values, converted.values
|
155
|
+
# end
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
# describe '#compact' do
|
159
|
+
# it 'removes nils but not empties or falsehoods' do
|
160
|
+
# AwesomeHash.new({ :a => nil }).compact.should == {}
|
161
|
+
# AwesomeHash.new({ :a => nil, :b => false, :c => {}, :d => "", :remains => true }).compact.should == { :b => false, :c => {}, :d => "", :remains => true }
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# it 'leaves original alone' do
|
165
|
+
# deep_hash = AwesomeHash.new({ :a => nil, :remains => true })
|
166
|
+
# deep_hash.compact.should == { :remains => true }
|
167
|
+
# deep_hash.should == { :a => nil, :remains => true }
|
168
|
+
# end
|
169
|
+
# end
|
170
|
+
#
|
171
|
+
# describe '#compact!' do
|
172
|
+
# it 'removes nils but not empties or falsehoods' do
|
173
|
+
# AwesomeHash.new({ :a => nil}).compact!.should == {}
|
174
|
+
# AwesomeHash.new({ :a => nil, :b => false, :c => {}, :d => "", :remains => true }).compact!.should == { :b => false, :c => {}, :d => "", :remains => true }
|
175
|
+
# end
|
176
|
+
#
|
177
|
+
# it 'modifies in-place' do
|
178
|
+
# deep_hash = AwesomeHash.new({ :a => nil, :remains => true })
|
179
|
+
# deep_hash.compact!.should == { :remains => true }
|
180
|
+
# deep_hash.should == { :remains => true }
|
181
|
+
# end
|
182
|
+
# end
|
183
|
+
#
|
184
|
+
# describe '#slice' do
|
185
|
+
# before do
|
186
|
+
# @deep_hash = AwesomeHash.new({ :a => 'x', :b => 'y', :c => 10 })
|
187
|
+
# end
|
188
|
+
#
|
189
|
+
# it 'returns a new hash with only the given keys' do
|
190
|
+
# @deep_hash.slice(:a, :b).should == { :a => 'x', :b => 'y' }
|
191
|
+
# @deep_hash.should == { :a => 'x', :b => 'y', :c => 10 }
|
192
|
+
# end
|
193
|
+
#
|
194
|
+
# it 'with bang replaces the hash with only the given keys' do
|
195
|
+
# @deep_hash.slice!(:a, :b).should == { :c => 10 }
|
196
|
+
# @deep_hash.should == { :a => 'x', :b => 'y' }
|
197
|
+
# end
|
198
|
+
#
|
199
|
+
# it 'ignores an array key' do
|
200
|
+
# @deep_hash.slice([:a, :b], :c).should == { :c => 10 }
|
201
|
+
# @deep_hash.should == { :a => 'x', :b => 'y', :c => 10 }
|
202
|
+
# end
|
203
|
+
#
|
204
|
+
# it 'with bang ignores an array key' do
|
205
|
+
# @deep_hash.slice!([:a, :b], :c).should == { :a => 'x', :b => 'y' }
|
206
|
+
# @deep_hash.should == { :c => 10 }
|
207
|
+
# end
|
208
|
+
#
|
209
|
+
# it 'uses splatted keys individually' do
|
210
|
+
# @deep_hash.slice(*[:a, :b]).should == { :a => 'x', :b => 'y' }
|
211
|
+
# @deep_hash.should == { :a => 'x', :b => 'y', :c => 10 }
|
212
|
+
# end
|
213
|
+
#
|
214
|
+
# it 'with bank uses splatted keys individually' do
|
215
|
+
# @deep_hash.slice!(*[:a, :b]).should == { :c => 10 }
|
216
|
+
# @deep_hash.should == { :a => 'x', :b => 'y' }
|
217
|
+
# end
|
218
|
+
# end
|
219
|
+
#
|
220
|
+
# describe '#extract' do
|
221
|
+
# before do
|
222
|
+
# @deep_hash = AwesomeHash.new({ :a => 'x', :b => 'y', :c => 10 })
|
223
|
+
# end
|
224
|
+
#
|
225
|
+
# it 'replaces the hash with only the given keys' do
|
226
|
+
# @deep_hash.extract!(:a, :b).should == { :a => 'x', :b => 'y' }
|
227
|
+
# @deep_hash.should == { :c => 10 }
|
228
|
+
# end
|
229
|
+
#
|
230
|
+
# it 'leaves the hash empty if all keys are gone' do
|
231
|
+
# @deep_hash.extract!(:a, :b, :c).should == { :a => 'x', :b => 'y', :c => 10 }
|
232
|
+
# @deep_hash.should == {}
|
233
|
+
# end
|
234
|
+
#
|
235
|
+
# it 'gets values for all given keys even if missing' do
|
236
|
+
# @deep_hash.extract!(:bob, :c).should == { :bob => nil, :c => 10 }
|
237
|
+
# @deep_hash.should == { :a => 'x', :b => 'y' }
|
238
|
+
# end
|
239
|
+
#
|
240
|
+
# it 'is OK when empty' do
|
241
|
+
# AwesomeHash.new.slice!(:a, :b, :c).should == {}
|
242
|
+
# end
|
243
|
+
#
|
244
|
+
# it 'returns an instance of the same class' do
|
245
|
+
# @deep_hash.slice(:a).should be_a(AwesomeHash)
|
246
|
+
# end
|
247
|
+
# end
|
248
|
+
#
|
249
|
+
# describe "#delete" do
|
250
|
+
# it 'converts Symbol key into String before deleting' do
|
251
|
+
# deep_hash = AwesomeHash.new(@hash)
|
252
|
+
#
|
253
|
+
# deep_hash.delete(:sym_key)
|
254
|
+
# deep_hash.key?("hash").should be_false
|
255
|
+
# end
|
256
|
+
#
|
257
|
+
# it 'works with String keys as well' do
|
258
|
+
# deep_hash = AwesomeHash.new(@hash)
|
259
|
+
#
|
260
|
+
# deep_hash.delete("str_key")
|
261
|
+
# deep_hash.key?("str_key").should be_false
|
262
|
+
# end
|
263
|
+
# end
|
264
|
+
#
|
265
|
+
# describe "#fetch" do
|
266
|
+
# before(:each) do
|
267
|
+
# @deep_hash = AwesomeHash.new(:no => "in between")
|
268
|
+
# end
|
269
|
+
#
|
270
|
+
# it 'converts key before fetching' do
|
271
|
+
# @deep_hash.fetch("no").should == "in between"
|
272
|
+
# end
|
273
|
+
#
|
274
|
+
# it 'returns alternative value if key lookup fails' do
|
275
|
+
# @deep_hash.fetch("flying", "screwdriver").should == "screwdriver"
|
276
|
+
# end
|
277
|
+
# end
|
278
|
+
#
|
279
|
+
# describe "#values_at" do
|
280
|
+
# before(:each) do
|
281
|
+
# @deep_hash = AwesomeHash.new(@hash).merge(:no => "in between")
|
282
|
+
# end
|
283
|
+
#
|
284
|
+
# it 'is indifferent to whether keys are strings or symbols' do
|
285
|
+
# @deep_hash.values_at("sym_key", :str_key, :no).should == ["symk_val", "strk_val", "in between"]
|
286
|
+
# end
|
287
|
+
# end
|
288
|
+
#
|
289
|
+
# it 'responds to #symbolize_keys, #symbolize_keys! and #stringify_keys but not #stringify_keys!' do
|
290
|
+
# AwesomeHash.new.should respond_to(:symbolize_keys )
|
291
|
+
# AwesomeHash.new.should respond_to(:symbolize_keys!)
|
292
|
+
# AwesomeHash.new.should respond_to(:stringify_keys )
|
293
|
+
# AwesomeHash.new.should_not respond_to(:stringify_keys!)
|
294
|
+
# end
|
295
|
+
#
|
296
|
+
# describe '#symbolize_keys' do
|
297
|
+
# it 'returns a dup of itself' do
|
298
|
+
# deep_hash = AwesomeHash.new(@hash)
|
299
|
+
# deep_hash.symbolize_keys.should == deep_hash
|
300
|
+
# end
|
301
|
+
# end
|
302
|
+
#
|
303
|
+
# describe '#symbolize_keys!' do
|
304
|
+
# it 'with bang returns the deep_hash itself' do
|
305
|
+
# deep_hash = AwesomeHash.new(@hash)
|
306
|
+
# deep_hash.symbolize_keys!.object_id.should == deep_hash.object_id
|
307
|
+
# end
|
308
|
+
# end
|
309
|
+
#
|
310
|
+
# describe '#stringify_keys' do
|
311
|
+
# it 'converts keys that are all symbols' do
|
312
|
+
# @deep_hash.stringify_keys.should ==
|
313
|
+
# { 'nested_1' => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, 'leaf_at_top' => 'val1b' }
|
314
|
+
# end
|
315
|
+
#
|
316
|
+
# it 'returns a Hash, not a AwesomeHash' do
|
317
|
+
# @deep_hash.stringify_keys.class.should == Hash
|
318
|
+
# @deep_hash.stringify_keys.should_not be_a(AwesomeHash)
|
319
|
+
# end
|
320
|
+
#
|
321
|
+
# it 'only stringifies and hashifies the top level' do
|
322
|
+
# stringified = @deep_hash.stringify_keys
|
323
|
+
# stringified.should == { 'nested_1' => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, 'leaf_at_top' => 'val1b' }
|
324
|
+
# stringified['nested_1'].should be_a(AwesomeHash)
|
325
|
+
# end
|
326
|
+
# end
|
327
|
+
#
|
328
|
+
# describe '#assert_valid_keys' do
|
329
|
+
# before do
|
330
|
+
# @deep_hash = AwesomeHash.new({ :failure => "stuff", :funny => "business" })
|
331
|
+
# end
|
332
|
+
#
|
333
|
+
# it 'is true and does not raise when valid' do
|
334
|
+
# @deep_hash.assert_valid_keys([ :failure, :funny ]).should be_nil
|
335
|
+
# @deep_hash.assert_valid_keys(:failure, :funny).should be_nil
|
336
|
+
# end
|
337
|
+
#
|
338
|
+
# it 'fails when invalid' do
|
339
|
+
# @deep_hash[:failore] = @deep_hash.delete(:failure)
|
340
|
+
# lambda{ @deep_hash.assert_valid_keys([ :failure, :funny ]) }.should raise_error(ArgumentError, "Unknown key(s): failore")
|
341
|
+
# lambda{ @deep_hash.assert_valid_keys(:failure, :funny) }.should raise_error(ArgumentError, "Unknown key(s): failore")
|
342
|
+
# end
|
343
|
+
# end
|
344
|
+
#
|
345
|
+
# describe "#merge" do
|
346
|
+
# it 'merges given Hash' do
|
347
|
+
# merged = @deep_hash.merge(:no => "in between")
|
348
|
+
# merged.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b', :no => 'in between' }
|
349
|
+
# end
|
350
|
+
#
|
351
|
+
# it 'returns a new instance' do
|
352
|
+
# merged = @deep_hash.merge(:no => "in between")
|
353
|
+
# merged.should_not equal(@deep_hash)
|
354
|
+
# end
|
355
|
+
#
|
356
|
+
# it 'returns instance of AwesomeHash' do
|
357
|
+
# merged = @deep_hash.merge(:no => "in between")
|
358
|
+
# merged.should be_an_instance_of(AwesomeHash)
|
359
|
+
# merged[:no].should == "in between"
|
360
|
+
# merged["no"].should == "in between"
|
361
|
+
# end
|
362
|
+
#
|
363
|
+
# it "converts all Hash values into AwesomeHashes" do
|
364
|
+
# merged = @deep_hash.merge({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }})
|
365
|
+
# merged[:nested_1].should be_an_instance_of(AwesomeHash)
|
366
|
+
# merged[:nested_1][:nested_2].should be_an_instance_of(AwesomeHash)
|
367
|
+
# merged[:other1].should be_an_instance_of(AwesomeHash)
|
368
|
+
# end
|
369
|
+
#
|
370
|
+
# it "converts string keys to symbol keys even if they occur deep in the given hash" do
|
371
|
+
# merged = @deep_hash.merge({ 'a' => { 'b' => { 'c' => { :d => :e }}}})
|
372
|
+
# merged[:a].should == { :b => { :c => { :d => :e }}}
|
373
|
+
# merged[:a].should_not == { 'b' => { 'c' => { :d => :e }}}
|
374
|
+
# end
|
375
|
+
#
|
376
|
+
# it "DOES merge values where given hash has nil value" do
|
377
|
+
# merged = @deep_hash.merge(:a => { :b => nil }, :c => nil, :leaf_3_also => nil)
|
378
|
+
# merged[:a][:b].should be_nil
|
379
|
+
# merged[:c].should be_nil
|
380
|
+
# merged[:leaf_3_also].should be_nil
|
381
|
+
# end
|
382
|
+
#
|
383
|
+
# it "replaces child hashes, and does not merge them" do
|
384
|
+
# merged = @deep_hash.merge({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }})
|
385
|
+
# merged.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }, :other1 => { :other2 => "other_val2" }, :leaf_at_top => 'val1b' }
|
386
|
+
# end
|
387
|
+
# end
|
388
|
+
#
|
389
|
+
# describe "#merge!" do
|
390
|
+
# it 'merges given Hash' do
|
391
|
+
# @deep_hash.merge!(:no => "in between")
|
392
|
+
# @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b', :no => 'in between' }
|
393
|
+
# end
|
394
|
+
#
|
395
|
+
# it 'returns a new instance' do
|
396
|
+
# @deep_hash.merge!(:no => "in between")
|
397
|
+
# @deep_hash.should equal(@deep_hash)
|
398
|
+
# end
|
399
|
+
#
|
400
|
+
# it 'returns instance of AwesomeHash' do
|
401
|
+
# @deep_hash.merge!(:no => "in between")
|
402
|
+
# @deep_hash.should be_an_instance_of(AwesomeHash)
|
403
|
+
# @deep_hash[:no].should == "in between"
|
404
|
+
# @deep_hash["no"].should == "in between"
|
405
|
+
# end
|
406
|
+
#
|
407
|
+
# it "converts all Hash values into AwesomeHashes" do
|
408
|
+
# @deep_hash.merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }})
|
409
|
+
# @deep_hash[:nested_1].should be_an_instance_of(AwesomeHash)
|
410
|
+
# @deep_hash[:nested_1][:nested_2].should be_an_instance_of(AwesomeHash)
|
411
|
+
# @deep_hash[:other1].should be_an_instance_of(AwesomeHash)
|
412
|
+
# end
|
413
|
+
#
|
414
|
+
# it "converts string keys to symbol keys even if they occur deep in the given hash" do
|
415
|
+
# @deep_hash.merge!({ 'a' => { 'b' => { 'c' => { :d => :e }}}})
|
416
|
+
# @deep_hash[:a].should == { :b => { :c => { :d => :e }}}
|
417
|
+
# @deep_hash[:a].should_not == { 'b' => { 'c' => { :d => :e }}}
|
418
|
+
# end
|
419
|
+
#
|
420
|
+
# it "DOES merge values where given hash has nil value" do
|
421
|
+
# @deep_hash.merge!(:a => { :b => nil }, :c => nil, :leaf_3_also => nil)
|
422
|
+
# @deep_hash[:a][:b].should be_nil
|
423
|
+
# @deep_hash[:c].should be_nil
|
424
|
+
# @deep_hash[:leaf_3_also].should be_nil
|
425
|
+
# end
|
426
|
+
#
|
427
|
+
# it "replaces child hashes, and does not merge them" do
|
428
|
+
# @deep_hash = @deep_hash.merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }})
|
429
|
+
# @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }, :other1 => { :other2 => "other_val2" }, :leaf_at_top => 'val1b' }
|
430
|
+
# @deep_hash.should_not == { :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }, :leaf_at_top => 'val1b' }
|
431
|
+
# end
|
432
|
+
# end
|
433
|
+
#
|
434
|
+
# describe "#reverse_merge" do
|
435
|
+
# it 'merges given Hash' do
|
436
|
+
# @deep_hash.reverse_merge!(:no => "in between", :leaf_at_top => 'NOT_USED')
|
437
|
+
# @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b', :no => 'in between' }
|
438
|
+
# end
|
439
|
+
#
|
440
|
+
# it 'returns a new instance' do
|
441
|
+
# @deep_hash.reverse_merge!(:no => "in between")
|
442
|
+
# @deep_hash.should equal(@deep_hash)
|
443
|
+
# end
|
444
|
+
#
|
445
|
+
# it 'returns instance of AwesomeHash' do
|
446
|
+
# @deep_hash.reverse_merge!(:no => "in between")
|
447
|
+
# @deep_hash.should be_an_instance_of(AwesomeHash)
|
448
|
+
# @deep_hash[:no].should == "in between"
|
449
|
+
# @deep_hash["no"].should == "in between"
|
450
|
+
# end
|
451
|
+
#
|
452
|
+
# it "converts all Hash values into AwesomeHashes" do
|
453
|
+
# @deep_hash.reverse_merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }})
|
454
|
+
# @deep_hash[:nested_1].should be_an_instance_of(AwesomeHash)
|
455
|
+
# @deep_hash[:nested_1][:nested_2].should be_an_instance_of(AwesomeHash)
|
456
|
+
# @deep_hash[:other1].should be_an_instance_of(AwesomeHash)
|
457
|
+
# end
|
458
|
+
#
|
459
|
+
# it "converts string keys to symbol keys even if they occur deep in the given hash" do
|
460
|
+
# merged = @deep_hash.reverse_merge({ 'a' => { 'b' => { 'c' => { :d => :e }}}})
|
461
|
+
# merged[:a].should == { :b => { :c => { :d => :e }}}
|
462
|
+
# merged[:a].should_not == { 'b' => { 'c' => { :d => :e }}}
|
463
|
+
# end
|
464
|
+
#
|
465
|
+
# it "DOES merge values where given hash has nil value" do
|
466
|
+
# @deep_hash.reverse_merge!(:a => { :b => nil }, :c => nil)
|
467
|
+
# @deep_hash[:a][:b].should be_nil
|
468
|
+
# @deep_hash[:c].should be_nil
|
469
|
+
# end
|
470
|
+
#
|
471
|
+
# it "replaces child hashes, and does not merge them" do
|
472
|
+
# @deep_hash = @deep_hash.reverse_merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }})
|
473
|
+
# @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :other1 => { :other2 => "other_val2" }, :leaf_at_top => 'val1b' }
|
474
|
+
# end
|
475
|
+
# end
|
476
|
+
#
|
477
|
+
# describe "#deep_merge!" do
|
478
|
+
# it "merges two subhashes when they share a key" do
|
479
|
+
# @deep_hash.deep_merge!(:nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } })
|
480
|
+
# @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a", :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b' }
|
481
|
+
# end
|
482
|
+
#
|
483
|
+
# it "merges two subhashes when they share a symbolized key" do
|
484
|
+
# @deep_hash.deep_merge!(:nested_1 => { "nested_2" => { "leaf_3_also" => "val3a" } })
|
485
|
+
# @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a", :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => "val1b" }
|
486
|
+
# end
|
487
|
+
#
|
488
|
+
# it "preserves values in the original" do
|
489
|
+
# @deep_hash.deep_merge! :other_key => "other_val"
|
490
|
+
# @deep_hash[:nested_1][:leaf_2].should == ['arr']
|
491
|
+
# @deep_hash[:other_key].should == "other_val"
|
492
|
+
# end
|
493
|
+
#
|
494
|
+
# it "converts all Hash values into AwesomeHashes" do
|
495
|
+
# @deep_hash.deep_merge!({:nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }})
|
496
|
+
# @deep_hash[:nested_1].should be_an_instance_of(AwesomeHash)
|
497
|
+
# @deep_hash[:nested_1][:nested_2].should be_an_instance_of(AwesomeHash)
|
498
|
+
# @deep_hash[:other1].should be_an_instance_of(AwesomeHash)
|
499
|
+
# end
|
500
|
+
#
|
501
|
+
# it "converts string keys to symbol keys even if they occur deep in the given hash" do
|
502
|
+
# @deep_hash.deep_merge!({ 'a' => { 'b' => { 'c' => { :d => :e }}}})
|
503
|
+
# @deep_hash[:a].should == { :b => { :c => { :d => :e }}}
|
504
|
+
# @deep_hash[:a].should_not == { 'b' => { 'c' => { :d => :e }}}
|
505
|
+
# end
|
506
|
+
#
|
507
|
+
# it "replaces values from the given hash" do
|
508
|
+
# @deep_hash.deep_merge!(:nested_1 => { :nested_2 => { :leaf_3 => "new_val3" }, :leaf_2 => { "other2" => "other_val2" }})
|
509
|
+
# @deep_hash[:nested_1][:nested_2][:leaf_3].should == 'new_val3'
|
510
|
+
# @deep_hash[:nested_1][:leaf_2].should == { :other2 => "other_val2" }
|
511
|
+
# end
|
512
|
+
#
|
513
|
+
# it "replaces arrays and does not append to them" do
|
514
|
+
# @deep_hash.deep_merge!(:nested_1 => { :nested_2 => { :leaf_3 => [] }, :leaf_2 => ['val2'] })
|
515
|
+
# @deep_hash[:nested_1][:nested_2][:leaf_3].should == []
|
516
|
+
# @deep_hash[:nested_1][:leaf_2].should == ['val2']
|
517
|
+
# end
|
518
|
+
#
|
519
|
+
# it "does not replaces values where given hash has nil value" do
|
520
|
+
# @deep_hash.deep_merge!(:nested_1 => { :leaf_2 => nil }, :leaf_at_top => '')
|
521
|
+
# @deep_hash[:nested_1][:leaf_2].should == ['arr']
|
522
|
+
# @deep_hash[:leaf_at_top].should == ""
|
523
|
+
# end
|
524
|
+
# end
|
525
|
+
#
|
526
|
+
#
|
527
|
+
# describe "#deep_set" do
|
528
|
+
# it 'should set a new value (single arg)' do
|
529
|
+
# @deep_hash.deep_set :new_key, 'new_val'
|
530
|
+
# @deep_hash[:new_key].should == 'new_val'
|
531
|
+
# end
|
532
|
+
# it 'should set a new value (multiple args)' do
|
533
|
+
# @deep_hash.deep_set :nested_1, :nested_2, :new_key, 'new_val'
|
534
|
+
# @deep_hash[:nested_1][:nested_2][:new_key].should == 'new_val'
|
535
|
+
# end
|
536
|
+
# it 'should replace an existing value (single arg)' do
|
537
|
+
# @deep_hash.deep_set :leaf_at_top, 'new_val'
|
538
|
+
# @deep_hash[:leaf_at_top].should == 'new_val'
|
539
|
+
# end
|
540
|
+
# it 'should replace an existing value (multiple args)' do
|
541
|
+
# @deep_hash.deep_set :nested_1, :nested_2, 'new_val'
|
542
|
+
# @deep_hash[:nested_1][:nested_2].should == 'new_val'
|
543
|
+
# end
|
544
|
+
# it 'should auto-vivify intermediate hashes' do
|
545
|
+
# @deep_hash.deep_set :one, :two, :three, :four, 'new_val'
|
546
|
+
# @deep_hash[:one][:two][:three][:four].should == 'new_val'
|
547
|
+
# end
|
548
|
+
# end
|
549
|
+
#
|
550
|
+
# describe "#deep_delete" do
|
551
|
+
# it 'should remove the key from the array (multiple args)' do
|
552
|
+
# @deep_hash.deep_delete(:nested_1)
|
553
|
+
# @deep_hash[:nested_1].should be_nil
|
554
|
+
# @deep_hash.should == { :leaf_at_top => 'val1b'}
|
555
|
+
# end
|
556
|
+
# it 'should remove the key from the array (multiple args)' do
|
557
|
+
# @deep_hash.deep_delete(:nested_1, :nested_2, :leaf_3)
|
558
|
+
# @deep_hash[:nested_1][:nested_2][:leaf_3].should be_nil
|
559
|
+
# @deep_hash.should == {:leaf_at_top => "val1b", :nested_1 => {:leaf_2 => ['arr'], :nested_2 => {}}}
|
560
|
+
# end
|
561
|
+
# it 'should return the value if present (single args)' do
|
562
|
+
# returned_val = @deep_hash.deep_delete(:leaf_at_top)
|
563
|
+
# returned_val.should == 'val1b'
|
564
|
+
# end
|
565
|
+
# it 'should return the value if present (multiple args)' do
|
566
|
+
# returned_val = @deep_hash.deep_delete(:nested_1, :nested_2, :leaf_3)
|
567
|
+
# returned_val.should == 'val3'
|
568
|
+
# end
|
569
|
+
# it 'should return nil if the key is absent (single arg)' do
|
570
|
+
# returned_val = @deep_hash.deep_delete(:nested_1, :nested_2, :missing_key)
|
571
|
+
# returned_val.should be_nil
|
572
|
+
# end
|
573
|
+
# it 'should return nil if the key is absent (multiple args)' do
|
574
|
+
# returned_val = @deep_hash.deep_delete(:missing_key)
|
575
|
+
# returned_val.should be_nil
|
576
|
+
# end
|
577
|
+
# end
|
578
|
+
|
579
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gorillib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
- 4
|
10
|
-
version: 0.1.4
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.5
|
11
6
|
platform: ruby
|
12
7
|
authors:
|
13
8
|
- Infochimps
|
@@ -15,243 +10,171 @@ autorequire:
|
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
12
|
|
18
|
-
date: 2011-
|
19
|
-
default_executable:
|
13
|
+
date: 2011-08-02 00:00:00 Z
|
20
14
|
dependencies:
|
21
15
|
- !ruby/object:Gem::Dependency
|
16
|
+
name: bundler
|
22
17
|
requirement: &id001 !ruby/object:Gem::Requirement
|
23
18
|
none: false
|
24
19
|
requirements:
|
25
20
|
- - ~>
|
26
21
|
- !ruby/object:Gem::Version
|
27
|
-
hash: 15
|
28
|
-
segments:
|
29
|
-
- 1
|
30
|
-
- 0
|
31
|
-
- 12
|
32
22
|
version: 1.0.12
|
33
23
|
type: :development
|
34
|
-
name: bundler
|
35
24
|
prerelease: false
|
36
25
|
version_requirements: *id001
|
37
26
|
- !ruby/object:Gem::Dependency
|
27
|
+
name: yard
|
38
28
|
requirement: &id002 !ruby/object:Gem::Requirement
|
39
29
|
none: false
|
40
30
|
requirements:
|
41
31
|
- - ~>
|
42
32
|
- !ruby/object:Gem::Version
|
43
|
-
hash: 9
|
44
|
-
segments:
|
45
|
-
- 0
|
46
|
-
- 6
|
47
|
-
- 7
|
48
33
|
version: 0.6.7
|
49
34
|
type: :development
|
50
|
-
name: yard
|
51
35
|
prerelease: false
|
52
36
|
version_requirements: *id002
|
53
37
|
- !ruby/object:Gem::Dependency
|
38
|
+
name: jeweler
|
54
39
|
requirement: &id003 !ruby/object:Gem::Requirement
|
55
40
|
none: false
|
56
41
|
requirements:
|
57
42
|
- - ~>
|
58
43
|
- !ruby/object:Gem::Version
|
59
|
-
hash: 7
|
60
|
-
segments:
|
61
|
-
- 1
|
62
|
-
- 5
|
63
|
-
- 2
|
64
44
|
version: 1.5.2
|
65
45
|
type: :development
|
66
|
-
name: jeweler
|
67
46
|
prerelease: false
|
68
47
|
version_requirements: *id003
|
69
48
|
- !ruby/object:Gem::Dependency
|
49
|
+
name: rspec
|
70
50
|
requirement: &id004 !ruby/object:Gem::Requirement
|
71
51
|
none: false
|
72
52
|
requirements:
|
73
53
|
- - ~>
|
74
54
|
- !ruby/object:Gem::Version
|
75
|
-
hash: 27
|
76
|
-
segments:
|
77
|
-
- 2
|
78
|
-
- 5
|
79
|
-
- 0
|
80
55
|
version: 2.5.0
|
81
56
|
type: :development
|
82
|
-
name: rspec
|
83
57
|
prerelease: false
|
84
58
|
version_requirements: *id004
|
85
59
|
- !ruby/object:Gem::Dependency
|
60
|
+
name: spork
|
86
61
|
requirement: &id005 !ruby/object:Gem::Requirement
|
87
62
|
none: false
|
88
63
|
requirements:
|
89
64
|
- - ~>
|
90
65
|
- !ruby/object:Gem::Version
|
91
|
-
hash: 977940482
|
92
|
-
segments:
|
93
|
-
- 0
|
94
|
-
- 9
|
95
|
-
- 0
|
96
|
-
- rc5
|
97
66
|
version: 0.9.0.rc5
|
98
67
|
type: :development
|
99
|
-
name: spork
|
100
68
|
prerelease: false
|
101
69
|
version_requirements: *id005
|
102
70
|
- !ruby/object:Gem::Dependency
|
71
|
+
name: RedCloth
|
103
72
|
requirement: &id006 !ruby/object:Gem::Requirement
|
104
73
|
none: false
|
105
74
|
requirements:
|
106
75
|
- - ">="
|
107
76
|
- !ruby/object:Gem::Version
|
108
|
-
hash: 3
|
109
|
-
segments:
|
110
|
-
- 0
|
111
77
|
version: "0"
|
112
78
|
type: :development
|
113
|
-
name: RedCloth
|
114
79
|
prerelease: false
|
115
80
|
version_requirements: *id006
|
116
81
|
- !ruby/object:Gem::Dependency
|
82
|
+
name: rcov
|
117
83
|
requirement: &id007 !ruby/object:Gem::Requirement
|
118
84
|
none: false
|
119
85
|
requirements:
|
120
86
|
- - ">="
|
121
87
|
- !ruby/object:Gem::Version
|
122
|
-
hash: 41
|
123
|
-
segments:
|
124
|
-
- 0
|
125
|
-
- 9
|
126
|
-
- 9
|
127
88
|
version: 0.9.9
|
128
89
|
type: :development
|
129
|
-
name: rcov
|
130
90
|
prerelease: false
|
131
91
|
version_requirements: *id007
|
132
92
|
- !ruby/object:Gem::Dependency
|
93
|
+
name: watchr
|
133
94
|
requirement: &id008 !ruby/object:Gem::Requirement
|
134
95
|
none: false
|
135
96
|
requirements:
|
136
97
|
- - ">="
|
137
98
|
- !ruby/object:Gem::Version
|
138
|
-
hash: 3
|
139
|
-
segments:
|
140
|
-
- 0
|
141
99
|
version: "0"
|
142
100
|
type: :development
|
143
|
-
name: watchr
|
144
101
|
prerelease: false
|
145
102
|
version_requirements: *id008
|
146
103
|
- !ruby/object:Gem::Dependency
|
104
|
+
name: bundler
|
147
105
|
requirement: &id009 !ruby/object:Gem::Requirement
|
148
106
|
none: false
|
149
107
|
requirements:
|
150
108
|
- - ~>
|
151
109
|
- !ruby/object:Gem::Version
|
152
|
-
hash: 15
|
153
|
-
segments:
|
154
|
-
- 1
|
155
|
-
- 0
|
156
|
-
- 12
|
157
110
|
version: 1.0.12
|
158
111
|
type: :development
|
159
|
-
name: bundler
|
160
112
|
prerelease: false
|
161
113
|
version_requirements: *id009
|
162
114
|
- !ruby/object:Gem::Dependency
|
115
|
+
name: yard
|
163
116
|
requirement: &id010 !ruby/object:Gem::Requirement
|
164
117
|
none: false
|
165
118
|
requirements:
|
166
119
|
- - ~>
|
167
120
|
- !ruby/object:Gem::Version
|
168
|
-
hash: 9
|
169
|
-
segments:
|
170
|
-
- 0
|
171
|
-
- 6
|
172
|
-
- 7
|
173
121
|
version: 0.6.7
|
174
122
|
type: :development
|
175
|
-
name: yard
|
176
123
|
prerelease: false
|
177
124
|
version_requirements: *id010
|
178
125
|
- !ruby/object:Gem::Dependency
|
126
|
+
name: jeweler
|
179
127
|
requirement: &id011 !ruby/object:Gem::Requirement
|
180
128
|
none: false
|
181
129
|
requirements:
|
182
130
|
- - ~>
|
183
131
|
- !ruby/object:Gem::Version
|
184
|
-
hash: 7
|
185
|
-
segments:
|
186
|
-
- 1
|
187
|
-
- 5
|
188
|
-
- 2
|
189
132
|
version: 1.5.2
|
190
133
|
type: :development
|
191
|
-
name: jeweler
|
192
134
|
prerelease: false
|
193
135
|
version_requirements: *id011
|
194
136
|
- !ruby/object:Gem::Dependency
|
137
|
+
name: rspec
|
195
138
|
requirement: &id012 !ruby/object:Gem::Requirement
|
196
139
|
none: false
|
197
140
|
requirements:
|
198
141
|
- - ~>
|
199
142
|
- !ruby/object:Gem::Version
|
200
|
-
hash: 27
|
201
|
-
segments:
|
202
|
-
- 2
|
203
|
-
- 5
|
204
|
-
- 0
|
205
143
|
version: 2.5.0
|
206
144
|
type: :development
|
207
|
-
name: rspec
|
208
145
|
prerelease: false
|
209
146
|
version_requirements: *id012
|
210
147
|
- !ruby/object:Gem::Dependency
|
148
|
+
name: rcov
|
211
149
|
requirement: &id013 !ruby/object:Gem::Requirement
|
212
150
|
none: false
|
213
151
|
requirements:
|
214
152
|
- - ">="
|
215
153
|
- !ruby/object:Gem::Version
|
216
|
-
hash: 41
|
217
|
-
segments:
|
218
|
-
- 0
|
219
|
-
- 9
|
220
|
-
- 9
|
221
154
|
version: 0.9.9
|
222
155
|
type: :development
|
223
|
-
name: rcov
|
224
156
|
prerelease: false
|
225
157
|
version_requirements: *id013
|
226
158
|
- !ruby/object:Gem::Dependency
|
159
|
+
name: spork
|
227
160
|
requirement: &id014 !ruby/object:Gem::Requirement
|
228
161
|
none: false
|
229
162
|
requirements:
|
230
163
|
- - ~>
|
231
164
|
- !ruby/object:Gem::Version
|
232
|
-
hash: 977940482
|
233
|
-
segments:
|
234
|
-
- 0
|
235
|
-
- 9
|
236
|
-
- 0
|
237
|
-
- rc5
|
238
165
|
version: 0.9.0.rc5
|
239
166
|
type: :development
|
240
|
-
name: spork
|
241
167
|
prerelease: false
|
242
168
|
version_requirements: *id014
|
243
169
|
- !ruby/object:Gem::Dependency
|
170
|
+
name: watchr
|
244
171
|
requirement: &id015 !ruby/object:Gem::Requirement
|
245
172
|
none: false
|
246
173
|
requirements:
|
247
174
|
- - ">="
|
248
175
|
- !ruby/object:Gem::Version
|
249
|
-
hash: 3
|
250
|
-
segments:
|
251
|
-
- 0
|
252
176
|
version: "0"
|
253
177
|
type: :development
|
254
|
-
name: watchr
|
255
178
|
prerelease: false
|
256
179
|
version_requirements: *id015
|
257
180
|
description: "Gorillib: infochimps lightweight subset of ruby convenience methods"
|
@@ -268,7 +191,6 @@ files:
|
|
268
191
|
- .rspec
|
269
192
|
- CHANGELOG.textile
|
270
193
|
- Gemfile
|
271
|
-
- Gemfile.lock
|
272
194
|
- LICENSE.textile
|
273
195
|
- README.textile
|
274
196
|
- Rakefile
|
@@ -296,6 +218,7 @@ files:
|
|
296
218
|
- lib/gorillib/hashlike/compact.rb
|
297
219
|
- lib/gorillib/hashlike/deep_compact.rb
|
298
220
|
- lib/gorillib/hashlike/deep_dup.rb
|
221
|
+
- lib/gorillib/hashlike/deep_hash.rb
|
299
222
|
- lib/gorillib/hashlike/deep_merge.rb
|
300
223
|
- lib/gorillib/hashlike/hashlike_via_accessors.rb
|
301
224
|
- lib/gorillib/hashlike/keys.rb
|
@@ -345,6 +268,7 @@ files:
|
|
345
268
|
- spec/hash/slice_spec.rb
|
346
269
|
- spec/hash/zip_spec.rb
|
347
270
|
- spec/hashlike/behave_same_as_hash_spec.rb
|
271
|
+
- spec/hashlike/deep_hash_spec.rb
|
348
272
|
- spec/hashlike/hashlike_behavior_spec.rb
|
349
273
|
- spec/hashlike/hashlike_via_accessors_spec.rb
|
350
274
|
- spec/hashlike_spec.rb
|
@@ -378,7 +302,6 @@ files:
|
|
378
302
|
- spec/support/matchers/be_hash_eql.rb
|
379
303
|
- spec/support/matchers/enumerate_method.rb
|
380
304
|
- spec/support/matchers/evaluate_to_true.rb
|
381
|
-
has_rdoc: true
|
382
305
|
homepage: http://infochimps.com/labs
|
383
306
|
licenses:
|
384
307
|
- MIT
|
@@ -392,7 +315,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
392
315
|
requirements:
|
393
316
|
- - ">="
|
394
317
|
- !ruby/object:Gem::Version
|
395
|
-
hash:
|
318
|
+
hash: 168082518924887953
|
396
319
|
segments:
|
397
320
|
- 0
|
398
321
|
version: "0"
|
@@ -401,14 +324,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
401
324
|
requirements:
|
402
325
|
- - ">="
|
403
326
|
- !ruby/object:Gem::Version
|
404
|
-
hash: 3
|
405
|
-
segments:
|
406
|
-
- 0
|
407
327
|
version: "0"
|
408
328
|
requirements: []
|
409
329
|
|
410
330
|
rubyforge_project:
|
411
|
-
rubygems_version: 1.
|
331
|
+
rubygems_version: 1.8.6
|
412
332
|
signing_key:
|
413
333
|
specification_version: 3
|
414
334
|
summary: include only what you need. No dependencies, no creep
|
@@ -427,6 +347,7 @@ test_files:
|
|
427
347
|
- spec/hash/slice_spec.rb
|
428
348
|
- spec/hash/zip_spec.rb
|
429
349
|
- spec/hashlike/behave_same_as_hash_spec.rb
|
350
|
+
- spec/hashlike/deep_hash_spec.rb
|
430
351
|
- spec/hashlike/hashlike_behavior_spec.rb
|
431
352
|
- spec/hashlike/hashlike_via_accessors_spec.rb
|
432
353
|
- spec/hashlike_spec.rb
|
data/Gemfile.lock
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
GEM
|
2
|
-
remote: http://rubygems.org/
|
3
|
-
specs:
|
4
|
-
RedCloth (4.2.7)
|
5
|
-
diff-lcs (1.1.2)
|
6
|
-
git (1.2.5)
|
7
|
-
jeweler (1.5.2)
|
8
|
-
bundler (~> 1.0.0)
|
9
|
-
git (>= 1.2.5)
|
10
|
-
rake
|
11
|
-
rake (0.8.7)
|
12
|
-
rcov (0.9.9)
|
13
|
-
rspec (2.5.0)
|
14
|
-
rspec-core (~> 2.5.0)
|
15
|
-
rspec-expectations (~> 2.5.0)
|
16
|
-
rspec-mocks (~> 2.5.0)
|
17
|
-
rspec-core (2.5.2)
|
18
|
-
rspec-expectations (2.5.0)
|
19
|
-
diff-lcs (~> 1.1.2)
|
20
|
-
rspec-mocks (2.5.0)
|
21
|
-
spork (0.9.0.rc7)
|
22
|
-
watchr (0.7)
|
23
|
-
yard (0.6.8)
|
24
|
-
|
25
|
-
PLATFORMS
|
26
|
-
ruby
|
27
|
-
|
28
|
-
DEPENDENCIES
|
29
|
-
RedCloth
|
30
|
-
bundler (~> 1.0.12)
|
31
|
-
jeweler (~> 1.5.2)
|
32
|
-
rcov (>= 0.9.9)
|
33
|
-
rspec (~> 2.5.0)
|
34
|
-
spork (~> 0.9.0.rc5)
|
35
|
-
watchr
|
36
|
-
yard (~> 0.6.7)
|