insensitive_hash_bxf 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 280229258001084e66fcc3b09e9f8691759dd9b2f1dff3d93b8e0346720edf9a
4
+ data.tar.gz: 67b834f76f1158d9bd11cca3c9a3364a262eacd77d3532df7c7f242416760157
5
+ SHA512:
6
+ metadata.gz: 9486c4192d63d6ce5f8b07aa980ac9aea15d23e9225d723c08945254bd0e28bf85101c8174995ab8822cf739d58f9376fbd3d67ce8088d28100b6ad78f09c7df
7
+ data.tar.gz: 8c5212705169beb3e7cc8f6cde8074fe29118b1d814578500bb9d173b5e8dce1cba20a404e4c756e6e581676a25c0191bda1b6a6da6fd5a95db6c7e35fdc06fd
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.swp
6
+ *.swo
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.4
4
+ - 2.5
5
+ - 2.6
6
+ - 2.7
@@ -0,0 +1,65 @@
1
+ ### 0.3.3
2
+
3
+ - Code refactoring
4
+ - List new contributors
5
+
6
+ ### 0.3.2
7
+
8
+ - Supports custom key encoder
9
+ - Added `InsensitiveHash#merge_recursive!`
10
+
11
+ ### 0.3.0
12
+
13
+ - Now allowing underscores for spaces is by default on, and cannot be turned off.
14
+ - "Inherited insensitivity" had been a great source of confusion,
15
+ as it stores transformed version of the given Array of Hash.
16
+ From 0.3.0, Hash values and descendant Hashes are converted to be insensitive
17
+ only on the initialization of InsensitiveHash.
18
+ Refer to the following code
19
+
20
+ ```ruby
21
+ ih = {}.insensitive
22
+ ih[:a] = { :b => :c }
23
+
24
+ ih[:a]['B'] # nil
25
+
26
+ # Initialize another InsensitiveHash
27
+ ih2 = ih.insensitive
28
+ ih2[:a]['B'] # :c
29
+ ```
30
+
31
+ ### 0.2.4
32
+
33
+ - Bug fix: Invalid `dup`, `clone` behavior
34
+ - :safe setting from `Hash#insensitive`
35
+
36
+ ### 0.2.3 / 2012/02/18
37
+
38
+ - Key-clash detection made optional with `InsensitiveHash#safe=`
39
+ - Ruby 1.8 compatibility
40
+
41
+ ### 0.2.2 / 2012/02/13
42
+
43
+ - :underscore option added
44
+ - `InsensitiveHash::KeyClashError` added for safer operations
45
+ - Bug fix: Preserve default/default_proc on merge/replace/insensitive
46
+ - Subtle behavioral change in `InsensitiveHash#replace` when called with an ordinary Hash
47
+
48
+ ### 0.2.1 / 2012/02/10
49
+
50
+ - Bug fix: Insensitive `fetch`
51
+
52
+ ### 0.2.0 / 2012/02/01
53
+
54
+ - Major rewrite
55
+ - Constructor syntaxes have been changed to match those of standard Hash (not backward-compatible)
56
+ - (Almost) Complete implementation of standard Hash spec.
57
+
58
+ ### 0.1.0 / 2011/12/20
59
+
60
+ - Can opt-out of monkey-patching Hash with `require 'insensitive_hash/minimal'`
61
+
62
+ ### 0.0.2-3 / 2011/10/28
63
+
64
+ - Removed duplicated code in the constructor
65
+ - More tests
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'http://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in insensitive_hash.gemspec
6
+ gemspec
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2013 Junegunn Choi
2
+ Copyright (c) 2018-2020 Box Factura
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,149 @@
1
+ # insensitive_hash_bxf
2
+
3
+ [![Build Status](https://travis-ci.org/BoxFactura/insensitive_hash.svg?branch=master)](https://travis-ci.org/BoxFactura/insensitive_hash)
4
+
5
+ Hash with case-insensitive, Symbol/String-indifferent key access.
6
+
7
+ > This project is a fork from [https://github.com/junegunn/insensitive_hash](https://github.com/junegunn/insensitive_hash) and it was made to continue its maintenance.
8
+
9
+ ## Installation
10
+
11
+ ```
12
+ gem install insensitive_hash_bxf
13
+ ```
14
+
15
+ ## Instantiation
16
+
17
+ ### Hash#insensitive
18
+
19
+ ```ruby
20
+ require 'insensitive_hash'
21
+
22
+ ih = {}.insensitive
23
+
24
+ ih = { :abc => 1, 'hello world' => true }.insensitive
25
+
26
+ ih['ABC'] # 1
27
+ ih[:Hello_World] # true
28
+ ```
29
+
30
+ ### Without monkey-patching Hash
31
+
32
+ If you don't like to have Hash#insensitive method, `require 'insensitive_hash/minimal'`
33
+
34
+ ```ruby
35
+ require 'insensitive_hash/minimal'
36
+
37
+ ih = InsensitiveHash.new
38
+ ih = InsensitiveHash.new(:default_value)
39
+ ih = InsensitiveHash.new { |ih, k| ih[k] = InsensitiveHash.new }
40
+
41
+ ih = InsensitiveHash[ 'abc' => 1, :def => 2 ]
42
+ ih = InsensitiveHash[ 'abc', 1, :def, 2 ]
43
+ ih = InsensitiveHash[ [['abc', 1], [:def, 2]] ]
44
+
45
+ ih = InsensitiveHash[ 'hello world' => true ]
46
+ ```
47
+
48
+ ### Revert to normal Hash
49
+
50
+ ```ruby
51
+ h = ih.sensitive
52
+ h = ih.to_hash
53
+ ```
54
+
55
+ ## Basic usage
56
+
57
+ ```ruby
58
+ ih = {:abc => 1, 'DEF' => 2}.insensitive
59
+
60
+ # Case-insensitive, Symbol/String-indifferent access.
61
+ ih['Abc'] # 1
62
+ ih[:ABC] # 1
63
+ ih['abc'] # 1
64
+ ih[:abc] # 1
65
+ ih.has_key?(:DeF) # true
66
+
67
+ ih['ABC'] = 10
68
+
69
+ # keys and values
70
+ ih.keys # ['DEF', 'ABC']
71
+ ih.values # [2, 10]
72
+
73
+ # delete
74
+ ih.delete :Abc # 10
75
+ ih.keys # ['DEF']
76
+ ```
77
+
78
+ ## Inherited insensitivity
79
+
80
+ When an InsensitiveHash is built from another Hash,
81
+ descendant Hash values are recursively converted to be insensitive.
82
+
83
+ ```ruby
84
+
85
+ ih = { 'kids' => { :hello => [ { :world => '!!!' } ] } }.insensitive
86
+ ih[:kids]['Hello'].first['WORLD'] # !!!
87
+
88
+ ih = {:one => [ [ [ { :a => { :b => { :c => 'd' } } } ] ] ]}.insensitive
89
+ ih['one'].first.first.first['A']['b'][:C] # 'd'
90
+ ```
91
+
92
+ However, once InsensitiveHash is initialized,
93
+ descendant Hashes (or Hashes in Arrays) are not automatically converted.
94
+
95
+ ```ruby
96
+ ih = {}.insensitive
97
+ ih[:abc] = { :def => true }
98
+
99
+ ih['ABC']['DEF'] # nil
100
+ ```
101
+
102
+ Simply build a new InsensitiveHash out of it if you need recursive conversion.
103
+
104
+ ```ruby
105
+ ih2 = ih.insensitive
106
+ ih2['ABC']['DEF'] # true
107
+ ```
108
+
109
+ ### Example: Processing case-insensitive YAML input
110
+
111
+ ```ruby
112
+ db = YAML.load(File.read 'database.yml').insensitive
113
+
114
+ # Access values however you like
115
+ db['Development']['ADAPTER']
116
+ db[:production][:adapter]
117
+ ```
118
+
119
+ ## Enabling key-clash detection (Safe mode)
120
+
121
+ ```ruby
122
+ ih = InsensitiveHash.new
123
+ ih.safe = true
124
+
125
+ # Will raise InsensitiveHash::KeyClashError
126
+ h.merge!('hello world' => 1, :hello_world => 2)
127
+
128
+ # Disables key-clash detection
129
+ h.safe = false
130
+ h.merge!('hello world' => 1, :hello_world => 2)
131
+ h['Hello World'] # 2
132
+ ```
133
+
134
+ ## Contributing to insensitive_hash
135
+
136
+ - Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
137
+ - Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
138
+ - Fork the project
139
+ - Start a feature/bugfix branch
140
+ - Commit and push until you are happy with your contribution
141
+ - Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
142
+ - Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
143
+
144
+ ## Copyright
145
+
146
+ - Copyright (c) 2013 Junegunn Choi.
147
+ - Copyright (c) 2018-2020 Box Factura
148
+
149
+ See LICENSE.txt for further details.
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |test|
7
+ test.libs << 'lib' << 'test'
8
+ test.pattern = 'test/**/test_*.rb'
9
+ test.verbose = true
10
+ end
11
+
12
+ desc 'Run tests'
13
+ task default: :test
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ $:.push File.expand_path('../lib', __FILE__)
4
+ require 'insensitive_hash/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'insensitive_hash_bxf'
8
+ s.version = InsensitiveHash::VERSION
9
+ s.authors = ['Junegunn Choi', 'Arandi Lopez']
10
+ s.email = ['junegunn.c@gmail.com', 'arandilopez.93@gmail.com']
11
+ s.homepage = 'https://github.com/BoxFactura/insensitive_hash'
12
+ s.summary = 'Case-insensitive Ruby Hash'
13
+
14
+ s.description = 'Hash with case-insensitive, Symbol/String-indifferent key access'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ['lib']
20
+
21
+ s.add_development_dependency 'rake'
22
+ s.add_development_dependency 'test-unit', '>= 2.3.0'
23
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'insensitive_hash/version'
4
+ require 'insensitive_hash/insensitive_hash'
5
+
6
+ # :nodoc:
7
+ class Hash
8
+ # @param [Hash] options Options
9
+ # @option options [Boolean] :safe Whether to detect key clash on merge
10
+ # @return [InsensitiveHash]
11
+ def insensitive options = {}
12
+ InsensitiveHash.new.tap do |ih|
13
+ ih.safe = options[:safe] if options.key?(:safe)
14
+ ih.default = default
15
+ ih.default_proc = default_proc if default_proc
16
+
17
+ ih.merge_recursive!(self)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Insensitive Hash.
4
+ # @author Junegunn Choi <junegunn.c@gmail.com>
5
+ class InsensitiveHash < Hash
6
+ # Thrown when safe mode is on and another Hash with conflicting keys cannot be merged safely
7
+ class KeyClashError < RuntimeError; end
8
+
9
+ def initialize(default = nil, &block)
10
+ if block_given?
11
+ raise ArgumentError, 'wrong number of arguments' unless default.nil?
12
+
13
+ super(&block)
14
+ else
15
+ super
16
+ end
17
+
18
+ @key_map = {}
19
+ @safe = false
20
+ end
21
+
22
+ # Sets whether to detect key clashes
23
+ # @param [Boolean]
24
+ # @return [Boolean]
25
+ def safe=(safe_val)
26
+ raise ArgumentError, 'Neither true nor false' unless [true, false].include?(safe_val)
27
+
28
+ @safe = safe_val
29
+ end
30
+
31
+ # @return [Boolean] Key-clash detection enabled?
32
+ def safe?
33
+ @safe
34
+ end
35
+
36
+ # Returns a normal, sensitive Hash
37
+ # @return [Hash]
38
+ def to_hash
39
+ {}.merge self
40
+ end
41
+ alias sensitive to_hash
42
+
43
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
44
+ def self.[](*init)
45
+ h = Hash[*init]
46
+ new.tap do |ih|
47
+ ih.merge_recursive! h
48
+ end
49
+ end
50
+
51
+ %w[[] assoc has_key? include? key? member?].each do |symb|
52
+ class_eval <<-EVAL, __FILE__, __LINE__ + 1
53
+ def #{symb}(key)
54
+ super lookup_key(key)
55
+ end
56
+ EVAL
57
+ end
58
+
59
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
60
+ def []=(key, value)
61
+ delete key
62
+ ekey = encode key
63
+ @key_map[ekey] = key
64
+ super key, value
65
+ end
66
+ alias store []=
67
+
68
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
69
+ def merge!(other_hash)
70
+ detect_clash other_hash
71
+ other_hash.each do |key, value|
72
+ store key, value
73
+ end
74
+ self
75
+ end
76
+ alias update! merge!
77
+
78
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
79
+ def merge(other_hash)
80
+ self.class.new.tap do |ih|
81
+ ih.replace self
82
+ ih.merge! other_hash
83
+ end
84
+ end
85
+ alias update merge
86
+
87
+ # Merge another hash recursively.
88
+ # @param [Hash|InsensitiveHash] other_hash
89
+ # @return [self]
90
+ def merge_recursive!(other_hash)
91
+ detect_clash other_hash
92
+ other_hash.each do |key, value|
93
+ deep_set key, value
94
+ end
95
+ self
96
+ end
97
+ alias update_recursive! merge_recursive!
98
+
99
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
100
+ def delete(key, &block)
101
+ super lookup_key(key, true), &block
102
+ end
103
+
104
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
105
+ def clear
106
+ @key_map.clear
107
+ super
108
+ end
109
+
110
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
111
+ def replace(other)
112
+ super other
113
+
114
+ self.safe = other.respond_to?(:safe?) ? other.safe? : safe?
115
+
116
+ @key_map.clear
117
+ each do |k, _v|
118
+ ekey = encode k
119
+ @key_map[ekey] = k
120
+ end
121
+ end
122
+
123
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
124
+ def shift
125
+ super.tap do |ret|
126
+ @key_map.delete_if { |_k, v| v == ret.first }
127
+ end
128
+ end
129
+
130
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
131
+ def values_at(*keys)
132
+ keys.map { |k| self[k] }
133
+ end
134
+
135
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
136
+ def fetch(*args, &block)
137
+ args[0] = lookup_key(args[0]) if args.first
138
+ super(*args, &block)
139
+ end
140
+
141
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
142
+ def dup
143
+ super.tap { |copy| copy.instance_variable_set :@key_map, @key_map.dup }
144
+ end
145
+
146
+ # @see http://www.ruby-doc.org/core-1.9.3/Hash.html Hash
147
+ def clone
148
+ super.tap { |copy| copy.instance_variable_set :@key_map, @key_map.dup }
149
+ end
150
+
151
+ private
152
+
153
+ def deep_set(key, value)
154
+ wv = wrap value
155
+ self[key] = wv
156
+ end
157
+
158
+ def wrap(value)
159
+ case value
160
+ when InsensitiveHash
161
+ value.tap { |ih| ih.safe = safe? }
162
+ when Hash
163
+ self.class.new.tap do |ih|
164
+ ih.safe = safe?
165
+ ih.merge_recursive!(value)
166
+ end
167
+ when Array
168
+ value.map { |v| wrap v }
169
+ else
170
+ value
171
+ end
172
+ end
173
+
174
+ def lookup_key(key, delete = false)
175
+ @key_map = {} if @key_map.nil?
176
+ ekey = encode key
177
+ if @key_map.key?(ekey)
178
+ delete ? @key_map.delete(ekey) : @key_map[ekey]
179
+ else
180
+ key
181
+ end
182
+ end
183
+
184
+ def encode(key)
185
+ case key
186
+ when String, Symbol
187
+ key.to_s.downcase.gsub(' ', '_')
188
+ else
189
+ key
190
+ end
191
+ end
192
+
193
+ def detect_clash(hash)
194
+ return unless @safe
195
+
196
+ hash.keys.map { |k| encode k }.tap do |ekeys|
197
+ raise KeyClashError, 'Key clash detected' if ekeys != ekeys.uniq
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'insensitive_hash/version'
4
+ require 'insensitive_hash/insensitive_hash'
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ class InsensitiveHash < Hash
5
+ VERSION = '0.4.0'
6
+ end
@@ -0,0 +1,572 @@
1
+ # frozen_string_literal: true
2
+
3
+ $VERBOSE = true
4
+ require 'rubygems'
5
+ require 'bundler'
6
+ Bundler.setup(:default, :development)
7
+ require 'test/unit'
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+ require 'insensitive_hash'
10
+ require 'yaml'
11
+
12
+ class TestInsensitiveHash < Test::Unit::TestCase
13
+ def eight?
14
+ RUBY_VERSION =~ /^1\.8\./
15
+ end
16
+
17
+ def assert_keys set1, set2
18
+ if eight?
19
+ assert_equal(set1.sort { |a, b| a.to_s <=> b.to_s }, set2.sort { |a, b| a.to_s <=> b.to_s })
20
+ else
21
+ assert_equal set1, set2
22
+ end
23
+ end
24
+
25
+ def test_has_key_set
26
+ ih = InsensitiveHash.new
27
+ ih['a'] = 1
28
+ ih.store 'A', 2
29
+
30
+ ['a', 'A', :a, :A].each do |a|
31
+ assert ih.has_key?(a)
32
+ assert ih.key?(a)
33
+ assert ih.include?(a)
34
+ assert ih.member?(a)
35
+ assert_equal 2, ih[a]
36
+ end
37
+
38
+ assert_keys ['A'], ih.keys
39
+ assert_equal [2], ih.values
40
+
41
+ ih[:A] = 4
42
+ assert_keys [:A], ih.keys
43
+ assert_equal [4], ih.values
44
+
45
+ ih[:b] = { :c => 5 }
46
+ assert ih.keys.include?(:A)
47
+ assert ih.keys.include?(:b)
48
+ assert_equal 2, ih.keys.length
49
+ assert_equal({ :c => 5 }, ih['B'])
50
+ assert_equal 5, ih['B'][:c]
51
+ assert_equal nil, ih['B']['C']
52
+
53
+ ih['c'] = [{ 'x' => 1 }, { :y => 2 }]
54
+ assert ih.keys.include?('c')
55
+ assert_equal 3, ih.keys.length
56
+
57
+ assert_equal nil, ih[:c].first[:x]
58
+ assert_equal nil, ih[:c].last['Y']
59
+
60
+ ih = ih.insensitive
61
+ assert_equal 1, ih[:c].first[:x]
62
+ assert_equal 2, ih[:c].last['Y']
63
+
64
+ # Deeeeeper nesting
65
+ ih['c'] = [[[{ 'x' => 1 }, { :y => 2 }]]]
66
+ assert_equal nil, ih[:c].first.first.last[:Y]
67
+ ih = ih.insensitive
68
+ assert_equal 2, ih[:c].first.first.last[:Y]
69
+
70
+ ih['c'] = { 'a' => { 'a' => { 'a' => 100 } } }.insensitive
71
+ assert_equal 100, ih[:C][:a][:A]['A']
72
+
73
+ ih[5] = 50
74
+ assert_equal 50, ih[5]
75
+ end
76
+
77
+ def test_from_hash
78
+ hash = {
79
+ 'a' => 1,
80
+ 'B' => 2,
81
+ :c => { :D => [{ 'e' => 3 }] }
82
+ }
83
+
84
+ pend('TODO') do
85
+ assert_raise(NoMethodError) { hash.insensitive }
86
+ end
87
+
88
+ [hash.insensitive, InsensitiveHash[hash]].each do |ih|
89
+ assert ih.is_a?(Hash)
90
+ assert_equal InsensitiveHash, ih.class
91
+ ['c', 'C', :c, :C].each do |c|
92
+ assert_equal InsensitiveHash, ih[c].class
93
+
94
+ ['d', 'D', :d, :D].each do |d|
95
+ assert_equal InsensitiveHash, ih[c][d].first.class
96
+ end
97
+ end
98
+ end
99
+
100
+ # Changelog 0.3.0
101
+ ih = {}.insensitive
102
+ ih[:a] = { :b => :c }
103
+ assert_nil ih[:a]['B']
104
+ ih2 = ih.insensitive
105
+ assert_equal :c, ih2[:a]['B']
106
+
107
+ # InsensitiveHash#insensitive
108
+ ihash1 = hash.insensitive
109
+ ihash1.default = :default
110
+ ihash2 = ihash1.insensitive.insensitive.insensitive
111
+
112
+ assert_equal InsensitiveHash, ihash2.class
113
+ assert_equal :default, ihash2.default
114
+
115
+ # Preserving default
116
+ [:default, nil].each do |d|
117
+ hash.default = d
118
+ assert_equal d, hash.insensitive.insensitive.insensitive[:anything]
119
+ end
120
+
121
+ # Preserving default_proc
122
+ unless eight?
123
+ hash2 = {}
124
+ hash2.replace hash
125
+ hash2.default_proc = proc { |h, k| h[k] = :default }
126
+
127
+ ihash2 = hash2.insensitive.insensitive.insensitive
128
+ assert !ihash2.has_key?(:anything)
129
+ assert_equal :default, ihash2[:anything]
130
+ assert ihash2.has_key?(:anything)
131
+ end
132
+
133
+ # FIXME: test insensitive call with encoder
134
+ h = InsensitiveHash[{ 'Key with spaces' => 1 }]
135
+ h2 = h.insensitive
136
+
137
+ assert_equal 1, h['key with spaces']
138
+ assert_equal 1, h[:key_with_spaces]
139
+ assert_equal 1, h2[:KEY_WITH_SPACES]
140
+ assert_equal 1, h2[:Key_with_spaces]
141
+
142
+ assert_equal ['Key with spaces'], h.keys
143
+ assert_equal ['Key with spaces'], h2.keys
144
+
145
+ h = { 'A key with spaces' => true }
146
+ ih = h.insensitive
147
+ assert ih[:a_key_with_spaces]
148
+
149
+ # FIXME: from README
150
+ ih = InsensitiveHash[h]
151
+ assert ih[:a_key_with_spaces]
152
+
153
+ # :safe
154
+ ih = {}.insensitive
155
+ assert_raise(ArgumentError) { ihs = {'a' => 1}.insensitive(:safe => 1) }
156
+ ihs = {'a' => 1}.insensitive(:safe => true)
157
+ assert !ih.safe?
158
+ assert ihs.safe?
159
+
160
+ assert_raise(InsensitiveHash::KeyClashError) { ihs.merge(:a => 2, 'A' => 3) }
161
+ end
162
+
163
+ def test_delete
164
+ ih = InsensitiveHash[:a => 1]
165
+ assert_equal [:a], ih.keys
166
+ assert_equal [1], ih.values
167
+
168
+ assert_equal nil, ih.delete('B')
169
+ assert_equal 1, ih.delete('A')
170
+ assert_equal :notfound, ih.delete('A') { |a| :notfound }
171
+ assert_equal [], ih.keys
172
+ end
173
+
174
+ def test_merge
175
+ %i[merge update].each do |method|
176
+ ih = InsensitiveHash[:a => 1, 'hello world' => 2]
177
+ nh = { :d => :e }
178
+ ih2 = ih.send(method, :b => 2, :c => nh)
179
+
180
+ assert_keys [:a, 'hello world'], ih.keys
181
+ assert_keys [:a, 'hello world', :b, :c], ih2.keys
182
+ assert ih2.has_key?('B'), 'correctly merged another hash'
183
+ # No recursive merge
184
+ assert_equal :e, ih2[:c][:d]
185
+ assert_equal nil, ih2[:c][:D]
186
+ assert_equal nh.object_id, ih2[:c].object_id
187
+
188
+ assert_equal 2, ih[:hello_world]
189
+ assert_equal 2, ih.send(method, :b => 2)[:hello_world]
190
+
191
+ ih.default = 10
192
+ assert_equal 10, ih.send(method, :b => 2)[:anything]
193
+
194
+ ih.default = nil
195
+ assert_nil ih.send(method, :b => 2)[:anything]
196
+
197
+ unless eight?
198
+ ih.default_proc = proc { |h, k| h[k] = :default }
199
+ mh = ih.send(method, :b => 2)
200
+ assert !mh.has_key?(:anything)
201
+ assert_equal :default, mh[:anything]
202
+ assert mh.has_key?(:anything)
203
+ end
204
+
205
+ ih2.delete 'b'
206
+ assert ih2.has_key?('B') == false
207
+ end
208
+ end
209
+
210
+ def test_merge!
211
+ %i[merge! update!].each do |method|
212
+ ih = InsensitiveHash[:a => 1]
213
+ ih2 = ih.send(method, :b => 2)
214
+
215
+ assert_keys [:a, :b], ih.keys
216
+ assert_keys [:a, :b], ih2.keys
217
+ assert ih2.has_key?('B'), 'correctly merged another hash'
218
+
219
+ ih2.delete 'b'
220
+ assert ih2.has_key?('B') == false
221
+ end
222
+ end
223
+
224
+ def test_merge_recursive!
225
+ %i[merge_recursive! update_recursive!].each do |method|
226
+ ih = InsensitiveHash.new
227
+ ih[:a] = 1
228
+ ih[:b] = 2
229
+
230
+ nh = { 'b' => 3, :c => :d }
231
+ ih.send(method, nh)
232
+ assert_equal 3, ih[:b]
233
+ assert_equal :d, ih['C']
234
+
235
+ ih.safe = true
236
+ nh = { 'd' => 4, 'D' => 5 }
237
+ assert_raise(InsensitiveHash::KeyClashError) { ih.send(method, nh) }
238
+ ih.safe = false
239
+ ih.send(method, nh)
240
+ end
241
+ end
242
+
243
+ def test_merge_clash_overwritten
244
+ ih = InsensitiveHash.new
245
+
246
+ ih = ih.merge(:a => 1, :A =>2)
247
+ # No guarantee in order in 1.8
248
+ unless eight?
249
+ assert_equal 2, ih.values.first
250
+ assert_equal 2, ih['A']
251
+ end
252
+ assert_equal 1, ih.length
253
+
254
+ ih = InsensitiveHash.new
255
+ ih.merge!(:hello_world => 1, 'hello world' => 2)
256
+ unless eight?
257
+ assert_equal 2, ih.values.first
258
+ assert_equal 2, ih[:Hello_World]
259
+ end
260
+ assert_equal 1, ih.length
261
+ end
262
+
263
+ def test_merge_clash
264
+ ih = InsensitiveHash.new
265
+ ih2 = InsensitiveHash.new
266
+
267
+ sih = InsensitiveHash.new
268
+ sih.safe = true
269
+
270
+ %i[merge merge! update update!].each do |method|
271
+ # No problem
272
+ [ih, ih2].each do |h|
273
+ h.send(method, { :a => 1, :A => 1, 'A' => 1})
274
+ h.send(method, { 'a' => 1, 'A' => 1 })
275
+ end
276
+
277
+ assert_raise(InsensitiveHash::KeyClashError) {
278
+ sih.send(method, { :a => 1, :A => 1, 'A' => 1})
279
+ }
280
+ assert_raise(InsensitiveHash::KeyClashError) {
281
+ sih.send(method, { 'a' => 1, 'A' => 1 })
282
+ }
283
+
284
+ assert_raise(InsensitiveHash::KeyClashError) {
285
+ sih.send(method, { :hello_world => 1, 'hello world' => 2})
286
+ }
287
+ ih2.send(method, { :hello_world => 1, 'hello world' => 2})
288
+ end
289
+
290
+ ih.merge({ :a => 1, :A => 1, 'A' => 1})
291
+ assert_raise(InsensitiveHash::KeyClashError) {
292
+ sih.merge({ :a => 1, :A => 1, 'A' => 1})
293
+ }
294
+ end
295
+
296
+ def test_assoc
297
+ pend "1.8" if eight?
298
+
299
+ h = InsensitiveHash[{
300
+ 'colors' => ['red', 'blue', 'green'],
301
+ :Letters => ['a', 'b', 'c' ]
302
+ }]
303
+ assert_equal [:Letters, ['a', 'b', 'c']], h.assoc('letters')
304
+ assert_equal ['colors', ['red', 'blue', 'green']], h.assoc(:COLORS)
305
+ end
306
+
307
+ def test_clear_empty?
308
+ h = InsensitiveHash[:a, 1]
309
+ h.clear
310
+ assert_equal [], h.keys
311
+ assert h.empty?
312
+ end
313
+
314
+ def test_to_hash
315
+ h = InsensitiveHash[:a, 1, :b, { :c => 2 }]
316
+ assert_equal 1, h[:A]
317
+ assert_equal 2, h['B']['C']
318
+
319
+ h = h.to_hash
320
+ assert_equal Hash, h.class
321
+ assert_equal nil, h[:A]
322
+ assert_equal 1, h[:a]
323
+ assert_equal 2, h[:b][:c]
324
+ pend('TBD: Recursive conversion') do
325
+ assert_equal nil, h[:b]['C']
326
+ end
327
+ end
328
+
329
+ def test_compare_by_identity
330
+ pend 'Not Implemented'
331
+
332
+ key = 'a'
333
+ key2 = 'a'.clone
334
+ h = InsensitiveHash.new
335
+ h[key] = 1
336
+ h[:b] = 2
337
+
338
+ assert !h.compare_by_identity?
339
+
340
+ assert_equal 1, h['A']
341
+ assert_equal 2, h[:b]
342
+ h.compare_by_identity
343
+
344
+ assert h.compare_by_identity?
345
+
346
+ assert_equal nil, h[key2]
347
+ assert_equal nil, h[key]
348
+ assert_equal 2, h[:B]
349
+
350
+ h[key] = 3
351
+ assert_not_equal 'a'.object_id, key.object_id
352
+ assert_equal nil, h[key2]
353
+ assert_equal 3, h[key]
354
+ end
355
+
356
+ def test_initializer
357
+ # Hash
358
+ h = InsensitiveHash[{ :a => 1 }]
359
+ assert_equal 1, h['A']
360
+ assert_equal [:a], h.keys
361
+ assert_equal [1], h.values
362
+
363
+ # Pairs
364
+ h = InsensitiveHash['a', 2, 3, 4]
365
+ assert_equal 2, h[:a]
366
+
367
+ # Wrong number of arguments
368
+ assert_raise(ArgumentError) { h = InsensitiveHash['a', 2, 3] }
369
+ end
370
+
371
+ def test_default
372
+ h = InsensitiveHash.new
373
+ assert_nil h.default
374
+
375
+ h = InsensitiveHash.new 'a'
376
+ assert_equal 'a', h.default
377
+ assert_equal 'a', h[:not_there]
378
+
379
+ h = InsensitiveHash.new { |h, k| h[k] = k == :right ? :ok : nil }
380
+ assert_equal nil, h.default
381
+ assert_equal nil, h.default(:wrong)
382
+ assert_equal :ok, h.default(:right)
383
+ end
384
+
385
+ def test_default_proc_patch
386
+ h = InsensitiveHash.new { |h, k| k }
387
+ assert_equal 1, h[1]
388
+ assert_equal 2, h[2]
389
+
390
+ h = InsensitiveHash.new { |h, k| h[k] = [] }
391
+ h[:abc] << 1
392
+ h[:abc] << 2
393
+ h[:abc] << 3
394
+ assert_equal [1, 2, 3], h[:abc]
395
+
396
+ h = InsensitiveHash.new { |h, k| h[k] = {} }
397
+ h[:abc][:def] = 1
398
+ h[:abc][:ghi] = { :xyz => true }
399
+ assert_equal 1, h[:abc][:def]
400
+ assert_equal true, h[:abc][:ghi][:xyz]
401
+ assert_equal nil, h[:abc][:ghi][:XYZ] # This shouldn't work anymore from 0.3.0
402
+
403
+ h = InsensitiveHash.new
404
+ h[:abc] = arr = [1, 2, 3]
405
+ arr << 4
406
+ assert_equal [1, 2, 3, 4], h[:ABC]
407
+ end
408
+
409
+ def test_delete_if
410
+ # FIXME: Test passes, but key_map not updated
411
+ h = InsensitiveHash[:a => 100, :tmp_a => 200, :c => 300]
412
+ h.delete_if.each { |k, v| k == :tmp_a }
413
+ assert_keys [:a, :c], h.keys
414
+ end
415
+
416
+ def test_has_key_after_delete
417
+ set = [:a, :A, 'a', 'A', :b, :B, 'b', 'B']
418
+ h = InsensitiveHash[:a => 1, :b => 2]
419
+
420
+ set.each { |s| assert h.has_key?(s) }
421
+ h.delete_if { |k, v| true }
422
+ set.each { |s| assert !h.has_key?(s) }
423
+ end
424
+
425
+ def test_nil
426
+ h = InsensitiveHash.new(1)
427
+ h[nil] = 2
428
+ assert h.has_key?(nil)
429
+ assert !h.has_key?(:not_there)
430
+ assert_equal 1, h[:not_there]
431
+ assert_equal 2, h[nil]
432
+
433
+ h = InsensitiveHash[nil => 1]
434
+ assert_equal :notfound, h.delete('a') { :notfound }
435
+ assert_equal 1, h.delete(nil) { :notfound }
436
+ end
437
+
438
+ def test_each
439
+ h = InsensitiveHash[{ :a => 1, :b => 2, :c => 3}]
440
+ assert_equal 3, h.each.count
441
+ end
442
+
443
+ def test_has_value
444
+ h = InsensitiveHash[{ :a => 1, :b => 2, :c => 3}]
445
+ assert h.value?(3)
446
+ end
447
+
448
+ def test_replace
449
+ h = InsensitiveHash[:a, 1]
450
+ assert h.has_key?('A')
451
+
452
+ h.replace({ :b => 2 })
453
+
454
+ assert !h.has_key?('A')
455
+ assert h.has_key?('B')
456
+
457
+ # Default value
458
+ h.replace(Hash.new(5))
459
+ assert_equal 5, h[:anything]
460
+ assert_equal [], h.keys
461
+
462
+ # Default proc
463
+ h.replace(Hash.new { |h, k| h[k] = :default })
464
+ assert_equal :default, h[:anything]
465
+ assert_equal [:anything], h.keys
466
+ end
467
+
468
+ def test_rassoc
469
+ pend "1.8" if eight?
470
+
471
+ a = InsensitiveHash[{1=> 'one', 2 => 'two', 3 => 'three', 'ii' => 'two'}]
472
+ assert_equal [2, 'two'], a.rassoc('two')
473
+ assert_nil a.rassoc('four')
474
+ end
475
+
476
+ def test_shift
477
+ h = InsensitiveHash[{:a => 1, :b => 2}]
478
+ assert_equal [:a, 1], h.shift
479
+ assert !h.has_key?('A')
480
+ assert h.has_key?('B')
481
+ end
482
+
483
+ def test_values_at
484
+ h = InsensitiveHash[{:a => 1, :b => 2}]
485
+ assert_equal [2, 1], h.values_at('B', :A)
486
+ end
487
+
488
+ def test_fetch
489
+ h = InsensitiveHash[{:a => 1, :b => 2}]
490
+ ['a', 'A', :A].each do |k|
491
+ assert_equal 1, h.fetch(k)
492
+ assert_equal 1, h.fetch(k) { nil }
493
+ end
494
+ assert_equal 3, h.fetch(:c, 3)
495
+ assert_equal 3, h.fetch(:c) { 3 }
496
+ assert_raise(eight? ? IndexError : KeyError) { h.fetch('D') }
497
+ end
498
+
499
+ def test_underscore_inheritance
500
+ h =
501
+ {
502
+ 'Key with spaces' => {
503
+ 'Another key with spaces' => 1
504
+ },
505
+ 'Key 2 with spaces' =>
506
+ InsensitiveHash['Yet another key with spaces' => 2],
507
+ 'Key 3 with spaces' =>
508
+ InsensitiveHash['Yet another key with spaces' => 3]
509
+ }.insensitive
510
+
511
+ assert_equal 1, h['key with spaces']['another key with spaces']
512
+
513
+ assert_equal 2, h['key 2 with spaces']['yet another key with spaces']
514
+ assert_equal 2, h['key 2 with spaces'][:yet_another_key_with_spaces]
515
+
516
+ assert_equal 3, h['key 3 with spaces']['yet another key with spaces']
517
+
518
+ assert_equal 1, h[:key_with_spaces][:another_key_with_spaces]
519
+ assert_equal 2, h[:key_2_with_spaces][:yet_another_key_with_spaces]
520
+ assert_equal 3, h[:key_3_with_spaces][:yet_another_key_with_spaces]
521
+ end
522
+
523
+ def test_constructor_default
524
+ h = InsensitiveHash.new :default
525
+ assert_equal :default, h[:xxx]
526
+
527
+ h = InsensitiveHash.new { :default_from_block }
528
+ assert_equal :default_from_block, h[:xxx]
529
+
530
+ # But not both!
531
+ assert_raise(ArgumentError) {
532
+ InsensitiveHash.new(:default) { :default_from_block }
533
+ }
534
+ end
535
+
536
+ def test_dup_clone
537
+ a = InsensitiveHash.new
538
+ a[:key] = :value
539
+
540
+ b = a.dup
541
+ b.delete 'key'
542
+ assert_nil b[:key]
543
+
544
+ assert_equal :value, a[:key]
545
+
546
+ c = a.clone
547
+ c.delete 'KEY'
548
+ assert_nil c[:key]
549
+
550
+ assert_equal :value, a[:key]
551
+ end
552
+
553
+ def test_yaml_serialization
554
+ hash = { 'hello' => 'Hola' }.insensitive
555
+
556
+ yaml = YAML.dump hash
557
+ assert yaml.is_a? String
558
+
559
+ new_hash = YAML.load yaml
560
+ assert new_hash.is_a? InsensitiveHash
561
+ assert new_hash.is_a? Hash
562
+ end
563
+
564
+ class MyInsensitiveHash < InsensitiveHash
565
+ end
566
+
567
+ def test_subclassing
568
+ mih = MyInsensitiveHash[{ 'a' => { 'b' => 3 } }]
569
+ assert_instance_of MyInsensitiveHash, mih
570
+ assert_instance_of MyInsensitiveHash, mih['a']
571
+ end
572
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: insensitive_hash_bxf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Junegunn Choi
8
+ - Arandi Lopez
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2020-10-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: test-unit
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 2.3.0
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 2.3.0
42
+ description: Hash with case-insensitive, Symbol/String-indifferent key access
43
+ email:
44
+ - junegunn.c@gmail.com
45
+ - arandilopez.93@gmail.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".gitignore"
51
+ - ".travis.yml"
52
+ - CHANGELOG.markdown
53
+ - Gemfile
54
+ - LICENSE.txt
55
+ - README.md
56
+ - Rakefile
57
+ - insensitive_hash.gemspec
58
+ - lib/insensitive_hash.rb
59
+ - lib/insensitive_hash/insensitive_hash.rb
60
+ - lib/insensitive_hash/minimal.rb
61
+ - lib/insensitive_hash/version.rb
62
+ - test/test_insensitive_hash.rb
63
+ homepage: https://github.com/BoxFactura/insensitive_hash
64
+ licenses: []
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubygems_version: 3.1.4
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Case-insensitive Ruby Hash
85
+ test_files: []