hashie 2.1.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +3 -2
- data/CHANGELOG.md +11 -3
- data/Gemfile +1 -1
- data/README.md +72 -79
- data/UPGRADING.md +93 -0
- data/hashie.gemspec +8 -8
- data/lib/hashie.rb +18 -11
- data/lib/hashie/dash.rb +8 -9
- data/lib/hashie/extensions/coercion.rb +1 -1
- data/lib/hashie/extensions/dash/indifferent_access.rb +21 -0
- data/lib/hashie/extensions/deep_fetch.rb +1 -1
- data/lib/hashie/extensions/ignore_undeclared.rb +5 -1
- data/lib/hashie/extensions/key_conversion.rb +0 -82
- data/lib/hashie/extensions/pretty_inspect.rb +19 -0
- data/lib/hashie/extensions/stringify_keys.rb +44 -0
- data/lib/hashie/extensions/symbolize_keys.rb +44 -0
- data/lib/hashie/hash.rb +17 -5
- data/lib/hashie/mash.rb +24 -13
- data/lib/hashie/rash.rb +1 -1
- data/lib/hashie/trash.rb +9 -10
- data/lib/hashie/version.rb +1 -1
- data/spec/hashie/dash_spec.rb +69 -24
- data/spec/hashie/extensions/dash/indifferent_access_spec.rb +58 -0
- data/spec/hashie/extensions/deep_fetch_spec.rb +27 -0
- data/spec/hashie/extensions/ignore_undeclared_spec.rb +36 -13
- data/spec/hashie/extensions/indifferent_access_spec.rb +2 -2
- data/spec/hashie/hash_spec.rb +15 -23
- data/spec/hashie/mash_spec.rb +41 -29
- data/spec/hashie/trash_spec.rb +5 -2
- data/spec/spec_helper.rb +0 -1
- metadata +20 -28
- data/lib/hashie/hash_extensions.rb +0 -47
- data/spec/spec.opts +0 -3
data/lib/hashie.rb
CHANGED
@@ -1,17 +1,14 @@
|
|
1
1
|
module Hashie
|
2
|
-
autoload :Clash,
|
3
|
-
autoload :Dash,
|
4
|
-
autoload :Hash,
|
5
|
-
autoload :
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
autoload :Trash, 'hashie/trash'
|
9
|
-
autoload :Rash, 'hashie/rash'
|
2
|
+
autoload :Clash, 'hashie/clash'
|
3
|
+
autoload :Dash, 'hashie/dash'
|
4
|
+
autoload :Hash, 'hashie/hash'
|
5
|
+
autoload :Mash, 'hashie/mash'
|
6
|
+
autoload :Trash, 'hashie/trash'
|
7
|
+
autoload :Rash, 'hashie/rash'
|
10
8
|
|
11
9
|
module Extensions
|
12
10
|
autoload :Coercion, 'hashie/extensions/coercion'
|
13
11
|
autoload :DeepMerge, 'hashie/extensions/deep_merge'
|
14
|
-
autoload :KeyConversion, 'hashie/extensions/key_conversion'
|
15
12
|
autoload :IgnoreUndeclared, 'hashie/extensions/ignore_undeclared'
|
16
13
|
autoload :IndifferentAccess, 'hashie/extensions/indifferent_access'
|
17
14
|
autoload :MergeInitializer, 'hashie/extensions/merge_initializer'
|
@@ -19,8 +16,18 @@ module Hashie
|
|
19
16
|
autoload :MethodQuery, 'hashie/extensions/method_access'
|
20
17
|
autoload :MethodReader, 'hashie/extensions/method_access'
|
21
18
|
autoload :MethodWriter, 'hashie/extensions/method_access'
|
22
|
-
autoload :StringifyKeys, 'hashie/extensions/
|
23
|
-
autoload :SymbolizeKeys, 'hashie/extensions/
|
19
|
+
autoload :StringifyKeys, 'hashie/extensions/stringify_keys'
|
20
|
+
autoload :SymbolizeKeys, 'hashie/extensions/symbolize_keys'
|
24
21
|
autoload :DeepFetch, 'hashie/extensions/deep_fetch'
|
22
|
+
autoload :PrettyInspect, 'hashie/extensions/pretty_inspect'
|
23
|
+
autoload :KeyConversion, 'hashie/extensions/key_conversion'
|
24
|
+
|
25
|
+
module Mash
|
26
|
+
autoload :ActiveModel, 'hashie/extensions/mash/active_model'
|
27
|
+
end
|
28
|
+
|
29
|
+
module Dash
|
30
|
+
autoload :IndifferentAccess, 'hashie/extensions/dash/indifferent_access'
|
31
|
+
end
|
25
32
|
end
|
26
33
|
end
|
data/lib/hashie/dash.rb
CHANGED
@@ -13,7 +13,8 @@ module Hashie
|
|
13
13
|
# It is preferrable to a Struct because of the in-class
|
14
14
|
# API for defining properties as well as per-property defaults.
|
15
15
|
class Dash < Hash
|
16
|
-
include PrettyInspect
|
16
|
+
include Hashie::Extensions::PrettyInspect
|
17
|
+
|
17
18
|
alias_method :to_s, :inspect
|
18
19
|
|
19
20
|
# Defines a property on the Dash. Options are
|
@@ -28,8 +29,6 @@ module Hashie
|
|
28
29
|
# existing Dash.
|
29
30
|
#
|
30
31
|
def self.property(property_name, options = {})
|
31
|
-
property_name = property_name.to_sym
|
32
|
-
|
33
32
|
properties << property_name
|
34
33
|
|
35
34
|
if options.key?(:default)
|
@@ -39,9 +38,9 @@ module Hashie
|
|
39
38
|
end
|
40
39
|
|
41
40
|
unless instance_methods.map { |m| m.to_s }.include?("#{property_name}=")
|
42
|
-
define_method(property_name) { |&block| self.[](property_name
|
41
|
+
define_method(property_name) { |&block| self.[](property_name, &block) }
|
43
42
|
property_assignment = property_name.to_s.concat('=').to_sym
|
44
|
-
define_method(property_assignment) { |value| self.[]=(property_name
|
43
|
+
define_method(property_assignment) { |value| self.[]=(property_name, value) }
|
45
44
|
end
|
46
45
|
|
47
46
|
if defined? @subclasses
|
@@ -69,13 +68,13 @@ module Hashie
|
|
69
68
|
# Check to see if the specified property has already been
|
70
69
|
# defined.
|
71
70
|
def self.property?(name)
|
72
|
-
properties.include? name
|
71
|
+
properties.include? name
|
73
72
|
end
|
74
73
|
|
75
74
|
# Check to see if the specified property is
|
76
75
|
# required.
|
77
76
|
def self.required?(name)
|
78
|
-
required_properties.include? name
|
77
|
+
required_properties.include? name
|
79
78
|
end
|
80
79
|
|
81
80
|
# You may initialize a Dash with an attributes hash
|
@@ -103,7 +102,7 @@ module Hashie
|
|
103
102
|
# property's default value if it hasn't been set).
|
104
103
|
def [](property)
|
105
104
|
assert_property_exists! property
|
106
|
-
value = super(property
|
105
|
+
value = super(property)
|
107
106
|
# If the value is a lambda, proc, or whatever answers to call, eval the thing!
|
108
107
|
if value.is_a? Proc
|
109
108
|
self[property] = value.call # Set the result of the call as a value
|
@@ -118,7 +117,7 @@ module Hashie
|
|
118
117
|
def []=(property, value)
|
119
118
|
assert_property_required! property, value
|
120
119
|
assert_property_exists! property
|
121
|
-
super(property
|
120
|
+
super(property, value)
|
122
121
|
end
|
123
122
|
|
124
123
|
def merge(other_hash)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module Dash
|
4
|
+
module IndifferentAccess
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
base.send :include, Hashie::Extensions::IndifferentAccess
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
# Check to see if the specified property has already been
|
12
|
+
# defined.
|
13
|
+
def property?(name)
|
14
|
+
name = name.to_s
|
15
|
+
!!properties.find { |property| property.to_s == name }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -18,7 +18,7 @@ module Hashie
|
|
18
18
|
begin
|
19
19
|
arg = Integer(arg) if obj.kind_of? Array
|
20
20
|
obj.fetch(arg)
|
21
|
-
rescue ArgumentError, IndexError => e
|
21
|
+
rescue ArgumentError, IndexError, NoMethodError => e
|
22
22
|
break block.call(arg) if block
|
23
23
|
raise UndefinedPathError, "Could not fetch path (#{args.join(' > ')}) at #{arg}", e.backtrace
|
24
24
|
end
|
@@ -5,7 +5,7 @@ module Hashie
|
|
5
5
|
# raising an error. This is useful when using a Trash to
|
6
6
|
# capture a subset of a larger hash.
|
7
7
|
#
|
8
|
-
# Note that attempting to retrieve an undeclared property
|
8
|
+
# Note that attempting to retrieve or set an undeclared property
|
9
9
|
# will still raise a NoMethodError, even if a value for
|
10
10
|
# that property was provided at initialization.
|
11
11
|
#
|
@@ -36,6 +36,10 @@ module Hashie
|
|
36
36
|
end
|
37
37
|
end if attributes
|
38
38
|
end
|
39
|
+
|
40
|
+
def property_exists?(property)
|
41
|
+
self.class.property?(property)
|
42
|
+
end
|
39
43
|
end
|
40
44
|
end
|
41
45
|
end
|
@@ -1,87 +1,5 @@
|
|
1
1
|
module Hashie
|
2
2
|
module Extensions
|
3
|
-
module StringifyKeys
|
4
|
-
# Convert all keys in the hash to strings.
|
5
|
-
#
|
6
|
-
# @example
|
7
|
-
# test = {:abc => 'def'}
|
8
|
-
# test.stringify_keys!
|
9
|
-
# test # => {'abc' => 'def'}
|
10
|
-
def stringify_keys!
|
11
|
-
keys.each do |k|
|
12
|
-
stringify_keys_recursively!(self[k])
|
13
|
-
self[k.to_s] = delete(k)
|
14
|
-
end
|
15
|
-
self
|
16
|
-
end
|
17
|
-
|
18
|
-
# Return a new hash with all keys converted
|
19
|
-
# to strings.
|
20
|
-
def stringify_keys
|
21
|
-
dup.stringify_keys!
|
22
|
-
end
|
23
|
-
|
24
|
-
protected
|
25
|
-
|
26
|
-
# Stringify all keys recursively within nested
|
27
|
-
# hashes and arrays.
|
28
|
-
def stringify_keys_recursively!(object)
|
29
|
-
if self.class === object
|
30
|
-
object.stringify_keys!
|
31
|
-
elsif ::Array === object
|
32
|
-
object.each do |i|
|
33
|
-
stringify_keys_recursively!(i)
|
34
|
-
end
|
35
|
-
object
|
36
|
-
elsif object.respond_to?(:stringify_keys!)
|
37
|
-
object.stringify_keys!
|
38
|
-
else
|
39
|
-
object
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
module SymbolizeKeys
|
45
|
-
# Convert all keys in the hash to symbols.
|
46
|
-
#
|
47
|
-
# @example
|
48
|
-
# test = {'abc' => 'def'}
|
49
|
-
# test.symbolize_keys!
|
50
|
-
# test # => {:abc => 'def'}
|
51
|
-
def symbolize_keys!
|
52
|
-
keys.each do |k|
|
53
|
-
symbolize_keys_recursively!(self[k])
|
54
|
-
self[k.to_sym] = delete(k)
|
55
|
-
end
|
56
|
-
self
|
57
|
-
end
|
58
|
-
|
59
|
-
# Return a new hash with all keys converted
|
60
|
-
# to symbols.
|
61
|
-
def symbolize_keys
|
62
|
-
dup.symbolize_keys!
|
63
|
-
end
|
64
|
-
|
65
|
-
protected
|
66
|
-
|
67
|
-
# Symbolize all keys recursively within nested
|
68
|
-
# hashes and arrays.
|
69
|
-
def symbolize_keys_recursively!(object)
|
70
|
-
if self.class === object
|
71
|
-
object.symbolize_keys!
|
72
|
-
elsif ::Array === object
|
73
|
-
object.each do |i|
|
74
|
-
symbolize_keys_recursively!(i)
|
75
|
-
end
|
76
|
-
object
|
77
|
-
elsif object.respond_to?(:symbolize_keys!)
|
78
|
-
object.symbolize_keys!
|
79
|
-
else
|
80
|
-
object
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
3
|
module KeyConversion
|
86
4
|
def self.included(base)
|
87
5
|
base.send :include, SymbolizeKeys
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module PrettyInspect
|
4
|
+
def self.included(base)
|
5
|
+
base.send :alias_method, :hash_inspect, :inspect
|
6
|
+
base.send :alias_method, :inspect, :hashie_inspect
|
7
|
+
end
|
8
|
+
|
9
|
+
def hashie_inspect
|
10
|
+
ret = "#<#{self.class}"
|
11
|
+
keys.sort_by { |key| key.to_s }.each do |key|
|
12
|
+
ret << " #{key}=#{self[key].inspect}"
|
13
|
+
end
|
14
|
+
ret << '>'
|
15
|
+
ret
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module StringifyKeys
|
4
|
+
# Convert all keys in the hash to strings.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# test = {:abc => 'def'}
|
8
|
+
# test.stringify_keys!
|
9
|
+
# test # => {'abc' => 'def'}
|
10
|
+
def stringify_keys!
|
11
|
+
keys.each do |k|
|
12
|
+
stringify_keys_recursively!(self[k])
|
13
|
+
self[k.to_s] = delete(k)
|
14
|
+
end
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return a new hash with all keys converted
|
19
|
+
# to strings.
|
20
|
+
def stringify_keys
|
21
|
+
dup.stringify_keys!
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
# Stringify all keys recursively within nested
|
27
|
+
# hashes and arrays.
|
28
|
+
def stringify_keys_recursively!(object)
|
29
|
+
if self.class === object
|
30
|
+
object.stringify_keys!
|
31
|
+
elsif ::Array === object
|
32
|
+
object.each do |i|
|
33
|
+
stringify_keys_recursively!(i)
|
34
|
+
end
|
35
|
+
object
|
36
|
+
elsif object.respond_to?(:stringify_keys!)
|
37
|
+
object.stringify_keys!
|
38
|
+
else
|
39
|
+
object
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module SymbolizeKeys
|
4
|
+
# Convert all keys in the hash to symbols.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# test = {'abc' => 'def'}
|
8
|
+
# test.symbolize_keys!
|
9
|
+
# test # => {:abc => 'def'}
|
10
|
+
def symbolize_keys!
|
11
|
+
keys.each do |k|
|
12
|
+
symbolize_keys_recursively!(self[k])
|
13
|
+
self[k.to_sym] = delete(k)
|
14
|
+
end
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return a new hash with all keys converted
|
19
|
+
# to symbols.
|
20
|
+
def symbolize_keys
|
21
|
+
dup.symbolize_keys!
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
# Symbolize all keys recursively within nested
|
27
|
+
# hashes and arrays.
|
28
|
+
def symbolize_keys_recursively!(object)
|
29
|
+
if self.class === object
|
30
|
+
object.symbolize_keys!
|
31
|
+
elsif ::Array === object
|
32
|
+
object.each do |i|
|
33
|
+
symbolize_keys_recursively!(i)
|
34
|
+
end
|
35
|
+
object
|
36
|
+
elsif object.respond_to?(:symbolize_keys!)
|
37
|
+
object.symbolize_keys!
|
38
|
+
else
|
39
|
+
object
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/hashie/hash.rb
CHANGED
@@ -1,25 +1,37 @@
|
|
1
|
-
require 'hashie/
|
1
|
+
require 'hashie/extensions/stringify_keys'
|
2
|
+
require 'hashie/extensions/pretty_inspect'
|
2
3
|
|
3
4
|
module Hashie
|
4
5
|
# A Hashie Hash is simply a Hash that has convenience
|
5
6
|
# functions baked in such as stringify_keys that may
|
6
7
|
# not be available in all libraries.
|
7
8
|
class Hash < ::Hash
|
8
|
-
include
|
9
|
+
include Hashie::Extensions::PrettyInspect
|
10
|
+
include Hashie::Extensions::StringifyKeys
|
11
|
+
|
12
|
+
# Convert this hash into a Mash
|
13
|
+
def to_mash
|
14
|
+
::Hashie::Mash.new(self)
|
15
|
+
end
|
9
16
|
|
10
17
|
# Converts a mash back to a hash (with stringified or symbolized keys)
|
11
18
|
def to_hash(options = {})
|
12
19
|
out = {}
|
13
20
|
keys.each do |k|
|
14
|
-
assignment_key =
|
15
|
-
|
21
|
+
assignment_key = if options[:stringify_keys]
|
22
|
+
k.to_s
|
23
|
+
elsif options[:symbolize_keys]
|
24
|
+
k.to_s.to_sym
|
25
|
+
else
|
26
|
+
k
|
27
|
+
end
|
16
28
|
if self[k].is_a?(Array)
|
17
29
|
out[assignment_key] ||= []
|
18
30
|
self[k].each do |array_object|
|
19
31
|
out[assignment_key] << (Hash === array_object ? flexibly_convert_to_hash(array_object, options) : array_object)
|
20
32
|
end
|
21
33
|
else
|
22
|
-
out[assignment_key] =
|
34
|
+
out[assignment_key] = Hash === self[k] ? flexibly_convert_to_hash(self[k], options) : self[k]
|
23
35
|
end
|
24
36
|
end
|
25
37
|
out
|
data/lib/hashie/mash.rb
CHANGED
@@ -55,8 +55,10 @@ module Hashie
|
|
55
55
|
# mash.author # => <Mash>
|
56
56
|
#
|
57
57
|
class Mash < Hash
|
58
|
+
include Hashie::Extensions::PrettyInspect
|
59
|
+
|
58
60
|
ALLOWED_SUFFIXES = %w(? ! = _)
|
59
|
-
|
61
|
+
|
60
62
|
alias_method :to_s, :inspect
|
61
63
|
|
62
64
|
# If you pass in an existing hash, it will
|
@@ -185,11 +187,15 @@ module Hashie
|
|
185
187
|
self
|
186
188
|
end
|
187
189
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
190
|
+
def respond_to_missing?(method_name, *args)
|
191
|
+
return true if key?(method_name)
|
192
|
+
_, suffix = method_suffix(method_name)
|
193
|
+
case suffix
|
194
|
+
when '=', '?', '!', '_'
|
195
|
+
return true
|
196
|
+
else
|
197
|
+
super
|
198
|
+
end
|
193
199
|
end
|
194
200
|
|
195
201
|
def prefix_method?(method_name)
|
@@ -199,17 +205,16 @@ module Hashie
|
|
199
205
|
|
200
206
|
def method_missing(method_name, *args, &blk)
|
201
207
|
return self.[](method_name, &blk) if key?(method_name)
|
202
|
-
|
203
|
-
|
204
|
-
case match[2]
|
208
|
+
name, suffix = method_suffix(method_name)
|
209
|
+
case suffix
|
205
210
|
when '='
|
206
|
-
self[
|
211
|
+
self[name] = args.first
|
207
212
|
when '?'
|
208
|
-
!!self[
|
213
|
+
!!self[name]
|
209
214
|
when '!'
|
210
|
-
initializing_reader(
|
215
|
+
initializing_reader(name)
|
211
216
|
when '_'
|
212
|
-
underbang_reader(
|
217
|
+
underbang_reader(name)
|
213
218
|
else
|
214
219
|
default(method_name)
|
215
220
|
end
|
@@ -217,6 +222,12 @@ module Hashie
|
|
217
222
|
|
218
223
|
protected
|
219
224
|
|
225
|
+
def method_suffix(method_name)
|
226
|
+
suffixes_regex = ALLOWED_SUFFIXES.join
|
227
|
+
match = method_name.to_s.match(/(.*?)([#{suffixes_regex}]?)$/)
|
228
|
+
[match[1], match[2]]
|
229
|
+
end
|
230
|
+
|
220
231
|
def convert_key(key) #:nodoc:
|
221
232
|
key.to_s
|
222
233
|
end
|