hashie 3.4.2 → 3.4.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dbee5492036900b505f6bf76d5133640e02b525b
4
- data.tar.gz: 5a358bc6a11697e33e7d2c806d39b01d79cb3dba
3
+ metadata.gz: 6121c5c81c02643a5ac8bcf1633e09c0d18dd238
4
+ data.tar.gz: bfa9a6bed6dc2aef1f034297f30bc66ebe72b7ef
5
5
  SHA512:
6
- metadata.gz: 660342313eec115f629d20bc0d68fa1f1ee6361860234dbf2a90f707fe92d2c609bb03533748d3fbb0ea8dbab4197f332281c2c70fdad87fb0a34485e0b2ae56
7
- data.tar.gz: ee46052bf7249f60c4c23825f782f19b879582f8cb009a156644b3c89f7aba1a76a19f3f01f235b4915e00e5b773e2559a32bde349ae7d541d2bc0c6f19c4163
6
+ metadata.gz: cad338b78028860f36e964e2d531d793419e7ae533f2f33c452c60cd5e3df0a5e1f7838da102ed7b3b39f2be5c8ef3123bed779f477d970533b69d91ff6c27fd
7
+ data.tar.gz: 5bbb4adec507c10102832cca447f302ff5693e97131bc8d4c13f0e14038cff6605ceed36e77153d7bfa078aefe838513216ac64da945af464cb9a8213dbd5cd2
@@ -1,3 +1,12 @@
1
+ ## 3.4.2 (10/25/2015)
2
+
3
+ * [#314](https://github.com/intridea/hashie/pull/314): Added a `StrictKeyAccess` extension that will raise an error whenever a key is accessed that does not exist in the hash - [@pboling](https://github.com/pboling).
4
+ * [#304](https://github.com/intridea/hashie/pull/304): Ensured compatibility of `Hash` extensions with singleton objects - [@regexident](https://github.com/regexident).
5
+ * [#306](https://github.com/intridea/hashie/pull/306): Added `Hashie::Extensions::Dash::Coercion` - [@marshall-lee](https://github.com/marshall-lee).
6
+ * [#310](https://github.com/intridea/hashie/pull/310): Fixed `Hashie::Extensions::SafeAssignment` bug with private methods - [@marshall-lee](https://github.com/marshall-lee).
7
+ * [#313](https://github.com/intridea/hashie/pull/313): Restrict pending spec to only Ruby versions 2.2.0-2.2.2 - [@pboling](https://github.com/pboling).
8
+ * [#315](https://github.com/intridea/hashie/pull/315): Default `bin/` scripts: `console` and `setup` - [@pboling](https://github.com/pboling).
9
+
1
10
  ## 3.4.2 (6/2/2015)
2
11
 
3
12
  * [#292](https://github.com/intridea/hashie/pull/292): Removed `Mash#id` and `Mash#type` - [@jrochkind](https://github.com/jrochkind).
data/README.md CHANGED
@@ -18,9 +18,9 @@ Hashie is available as a RubyGem:
18
18
  $ gem install hashie
19
19
  ```
20
20
 
21
- ## Upgrading
21
+ ## Stable Release
22
22
 
23
- You're reading the documentation for the stable release of Hashie, which is 3.4.2. Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
23
+ You're reading the documentation for the stable release [3.4.3](https://github.com/intridea/hashie/blob/v3.4.3/README.md).
24
24
 
25
25
  ## Hash Extensions
26
26
 
@@ -392,6 +392,25 @@ books.deep_locate -> (key, value, object) { key == :pages && value <= 120 }
392
392
  # => [{:title=>"Ruby for beginners", :pages=>120}, {:title=>"CSS for intermediates", :pages=>80}]
393
393
  ```
394
394
 
395
+ ## StrictKeyAccess
396
+
397
+ This extension can be mixed in to allow a Hash to raise an error when attempting to extract a value using a non-existent key.
398
+
399
+ ### Example:
400
+
401
+ ```ruby
402
+ class StrictKeyAccessHash < Hash
403
+ include Hashie::Extensions::StrictKeyAccess
404
+ end
405
+
406
+ >> hash = StrictKeyAccessHash[foo: "bar"]
407
+ => {:foo=>"bar"}
408
+ >> hash[:foo]
409
+ => "bar"
410
+ >> hash[:cow]
411
+ KeyError: key not found: :cow
412
+ ```
413
+
395
414
  ## Mash
396
415
 
397
416
  Mash is an extended Hash that gives simple pseudo-object functionality that can be built from hashes and easily extended. It is intended to give the user easier access to the objects within the Mash through a property-like syntax, while still retaining all Hash functionality.
@@ -607,6 +626,36 @@ model.created_at.class #=> Time
607
626
 
608
627
  To enable compatibility with Rails 4 use the [hashie-forbidden_attributes](https://github.com/Maxim-Filimonov/hashie-forbidden_attributes) gem.
609
628
 
629
+ ### Dash Extension: Coercion.
630
+
631
+ If you want to use `Hashie::Extensions::Coercion` together with `Dash` then
632
+ you may probably want to use `Hashie::Extensions::Dash::Coercion` instead.
633
+ This extension automatically includes `Hashie::Extensions::Coercion`
634
+ and also adds a convenient `:coerce` option to `property` so you can define coercion in one line
635
+ instead of using `property` and `coerce_key` separate:
636
+
637
+ ```ruby
638
+ class UserHash < Hashie::Dash
639
+ include Hashie::Extensions::Coercion
640
+
641
+ property :id
642
+ property :posts
643
+
644
+ coerce_key :posts, Array[PostHash]
645
+ end
646
+ ```
647
+
648
+ This is the same as:
649
+
650
+ ```ruby
651
+ class UserHash < Hashie::Dash
652
+ include Hashie::Extensions::Dash::Coercion
653
+
654
+ property :id
655
+ property :posts, coerce: Array[PostHash]
656
+ end
657
+ ```
658
+
610
659
  ## Trash
611
660
 
612
661
  A Trash is a Dash that allows you to translate keys on initialization. It mixes
@@ -18,4 +18,5 @@ Gem::Specification.new do |gem|
18
18
 
19
19
  gem.add_development_dependency 'rake'
20
20
  gem.add_development_dependency 'rspec', '~> 3.0'
21
+ gem.add_development_dependency 'rspec-pending_for', '~> 0.1'
21
22
  end
@@ -26,6 +26,7 @@ module Hashie
26
26
  autoload :PrettyInspect, 'hashie/extensions/pretty_inspect'
27
27
  autoload :KeyConversion, 'hashie/extensions/key_conversion'
28
28
  autoload :MethodAccessWithOverride, 'hashie/extensions/method_access'
29
+ autoload :StrictKeyAccess, 'hashie/extensions/strict_key_access'
29
30
 
30
31
  module Parsers
31
32
  autoload :YamlErbParser, 'hashie/extensions/parsers/yaml_erb_parser'
@@ -34,6 +35,7 @@ module Hashie
34
35
  module Dash
35
36
  autoload :IndifferentAccess, 'hashie/extensions/dash/indifferent_access'
36
37
  autoload :PropertyTranslation, 'hashie/extensions/dash/property_translation'
38
+ autoload :Coercion, 'hashie/extensions/dash/coercion'
37
39
  end
38
40
 
39
41
  module Mash
@@ -29,51 +29,17 @@ module Hashie
29
29
  def set_value_with_coercion(key, value)
30
30
  into = self.class.key_coercion(key) || self.class.value_coercion(value)
31
31
 
32
- return set_value_without_coercion(key, value) if value.nil? || into.nil?
33
-
34
- begin
35
- return set_value_without_coercion(key, coerce_or_init(into).call(value)) unless into.is_a?(Enumerable)
36
-
37
- if into.class <= ::Hash
38
- key_coerce = coerce_or_init(into.flatten[0])
39
- value_coerce = coerce_or_init(into.flatten[-1])
40
- value = into.class[value.map { |k, v| [key_coerce.call(k), value_coerce.call(v)] }]
41
- else # Enumerable but not Hash: Array, Set
42
- value_coerce = coerce_or_init(into.first)
43
- value = into.class.new(value.map { |v| value_coerce.call(v) })
32
+ unless value.nil? || into.nil?
33
+ begin
34
+ value = self.class.fetch_coercion(into).call(value)
35
+ rescue NoMethodError, TypeError => e
36
+ raise CoercionError, "Cannot coerce property #{key.inspect} from #{value.class} to #{into}: #{e.message}"
44
37
  end
45
- rescue NoMethodError, TypeError => e
46
- raise CoercionError, "Cannot coerce property #{key.inspect} from #{value.class} to #{into}: #{e.message}"
47
38
  end
48
39
 
49
40
  set_value_without_coercion(key, value)
50
41
  end
51
42
 
52
- def coerce_or_init(type)
53
- return type if type.is_a? Proc
54
-
55
- if CORE_TYPES.key?(type)
56
- lambda do |v|
57
- return v if v.is_a? type
58
- return v.send(CORE_TYPES[type])
59
- end
60
- elsif type.respond_to?(:coerce)
61
- lambda do |v|
62
- return v if v.is_a? type
63
- type.coerce(v)
64
- end
65
- elsif type.respond_to?(:new)
66
- lambda do |v|
67
- return v if v.is_a? type
68
- type.new(v)
69
- end
70
- else
71
- fail TypeError, "#{type} is not a coercable type"
72
- end
73
- end
74
-
75
- private :coerce_or_init
76
-
77
43
  def custom_writer(key, value, _convert = true)
78
44
  self[key] = value
79
45
  end
@@ -175,6 +141,66 @@ module Hashie
175
141
  strict_value_coercions[from] || lenient_value_coercions[from]
176
142
  end
177
143
 
144
+ def fetch_coercion(type)
145
+ return type if type.is_a? Proc
146
+ coercion_cache[type]
147
+ end
148
+
149
+ def coercion_cache
150
+ @coercion_cache ||= ::Hash.new do |hash, type|
151
+ hash[type] = build_coercion(type)
152
+ end
153
+ end
154
+
155
+ def build_coercion(type)
156
+ if type.is_a? Enumerable
157
+ if type.class <= ::Hash
158
+ type, key_type, value_type = type.class, *type.first
159
+ build_hash_coercion(type, key_type, value_type)
160
+ else # Enumerable but not Hash: Array, Set
161
+ type, value_type = type.class, type.first
162
+ build_container_coercion(type, value_type)
163
+ end
164
+ elsif CORE_TYPES.key? type
165
+ build_core_type_coercion(type)
166
+ elsif type.respond_to? :coerce
167
+ lambda do |value|
168
+ return value if value.is_a? type
169
+ type.coerce(value)
170
+ end
171
+ elsif type.respond_to? :new
172
+ lambda do |value|
173
+ return value if value.is_a? type
174
+ type.new(value)
175
+ end
176
+ else
177
+ fail TypeError, "#{type} is not a coercable type"
178
+ end
179
+ end
180
+
181
+ def build_hash_coercion(type, key_type, value_type)
182
+ key_coerce = fetch_coercion(key_type)
183
+ value_coerce = fetch_coercion(value_type)
184
+ lambda do |value|
185
+ type[value.map { |k, v| [key_coerce.call(k), value_coerce.call(v)] }]
186
+ end
187
+ end
188
+
189
+ def build_container_coercion(type, value_type)
190
+ value_coerce = fetch_coercion(value_type)
191
+ lambda do |value|
192
+ type.new(value.map { |v| value_coerce.call(v) })
193
+ end
194
+ end
195
+
196
+ def build_core_type_coercion(type)
197
+ name = CORE_TYPES[type]
198
+ lambda do |value|
199
+ return value if value.is_a? type
200
+ return value.send(name)
201
+ end
202
+ end
203
+
178
204
  def inherited(klass)
179
205
  super
180
206
 
@@ -0,0 +1,25 @@
1
+ module Hashie
2
+ module Extensions
3
+ module Dash
4
+ module Coercion
5
+ # Extends a Dash with the ability to define coercion for properties.
6
+
7
+ def self.included(base)
8
+ base.send :include, Hashie::Extensions::Coercion
9
+ base.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+ # Defines a property on the Dash. Options are the standard
14
+ # <tt>Hashie::Dash#property</tt> options plus:
15
+ #
16
+ # * <tt>:coerce</tt> - The class into which you want the property coerced.
17
+ def property(property_name, options = {})
18
+ super
19
+ coerce_key property_name, options[:coerce] if options[:coerce]
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -72,8 +72,6 @@ module Hashie
72
72
  def property(property_name, options = {})
73
73
  super
74
74
 
75
- options[:from] = options[:from] if options[:from]
76
-
77
75
  if options[:from]
78
76
  if property_name == options[:from]
79
77
  fail ArgumentError, "Property name (#{property_name}) and :from option must not be the same"
@@ -3,7 +3,9 @@ module Hashie
3
3
  module DeepMerge
4
4
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
5
5
  def deep_merge(other_hash, &block)
6
- dup.deep_merge!(other_hash, &block)
6
+ copy = dup
7
+ copy.extend(Hashie::Extensions::DeepMerge) unless copy.respond_to?(:deep_merge!)
8
+ copy.deep_merge!(other_hash, &block)
7
9
  end
8
10
 
9
11
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
@@ -3,7 +3,7 @@ module Hashie
3
3
  module Mash
4
4
  module SafeAssignment
5
5
  def custom_writer(key, *args) #:nodoc:
6
- fail ArgumentError, "The property #{key} clashes with an existing method." if methods.include?(key.to_sym)
6
+ fail ArgumentError, "The property #{key} clashes with an existing method." if !key?(key) && respond_to?(key, true)
7
7
  super
8
8
  end
9
9
 
@@ -0,0 +1,74 @@
1
+ module Hashie
2
+ module Extensions
3
+ # SRP: This extension will fail an error whenever a key is accessed that does not exist in the hash.
4
+ #
5
+ # EXAMPLE:
6
+ #
7
+ # class StrictKeyAccessHash < Hash
8
+ # include Hashie::Extensions::StrictKeyAccess
9
+ # end
10
+ #
11
+ # >> hash = StrictKeyAccessHash[foo: "bar"]
12
+ # => {:foo=>"bar"}
13
+ # >> hash[:foo]
14
+ # => "bar"
15
+ # >> hash[:cow]
16
+ # KeyError: key not found: :cow
17
+ #
18
+ # NOTE: For googlers coming from Python to Ruby, this extension makes a Hash behave more like a "Dictionary".
19
+ #
20
+ module StrictKeyAccess
21
+ class DefaultError < StandardError
22
+ def initialize(msg = 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense', *args)
23
+ super
24
+ end
25
+ end
26
+
27
+ # NOTE: Defaults don't make any sense with a StrictKeyAccess.
28
+ # NOTE: When key lookup fails a KeyError is raised.
29
+ #
30
+ # Normal:
31
+ #
32
+ # >> a = Hash.new(123)
33
+ # => {}
34
+ # >> a["noes"]
35
+ # => 123
36
+ #
37
+ # With StrictKeyAccess:
38
+ #
39
+ # >> a = StrictKeyAccessHash.new(123)
40
+ # => {}
41
+ # >> a["noes"]
42
+ # KeyError: key not found: "noes"
43
+ #
44
+ def [](key)
45
+ fetch(key)
46
+ end
47
+
48
+ def default(_ = nil)
49
+ fail DefaultError
50
+ end
51
+
52
+ def default=(_)
53
+ fail DefaultError
54
+ end
55
+
56
+ def default_proc
57
+ fail DefaultError
58
+ end
59
+
60
+ def default_proc=(_)
61
+ fail DefaultError
62
+ end
63
+
64
+ def key(value)
65
+ result = super
66
+ if result.nil? && (!key?(result) || self[result] != value)
67
+ fail KeyError, "key not found with value of #{value.inspect}"
68
+ else
69
+ result
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -15,7 +15,7 @@ module Hashie
15
15
  # Return a new hash with all keys converted
16
16
  # to strings.
17
17
  def stringify_keys
18
- dup.stringify_keys!
18
+ StringifyKeys.stringify_keys(self)
19
19
  end
20
20
 
21
21
  module ClassMethods
@@ -25,7 +25,7 @@ module Hashie
25
25
  def stringify_keys_recursively!(object)
26
26
  case object
27
27
  when self.class
28
- object.stringify_keys!
28
+ stringify_keys!(object)
29
29
  when ::Array
30
30
  object.each do |i|
31
31
  stringify_keys_recursively!(i)
@@ -43,6 +43,7 @@ module Hashie
43
43
  # test.stringify_keys!
44
44
  # test # => {'abc' => 'def'}
45
45
  def stringify_keys!(hash)
46
+ hash.extend(Hashie::Extensions::StringifyKeys) unless hash.respond_to?(:stringify_keys!)
46
47
  hash.keys.each do |k|
47
48
  stringify_keys_recursively!(hash[k])
48
49
  hash[k.to_s] = hash.delete(k)
@@ -54,8 +55,10 @@ module Hashie
54
55
  # to strings.
55
56
  # @param [::Hash] hash
56
57
  def stringify_keys(hash)
57
- hash.dup.tap do | new_hash |
58
- stringify_keys! new_hash
58
+ copy = hash.dup
59
+ copy.extend(Hashie::Extensions::StringifyKeys) unless copy.respond_to?(:stringify_keys!)
60
+ copy.tap do |new_hash|
61
+ stringify_keys!(new_hash)
59
62
  end
60
63
  end
61
64
  end
@@ -15,7 +15,7 @@ module Hashie
15
15
  # Return a new hash with all keys converted
16
16
  # to symbols.
17
17
  def symbolize_keys
18
- dup.symbolize_keys!
18
+ SymbolizeKeys.symbolize_keys(self)
19
19
  end
20
20
 
21
21
  module ClassMethods
@@ -23,9 +23,9 @@ module Hashie
23
23
  # hashes and arrays.
24
24
  # @api private
25
25
  def symbolize_keys_recursively!(object)
26
- object.symbolize_keys! if object.respond_to? :symbolize_keys!
27
-
28
26
  case object
27
+ when self.class
28
+ symbolize_keys!(object)
29
29
  when ::Array
30
30
  object.each do |i|
31
31
  symbolize_keys_recursively!(i)
@@ -43,6 +43,7 @@ module Hashie
43
43
  # Hashie.symbolize_keys! test
44
44
  # test # => {:abc => 'def'}
45
45
  def symbolize_keys!(hash)
46
+ hash.extend(Hashie::Extensions::SymbolizeKeys) unless hash.respond_to?(:symbolize_keys!)
46
47
  hash.keys.each do |k|
47
48
  symbolize_keys_recursively!(hash[k])
48
49
  hash[k.to_sym] = hash.delete(k)
@@ -54,8 +55,10 @@ module Hashie
54
55
  # to symbols.
55
56
  # @param [::Hash] hash
56
57
  def symbolize_keys(hash)
57
- hash.dup.tap do | new_hash |
58
- symbolize_keys! new_hash
58
+ copy = hash.dup
59
+ copy.extend(Hashie::Extensions::SymbolizeKeys) unless copy.respond_to?(:symbolize_keys!)
60
+ copy.tap do |new_hash|
61
+ symbolize_keys!(new_hash)
59
62
  end
60
63
  end
61
64
  end
@@ -58,7 +58,6 @@ module Hashie
58
58
  include Hashie::Extensions::PrettyInspect
59
59
 
60
60
  ALLOWED_SUFFIXES = %w(? ! = _)
61
- SUFFIXES_PARSER = /(.*?)([#{ALLOWED_SUFFIXES.join}]?)$/
62
61
 
63
62
  def self.load(path, options = {})
64
63
  @_mashes ||= new
@@ -211,10 +210,9 @@ module Hashie
211
210
 
212
211
  def respond_to_missing?(method_name, *args)
213
212
  return true if key?(method_name)
214
- _, suffix = method_suffix(method_name)
215
- case suffix
216
- when '=', '?', '!', '_'
217
- return true
213
+ suffix = method_suffix(method_name)
214
+ if suffix
215
+ true
218
216
  else
219
217
  super
220
218
  end
@@ -227,15 +225,15 @@ module Hashie
227
225
 
228
226
  def method_missing(method_name, *args, &blk)
229
227
  return self.[](method_name, &blk) if key?(method_name)
230
- name, suffix = method_suffix(method_name)
228
+ name, suffix = method_name_and_suffix(method_name)
231
229
  case suffix
232
- when '='
230
+ when '='.freeze
233
231
  assign_property(name, args.first)
234
- when '?'
232
+ when '?'.freeze
235
233
  !!self[name]
236
- when '!'
234
+ when '!'.freeze
237
235
  initializing_reader(name)
238
- when '_'
236
+ when '_'.freeze
239
237
  underbang_reader(name)
240
238
  else
241
239
  self[method_name]
@@ -254,9 +252,18 @@ module Hashie
254
252
 
255
253
  protected
256
254
 
255
+ def method_name_and_suffix(method_name)
256
+ method_name = method_name.to_s
257
+ if method_name.end_with?(*ALLOWED_SUFFIXES)
258
+ [method_name[0..-2], method_name[-1]]
259
+ else
260
+ [method_name[0..-1], nil]
261
+ end
262
+ end
263
+
257
264
  def method_suffix(method_name)
258
- match = method_name.to_s.match(SUFFIXES_PARSER)
259
- [match[1], match[2]]
265
+ method_name = method_name.to_s
266
+ method_name[-1] if method_name.end_with?(*ALLOWED_SUFFIXES)
260
267
  end
261
268
 
262
269
  def convert_key(key) #:nodoc:
@@ -1,3 +1,3 @@
1
1
  module Hashie
2
- VERSION = '3.4.2'
2
+ VERSION = '3.4.3'
3
3
  end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashie::Extensions::Dash::Coercion do
4
+ class DashWithCoercion < Hashie::Dash
5
+ include Hashie::Extensions::Dash::Coercion
6
+
7
+ property :type, coerce: Symbol
8
+ end
9
+
10
+ it 'does the coercion of properties' do
11
+ expect(DashWithCoercion.new(type: 'something')).to eq(type: :something)
12
+ end
13
+ end
@@ -42,4 +42,24 @@ describe Hashie::Extensions::DeepMerge do
42
42
  expect(h1).to eq expected_hash
43
43
  end
44
44
  end
45
+
46
+ context 'from extended object' do
47
+ subject { Hash }
48
+ let(:h1) { subject.new.merge(a: 100, c: { c1: 100 }).extend(Hashie::Extensions::DeepMerge) }
49
+ let(:h2) { { b: 250, c: { c1: 200 } } }
50
+ let(:expected_hash) { { a: 100, b: 250, c: { c1: 200 } } }
51
+
52
+ it 'does not raise error' do
53
+ expect { h1.deep_merge(h2) } .not_to raise_error
54
+ end
55
+
56
+ it 'deep merges two hashes' do
57
+ expect(h1.deep_merge(h2)).to eq expected_hash
58
+ end
59
+
60
+ it 'deep merges another hash in place via bang method' do
61
+ h1.deep_merge!(h2)
62
+ expect(h1).to eq expected_hash
63
+ end
64
+ end
45
65
  end
@@ -3,17 +3,44 @@ require 'spec_helper'
3
3
  describe Hashie::Extensions::Mash::SafeAssignment do
4
4
  class MashWithSafeAssignment < Hashie::Mash
5
5
  include Hashie::Extensions::Mash::SafeAssignment
6
+
7
+ private
8
+
9
+ def my_own_private
10
+ :hello!
11
+ end
6
12
  end
7
13
 
8
14
  context 'when included in Mash' do
9
15
  subject { MashWithSafeAssignment.new }
10
16
 
17
+ context 'when not attempting to override a method' do
18
+ it 'assigns just fine' do
19
+ expect do
20
+ subject.blabla = 'Test'
21
+ subject.blabla = 'Test'
22
+ end.to_not raise_error
23
+ end
24
+ end
25
+
11
26
  context 'when attempting to override a method' do
12
27
  it 'raises an error' do
13
28
  expect { subject.zip = 'Test' }.to raise_error(ArgumentError)
14
29
  end
15
30
  end
16
31
 
32
+ context 'when attempting to override a private method' do
33
+ it 'raises an error' do
34
+ expect { subject.my_own_private = 'Test' }.to raise_error(ArgumentError)
35
+ end
36
+ end
37
+
38
+ context 'when attempting to initialize with predefined method' do
39
+ it 'raises an error' do
40
+ expect { MashWithSafeAssignment.new(zip: true) }.to raise_error(ArgumentError)
41
+ end
42
+ end
43
+
17
44
  context 'when setting as a hash key' do
18
45
  it 'still raises if conflicts with a method' do
19
46
  expect { subject[:zip] = 'Test' }.to raise_error(ArgumentError)
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hashie::Extensions::StrictKeyAccess do
4
+ class StrictKeyAccessHash < Hash
5
+ include Hashie::Extensions::StrictKeyAccess
6
+ end
7
+
8
+ shared_examples_for 'StrictKeyAccess with valid key' do |options = {}|
9
+ before { pending_for(options[:pending]) } if options[:pending]
10
+ context 'set' do
11
+ let(:new_value) { 42 }
12
+ it('returns value') do
13
+ expect(instance.send(:[]=, valid_key, new_value)).to eq new_value
14
+ end
15
+ end
16
+ context 'access' do
17
+ it('returns value') do
18
+ expect(instance[valid_key]).to eq valid_value
19
+ end
20
+ end
21
+ context 'lookup' do
22
+ it('returns key') do
23
+ expect(instance.key(valid_value)).to eq valid_key
24
+ end
25
+ end
26
+ end
27
+ shared_examples_for 'StrictKeyAccess with invalid key' do |options = {}|
28
+ before { pending_for(options[:pending]) } if options[:pending]
29
+ context 'access' do
30
+ it('raises an error') do
31
+ # Formatting of the error message varies on Rubinius and ruby-head
32
+ expect { instance[invalid_key] }.to raise_error KeyError
33
+ end
34
+ end
35
+ context 'lookup' do
36
+ it('raises an error') do
37
+ # Formatting of the error message does not vary here because raised by StrictKeyAccess
38
+ expect { instance.key(invalid_value) }.to raise_error KeyError,
39
+ %(key not found with value of #{invalid_value.inspect})
40
+ end
41
+ end
42
+ end
43
+ shared_examples_for 'StrictKeyAccess raises KeyError instead of allowing defaults' do
44
+ context '#default' do
45
+ it 'raises an error' do
46
+ expect { instance.default(invalid_key) }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
47
+ 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
48
+ end
49
+ end
50
+ context '#default=' do
51
+ it 'raises an error' do
52
+ expect { instance.default = invalid_key }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
53
+ 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
54
+ end
55
+ end
56
+ context '#default_proc' do
57
+ it 'raises an error' do
58
+ expect { instance.default_proc }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
59
+ 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
60
+ end
61
+ end
62
+ context '#default_proc=' do
63
+ it 'raises an error' do
64
+ expect { instance.default_proc = proc {} }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
65
+ 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
66
+ end
67
+ end
68
+ end
69
+
70
+ let(:klass) { StrictKeyAccessHash }
71
+ let(:instance) { StrictKeyAccessHash.new(*initialization_args) }
72
+ let(:initialization_args) do
73
+ [
74
+ { valid_key => valid_value }
75
+ ]
76
+ end
77
+ let(:valid_key) { :abc }
78
+ let(:valid_value) { 'def' }
79
+ let(:invalid_key) { :mega }
80
+ let(:invalid_value) { 'death' }
81
+
82
+ context '.new' do
83
+ context 'no defaults at initialization' do
84
+ let(:initialization_args) { [] }
85
+ before do
86
+ instance.merge!(valid_key => valid_value)
87
+ end
88
+ it_behaves_like 'StrictKeyAccess with valid key'
89
+ it_behaves_like 'StrictKeyAccess with invalid key'
90
+ it_behaves_like 'StrictKeyAccess raises KeyError instead of allowing defaults'
91
+ end
92
+ context 'with defaults at initialization' do
93
+ before do
94
+ instance.merge!(valid_key => valid_value)
95
+ end
96
+ it_behaves_like 'StrictKeyAccess with valid key'
97
+ it_behaves_like 'StrictKeyAccess with invalid key'
98
+ it_behaves_like 'StrictKeyAccess raises KeyError instead of allowing defaults'
99
+ end
100
+ it_behaves_like 'StrictKeyAccess with invalid key'
101
+ it_behaves_like 'StrictKeyAccess raises KeyError instead of allowing defaults'
102
+ end
103
+
104
+ context '.[]' do
105
+ let(:instance) { StrictKeyAccessHash[*initialization_args] }
106
+ it_behaves_like 'StrictKeyAccess with valid key', pending: { engine: 'rbx' }
107
+ it_behaves_like 'StrictKeyAccess with invalid key', pending: { engine: 'rbx' }
108
+ it_behaves_like 'StrictKeyAccess raises KeyError instead of allowing defaults'
109
+ end
110
+ end
@@ -80,6 +80,29 @@ describe Hashie::Extensions::StringifyKeys do
80
80
  include_examples 'stringify_keys!'
81
81
  end
82
82
  end
83
+
84
+ context 'singleton methods' do
85
+ subject { Hash }
86
+ let(:object) { subject.new.merge(a: 1, b: { c: 2 }).extend(Hashie::Extensions::StringifyKeys) }
87
+ let(:expected_hash) { { 'a' => 1, 'b' => { 'c' => 2 } } }
88
+
89
+ describe '.stringify_keys' do
90
+ it 'does not raise error' do
91
+ expect { object.stringify_keys } .not_to raise_error
92
+ end
93
+ it 'produces expected stringified hash' do
94
+ expect(object.stringify_keys).to eq(expected_hash)
95
+ end
96
+ end
97
+ describe '.stringify_keys!' do
98
+ it 'does not raise error' do
99
+ expect { object.stringify_keys! } .not_to raise_error
100
+ end
101
+ it 'produces expected stringified hash' do
102
+ expect(object.stringify_keys!).to eq(expected_hash)
103
+ end
104
+ end
105
+ end
83
106
  end
84
107
 
85
108
  describe Hashie do
@@ -85,6 +85,29 @@ describe Hashie::Extensions::SymbolizeKeys do
85
85
  include_examples 'symbolize_keys!'
86
86
  end
87
87
  end
88
+
89
+ context 'singleton methods' do
90
+ subject { Hash }
91
+ let(:object) { subject.new.merge('a' => 1, 'b' => { 'c' => 2 }).extend(Hashie::Extensions::SymbolizeKeys) }
92
+ let(:expected_hash) { { a: 1, b: { c: 2 } } }
93
+
94
+ describe '.symbolize_keys' do
95
+ it 'does not raise error' do
96
+ expect { object.symbolize_keys }.not_to raise_error
97
+ end
98
+ it 'produces expected symbolized hash' do
99
+ expect(object.symbolize_keys).to eq(expected_hash)
100
+ end
101
+ end
102
+ describe '.symbolize_keys!' do
103
+ it 'does not raise error' do
104
+ expect { object.symbolize_keys! }.not_to raise_error
105
+ end
106
+ it 'produces expected symbolized hash' do
107
+ expect(object.symbolize_keys!).to eq(expected_hash)
108
+ end
109
+ end
110
+ end
88
111
  end
89
112
 
90
113
  describe Hashie do
@@ -1,6 +1,5 @@
1
1
  require 'spec_helper'
2
2
  require 'delegate'
3
- require 'support/ruby_version'
4
3
 
5
4
  describe Hashie::Mash do
6
5
  subject { Hashie::Mash.new }
@@ -364,9 +363,7 @@ describe Hashie::Mash do
364
363
 
365
364
  it 'is able to access an unknown suffixed key as a method' do
366
365
  # See https://github.com/intridea/hashie/pull/285 for more information
367
- if mri22?
368
- pending 'Bug in MRI 2.2.x means this behavior is broken in those versions'
369
- end
366
+ pending_for(engine: 'ruby', versions: %w(2.2.0 2.2.1 2.2.2))
370
367
 
371
368
  %w(= ? ! _).each do |suffix|
372
369
  expect(subject.method(:"xyz#{suffix}")).to_not be_nil
@@ -7,6 +7,7 @@ require 'pry'
7
7
 
8
8
  require 'rspec'
9
9
  require 'hashie'
10
+ require 'rspec/pending_for'
10
11
 
11
12
  RSpec.configure do |config|
12
13
  config.expect_with :rspec do |expect|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashie
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.2
4
+ version: 3.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-06-02 00:00:00.000000000 Z
12
+ date: 2015-10-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -39,6 +39,20 @@ dependencies:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '3.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec-pending_for
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0.1'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '0.1'
42
56
  description: Hashie is a collection of classes and mixins that make hashes more powerful.
43
57
  email:
44
58
  - michael@intridea.com
@@ -59,6 +73,7 @@ files:
59
73
  - lib/hashie/clash.rb
60
74
  - lib/hashie/dash.rb
61
75
  - lib/hashie/extensions/coercion.rb
76
+ - lib/hashie/extensions/dash/coercion.rb
62
77
  - lib/hashie/extensions/dash/indifferent_access.rb
63
78
  - lib/hashie/extensions/dash/property_translation.rb
64
79
  - lib/hashie/extensions/deep_fetch.rb
@@ -73,6 +88,7 @@ files:
73
88
  - lib/hashie/extensions/method_access.rb
74
89
  - lib/hashie/extensions/parsers/yaml_erb_parser.rb
75
90
  - lib/hashie/extensions/pretty_inspect.rb
91
+ - lib/hashie/extensions/strict_key_access.rb
76
92
  - lib/hashie/extensions/stringify_keys.rb
77
93
  - lib/hashie/extensions/symbolize_keys.rb
78
94
  - lib/hashie/hash.rb
@@ -84,6 +100,7 @@ files:
84
100
  - spec/hashie/dash_spec.rb
85
101
  - spec/hashie/extensions/autoload_spec.rb
86
102
  - spec/hashie/extensions/coercion_spec.rb
103
+ - spec/hashie/extensions/dash/coercion_spec.rb
87
104
  - spec/hashie/extensions/dash/indifferent_access_spec.rb
88
105
  - spec/hashie/extensions/deep_fetch_spec.rb
89
106
  - spec/hashie/extensions/deep_find_spec.rb
@@ -96,6 +113,7 @@ files:
96
113
  - spec/hashie/extensions/mash/safe_assignment_spec.rb
97
114
  - spec/hashie/extensions/merge_initializer_spec.rb
98
115
  - spec/hashie/extensions/method_access_spec.rb
116
+ - spec/hashie/extensions/strict_key_access_spec.rb
99
117
  - spec/hashie/extensions/stringify_keys_spec.rb
100
118
  - spec/hashie/extensions/symbolize_keys_spec.rb
101
119
  - spec/hashie/hash_spec.rb
@@ -106,7 +124,6 @@ files:
106
124
  - spec/hashie/version_spec.rb
107
125
  - spec/spec_helper.rb
108
126
  - spec/support/module_context.rb
109
- - spec/support/ruby_version.rb
110
127
  homepage: https://github.com/intridea/hashie
111
128
  licenses:
112
129
  - MIT
@@ -127,35 +144,36 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
144
  version: '0'
128
145
  requirements: []
129
146
  rubyforge_project:
130
- rubygems_version: 2.4.5
147
+ rubygems_version: 2.4.8
131
148
  signing_key:
132
149
  specification_version: 4
133
150
  summary: Your friendly neighborhood hash library.
134
151
  test_files:
135
- - spec/spec_helper.rb
136
- - spec/support/ruby_version.rb
137
- - spec/support/module_context.rb
138
- - spec/hashie/parsers/yaml_erb_parser_spec.rb
139
- - spec/hashie/version_spec.rb
140
- - spec/hashie/rash_spec.rb
141
- - spec/hashie/dash_spec.rb
142
- - spec/hashie/hash_spec.rb
143
152
  - spec/hashie/clash_spec.rb
144
- - spec/hashie/mash_spec.rb
145
- - spec/hashie/trash_spec.rb
153
+ - spec/hashie/dash_spec.rb
154
+ - spec/hashie/extensions/autoload_spec.rb
155
+ - spec/hashie/extensions/coercion_spec.rb
156
+ - spec/hashie/extensions/dash/coercion_spec.rb
157
+ - spec/hashie/extensions/dash/indifferent_access_spec.rb
146
158
  - spec/hashie/extensions/deep_fetch_spec.rb
147
- - spec/hashie/extensions/method_access_spec.rb
148
- - spec/hashie/extensions/key_conversion_spec.rb
159
+ - spec/hashie/extensions/deep_find_spec.rb
149
160
  - spec/hashie/extensions/deep_locate_spec.rb
150
161
  - spec/hashie/extensions/deep_merge_spec.rb
151
- - spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb
152
- - spec/hashie/extensions/symbolize_keys_spec.rb
153
- - spec/hashie/extensions/mash/safe_assignment_spec.rb
154
- - spec/hashie/extensions/deep_find_spec.rb
155
162
  - spec/hashie/extensions/ignore_undeclared_spec.rb
156
- - spec/hashie/extensions/autoload_spec.rb
157
- - spec/hashie/extensions/stringify_keys_spec.rb
158
163
  - spec/hashie/extensions/indifferent_access_spec.rb
159
- - spec/hashie/extensions/coercion_spec.rb
164
+ - spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb
165
+ - spec/hashie/extensions/key_conversion_spec.rb
166
+ - spec/hashie/extensions/mash/safe_assignment_spec.rb
160
167
  - spec/hashie/extensions/merge_initializer_spec.rb
161
- - spec/hashie/extensions/dash/indifferent_access_spec.rb
168
+ - spec/hashie/extensions/method_access_spec.rb
169
+ - spec/hashie/extensions/strict_key_access_spec.rb
170
+ - spec/hashie/extensions/stringify_keys_spec.rb
171
+ - spec/hashie/extensions/symbolize_keys_spec.rb
172
+ - spec/hashie/hash_spec.rb
173
+ - spec/hashie/mash_spec.rb
174
+ - spec/hashie/parsers/yaml_erb_parser_spec.rb
175
+ - spec/hashie/rash_spec.rb
176
+ - spec/hashie/trash_spec.rb
177
+ - spec/hashie/version_spec.rb
178
+ - spec/spec_helper.rb
179
+ - spec/support/module_context.rb
@@ -1,10 +0,0 @@
1
- def mri22?
2
- ruby_version.start_with?('ruby_2.2')
3
- end
4
-
5
- def ruby_version
6
- interpreter = Object.const_defined?(:RUBY_ENGINE) && RUBY_ENGINE
7
- version = Object.const_defined?(:RUBY_VERSION) && RUBY_VERSION
8
-
9
- "#{interpreter}_#{version}"
10
- end