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 +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +51 -2
- data/hashie.gemspec +1 -0
- data/lib/hashie.rb +2 -0
- data/lib/hashie/extensions/coercion.rb +65 -39
- data/lib/hashie/extensions/dash/coercion.rb +25 -0
- data/lib/hashie/extensions/dash/property_translation.rb +0 -2
- data/lib/hashie/extensions/deep_merge.rb +3 -1
- data/lib/hashie/extensions/mash/safe_assignment.rb +1 -1
- data/lib/hashie/extensions/strict_key_access.rb +74 -0
- data/lib/hashie/extensions/stringify_keys.rb +7 -4
- data/lib/hashie/extensions/symbolize_keys.rb +8 -5
- data/lib/hashie/mash.rb +19 -12
- data/lib/hashie/version.rb +1 -1
- data/spec/hashie/extensions/dash/coercion_spec.rb +13 -0
- data/spec/hashie/extensions/deep_merge_spec.rb +20 -0
- data/spec/hashie/extensions/mash/safe_assignment_spec.rb +27 -0
- data/spec/hashie/extensions/strict_key_access_spec.rb +110 -0
- data/spec/hashie/extensions/stringify_keys_spec.rb +23 -0
- data/spec/hashie/extensions/symbolize_keys_spec.rb +23 -0
- data/spec/hashie/mash_spec.rb +1 -4
- data/spec/spec_helper.rb +1 -0
- metadata +42 -24
- data/spec/support/ruby_version.rb +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6121c5c81c02643a5ac8bcf1633e09c0d18dd238
|
4
|
+
data.tar.gz: bfa9a6bed6dc2aef1f034297f30bc66ebe72b7ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cad338b78028860f36e964e2d531d793419e7ae533f2f33c452c60cd5e3df0a5e1f7838da102ed7b3b39f2be5c8ef3123bed779f477d970533b69d91ff6c27fd
|
7
|
+
data.tar.gz: 5bbb4adec507c10102832cca447f302ff5693e97131bc8d4c13f0e14038cff6605ceed36e77153d7bfa078aefe838513216ac64da945af464cb9a8213dbd5cd2
|
data/CHANGELOG.md
CHANGED
@@ -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
|
-
##
|
21
|
+
## Stable Release
|
22
22
|
|
23
|
-
You're reading the documentation for the stable release
|
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
|
data/hashie.gemspec
CHANGED
data/lib/hashie.rb
CHANGED
@@ -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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
58
|
-
|
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
|
-
|
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
|
58
|
-
|
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
|
data/lib/hashie/mash.rb
CHANGED
@@ -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
|
-
|
215
|
-
|
216
|
-
|
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 =
|
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
|
-
|
259
|
-
[
|
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:
|
data/lib/hashie/version.rb
CHANGED
@@ -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
|
data/spec/hashie/mash_spec.rb
CHANGED
@@ -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
|
-
|
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
|
data/spec/spec_helper.rb
CHANGED
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.
|
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-
|
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.
|
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/
|
145
|
-
- spec/hashie/
|
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/
|
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/
|
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/
|
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
|