gorillib 0.0.8 → 0.1.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.textile +6 -0
- data/README.textile +34 -11
- data/VERSION +1 -1
- data/gorillib.gemspec +63 -5
- data/lib/gorillib/enumerable/sum.rb +2 -2
- data/lib/gorillib/hash/compact.rb +2 -29
- data/lib/gorillib/hash/deep_compact.rb +2 -12
- data/lib/gorillib/hash/deep_dup.rb +4 -0
- data/lib/gorillib/hash/deep_merge.rb +2 -14
- data/lib/gorillib/hash/indifferent_access.rb +207 -0
- data/lib/gorillib/hash/keys.rb +2 -40
- data/lib/gorillib/hash/reverse_merge.rb +2 -24
- data/lib/gorillib/hash/slice.rb +2 -51
- data/lib/gorillib/hash/tree_merge.rb +4 -0
- data/lib/gorillib/hashlike.rb +824 -0
- data/lib/gorillib/hashlike/compact.rb +60 -0
- data/lib/gorillib/hashlike/deep_compact.rb +18 -0
- data/lib/gorillib/hashlike/deep_dup.rb +15 -0
- data/lib/gorillib/hashlike/deep_merge.rb +20 -0
- data/lib/gorillib/hashlike/hashlike_via_accessors.rb +169 -0
- data/lib/gorillib/hashlike/keys.rb +59 -0
- data/lib/gorillib/hashlike/reverse_merge.rb +31 -0
- data/lib/gorillib/hashlike/slice.rb +67 -0
- data/lib/gorillib/hashlike/tree_merge.rb +76 -0
- data/lib/gorillib/metaprogramming/mattr_accessor.rb +1 -1
- data/lib/gorillib/receiver.rb +315 -0
- data/lib/gorillib/receiver/active_model_shim.rb +19 -0
- data/lib/gorillib/receiver/acts_as_hash.rb +191 -0
- data/lib/gorillib/receiver/acts_as_loadable.rb +42 -0
- data/lib/gorillib/receiver/tree_diff.rb +74 -0
- data/lib/gorillib/receiver/validations.rb +30 -0
- data/lib/gorillib/struct/acts_as_hash.rb +108 -0
- data/lib/gorillib/struct/hashlike_iteration.rb +0 -0
- data/notes/fancy_hashes_and_receivers.textile +120 -0
- data/notes/hash_rdocs.textile +97 -0
- data/spec/hash/deep_merge_spec.rb +0 -2
- data/spec/hash/indifferent_access_spec.rb +391 -0
- data/spec/hash/slice_spec.rb +35 -12
- data/spec/hashlike/behave_same_as_hash_spec.rb +105 -0
- data/spec/hashlike/hashlike_behavior_spec.rb +824 -0
- data/spec/hashlike/hashlike_via_accessors_fuzzing_spec.rb +37 -0
- data/spec/hashlike/hashlike_via_accessors_spec.rb +262 -0
- data/spec/hashlike_spec.rb +302 -0
- data/spec/metaprogramming/aliasing_spec.rb +3 -0
- data/spec/metaprogramming/cattr_accessor_spec.rb +2 -0
- data/spec/metaprogramming/class_attribute_spec.rb +2 -0
- data/spec/metaprogramming/delegation_spec.rb +2 -0
- data/spec/metaprogramming/mattr_accessor_spec.rb +2 -0
- data/spec/metaprogramming/singleton_class_spec.rb +3 -0
- data/spec/receiver/acts_as_hash_spec.rb +286 -0
- data/spec/receiver_spec.rb +478 -0
- data/spec/spec_helper.rb +11 -6
- data/spec/string/truncate_spec.rb +1 -0
- data/spec/struct/acts_as_hash_fuzz_spec.rb +67 -0
- data/spec/struct/acts_as_hash_spec.rb +426 -0
- data/spec/support/hashlike_fuzzing_helper.rb +127 -0
- data/spec/support/hashlike_helper.rb +75 -0
- data/spec/support/hashlike_struct_helper.rb +37 -0
- data/spec/support/hashlike_via_delegation.rb +30 -0
- data/spec/support/matchers/be_array_eql.rb +12 -0
- data/spec/support/matchers/be_hash_eql.rb +14 -0
- data/spec/support/matchers/enumerate_method.rb +10 -0
- data/spec/support/matchers/evaluate_to_true.rb +5 -0
- metadata +62 -4
data/CHANGELOG.textile
CHANGED
data/README.textile
CHANGED
@@ -1,15 +1,23 @@
|
|
1
|
+
|
1
2
|
h2. Gorillib: infochimps' lightweight subset of ruby convenience methods
|
2
3
|
|
3
|
-
|
4
|
-
* No dependencies unless audaciously advertised.
|
5
|
-
* Compatibile with active_record and extlib
|
4
|
+
We love the conveniences provided by active_support and extlib, we just don't love them at the same time and on top of each other. active_support is slow to load, has many dependencies, and is all intertwingled. We had too many collisions between active_support 2.x and 3.x and extlib.
|
6
5
|
|
6
|
+
What gorillib gives you is clarity over what features are brought in. If you want to *just* get <code>Object#blank?</code>, just <code>require 'gorillib/object/blank'</code>. No dependencies, no codependents.
|
7
7
|
|
8
|
-
|
8
|
+
* No creep: include only what you need
|
9
|
+
* No dependencies unless audaciously advertised.
|
10
|
+
* Upwards compatible with active_record and extlib
|
11
|
+
- the active_support components have significantly more robust internationalization, and some functions have rich option sets in active_support vs. basic functionality in gorillib. So the rule is if you were happy with gorillib you'll be happy with active_support, but not vice-versa.
|
9
12
|
|
10
|
-
|
13
|
+
h3. require 'gorillib/receiver'
|
11
14
|
|
15
|
+
Gorillib has at least one powerful addition to the canon: the receiver mixin.
|
12
16
|
|
17
|
+
* lightweight
|
18
|
+
* gives you weak type safety but doesn't jack around with setters/getters.
|
19
|
+
* object/hash semantics
|
20
|
+
|
13
21
|
h3. require 'gorillib'
|
14
22
|
|
15
23
|
* require 'gorrillib/base'
|
@@ -19,28 +27,30 @@ h3. require 'gorillib/base'
|
|
19
27
|
requires the following libraries:
|
20
28
|
|
21
29
|
* gorillib/object/blank
|
22
|
-
* gorillib/array/extract_options
|
23
30
|
* gorillib/hash/reverse_merge
|
24
31
|
* gorillib/hash/compact
|
25
|
-
* gorillib/array/
|
32
|
+
* gorillib/array/compact_blank
|
33
|
+
* gorillib/object/try
|
26
34
|
|
27
35
|
h3. require 'gorillib/some'
|
28
36
|
|
29
37
|
requires @gorillib/base@ and the following additional libraries:
|
30
38
|
|
39
|
+
* gorillib/logger/log.rb
|
31
40
|
* set
|
32
41
|
* time
|
33
42
|
* date
|
43
|
+
* gorillib/array/extract_options
|
34
44
|
* gorillib/enumerable/sum
|
35
45
|
* gorillib/datetime/flat
|
36
|
-
* gorillib/datetime/
|
46
|
+
* gorillib/datetime/parse
|
37
47
|
* gorillib/hash/zip
|
38
48
|
* gorillib/hash/slice
|
39
49
|
* gorillib/hash/keys
|
40
|
-
* gorillib/metaprogramming/singleton_class
|
41
|
-
* gorillib/metaprogramming/remove_method
|
42
50
|
* gorillib/metaprogramming/class_attribute
|
43
51
|
* gorillib/metaprogramming/cattr_accessor
|
52
|
+
* gorillib/metaprogramming/singleton_class
|
53
|
+
* gorillib/metaprogramming/remove_method
|
44
54
|
|
45
55
|
---------------------------------------------------------------------------
|
46
56
|
|
@@ -51,13 +61,15 @@ h3. gorillib/array
|
|
51
61
|
- Hash extractable_options? (helper method)
|
52
62
|
* *gorrillib/array/compact_blank*
|
53
63
|
- Array compact_blank, compact_blank!
|
64
|
+
* *gorrillib/array/deep_compact*
|
65
|
+
- Array deep_compact, deep_compact!
|
54
66
|
|
55
67
|
h3. gorillib/datetime
|
56
68
|
|
57
69
|
* *gorillib/datetime/flat*
|
58
70
|
- Date, Time to_flat
|
59
71
|
* *gorillib/datetime/parse*
|
60
|
-
-
|
72
|
+
- Time parse_safely
|
61
73
|
|
62
74
|
h3. gorillib/enumerable
|
63
75
|
|
@@ -68,6 +80,8 @@ h3. gorillib/hash
|
|
68
80
|
|
69
81
|
* *gorillib/hash/compact*
|
70
82
|
- Hash compact, compact!, compact_blank, compact_blank!
|
83
|
+
* *gorrilib/hash/deep_compact*
|
84
|
+
- Hash deep_compact, deep_compact!
|
71
85
|
* *gorrilib/hash/deep_merge*
|
72
86
|
- Hash deep_merge, deep_merge!
|
73
87
|
* *gorillib/hash/keys*
|
@@ -102,10 +116,19 @@ h3. gorillib/metaprogramming
|
|
102
116
|
* *gorillib/metaprogramming/delegation*
|
103
117
|
- Module# delegate
|
104
118
|
|
119
|
+
h3. gorillib/numeric
|
120
|
+
|
121
|
+
* *gorillib/numeric/clamp*
|
122
|
+
- Numeric clamp -- coerce a number to lie within a certain min/max
|
123
|
+
|
105
124
|
h3. gorillib/object
|
106
125
|
|
107
126
|
* *gorillib/object/blank*
|
108
127
|
- Object blank?, present? (and specialized for all other classes)
|
128
|
+
* *gorillib/object/try*
|
129
|
+
- Object try
|
130
|
+
* *gorillib/object/try_dup*
|
131
|
+
- Object try_dup
|
109
132
|
|
110
133
|
h3. gorillib/string
|
111
134
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
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.0
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Infochimps"]
|
12
|
-
s.date = %q{2011-
|
12
|
+
s.date = %q{2011-06-07}
|
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 = [
|
@@ -32,17 +32,29 @@ Gem::Specification.new do |s|
|
|
32
32
|
"lib/gorillib/array/deep_compact.rb",
|
33
33
|
"lib/gorillib/array/extract_options.rb",
|
34
34
|
"lib/gorillib/base.rb",
|
35
|
-
"lib/gorillib/datetime/#flat.rb#",
|
36
35
|
"lib/gorillib/datetime/flat.rb",
|
37
36
|
"lib/gorillib/datetime/parse.rb",
|
38
37
|
"lib/gorillib/enumerable/sum.rb",
|
39
38
|
"lib/gorillib/hash/compact.rb",
|
40
39
|
"lib/gorillib/hash/deep_compact.rb",
|
40
|
+
"lib/gorillib/hash/deep_dup.rb",
|
41
41
|
"lib/gorillib/hash/deep_merge.rb",
|
42
|
+
"lib/gorillib/hash/indifferent_access.rb",
|
42
43
|
"lib/gorillib/hash/keys.rb",
|
43
44
|
"lib/gorillib/hash/reverse_merge.rb",
|
44
45
|
"lib/gorillib/hash/slice.rb",
|
46
|
+
"lib/gorillib/hash/tree_merge.rb",
|
45
47
|
"lib/gorillib/hash/zip.rb",
|
48
|
+
"lib/gorillib/hashlike.rb",
|
49
|
+
"lib/gorillib/hashlike/compact.rb",
|
50
|
+
"lib/gorillib/hashlike/deep_compact.rb",
|
51
|
+
"lib/gorillib/hashlike/deep_dup.rb",
|
52
|
+
"lib/gorillib/hashlike/deep_merge.rb",
|
53
|
+
"lib/gorillib/hashlike/hashlike_via_accessors.rb",
|
54
|
+
"lib/gorillib/hashlike/keys.rb",
|
55
|
+
"lib/gorillib/hashlike/reverse_merge.rb",
|
56
|
+
"lib/gorillib/hashlike/slice.rb",
|
57
|
+
"lib/gorillib/hashlike/tree_merge.rb",
|
46
58
|
"lib/gorillib/logger/log.rb",
|
47
59
|
"lib/gorillib/metaprogramming/aliasing.rb",
|
48
60
|
"lib/gorillib/metaprogramming/cattr_accessor.rb",
|
@@ -55,11 +67,21 @@ Gem::Specification.new do |s|
|
|
55
67
|
"lib/gorillib/object/blank.rb",
|
56
68
|
"lib/gorillib/object/try.rb",
|
57
69
|
"lib/gorillib/object/try_dup.rb",
|
70
|
+
"lib/gorillib/receiver.rb",
|
71
|
+
"lib/gorillib/receiver/active_model_shim.rb",
|
72
|
+
"lib/gorillib/receiver/acts_as_hash.rb",
|
73
|
+
"lib/gorillib/receiver/acts_as_loadable.rb",
|
74
|
+
"lib/gorillib/receiver/tree_diff.rb",
|
75
|
+
"lib/gorillib/receiver/validations.rb",
|
58
76
|
"lib/gorillib/some.rb",
|
59
77
|
"lib/gorillib/string/constantize.rb",
|
60
78
|
"lib/gorillib/string/human.rb",
|
61
79
|
"lib/gorillib/string/inflections.rb",
|
62
80
|
"lib/gorillib/string/truncate.rb",
|
81
|
+
"lib/gorillib/struct/acts_as_hash.rb",
|
82
|
+
"lib/gorillib/struct/hashlike_iteration.rb",
|
83
|
+
"notes/fancy_hashes_and_receivers.textile",
|
84
|
+
"notes/hash_rdocs.textile",
|
63
85
|
"spec/array/compact_blank_spec.rb",
|
64
86
|
"spec/array/extract_options_spec.rb",
|
65
87
|
"spec/datetime/flat_spec.rb",
|
@@ -68,10 +90,16 @@ Gem::Specification.new do |s|
|
|
68
90
|
"spec/hash/compact_spec.rb",
|
69
91
|
"spec/hash/deep_compact_spec.rb",
|
70
92
|
"spec/hash/deep_merge_spec.rb",
|
93
|
+
"spec/hash/indifferent_access_spec.rb",
|
71
94
|
"spec/hash/keys_spec.rb",
|
72
95
|
"spec/hash/reverse_merge_spec.rb",
|
73
96
|
"spec/hash/slice_spec.rb",
|
74
97
|
"spec/hash/zip_spec.rb",
|
98
|
+
"spec/hashlike/behave_same_as_hash_spec.rb",
|
99
|
+
"spec/hashlike/hashlike_behavior_spec.rb",
|
100
|
+
"spec/hashlike/hashlike_via_accessors_fuzzing_spec.rb",
|
101
|
+
"spec/hashlike/hashlike_via_accessors_spec.rb",
|
102
|
+
"spec/hashlike_spec.rb",
|
75
103
|
"spec/logger/log_spec.rb",
|
76
104
|
"spec/metaprogramming/aliasing_spec.rb",
|
77
105
|
"spec/metaprogramming/cattr_accessor_spec.rb",
|
@@ -83,13 +111,25 @@ Gem::Specification.new do |s|
|
|
83
111
|
"spec/object/blank_spec.rb",
|
84
112
|
"spec/object/try_dup_spec.rb",
|
85
113
|
"spec/object/try_spec.rb",
|
114
|
+
"spec/receiver/acts_as_hash_spec.rb",
|
115
|
+
"spec/receiver_spec.rb",
|
86
116
|
"spec/spec_helper.rb",
|
87
117
|
"spec/string/constantize_spec.rb",
|
88
118
|
"spec/string/human_spec.rb",
|
89
119
|
"spec/string/inflections_spec.rb",
|
90
120
|
"spec/string/inflector_test_cases.rb",
|
91
121
|
"spec/string/truncate_spec.rb",
|
92
|
-
"spec/
|
122
|
+
"spec/struct/acts_as_hash_fuzz_spec.rb",
|
123
|
+
"spec/struct/acts_as_hash_spec.rb",
|
124
|
+
"spec/support/hashlike_fuzzing_helper.rb",
|
125
|
+
"spec/support/hashlike_helper.rb",
|
126
|
+
"spec/support/hashlike_struct_helper.rb",
|
127
|
+
"spec/support/hashlike_via_delegation.rb",
|
128
|
+
"spec/support/kcode_test_helper.rb",
|
129
|
+
"spec/support/matchers/be_array_eql.rb",
|
130
|
+
"spec/support/matchers/be_hash_eql.rb",
|
131
|
+
"spec/support/matchers/enumerate_method.rb",
|
132
|
+
"spec/support/matchers/evaluate_to_true.rb"
|
93
133
|
]
|
94
134
|
s.homepage = %q{http://infochimps.com/labs}
|
95
135
|
s.licenses = ["MIT"]
|
@@ -105,10 +145,16 @@ Gem::Specification.new do |s|
|
|
105
145
|
"spec/hash/compact_spec.rb",
|
106
146
|
"spec/hash/deep_compact_spec.rb",
|
107
147
|
"spec/hash/deep_merge_spec.rb",
|
148
|
+
"spec/hash/indifferent_access_spec.rb",
|
108
149
|
"spec/hash/keys_spec.rb",
|
109
150
|
"spec/hash/reverse_merge_spec.rb",
|
110
151
|
"spec/hash/slice_spec.rb",
|
111
152
|
"spec/hash/zip_spec.rb",
|
153
|
+
"spec/hashlike/behave_same_as_hash_spec.rb",
|
154
|
+
"spec/hashlike/hashlike_behavior_spec.rb",
|
155
|
+
"spec/hashlike/hashlike_via_accessors_fuzzing_spec.rb",
|
156
|
+
"spec/hashlike/hashlike_via_accessors_spec.rb",
|
157
|
+
"spec/hashlike_spec.rb",
|
112
158
|
"spec/logger/log_spec.rb",
|
113
159
|
"spec/metaprogramming/aliasing_spec.rb",
|
114
160
|
"spec/metaprogramming/cattr_accessor_spec.rb",
|
@@ -120,13 +166,25 @@ Gem::Specification.new do |s|
|
|
120
166
|
"spec/object/blank_spec.rb",
|
121
167
|
"spec/object/try_dup_spec.rb",
|
122
168
|
"spec/object/try_spec.rb",
|
169
|
+
"spec/receiver/acts_as_hash_spec.rb",
|
170
|
+
"spec/receiver_spec.rb",
|
123
171
|
"spec/spec_helper.rb",
|
124
172
|
"spec/string/constantize_spec.rb",
|
125
173
|
"spec/string/human_spec.rb",
|
126
174
|
"spec/string/inflections_spec.rb",
|
127
175
|
"spec/string/inflector_test_cases.rb",
|
128
176
|
"spec/string/truncate_spec.rb",
|
129
|
-
"spec/
|
177
|
+
"spec/struct/acts_as_hash_fuzz_spec.rb",
|
178
|
+
"spec/struct/acts_as_hash_spec.rb",
|
179
|
+
"spec/support/hashlike_fuzzing_helper.rb",
|
180
|
+
"spec/support/hashlike_helper.rb",
|
181
|
+
"spec/support/hashlike_struct_helper.rb",
|
182
|
+
"spec/support/hashlike_via_delegation.rb",
|
183
|
+
"spec/support/kcode_test_helper.rb",
|
184
|
+
"spec/support/matchers/be_array_eql.rb",
|
185
|
+
"spec/support/matchers/be_hash_eql.rb",
|
186
|
+
"spec/support/matchers/enumerate_method.rb",
|
187
|
+
"spec/support/matchers/evaluate_to_true.rb"
|
130
188
|
]
|
131
189
|
|
132
190
|
if s.respond_to? :specification_version then
|
@@ -25,7 +25,7 @@ unless Enumerable.method_defined?(:sum)
|
|
25
25
|
else
|
26
26
|
inject { |sum, element| sum + element } || identity
|
27
27
|
end
|
28
|
-
end
|
28
|
+
end unless method_defined?(:sum)
|
29
29
|
end
|
30
30
|
|
31
31
|
class Range #:nodoc:
|
@@ -35,6 +35,6 @@ unless Enumerable.method_defined?(:sum)
|
|
35
35
|
return super if block_given? || !(first.instance_of?(Integer) && last.instance_of?(Integer))
|
36
36
|
actual_last = exclude_end? ? (last - 1) : last
|
37
37
|
(actual_last - first + 1) * (actual_last + first) / 2
|
38
|
-
end
|
38
|
+
end unless method_defined?(:sum)
|
39
39
|
end
|
40
40
|
end
|
@@ -1,31 +1,4 @@
|
|
1
|
-
require 'gorillib/
|
2
|
-
|
1
|
+
require 'gorillib/hashlike/compact'
|
3
2
|
class Hash
|
4
|
-
|
5
|
-
# remove all key-value pairs where the value is nil
|
6
|
-
#
|
7
|
-
def compact
|
8
|
-
reject{|key,val| val.nil? }
|
9
|
-
end unless method_defined?(:compact)
|
10
|
-
|
11
|
-
#
|
12
|
-
# Replace the hash with its compacted self
|
13
|
-
#
|
14
|
-
def compact!
|
15
|
-
replace(compact)
|
16
|
-
end unless method_defined?(:compact!)
|
17
|
-
|
18
|
-
#
|
19
|
-
# remove all key-value pairs where the value is blank
|
20
|
-
#
|
21
|
-
def compact_blank
|
22
|
-
reject{|key,val| val.blank? }
|
23
|
-
end unless method_defined?(:compact_blank)
|
24
|
-
|
25
|
-
#
|
26
|
-
# Replace the hash with its compact_blank'ed self
|
27
|
-
#
|
28
|
-
def compact_blank!
|
29
|
-
replace(compact_blank)
|
30
|
-
end unless method_defined?(:compact_blank!)
|
3
|
+
include Gorillib::Hashlike::Compact
|
31
4
|
end
|
@@ -1,14 +1,4 @@
|
|
1
|
-
require 'gorillib/
|
2
|
-
|
3
|
-
#
|
4
|
-
# deep_compact! removes all keys with 'blank?' values in the hash, in place, recursively
|
5
|
-
#
|
1
|
+
require 'gorillib/hashlike/deep_compact'
|
6
2
|
class Hash
|
7
|
-
|
8
|
-
self.each do |key, val|
|
9
|
-
val.deep_compact! if val.respond_to?(:deep_compact!)
|
10
|
-
self.delete(key) if val.blank?
|
11
|
-
end
|
12
|
-
self
|
13
|
-
end
|
3
|
+
include Gorillib::Hashlike::DeepCompact
|
14
4
|
end
|
@@ -1,16 +1,4 @@
|
|
1
|
+
require 'gorillib/hashlike/deep_merge'
|
1
2
|
class Hash
|
2
|
-
|
3
|
-
def deep_merge(other_hash)
|
4
|
-
dup.deep_merge!(other_hash)
|
5
|
-
end unless method_defined?(:deep_merge)
|
6
|
-
|
7
|
-
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
8
|
-
# Modifies the receiver in place.
|
9
|
-
def deep_merge!(other_hash)
|
10
|
-
other_hash.each_pair do |k,v|
|
11
|
-
tv = self[k]
|
12
|
-
self[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v
|
13
|
-
end
|
14
|
-
self
|
15
|
-
end unless method_defined?(:deep_merge!)
|
3
|
+
include Gorillib::Hashlike::DeepMerge
|
16
4
|
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'gorillib/hash/keys'
|
2
|
+
|
3
|
+
# This class has dubious semantics and we only have it so that
|
4
|
+
# people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
|
5
|
+
# and they get the same value for both keys.
|
6
|
+
|
7
|
+
module Gorillib
|
8
|
+
class HashWithIndifferentAccess < Hash
|
9
|
+
def extractable_options?
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def with_indifferent_access
|
14
|
+
dup
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(constructor = {})
|
18
|
+
if constructor.is_a?(Hash)
|
19
|
+
super()
|
20
|
+
update(constructor)
|
21
|
+
else
|
22
|
+
super(constructor)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def default(key = nil)
|
27
|
+
if include?(converted = convert_key(key))
|
28
|
+
self[converted]
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.new_from_hash_copying_default(hash)
|
35
|
+
new(hash).tap do |new_hash|
|
36
|
+
new_hash.default = hash.default
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method(:regular_writer, :[]=) unless method_defined?(:regular_writer)
|
41
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
42
|
+
|
43
|
+
# Assigns a new value to the hash:
|
44
|
+
#
|
45
|
+
# hash = HashWithIndifferentAccess.new
|
46
|
+
# hash[:key] = "value"
|
47
|
+
#
|
48
|
+
def []=(key, value)
|
49
|
+
regular_writer(convert_key(key), convert_value(value))
|
50
|
+
end
|
51
|
+
|
52
|
+
alias_method :store, :[]=
|
53
|
+
|
54
|
+
# Updates the instantized hash with values from the second:
|
55
|
+
#
|
56
|
+
# hash_1 = HashWithIndifferentAccess.new
|
57
|
+
# hash_1[:key] = "value"
|
58
|
+
#
|
59
|
+
# hash_2 = HashWithIndifferentAccess.new
|
60
|
+
# hash_2[:key] = "New Value!"
|
61
|
+
#
|
62
|
+
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
|
63
|
+
#
|
64
|
+
def update(other_hash)
|
65
|
+
raise TypeError, "can't convert #{other_hash.nil? ? 'nil' : other_hash.class} into Hash" unless other_hash.respond_to?(:each_pair)
|
66
|
+
if other_hash.is_a? HashWithIndifferentAccess
|
67
|
+
super(other_hash)
|
68
|
+
else
|
69
|
+
other_hash.each_pair{|key, value| regular_writer(convert_key(key), convert_value(value)) }
|
70
|
+
self
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
alias_method :merge!, :update
|
75
|
+
|
76
|
+
# Checks the hash for a key matching the argument passed in:
|
77
|
+
#
|
78
|
+
# hash = HashWithIndifferentAccess.new
|
79
|
+
# hash["key"] = "value"
|
80
|
+
# hash.key? :key # => true
|
81
|
+
# hash.key? "key" # => true
|
82
|
+
#
|
83
|
+
def key?(key)
|
84
|
+
super(convert_key(key))
|
85
|
+
end
|
86
|
+
|
87
|
+
alias_method :include?, :key?
|
88
|
+
alias_method :has_key?, :key?
|
89
|
+
alias_method :member?, :key?
|
90
|
+
|
91
|
+
# Fetches the value for the specified key, same as doing hash[key]
|
92
|
+
def fetch(key, *extras)
|
93
|
+
super(convert_key(key), *extras)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns an array of the values at the specified indices:
|
97
|
+
#
|
98
|
+
# hash = HashWithIndifferentAccess.new
|
99
|
+
# hash[:a] = "x"
|
100
|
+
# hash[:b] = "y"
|
101
|
+
# hash.values_at("a", "b") # => ["x", "y"]
|
102
|
+
#
|
103
|
+
def values_at(*indices)
|
104
|
+
indices.collect {|key| self[convert_key(key)]}
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns an exact copy of the hash.
|
108
|
+
def dup
|
109
|
+
self.class.new(self).tap do |new_hash|
|
110
|
+
new_hash.default = default
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
|
115
|
+
# Does not overwrite the existing hash.
|
116
|
+
def merge(hash)
|
117
|
+
self.dup.update(hash)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
|
121
|
+
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
|
122
|
+
def reverse_merge(other_hash)
|
123
|
+
super self.class.new_from_hash_copying_default(other_hash)
|
124
|
+
end
|
125
|
+
|
126
|
+
def reverse_merge!(other_hash)
|
127
|
+
replace(reverse_merge( other_hash ))
|
128
|
+
end
|
129
|
+
|
130
|
+
# Removes a specified key from the hash.
|
131
|
+
def delete(key)
|
132
|
+
super(convert_key(key))
|
133
|
+
end
|
134
|
+
|
135
|
+
def stringify_keys!; self end
|
136
|
+
def stringify_keys; dup end
|
137
|
+
undef_method :symbolize_keys! if method_defined?(:symbolize_keys!)
|
138
|
+
def symbolize_keys; to_hash.symbolize_keys end
|
139
|
+
def to_options!; self end
|
140
|
+
|
141
|
+
# Convert to a Hash with String keys.
|
142
|
+
def to_hash
|
143
|
+
Hash.new(default).merge!(self)
|
144
|
+
end
|
145
|
+
|
146
|
+
def assoc key
|
147
|
+
key = convert_key(key)
|
148
|
+
return unless has_key?(key)
|
149
|
+
[key, self[key]]
|
150
|
+
end
|
151
|
+
|
152
|
+
def rassoc val
|
153
|
+
key = key(val) or return
|
154
|
+
[key, self[key]]
|
155
|
+
end
|
156
|
+
|
157
|
+
protected
|
158
|
+
def convert_key(key)
|
159
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
160
|
+
end
|
161
|
+
|
162
|
+
def convert_value(value)
|
163
|
+
if value.is_a? Hash
|
164
|
+
value.nested_under_indifferent_access
|
165
|
+
elsif value.is_a?(Array)
|
166
|
+
value.dup.replace(value.map{|e| convert_value(e) })
|
167
|
+
else
|
168
|
+
value
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
module Gorillib
|
176
|
+
class HashWithIndifferentSymbolKeys < Gorillib::HashWithIndifferentAccess
|
177
|
+
|
178
|
+
def convert_key key
|
179
|
+
return key if key.is_a?(Fixnum)
|
180
|
+
key.respond_to?(:to_sym) ? key.to_sym : key
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class Hash
|
186
|
+
|
187
|
+
# Returns an +ActiveSupport::HashWithIndifferentAccess+ out of its receiver:
|
188
|
+
#
|
189
|
+
# {:a => 1}.with_indifferent_access["a"] # => 1
|
190
|
+
#
|
191
|
+
def with_indifferent_access
|
192
|
+
Gorillib::HashWithIndifferentAccess.new_from_hash_copying_default(self)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Called when object is nested under an object that receives
|
196
|
+
# #with_indifferent_access. This method with be called on the current object
|
197
|
+
# by the enclosing object and is aliased to #with_indifferent_access by
|
198
|
+
# default. Subclasses of Hash may overwrite this method to return +self+ if
|
199
|
+
# converting to an +ActiveSupport::HashWithIndifferentAccess+ would not be
|
200
|
+
# desirable.
|
201
|
+
#
|
202
|
+
# b = {:b => 1}
|
203
|
+
# {:a => b}.with_indifferent_access["a"] # calls b.nested_under_indifferent_access
|
204
|
+
#
|
205
|
+
alias nested_under_indifferent_access with_indifferent_access
|
206
|
+
end
|
207
|
+
|