gorillib 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/CHANGELOG.textile +6 -0
  2. data/README.textile +34 -11
  3. data/VERSION +1 -1
  4. data/gorillib.gemspec +63 -5
  5. data/lib/gorillib/enumerable/sum.rb +2 -2
  6. data/lib/gorillib/hash/compact.rb +2 -29
  7. data/lib/gorillib/hash/deep_compact.rb +2 -12
  8. data/lib/gorillib/hash/deep_dup.rb +4 -0
  9. data/lib/gorillib/hash/deep_merge.rb +2 -14
  10. data/lib/gorillib/hash/indifferent_access.rb +207 -0
  11. data/lib/gorillib/hash/keys.rb +2 -40
  12. data/lib/gorillib/hash/reverse_merge.rb +2 -24
  13. data/lib/gorillib/hash/slice.rb +2 -51
  14. data/lib/gorillib/hash/tree_merge.rb +4 -0
  15. data/lib/gorillib/hashlike.rb +824 -0
  16. data/lib/gorillib/hashlike/compact.rb +60 -0
  17. data/lib/gorillib/hashlike/deep_compact.rb +18 -0
  18. data/lib/gorillib/hashlike/deep_dup.rb +15 -0
  19. data/lib/gorillib/hashlike/deep_merge.rb +20 -0
  20. data/lib/gorillib/hashlike/hashlike_via_accessors.rb +169 -0
  21. data/lib/gorillib/hashlike/keys.rb +59 -0
  22. data/lib/gorillib/hashlike/reverse_merge.rb +31 -0
  23. data/lib/gorillib/hashlike/slice.rb +67 -0
  24. data/lib/gorillib/hashlike/tree_merge.rb +76 -0
  25. data/lib/gorillib/metaprogramming/mattr_accessor.rb +1 -1
  26. data/lib/gorillib/receiver.rb +315 -0
  27. data/lib/gorillib/receiver/active_model_shim.rb +19 -0
  28. data/lib/gorillib/receiver/acts_as_hash.rb +191 -0
  29. data/lib/gorillib/receiver/acts_as_loadable.rb +42 -0
  30. data/lib/gorillib/receiver/tree_diff.rb +74 -0
  31. data/lib/gorillib/receiver/validations.rb +30 -0
  32. data/lib/gorillib/struct/acts_as_hash.rb +108 -0
  33. data/lib/gorillib/struct/hashlike_iteration.rb +0 -0
  34. data/notes/fancy_hashes_and_receivers.textile +120 -0
  35. data/notes/hash_rdocs.textile +97 -0
  36. data/spec/hash/deep_merge_spec.rb +0 -2
  37. data/spec/hash/indifferent_access_spec.rb +391 -0
  38. data/spec/hash/slice_spec.rb +35 -12
  39. data/spec/hashlike/behave_same_as_hash_spec.rb +105 -0
  40. data/spec/hashlike/hashlike_behavior_spec.rb +824 -0
  41. data/spec/hashlike/hashlike_via_accessors_fuzzing_spec.rb +37 -0
  42. data/spec/hashlike/hashlike_via_accessors_spec.rb +262 -0
  43. data/spec/hashlike_spec.rb +302 -0
  44. data/spec/metaprogramming/aliasing_spec.rb +3 -0
  45. data/spec/metaprogramming/cattr_accessor_spec.rb +2 -0
  46. data/spec/metaprogramming/class_attribute_spec.rb +2 -0
  47. data/spec/metaprogramming/delegation_spec.rb +2 -0
  48. data/spec/metaprogramming/mattr_accessor_spec.rb +2 -0
  49. data/spec/metaprogramming/singleton_class_spec.rb +3 -0
  50. data/spec/receiver/acts_as_hash_spec.rb +286 -0
  51. data/spec/receiver_spec.rb +478 -0
  52. data/spec/spec_helper.rb +11 -6
  53. data/spec/string/truncate_spec.rb +1 -0
  54. data/spec/struct/acts_as_hash_fuzz_spec.rb +67 -0
  55. data/spec/struct/acts_as_hash_spec.rb +426 -0
  56. data/spec/support/hashlike_fuzzing_helper.rb +127 -0
  57. data/spec/support/hashlike_helper.rb +75 -0
  58. data/spec/support/hashlike_struct_helper.rb +37 -0
  59. data/spec/support/hashlike_via_delegation.rb +30 -0
  60. data/spec/support/matchers/be_array_eql.rb +12 -0
  61. data/spec/support/matchers/be_hash_eql.rb +14 -0
  62. data/spec/support/matchers/enumerate_method.rb +10 -0
  63. data/spec/support/matchers/evaluate_to_true.rb +5 -0
  64. metadata +62 -4
@@ -1,3 +1,9 @@
1
+ h3. Version 0.1.0: Hashlike refactor, Receiver arrives
2
+
3
+ v0.1.0 brings
4
+ * Receiver module
5
+ * refeactoring of hash decorations into a new hashlike class
6
+ * ability to inject hashlike behavior into Struct
1
7
 
2
8
  h3. Version 0.0.7: full test coverage!
3
9
 
@@ -1,15 +1,23 @@
1
+
1
2
  h2. Gorillib: infochimps' lightweight subset of ruby convenience methods
2
3
 
3
- * No creep: include only what you need
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
- h3. TODO:
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
- * Gorillib.demands(:cattr_accessor) -- raises if not included and recommends what to include
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/compact
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/parse_safely
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
- - Date, Time parse_safely
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.8
1
+ 0.1.0
@@ -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"
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-05-20}
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/support/kcode_test_helper.rb"
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/support/kcode_test_helper.rb"
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/object/blank'
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/object/blank'
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
- def deep_compact!
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
@@ -0,0 +1,4 @@
1
+ require 'gorillib/hashlike/deep_dup'
2
+ class Hash
3
+ include Gorillib::Hashlike::DeepDup
4
+ end
@@ -1,16 +1,4 @@
1
+ require 'gorillib/hashlike/deep_merge'
1
2
  class Hash
2
- # Returns a new hash with +self+ and +other_hash+ merged recursively.
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
+