hashie 3.4.2 → 3.4.3

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