hashie 2.1.2 → 4.1.0
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 +7 -0
- data/CHANGELOG.md +524 -59
- data/CONTRIBUTING.md +24 -7
- data/README.md +781 -90
- data/Rakefile +19 -2
- data/UPGRADING.md +245 -0
- data/hashie.gemspec +21 -13
- data/lib/hashie.rb +60 -21
- data/lib/hashie/array.rb +21 -0
- data/lib/hashie/clash.rb +24 -12
- data/lib/hashie/dash.rb +96 -33
- data/lib/hashie/extensions/active_support/core_ext/hash.rb +14 -0
- data/lib/hashie/extensions/array/pretty_inspect.rb +19 -0
- data/lib/hashie/extensions/coercion.rb +124 -18
- data/lib/hashie/extensions/dash/coercion.rb +25 -0
- data/lib/hashie/extensions/dash/indifferent_access.rb +56 -0
- data/lib/hashie/extensions/dash/property_translation.rb +191 -0
- data/lib/hashie/extensions/deep_fetch.rb +7 -5
- data/lib/hashie/extensions/deep_find.rb +69 -0
- data/lib/hashie/extensions/deep_locate.rb +113 -0
- data/lib/hashie/extensions/deep_merge.rb +35 -12
- data/lib/hashie/extensions/ignore_undeclared.rb +11 -5
- data/lib/hashie/extensions/indifferent_access.rb +28 -16
- data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
- data/lib/hashie/extensions/key_conversion.rb +0 -82
- data/lib/hashie/extensions/mash/define_accessors.rb +90 -0
- data/lib/hashie/extensions/mash/keep_original_keys.rb +53 -0
- data/lib/hashie/extensions/mash/permissive_respond_to.rb +61 -0
- data/lib/hashie/extensions/mash/safe_assignment.rb +18 -0
- data/lib/hashie/extensions/mash/symbolize_keys.rb +38 -0
- data/lib/hashie/extensions/method_access.rb +154 -11
- data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +48 -0
- data/lib/hashie/extensions/pretty_inspect.rb +19 -0
- data/lib/hashie/extensions/ruby_version.rb +60 -0
- data/lib/hashie/extensions/ruby_version_check.rb +21 -0
- data/lib/hashie/extensions/strict_key_access.rb +77 -0
- data/lib/hashie/extensions/stringify_keys.rb +71 -0
- data/lib/hashie/extensions/symbolize_keys.rb +71 -0
- data/lib/hashie/hash.rb +27 -8
- data/lib/hashie/logger.rb +18 -0
- data/lib/hashie/mash.rb +235 -57
- data/lib/hashie/railtie.rb +21 -0
- data/lib/hashie/rash.rb +40 -16
- data/lib/hashie/trash.rb +2 -88
- data/lib/hashie/utils.rb +44 -0
- data/lib/hashie/version.rb +1 -1
- metadata +42 -81
- data/.gitignore +0 -9
- data/.rspec +0 -2
- data/.rubocop.yml +0 -36
- data/.travis.yml +0 -15
- data/Gemfile +0 -11
- data/Guardfile +0 -5
- data/lib/hashie/hash_extensions.rb +0 -47
- data/spec/hashie/clash_spec.rb +0 -48
- data/spec/hashie/dash_spec.rb +0 -338
- data/spec/hashie/extensions/coercion_spec.rb +0 -156
- data/spec/hashie/extensions/deep_fetch_spec.rb +0 -70
- data/spec/hashie/extensions/deep_merge_spec.rb +0 -22
- data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -23
- data/spec/hashie/extensions/indifferent_access_spec.rb +0 -152
- data/spec/hashie/extensions/key_conversion_spec.rb +0 -103
- data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
- data/spec/hashie/extensions/method_access_spec.rb +0 -121
- data/spec/hashie/hash_spec.rb +0 -66
- data/spec/hashie/mash_spec.rb +0 -467
- data/spec/hashie/rash_spec.rb +0 -44
- data/spec/hashie/trash_spec.rb +0 -193
- data/spec/hashie/version_spec.rb +0 -7
- data/spec/spec.opts +0 -3
- data/spec/spec_helper.rb +0 -8
@@ -2,28 +2,51 @@ module Hashie
|
|
2
2
|
module Extensions
|
3
3
|
module DeepMerge
|
4
4
|
# Returns a new hash with +self+ and +other_hash+ merged recursively.
|
5
|
-
def deep_merge(other_hash)
|
6
|
-
|
5
|
+
def deep_merge(other_hash, &block)
|
6
|
+
copy = _deep_dup(self)
|
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.
|
10
12
|
# Modifies the receiver in place.
|
11
|
-
def deep_merge!(other_hash)
|
12
|
-
|
13
|
+
def deep_merge!(other_hash, &block)
|
14
|
+
return self unless other_hash.is_a?(::Hash)
|
15
|
+
_recursive_merge(self, other_hash, &block)
|
13
16
|
self
|
14
17
|
end
|
15
18
|
|
16
19
|
private
|
17
20
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
def _deep_dup(hash)
|
22
|
+
copy = hash.dup
|
23
|
+
|
24
|
+
copy.each do |key, value|
|
25
|
+
copy[key] =
|
26
|
+
if value.is_a?(::Hash)
|
27
|
+
_deep_dup(value)
|
28
|
+
else
|
29
|
+
Hashie::Utils.safe_dup(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
copy
|
34
|
+
end
|
35
|
+
|
36
|
+
def _recursive_merge(hash, other_hash, &block)
|
37
|
+
other_hash.each do |k, v|
|
38
|
+
hash[k] =
|
39
|
+
if hash.key?(k) && hash[k].is_a?(::Hash) && v.is_a?(::Hash)
|
40
|
+
_recursive_merge(hash[k], v, &block)
|
41
|
+
elsif v.is_a?(::Hash)
|
42
|
+
_recursive_merge({}, v, &block)
|
43
|
+
elsif hash.key?(k) && block_given?
|
44
|
+
yield(k, hash[k], v)
|
45
|
+
else
|
46
|
+
v.respond_to?(:deep_dup) ? v.deep_dup : v
|
47
|
+
end
|
26
48
|
end
|
49
|
+
hash
|
27
50
|
end
|
28
51
|
end
|
29
52
|
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
|
#
|
@@ -30,11 +30,17 @@ module Hashie
|
|
30
30
|
# p.email # => NoMethodError
|
31
31
|
module IgnoreUndeclared
|
32
32
|
def initialize_attributes(attributes)
|
33
|
+
return unless attributes
|
34
|
+
klass = self.class
|
35
|
+
translations = klass.respond_to?(:translations) && klass.translations
|
33
36
|
attributes.each_pair do |att, value|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
next unless klass.property?(att) || (translations && translations.include?(att))
|
38
|
+
self[att] = value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def property_exists?(property)
|
43
|
+
self.class.property?(property)
|
38
44
|
end
|
39
45
|
end
|
40
46
|
end
|
@@ -24,16 +24,18 @@ module Hashie
|
|
24
24
|
#
|
25
25
|
module IndifferentAccess
|
26
26
|
def self.included(base)
|
27
|
+
Hashie::Extensions::Dash::IndifferentAccess.maybe_extend(base)
|
28
|
+
|
27
29
|
base.class_eval do
|
28
|
-
alias_method :regular_writer, :[]=
|
30
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
29
31
|
alias_method :[]=, :indifferent_writer
|
30
32
|
alias_method :store, :indifferent_writer
|
31
|
-
%w
|
32
|
-
alias_method "regular_#{m}", m
|
33
|
+
%w[default update replace fetch delete key? values_at].each do |m|
|
34
|
+
alias_method "regular_#{m}", m unless method_defined?("regular_#{m}")
|
33
35
|
alias_method m, "indifferent_#{m}"
|
34
36
|
end
|
35
37
|
|
36
|
-
%w
|
38
|
+
%w[include? member? has_key?].each do |key_alias|
|
37
39
|
alias_method key_alias, :indifferent_key?
|
38
40
|
end
|
39
41
|
|
@@ -71,17 +73,17 @@ module Hashie
|
|
71
73
|
# their proper indifferent state. Used when IndifferentAccess
|
72
74
|
# is injecting itself into member hashes.
|
73
75
|
def convert!
|
74
|
-
keys.each do |k|
|
75
|
-
regular_writer convert_key(k),
|
76
|
+
keys.each do |k| # rubocop:disable Performance/HashEachMethods
|
77
|
+
regular_writer convert_key(k), indifferent_value(regular_delete(k))
|
76
78
|
end
|
77
79
|
self
|
78
80
|
end
|
79
81
|
|
80
|
-
def
|
82
|
+
def indifferent_value(value)
|
81
83
|
if hash_lacking_indifference?(value)
|
82
|
-
IndifferentAccess.inject(value
|
84
|
+
IndifferentAccess.inject!(value)
|
83
85
|
elsif value.is_a?(::Array)
|
84
|
-
value.
|
86
|
+
value.replace(value.map { |e| indifferent_value(e) })
|
85
87
|
else
|
86
88
|
value
|
87
89
|
end
|
@@ -100,11 +102,11 @@ module Hashie
|
|
100
102
|
end
|
101
103
|
|
102
104
|
def indifferent_writer(key, value)
|
103
|
-
regular_writer convert_key(key),
|
105
|
+
regular_writer convert_key(key), indifferent_value(value)
|
104
106
|
end
|
105
107
|
|
106
|
-
def indifferent_fetch(key, *args)
|
107
|
-
regular_fetch convert_key(key), *args
|
108
|
+
def indifferent_fetch(key, *args, &block)
|
109
|
+
regular_fetch convert_key(key), *args, &block
|
108
110
|
end
|
109
111
|
|
110
112
|
def indifferent_delete(key)
|
@@ -129,18 +131,28 @@ module Hashie
|
|
129
131
|
self
|
130
132
|
end
|
131
133
|
|
134
|
+
def merge(*args)
|
135
|
+
result = super
|
136
|
+
IndifferentAccess.inject!(result) if hash_lacking_indifference?(result)
|
137
|
+
result.convert!
|
138
|
+
end
|
139
|
+
|
140
|
+
def merge!(*)
|
141
|
+
super.convert!
|
142
|
+
end
|
143
|
+
|
132
144
|
protected
|
133
145
|
|
134
146
|
def hash_lacking_indifference?(other)
|
135
147
|
other.is_a?(::Hash) &&
|
136
|
-
|
137
|
-
|
148
|
+
!(other.respond_to?(:indifferent_access?) &&
|
149
|
+
other.indifferent_access?)
|
138
150
|
end
|
139
151
|
|
140
152
|
def hash_with_indifference?(other)
|
141
153
|
other.is_a?(::Hash) &&
|
142
|
-
|
143
|
-
|
154
|
+
other.respond_to?(:indifferent_access?) &&
|
155
|
+
other.indifferent_access?
|
144
156
|
end
|
145
157
|
end
|
146
158
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module KeyConflictWarning
|
4
|
+
class CannotDisableMashWarnings < StandardError
|
5
|
+
def initialize
|
6
|
+
super(
|
7
|
+
'You cannot disable warnings on the base Mash class. ' \
|
8
|
+
'Please subclass the Mash and disable it in the subclass.'
|
9
|
+
)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Disable the logging of warnings based on keys conflicting keys/methods
|
14
|
+
#
|
15
|
+
# @api semipublic
|
16
|
+
# @return [void]
|
17
|
+
def disable_warnings(*method_keys)
|
18
|
+
raise CannotDisableMashWarnings if self == Hashie::Mash
|
19
|
+
if method_keys.any?
|
20
|
+
disabled_warnings.concat(method_keys).tap(&:flatten!).uniq!
|
21
|
+
else
|
22
|
+
disabled_warnings.clear
|
23
|
+
end
|
24
|
+
|
25
|
+
@disable_warnings = true
|
26
|
+
end
|
27
|
+
|
28
|
+
# Checks whether this class disables warnings for conflicting keys/methods
|
29
|
+
#
|
30
|
+
# @api semipublic
|
31
|
+
# @return [Boolean]
|
32
|
+
def disable_warnings?(method_key = nil)
|
33
|
+
return disabled_warnings.include?(method_key) if disabled_warnings.any? && method_key
|
34
|
+
@disable_warnings ||= false
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns an array of methods that this class disables warnings for.
|
38
|
+
#
|
39
|
+
# @api semipublic
|
40
|
+
# @return [Boolean]
|
41
|
+
def disabled_warnings
|
42
|
+
@_disabled_warnings ||= []
|
43
|
+
end
|
44
|
+
|
45
|
+
# Inheritance hook that sets class configuration when inherited.
|
46
|
+
#
|
47
|
+
# @api semipublic
|
48
|
+
# @return [void]
|
49
|
+
def inherited(subclass)
|
50
|
+
super
|
51
|
+
subclass.disable_warnings(disabled_warnings) if disable_warnings?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
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,90 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module Mash
|
4
|
+
module DefineAccessors
|
5
|
+
def self.included(klass)
|
6
|
+
klass.class_eval do
|
7
|
+
mod = Ext.new
|
8
|
+
include mod
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.extended(obj)
|
13
|
+
included(obj.singleton_class)
|
14
|
+
end
|
15
|
+
|
16
|
+
class Ext < Module
|
17
|
+
def initialize
|
18
|
+
mod = self
|
19
|
+
define_method(:method_missing) do |method_name, *args, &block|
|
20
|
+
key, suffix = method_name_and_suffix(method_name)
|
21
|
+
case suffix
|
22
|
+
when '='.freeze
|
23
|
+
mod.define_writer(key, method_name)
|
24
|
+
when '?'.freeze
|
25
|
+
mod.define_predicate(key, method_name)
|
26
|
+
when '!'.freeze
|
27
|
+
mod.define_initializing_reader(key, method_name)
|
28
|
+
when '_'.freeze
|
29
|
+
mod.define_underbang_reader(key, method_name)
|
30
|
+
else
|
31
|
+
mod.define_reader(key, method_name)
|
32
|
+
end
|
33
|
+
send(method_name, *args, &block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def define_reader(key, method_name)
|
38
|
+
define_method(method_name) do |&block|
|
39
|
+
if key? method_name
|
40
|
+
self.[](method_name, &block)
|
41
|
+
else
|
42
|
+
self.[](key, &block)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def define_writer(key, method_name)
|
48
|
+
define_method(method_name) do |value = nil|
|
49
|
+
if key? method_name
|
50
|
+
self.[](method_name, &proc)
|
51
|
+
else
|
52
|
+
assign_property(key, value)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def define_predicate(key, method_name)
|
58
|
+
define_method(method_name) do
|
59
|
+
if key? method_name
|
60
|
+
self.[](method_name, &proc)
|
61
|
+
else
|
62
|
+
!!self[key]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def define_initializing_reader(key, method_name)
|
68
|
+
define_method(method_name) do
|
69
|
+
if key? method_name
|
70
|
+
self.[](method_name, &proc)
|
71
|
+
else
|
72
|
+
initializing_reader(key)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def define_underbang_reader(key, method_name)
|
78
|
+
define_method(method_name) do
|
79
|
+
if key? method_name
|
80
|
+
self.[](key, &proc)
|
81
|
+
else
|
82
|
+
underbang_reader(key)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module Mash
|
4
|
+
# Overrides the indifferent access of a Mash to keep keys in the
|
5
|
+
# original format given to the Mash.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# class KeepingMash < Hashie::Mash
|
9
|
+
# include Hashie::Extensions::Mash::KeepOriginalKeys
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
# mash = KeepingMash.new(:symbol_key => :symbol, 'string_key' => 'string')
|
13
|
+
# mash.to_hash #=> { :symbol_key => :symbol, 'string_key' => 'string' }
|
14
|
+
# mash['string_key'] == mash[:string_key] #=> true
|
15
|
+
# mash[:symbol_key] == mash['symbol_key'] #=> true
|
16
|
+
module KeepOriginalKeys
|
17
|
+
def self.included(descendant)
|
18
|
+
error_message = "#{descendant} is not a kind of Hashie::Mash"
|
19
|
+
raise ArgumentError, error_message unless descendant <= Hashie::Mash
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Converts the key when necessary to access the correct Mash key.
|
25
|
+
#
|
26
|
+
# @param [Object, String, Symbol] key the key to access.
|
27
|
+
# @return [Object] the value assigned to the key.
|
28
|
+
def convert_key(key)
|
29
|
+
if regular_key?(key)
|
30
|
+
key
|
31
|
+
elsif (converted_key = __convert(key)) && regular_key?(converted_key)
|
32
|
+
converted_key
|
33
|
+
else
|
34
|
+
key
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Converts symbol/string keys to their alternative formats, but leaves
|
39
|
+
# other keys alone.
|
40
|
+
#
|
41
|
+
# @param [Object, String, Symbol] key the key to convert.
|
42
|
+
# @return [Object, String, Symbol] the converted key.
|
43
|
+
def __convert(key)
|
44
|
+
case key
|
45
|
+
when Symbol then key.to_s
|
46
|
+
when String then key.to_sym
|
47
|
+
else key
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|