insensitive_hash 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,6 @@
1
+ ### 0.3.1
2
+ * Supports custom key encoder
3
+
1
4
  ### 0.3.0
2
5
  * "Inherited insensitivity" had been a great source of confusion,
3
6
  as it stores transformed version of the given Array of Hash.
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Junegunn Choi
1
+ Copyright (c) 2013 Junegunn Choi
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -8,10 +8,10 @@ Installation
8
8
  gem install insensitive_hash
9
9
  ```
10
10
 
11
- Examples
12
- --------
11
+ Instantiation
12
+ -------------
13
13
 
14
- ### Instantiation
14
+ ### Hash#insensitive
15
15
 
16
16
  ```ruby
17
17
  require 'insensitive_hash'
@@ -24,7 +24,7 @@ ih['ABC'] # 1
24
24
  ih[:Hello_World] # true
25
25
  ```
26
26
 
27
- ### Instantiation without monkey-patching Hash
27
+ ### Without monkey-patching Hash
28
28
 
29
29
  If you don't like to have Hash#insensitive method, `require 'insensitive_hash/minimal'`
30
30
 
@@ -49,7 +49,9 @@ h = ih.sensitive
49
49
  h = ih.to_hash
50
50
  ```
51
51
 
52
- ### Basic usage
52
+ Basic usage
53
+ -----------
54
+
53
55
  ```ruby
54
56
  ih = {:abc => 1, 'DEF' => 2}.insensitive
55
57
 
@@ -71,7 +73,8 @@ ih.delete :Abc # 10
71
73
  ih.keys # ['DEF']
72
74
  ```
73
75
 
74
- ### "Inherited insensitivity"
76
+ Inherited insensitivity
77
+ -----------------------
75
78
 
76
79
  When InsensitiveHash is built from another Hash,
77
80
  descendant Hash values are recursively converted to be insensitive
@@ -86,20 +89,24 @@ ih = {:one => [ [ [ { :a => { :b => { :c => 'd' } } } ] ] ]}.insensitive
86
89
  ih['one'].first.first.first['A']['b'][:C] # 'd'
87
90
  ```
88
91
 
89
- Once InsensitiveHash is initialized, you can convert its descendant Hash values by
90
- building a new InsensitiveHash from it.
92
+ However, once InsensitiveHash is initialized,
93
+ descendant Hashes are not automatically converted.
91
94
 
92
95
  ```ruby
93
96
  ih = {}.insensitive
94
97
  ih[:abc] = { :def => true }
95
98
 
96
99
  ih['ABC']['DEF'] # nil
100
+ ```
101
+
102
+ Simply build a new InsensitiveHash again if you need recursive conversion.
97
103
 
104
+ ```ruby
98
105
  ih2 = ih.insensitive
99
106
  ih2['ABC']['DEF'] # true
100
107
  ```
101
108
 
102
- ### Processing case-insensitive YAML input
109
+ ### Example: Processing case-insensitive YAML input
103
110
  ```ruby
104
111
  db = YAML.load(File.read 'database.yml').insensitive
105
112
 
@@ -108,7 +115,41 @@ db['Development']['ADAPTER']
108
115
  db[:production][:adapter]
109
116
  ```
110
117
 
111
- ### Enabling key-clash detection (Safe mode)
118
+ Customizing insensitivity
119
+ -------------------------
120
+
121
+ You can provide a Proc object as the key encoder which determines the level of insensitivity.
122
+
123
+ ### Default encoder
124
+
125
+ The default encoder is as follows.
126
+
127
+ ```ruby
128
+ InsensitiveHash::DEFAULT_ENCODER =
129
+ proc { |key|
130
+ case key
131
+ when String, Symbol
132
+ key.to_s.downcase.gsub(' ', '_')
133
+ else
134
+ key
135
+ end
136
+ }
137
+ ```
138
+
139
+ ### Encoder examples
140
+
141
+ ```ruby
142
+ h1 = {}.insensitive(:encoder => proc { |key| key.to_s })
143
+ h2 = {}.insensitive(:encoder => proc { |key| key.to_s.downcase })
144
+ h3 = {}.insensitive(:encoder => proc { |key| key.to_s.downcase.gsub(/\s+/, '_') })
145
+
146
+ # Without `insensitive` method
147
+ h4 = InsensitiveHash.new
148
+ h4.encoder = proc { |key| key.to_s }
149
+ ```
150
+
151
+ Enabling key-clash detection (Safe mode)
152
+ ----------------------------------------
112
153
  ```ruby
113
154
  ih = InsensitiveHash.new
114
155
  ih.safe = true
@@ -123,7 +164,7 @@ h['Hello World'] # 2
123
164
  ```
124
165
 
125
166
  ## Contributing to insensitive_hash
126
-
167
+
127
168
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
128
169
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
129
170
  * Fork the project
@@ -134,6 +175,5 @@ h['Hello World'] # 2
134
175
 
135
176
  ## Copyright
136
177
 
137
- Copyright (c) 2012 Junegunn Choi. See LICENSE.txt for
138
- further details.
178
+ Copyright (c) 2013 Junegunn Choi. See LICENSE.txt for further details.
139
179
 
@@ -19,7 +19,5 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- # specify any dependencies here; for example:
23
22
  s.add_development_dependency "test-unit", ">= 2.3.0"
24
- # s.add_runtime_dependency "rest-client"
25
23
  end
@@ -2,10 +2,14 @@ require 'insensitive_hash/version'
2
2
  require 'insensitive_hash/insensitive_hash'
3
3
 
4
4
  class Hash
5
+ # @param [Hash] options Options
6
+ # @option options [Boolean] :safe Whether to detect key clash on merge
7
+ # @option options [Proc] :encoder Key encoding Proc
5
8
  # @return [InsensitiveHash]
6
9
  def insensitive options = {}
7
10
  InsensitiveHash[self].tap do |ih|
8
11
  ih.safe = options[:safe] if options.has_key?(:safe)
12
+ ih.encoder = options[:encoder] if options.has_key?(:encoder)
9
13
  ih.default = self.default
10
14
  ih.default_proc = self.default_proc if self.default_proc
11
15
  end
@@ -1,7 +1,21 @@
1
+ # @author Junegunn Choi <junegunn.c@gmail.com>
2
+ # @!attribute [r] encoder
3
+ # @return [Proc] Key encoding Proc
1
4
  class InsensitiveHash < Hash
5
+ attr_reader :encoder
6
+
2
7
  class KeyClashError < Exception
3
8
  end
4
9
 
10
+ DEFAULT_ENCODER = proc { |key|
11
+ case key
12
+ when String, Symbol
13
+ key.to_s.downcase.gsub(' ', '_')
14
+ else
15
+ key
16
+ end
17
+ }
18
+
5
19
  def initialize default = nil, &block
6
20
  if block_given?
7
21
  raise ArgumentError.new('wrong number of arguments') unless default.nil?
@@ -10,12 +24,13 @@ class InsensitiveHash < Hash
10
24
  super
11
25
  end
12
26
 
13
- @key_map = {}
14
- @safe = false
27
+ @key_map = {}
28
+ @safe = false
29
+ @encoder = DEFAULT_ENCODER
15
30
  end
16
31
 
17
32
  # Sets whether to detect key clashes
18
- # @param [Boolean]
33
+ # @param [Boolean]
19
34
  # @return [Boolean]
20
35
  def safe= s
21
36
  raise ArgumentError.new("Neither true nor false") unless [true, false].include?(s)
@@ -26,7 +41,22 @@ class InsensitiveHash < Hash
26
41
  def safe?
27
42
  @safe
28
43
  end
29
-
44
+
45
+ # @param [Proc] prc Key encoding Proc
46
+ # @return [Proc]
47
+ def encoder= prc
48
+ raise ArgumentError, "Proc object required" unless prc.is_a?(Proc)
49
+
50
+ kvs = to_a
51
+ clear
52
+ @encoder = prc
53
+ kvs.each do |pair|
54
+ store *pair
55
+ end
56
+
57
+ prc
58
+ end
59
+
30
60
  # Returns a normal, sensitive Hash
31
61
  # @return [Hash]
32
62
  def to_hash
@@ -87,6 +117,7 @@ class InsensitiveHash < Hash
87
117
  super other
88
118
 
89
119
  self.safe = other.respond_to?(:safe?) ? other.safe? : safe?
120
+ self.encoder = other.respond_to?(:encoder) ? other.encoder : DEFAULT_ENCODER
90
121
 
91
122
  @key_map.clear
92
123
  self.each do |k, v|
@@ -151,12 +182,7 @@ private
151
182
  end
152
183
 
153
184
  def encode key
154
- case key
155
- when String, Symbol
156
- key.to_s.downcase.gsub(' ', '_')
157
- else
158
- key
159
- end
185
+ @encoder.call key
160
186
  end
161
187
 
162
188
  def detect_clash hash
@@ -1,3 +1,3 @@
1
1
  class InsensitiveHash < Hash
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -528,5 +528,53 @@ class TestInsensitiveHash < Test::Unit::TestCase
528
528
 
529
529
  assert_equal :value, a[:key]
530
530
  end
531
+
532
+ def test_encoder
533
+ # With custom encoder
534
+ a = {}.insensitive(:encoder => prc = proc { |key| key.to_s.downcase })
535
+ a['1'] = 'one'
536
+ assert_equal 'one', a[1]
537
+ assert_equal prc, a.encoder
538
+
539
+ a['hello world'] = true
540
+ assert_equal true, a['HELLO WORLD']
541
+ assert_equal nil, a[:HELLO_WORLD]
542
+
543
+ # Update encoder
544
+ a.encoder = proc { |key| key.to_s.gsub(' ', '_').downcase }
545
+ assert_equal true, a[:HELLO_WORLD]
546
+
547
+ # Update again
548
+ a.encoder = proc { |key| key.to_s }
549
+ assert_equal true, a[:"hello world"]
550
+ assert_equal nil, a['HELLO WORLD']
551
+ assert_equal nil, a[:hello_world]
552
+ assert_equal nil, a[:HELLO_WORLD]
553
+ end
554
+
555
+ def test_encoder_invalid_type
556
+ assert_raise(ArgumentError) {
557
+ {}.insensitive(:encoder => 1)
558
+ }
559
+ assert_raise(ArgumentError) {
560
+ h = InsensitiveHash.new
561
+ h.encoder = 1
562
+ }
563
+ end
564
+
565
+ def test_encoder_replace
566
+ a = {}.insensitive(:encoder => proc { |key| key })
567
+ b = {}.insensitive(:encoder => proc { |key| key.to_s })
568
+ a[:key] = b[:key] = 1
569
+ assert_equal 1, a[:key]
570
+ assert_equal 1, b[:key]
571
+ assert_nil a['key']
572
+ assert_equal 1, b['key']
573
+
574
+ a.replace b
575
+ assert_equal 1, a['key']
576
+ a[:key2] = 2
577
+ assert_equal 2, a['key2']
578
+ end
531
579
  end
532
580
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: insensitive_hash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-28 00:00:00.000000000 Z
12
+ date: 2013-02-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: test-unit
@@ -72,3 +72,4 @@ specification_version: 3
72
72
  summary: Case-insensitive Ruby Hash
73
73
  test_files:
74
74
  - test/test_insensitive_hash.rb
75
+ has_rdoc: