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.
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
+