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 +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
|