insensitive_hash 0.1.0 → 0.2.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.
@@ -1,3 +1,8 @@
1
+ ### 0.2.0 / 2012/02/01
2
+ * Major rewrite
3
+ * Constructor syntaxes have been changed to match those of standard Hash (not backward-compatible)
4
+ * (Almost) Complete implementation of standard Hash spec.
5
+
1
6
  ### 0.1.0 / 2011/12/20
2
7
  * Can opt-out of monkey-patching Hash with `require 'insensitive_hash/minimal'`
3
8
 
@@ -13,17 +13,22 @@ gem install insensitive_hash
13
13
  require 'insensitive_hash'
14
14
 
15
15
  # Monkey-patched Hash#insensitive method
16
- {'abc' => 1, :def => 2}.insensitive
16
+ ih = {'abc' => 1, :def => 2}.insensitive
17
17
 
18
18
  # Or,
19
- InsensitiveHash.new(:abc => 1, 'DEF' => 2)
19
+ ih = InsensitiveHash[ :abc => 1, 'DEF' => 2 ]
20
+ ih = InsensitiveHash[ :abc, 1, 'DEF', 2 ]
21
+
22
+ # Revert to normal Hash
23
+ h = ih.sensitive
24
+ h = ih.to_hash
20
25
  ```
21
26
 
22
27
  If you don't like to have Hash#insensitive method, `require 'insensitive_hash/minimal'`
23
28
 
24
29
  ### Basic usage
25
30
  ```ruby
26
- ih = InsensitiveHash.new(:abc => 1, 'DEF' => 2)
31
+ ih = InsensitiveHash[:abc => 1, 'DEF' => 2]
27
32
 
28
33
  # Case-insensitive, Symbol/String-indifferent access.
29
34
  ih['Abc'] # 1
@@ -20,6 +20,6 @@ Gem::Specification.new do |s|
20
20
  s.require_paths = ["lib"]
21
21
 
22
22
  # specify any dependencies here; for example:
23
- # s.add_development_dependency "rspec"
23
+ s.add_development_dependency "test-unit", ">= 2.3.0"
24
24
  # s.add_runtime_dependency "rest-client"
25
25
  end
@@ -3,7 +3,7 @@ require 'insensitive_hash/insensitive_hash'
3
3
 
4
4
  class Hash
5
5
  def insensitive
6
- InsensitiveHash.new self
6
+ InsensitiveHash[ self ]
7
7
  end
8
8
  end
9
9
 
@@ -1,36 +1,97 @@
1
1
  class InsensitiveHash < Hash
2
- def initialize hash = {}
2
+ attr_reader :key_map
3
+
4
+ def initialize default = nil, &block
5
+ if block_given?
6
+ super &block
7
+ else
8
+ super
9
+ end
10
+
3
11
  @key_map = {}
12
+ end
13
+
14
+ # Returns a normal, sensitive Hash
15
+ # @return [Hash]
16
+ def to_hash
17
+ {}.merge self
18
+ end
19
+ alias sensitive to_hash
4
20
 
5
- hash.each do |key, value|
6
- self[key] = value
21
+ def self.[] *init
22
+ h = Hash[*init]
23
+ InsensitiveHash.new.tap do |ih|
24
+ h.each do |key, value|
25
+ ih[key] = value
26
+ end
7
27
  end
8
28
  end
9
29
 
10
- def [] key
11
- super(@key_map[encode key])
30
+ %w[[] assoc has_key? include? key? member?].each do |symb|
31
+ class_eval <<-EVAL
32
+ def #{symb} key
33
+ super lookup_key(key)
34
+ end
35
+ EVAL
12
36
  end
13
37
 
14
38
  def []= key, value
15
- delete key
39
+ ekey = encode key
40
+ if @key_map.has_key? ekey
41
+ delete @key_map[ekey]
42
+ end
43
+
16
44
  @key_map[encode key] = key
45
+ super(lookup_key(key), InsensitiveHash.wrap(value))
46
+ end
47
+ alias store []=
48
+
49
+ def merge! other_hash
50
+ other_hash.each do |key, value|
51
+ self[key] = value
52
+ end
53
+ self
54
+ end
55
+ alias update! merge!
17
56
 
18
- super(key, InsensitiveHash.wrap(value))
57
+ def merge other_hash
58
+ InsensitiveHash[self].tap do |ih|
59
+ ih.merge! other_hash
60
+ end
61
+ end
62
+ alias update merge
63
+
64
+ def delete key, &block
65
+ super lookup_key(key, true), &block
66
+ end
67
+
68
+ def clear
69
+ @key_map.clear
70
+ super
71
+ end
72
+
73
+ def replace other
74
+ clear
75
+ other.each do |k, v|
76
+ self[k] = v
77
+ end
19
78
  end
20
79
 
21
- def has_key? key
22
- super @key_map[encode key]
80
+ def shift
81
+ super.tap do |ret|
82
+ @key_map.delete_if { |k, v| v == ret.first }
83
+ end
23
84
  end
24
85
 
25
- def delete key
26
- super @key_map[encode key]
86
+ def values_at *keys
87
+ keys.map { |k| self[k] }
27
88
  end
28
89
 
29
90
  private
30
91
  def self.wrap value
31
92
  case value
32
93
  when Hash
33
- value.class == InsensitiveHash ? value : InsensitiveHash.new(value)
94
+ value.class == InsensitiveHash ? value : InsensitiveHash[value]
34
95
  when Array
35
96
  value.map { |v| InsensitiveHash.wrap v }
36
97
  else
@@ -38,6 +99,15 @@ private
38
99
  end
39
100
  end
40
101
 
102
+ def lookup_key key, delete = false
103
+ ekey = encode key
104
+ if @key_map.has_key?(ekey)
105
+ delete ? @key_map.delete(ekey) : @key_map[ekey]
106
+ else
107
+ key
108
+ end
109
+ end
110
+
41
111
  def encode key
42
112
  case key
43
113
  when String
@@ -1,3 +1,3 @@
1
1
  class InsensitiveHash < Hash
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,41 +1,21 @@
1
1
  require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup(:default, :development)
2
4
  require 'test/unit'
3
5
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
6
  require 'insensitive_hash/minimal'
5
7
 
6
8
  class TestInsensitiveHash < Test::Unit::TestCase
7
- def test_from_hash
8
- hash = {
9
- 'a' => 1,
10
- 'B' => 2,
11
- :c => { :D => [ { 'e' => 3 } ] }
12
- }
13
-
14
- assert_raise(NoMethodError) {
15
- hash.insensitive
16
- }
17
-
18
- require 'insensitive_hash'
19
- [hash.insensitive, InsensitiveHash.new(hash)].each do |ih|
20
- assert ih.is_a?(Hash)
21
- assert_equal InsensitiveHash, ih.class
22
- ['c', 'C', :c, :C].each do |c|
23
- assert_equal InsensitiveHash, ih[c].class
24
-
25
- ['d', 'D', :d, :D].each do |d|
26
- assert_equal InsensitiveHash, ih[c][d].first.class
27
- end
28
- end
29
- end
30
- end
31
-
32
9
  def test_has_key_set
33
10
  ih = InsensitiveHash.new
34
11
  ih['a'] = 1
35
- ih['A'] = 2
12
+ ih.store 'A', 2
36
13
 
37
14
  ['a', 'A', :a, :A].each do |a|
38
15
  assert ih.has_key?(a)
16
+ assert ih.key?(a)
17
+ assert ih.include?(a)
18
+ assert ih.member?(a)
39
19
  assert_equal 2, ih[a]
40
20
  end
41
21
 
@@ -43,6 +23,7 @@ class TestInsensitiveHash < Test::Unit::TestCase
43
23
  assert_equal [2], ih.values
44
24
 
45
25
  ih[:A] = 4
26
+ assert_equal ih.keys, [:A]
46
27
  assert_equal [:A], ih.keys
47
28
  assert_equal [4], ih.values
48
29
 
@@ -50,6 +31,7 @@ class TestInsensitiveHash < Test::Unit::TestCase
50
31
  assert ih.keys.include?(:A)
51
32
  assert ih.keys.include?(:b)
52
33
  assert_equal 2, ih.keys.length
34
+ assert_equal({ :c => 5 }, ih['B'])
53
35
  assert_equal 5, ih['B']['C']
54
36
 
55
37
  ih['c'] = [ { 'x' => 1 }, { :y => 2 } ]
@@ -68,12 +50,227 @@ class TestInsensitiveHash < Test::Unit::TestCase
68
50
  assert_equal 50, ih[5]
69
51
  end
70
52
 
53
+ def test_from_hash
54
+ hash = {
55
+ 'a' => 1,
56
+ 'B' => 2,
57
+ :c => { :D => [ { 'e' => 3 } ] }
58
+ }
59
+
60
+ assert_raise(NoMethodError) {
61
+ hash.insensitive
62
+ }
63
+
64
+ require 'insensitive_hash'
65
+ [hash.insensitive, InsensitiveHash[hash]].each do |ih|
66
+ assert ih.is_a?(Hash)
67
+ assert_equal InsensitiveHash, ih.class
68
+ ['c', 'C', :c, :C].each do |c|
69
+ assert_equal InsensitiveHash, ih[c].class
70
+
71
+ ['d', 'D', :d, :D].each do |d|
72
+ assert_equal InsensitiveHash, ih[c][d].first.class
73
+ end
74
+ end
75
+ end
76
+ end
77
+
71
78
  def test_delete
72
- ih = InsensitiveHash.new(:a => 1)
79
+ ih = InsensitiveHash[:a => 1]
73
80
  assert_equal [:a], ih.keys
74
81
  assert_equal [1], ih.values
75
82
 
83
+ assert_equal nil, ih.delete('B')
76
84
  assert_equal 1, ih.delete('A')
85
+ assert_equal :notfound, ih.delete('A') { |a| :notfound }
77
86
  assert_equal [], ih.keys
78
87
  end
88
+
89
+ def test_merge
90
+ [:merge, :update].each do |method|
91
+ ih = InsensitiveHash[:a => 1]
92
+ ih2 = ih.send(method, :b => 2)
93
+
94
+ assert_equal [:a], ih.keys
95
+ assert_equal [:a, :b], ih2.keys
96
+ assert ih2.has_key?('B'), 'correctly merged another hash'
97
+
98
+ ih2.delete 'b'
99
+ assert ih2.has_key?('B') == false
100
+ end
101
+ end
102
+
103
+ def test_merge!
104
+ [:merge!, :update!].each do |method|
105
+ ih = InsensitiveHash[:a => 1]
106
+ ih2 = ih.send(method, :b => 2)
107
+
108
+ assert_equal [:a, :b], ih.keys
109
+ assert_equal [:a, :b], ih2.keys
110
+ assert ih2.has_key?('B'), 'correctly merged another hash'
111
+
112
+ ih2.delete 'b'
113
+ assert ih2.has_key?('B') == false
114
+ end
115
+ end
116
+
117
+ def test_assoc
118
+ h = InsensitiveHash[{
119
+ "colors" => ["red", "blue", "green"],
120
+ :Letters => ["a", "b", "c" ]
121
+ }]
122
+ assert_equal [:Letters, ["a", "b", "c"]], h.assoc("letters")
123
+ assert_equal ["colors", ["red", "blue", "green"]], h.assoc(:COLORS)
124
+ end
125
+
126
+ def test_clear_empty?
127
+ h = InsensitiveHash[:a, 1]
128
+ h.clear
129
+ assert_equal [], h.keys
130
+ assert h.empty?
131
+ end
132
+
133
+ def test_to_hash
134
+ h = InsensitiveHash[:a, 1, :b, {:c => 2}]
135
+ assert_equal 1, h[:A]
136
+ assert_equal 2, h['B']['C']
137
+
138
+ h = h.to_hash
139
+ assert_equal Hash, h.class
140
+ assert_equal nil, h[:A]
141
+ assert_equal 1, h[:a]
142
+ assert_equal 2, h[:b][:c]
143
+ pend("TBD: Recursive conversion") do
144
+ assert_equal nil, h[:b]['C']
145
+ end
146
+ end
147
+
148
+ def test_compare_by_identity
149
+ pend 'Not Implemented'
150
+
151
+ key = 'a'
152
+ key2 = 'a'.clone
153
+ h = InsensitiveHash.new
154
+ h[key] = 1
155
+ h[:b] = 2
156
+
157
+ assert !h.compare_by_identity?
158
+
159
+ assert_equal 1, h['A']
160
+ assert_equal 2, h[:b]
161
+ h.compare_by_identity
162
+
163
+ assert h.compare_by_identity?
164
+
165
+ assert_equal nil, h[key2]
166
+ assert_equal nil, h[key]
167
+ assert_equal 2, h[:B]
168
+
169
+ h[key] = 3
170
+ assert_not_equal 'a'.object_id, key.object_id
171
+ assert_equal nil, h[key2]
172
+ assert_equal 3, h[key]
173
+ end
174
+
175
+ def test_initializer
176
+ # Hash
177
+ h = InsensitiveHash[ { :a => 1 } ]
178
+ assert_equal 1, h['A']
179
+ assert_equal [:a], h.keys
180
+ assert_equal [1], h.values
181
+
182
+ # Pairs
183
+ h = InsensitiveHash[ 'a', 2, 3, 4 ]
184
+ assert_equal 2, h[:a]
185
+
186
+ # Wrong number of arguments
187
+ assert_raise(ArgumentError) { h = InsensitiveHash[ 'a', 2, 3 ] }
188
+ end
189
+
190
+ def test_default
191
+ h = InsensitiveHash.new
192
+ assert_nil h.default
193
+
194
+ h = InsensitiveHash.new 'a'
195
+ assert_equal 'a', h.default
196
+ assert_equal 'a', h[:not_there]
197
+
198
+ h = InsensitiveHash.new { |h, k| h[k] = k == :right ? :ok : nil }
199
+ assert_equal nil, h.default
200
+ assert_equal nil, h.default(:wrong)
201
+ assert_equal :ok, h.default(:right)
202
+ end
203
+
204
+ def test_delete_if
205
+ # FIXME: Test passes, but key_map not updated
206
+ h = InsensitiveHash[ :a => 100, :tmp_a => 200, :c => 300 ]
207
+ h.delete_if.each { |k, v| k == :tmp_a }
208
+ assert_equal [:a, :c], h.keys
209
+ end
210
+
211
+ def test_has_key_after_delete
212
+ set = [:a, :A, 'a', 'A', :b, :B, 'b', 'B']
213
+ h = InsensitiveHash[ :a => 1, :b => 2 ]
214
+
215
+ set.each { |s| assert h.has_key?(s) }
216
+ h.delete_if { |e| true }
217
+ set.each { |s| assert !h.has_key?(s) }
218
+ end
219
+
220
+ def test_nil
221
+ h = InsensitiveHash.new(1)
222
+ h[nil] = 2
223
+ assert h.has_key?(nil)
224
+ assert !h.has_key?(:not_there)
225
+ assert_equal 1, h[:not_there]
226
+ assert_equal 2, h[nil]
227
+
228
+ h = InsensitiveHash[ nil => 1 ]
229
+ assert_equal :notfound, h.delete('a') { :notfound }
230
+ assert_equal 1, h.delete(nil) { :notfound }
231
+ end
232
+
233
+ def test_each
234
+ h = InsensitiveHash[{ :a => 1, :b => 2, :c => 3}]
235
+ assert_equal 3, h.each.count
236
+ end
237
+
238
+ def test_fetch
239
+ h = InsensitiveHash[{ :a => 1, :b => 2, :c => 3}]
240
+ assert_raise(KeyError) { h.fetch('D') }
241
+ end
242
+
243
+ def test_has_value
244
+ h = InsensitiveHash[{ :a => 1, :b => 2, :c => 3}]
245
+ assert h.value?(3)
246
+ end
247
+
248
+ def test_replace
249
+ h = InsensitiveHash[:a, 1]
250
+ assert h.has_key?('A')
251
+
252
+ h.replace({ :b => 2 })
253
+
254
+ assert !h.has_key?('A')
255
+ assert h.has_key?('B')
256
+ end
257
+
258
+ def test_rassoc
259
+ a = InsensitiveHash[{1=> "one", 2 => "two", 3 => "three", "ii" => "two"}]
260
+ assert_equal [2, "two"], a.rassoc("two")
261
+ assert_nil a.rassoc("four")
262
+ end
263
+
264
+ def test_shift
265
+ h = InsensitiveHash[{:a => 1, :b => 2}]
266
+ assert_equal [:a, 1], h.shift
267
+ assert !h.has_key?('A')
268
+ assert h.has_key?('B')
269
+ end
270
+
271
+ def test_values_at
272
+ h = InsensitiveHash[{:a => 1, :b => 2}]
273
+ assert_equal [2, 1], h.values_at('B', :A)
274
+ end
79
275
  end
276
+
metadata CHANGED
@@ -1,66 +1,70 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: insensitive_hash
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
4
5
  prerelease:
5
- version: 0.1.0
6
6
  platform: ruby
7
- authors:
8
- - Junegunn Choi
7
+ authors:
8
+ - Junegunn Choi
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-12-20 00:00:00 Z
14
- dependencies: []
15
-
12
+ date: 2012-02-01 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: test-unit
16
+ requirement: &2156362640 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.3.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2156362640
16
25
  description: Hash with case-insensitive, Symbol/String-indifferent key access
17
- email:
18
- - junegunn.c@gmail.com
26
+ email:
27
+ - junegunn.c@gmail.com
19
28
  executables: []
20
-
21
29
  extensions: []
22
-
23
30
  extra_rdoc_files: []
24
-
25
- files:
26
- - .gitignore
27
- - CHANGELOG.markdown
28
- - Gemfile
29
- - LICENSE.txt
30
- - README.markdown
31
- - Rakefile
32
- - insensitive_hash.gemspec
33
- - lib/insensitive_hash.rb
34
- - lib/insensitive_hash/insensitive_hash.rb
35
- - lib/insensitive_hash/minimal.rb
36
- - lib/insensitive_hash/version.rb
37
- - test/test_insensitive_hash.rb
31
+ files:
32
+ - .gitignore
33
+ - CHANGELOG.markdown
34
+ - Gemfile
35
+ - LICENSE.txt
36
+ - README.markdown
37
+ - Rakefile
38
+ - insensitive_hash.gemspec
39
+ - lib/insensitive_hash.rb
40
+ - lib/insensitive_hash/insensitive_hash.rb
41
+ - lib/insensitive_hash/minimal.rb
42
+ - lib/insensitive_hash/version.rb
43
+ - test/test_insensitive_hash.rb
38
44
  homepage: https://github.com/junegunn/insensitive_hash
39
45
  licenses: []
40
-
41
46
  post_install_message:
42
47
  rdoc_options: []
43
-
44
- require_paths:
45
- - lib
46
- required_ruby_version: !ruby/object:Gem::Requirement
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
47
51
  none: false
48
- requirements:
49
- - - ">="
50
- - !ruby/object:Gem::Version
51
- version: "0"
52
- required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
57
  none: false
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- version: "0"
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
58
62
  requirements: []
59
-
60
63
  rubyforge_project: insensitive_hash
61
- rubygems_version: 1.8.11
64
+ rubygems_version: 1.8.15
62
65
  signing_key:
63
66
  specification_version: 3
64
67
  summary: Case-insensitive Ruby Hash
65
- test_files:
66
- - test/test_insensitive_hash.rb
68
+ test_files:
69
+ - test/test_insensitive_hash.rb
70
+ has_rdoc: