insensitive_hash 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: