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
@@ -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
|
@@ -0,0 +1,56 @@
|
|
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
|
+
def self.maybe_extend(base)
|
11
|
+
return unless requires_class_methods?(base)
|
12
|
+
|
13
|
+
base.extend(ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.requires_class_methods?(klass)
|
17
|
+
klass <= Hashie::Dash &&
|
18
|
+
!klass.singleton_class.included_modules.include?(ClassMethods)
|
19
|
+
end
|
20
|
+
private_class_method :requires_class_methods?
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
# Check to see if the specified property has already been
|
24
|
+
# defined.
|
25
|
+
def property?(name)
|
26
|
+
name = translations[name.to_sym] if translation_for?(name)
|
27
|
+
name = name.to_s
|
28
|
+
!!properties.find { |property| property.to_s == name }
|
29
|
+
end
|
30
|
+
|
31
|
+
def translation_exists?(name)
|
32
|
+
name = name.to_s
|
33
|
+
!!translations.keys.find { |key| key.to_s == name }
|
34
|
+
end
|
35
|
+
|
36
|
+
def transformed_property(property_name, value)
|
37
|
+
transform = transforms[property_name] || transforms[property_name.to_sym]
|
38
|
+
transform.call(value)
|
39
|
+
end
|
40
|
+
|
41
|
+
def transformation_exists?(name)
|
42
|
+
name = name.to_s
|
43
|
+
!!transforms.keys.find { |key| key.to_s == name }
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def translation_for?(name)
|
49
|
+
included_modules.include?(Hashie::Extensions::Dash::PropertyTranslation) &&
|
50
|
+
translation_exists?(name)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module Dash
|
4
|
+
# Extends a Dash with the ability to remap keys from a source hash.
|
5
|
+
#
|
6
|
+
# Property translation is useful when you need to read data from another
|
7
|
+
# application -- such as a Java API -- where the keys are named
|
8
|
+
# differently from Ruby conventions.
|
9
|
+
#
|
10
|
+
# == Example from inconsistent APIs
|
11
|
+
#
|
12
|
+
# class PersonHash < Hashie::Dash
|
13
|
+
# include Hashie::Extensions::Dash::PropertyTranslation
|
14
|
+
#
|
15
|
+
# property :first_name, from :firstName
|
16
|
+
# property :last_name, from: :lastName
|
17
|
+
# property :first_name, from: :f_name
|
18
|
+
# property :last_name, from: :l_name
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# person = PersonHash.new(firstName: 'Michael', l_name: 'Bleigh')
|
22
|
+
# person[:first_name] #=> 'Michael'
|
23
|
+
# person[:last_name] #=> 'Bleigh'
|
24
|
+
#
|
25
|
+
# You can also use a lambda to translate the value. This is particularly
|
26
|
+
# useful when you want to ensure the type of data you're wrapping.
|
27
|
+
#
|
28
|
+
# == Example using translation lambdas
|
29
|
+
#
|
30
|
+
# class DataModelHash < Hashie::Dash
|
31
|
+
# include Hashie::Extensions::Dash::PropertyTranslation
|
32
|
+
#
|
33
|
+
# property :id, transform_with: ->(value) { value.to_i }
|
34
|
+
# property :created_at, from: :created, with: ->(value) { Time.parse(value) }
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# model = DataModelHash.new(id: '123', created: '2014-04-25 22:35:28')
|
38
|
+
# model.id.class #=> Integer (Fixnum if you are using Ruby 2.3 or lower)
|
39
|
+
# model.created_at.class #=> Time
|
40
|
+
module PropertyTranslation
|
41
|
+
def self.included(base)
|
42
|
+
base.instance_variable_set(:@transforms, {})
|
43
|
+
base.instance_variable_set(:@translations_hash, ::Hash.new { |hash, key| hash[key] = {} })
|
44
|
+
base.extend(ClassMethods)
|
45
|
+
base.send(:include, InstanceMethods)
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
attr_reader :transforms, :translations_hash
|
50
|
+
|
51
|
+
# Ensures that any inheriting classes maintain their translations.
|
52
|
+
#
|
53
|
+
# * <tt>:default</tt> - The class inheriting the translations.
|
54
|
+
def inherited(klass)
|
55
|
+
super
|
56
|
+
klass.instance_variable_set(:@transforms, transforms.dup)
|
57
|
+
klass.instance_variable_set(:@translations_hash, translations_hash.dup)
|
58
|
+
end
|
59
|
+
|
60
|
+
def permitted_input_keys
|
61
|
+
@permitted_input_keys ||=
|
62
|
+
properties
|
63
|
+
.map { |property| inverse_translations.fetch property, property }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Defines a property on the Trash. Options are as follows:
|
67
|
+
#
|
68
|
+
# * <tt>:default</tt> - Specify a default value for this property, to be
|
69
|
+
# returned before a value is set on the property in a new Dash.
|
70
|
+
# * <tt>:from</tt> - Specify the original key name that will be write only.
|
71
|
+
# * <tt>:with</tt> - Specify a lambda to be used to convert value.
|
72
|
+
# * <tt>:transform_with</tt> - Specify a lambda to be used to convert value
|
73
|
+
# without using the :from option. It transform the property itself.
|
74
|
+
def property(property_name, options = {})
|
75
|
+
super
|
76
|
+
|
77
|
+
from = options[:from]
|
78
|
+
converter = options[:with]
|
79
|
+
transformer = options[:transform_with]
|
80
|
+
|
81
|
+
if from
|
82
|
+
fail_self_transformation_error!(property_name) if property_name == from
|
83
|
+
define_translation(from, property_name, converter || transformer)
|
84
|
+
define_writer_for_source_property(from)
|
85
|
+
elsif valid_transformer?(transformer)
|
86
|
+
transforms[property_name] = transformer
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def transformed_property(property_name, value)
|
91
|
+
transforms[property_name].call(value)
|
92
|
+
end
|
93
|
+
|
94
|
+
def transformation_exists?(name)
|
95
|
+
transforms.key? name
|
96
|
+
end
|
97
|
+
|
98
|
+
def translation_exists?(name)
|
99
|
+
translations_hash.key? name
|
100
|
+
end
|
101
|
+
|
102
|
+
def translations
|
103
|
+
@translations ||= {}.tap do |translations|
|
104
|
+
translations_hash.each do |(property_name, property_translations)|
|
105
|
+
translations[property_name] =
|
106
|
+
if property_translations.size > 1
|
107
|
+
property_translations.keys
|
108
|
+
else
|
109
|
+
property_translations.keys.first
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def inverse_translations
|
116
|
+
@inverse_translations ||= {}.tap do |translations|
|
117
|
+
translations_hash.each do |(property_name, property_translations)|
|
118
|
+
property_translations.each_key do |key|
|
119
|
+
translations[key] = property_name
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def define_translation(from, property_name, translator)
|
128
|
+
translations_hash[from][property_name] = translator
|
129
|
+
end
|
130
|
+
|
131
|
+
def define_writer_for_source_property(property)
|
132
|
+
define_method "#{property}=" do |val|
|
133
|
+
__translations[property].each do |name, with|
|
134
|
+
self[name] = with.respond_to?(:call) ? with.call(val) : val
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def fail_self_transformation_error!(property_name)
|
140
|
+
raise ArgumentError,
|
141
|
+
"Property name (#{property_name}) and :from option must not be the same"
|
142
|
+
end
|
143
|
+
|
144
|
+
def valid_transformer?(transformer)
|
145
|
+
transformer.respond_to? :call
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
module InstanceMethods
|
150
|
+
# Sets a value on the Dash in a Hash-like way.
|
151
|
+
#
|
152
|
+
# Note: Only works on pre-existing properties.
|
153
|
+
def []=(property, value)
|
154
|
+
if self.class.translation_exists? property
|
155
|
+
send("#{property}=", value)
|
156
|
+
super(property, value) if self.class.properties.include?(property)
|
157
|
+
elsif self.class.transformation_exists? property
|
158
|
+
super property, self.class.transformed_property(property, value)
|
159
|
+
elsif property_exists? property
|
160
|
+
super
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Deletes any keys that have a translation
|
165
|
+
def initialize_attributes(attributes)
|
166
|
+
return unless attributes
|
167
|
+
attributes_copy = attributes.dup.delete_if do |k, v|
|
168
|
+
if self.class.translations_hash.include?(k)
|
169
|
+
self[k] = v
|
170
|
+
true
|
171
|
+
end
|
172
|
+
end
|
173
|
+
super attributes_copy
|
174
|
+
end
|
175
|
+
|
176
|
+
# Raises an NoMethodError if the property doesn't exist
|
177
|
+
def property_exists?(property)
|
178
|
+
fail_no_property_error!(property) unless self.class.property?(property)
|
179
|
+
true
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def __translations
|
185
|
+
self.class.translations_hash
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -9,18 +9,20 @@ module Hashie
|
|
9
9
|
#
|
10
10
|
# options.deep_fetch(:user, :non_existent_key) { 'a value' } #=> 'a value'
|
11
11
|
#
|
12
|
-
# This is particularly useful for fetching values from deeply nested api responses
|
12
|
+
# This is particularly useful for fetching values from deeply nested api responses
|
13
|
+
# or params hashes.
|
13
14
|
module DeepFetch
|
14
15
|
class UndefinedPathError < StandardError; end
|
15
16
|
|
16
17
|
def deep_fetch(*args, &block)
|
17
18
|
args.reduce(self) do |obj, arg|
|
18
19
|
begin
|
19
|
-
arg = Integer(arg) if obj.
|
20
|
+
arg = Integer(arg) if obj.is_a? Array
|
20
21
|
obj.fetch(arg)
|
21
|
-
rescue ArgumentError, IndexError => e
|
22
|
-
break
|
23
|
-
raise UndefinedPathError,
|
22
|
+
rescue ArgumentError, IndexError, NoMethodError => e
|
23
|
+
break yield(arg) if block
|
24
|
+
raise UndefinedPathError,
|
25
|
+
"Could not fetch path (#{args.join(' > ')}) at #{arg}", e.backtrace
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'hashie/extensions/deep_locate'
|
2
|
+
module Hashie
|
3
|
+
module Extensions
|
4
|
+
module DeepFind
|
5
|
+
# Performs a depth-first search on deeply nested data structures for
|
6
|
+
# a key and returns the first occurrence of the key.
|
7
|
+
#
|
8
|
+
# options = {user: {location: {address: '123 Street'}}}
|
9
|
+
# options.extend(Hashie::Extensions::DeepFind)
|
10
|
+
# options.deep_find(:address) # => '123 Street'
|
11
|
+
#
|
12
|
+
# class MyHash < Hash
|
13
|
+
# include Hashie::Extensions::DeepFind
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# my_hash = MyHash.new
|
17
|
+
# my_hash[:user] = {location: {address: '123 Street'}}
|
18
|
+
# my_hash.deep_find(:address) # => '123 Street'
|
19
|
+
def deep_find(key)
|
20
|
+
_deep_find(key)
|
21
|
+
end
|
22
|
+
|
23
|
+
alias deep_detect deep_find
|
24
|
+
|
25
|
+
# Performs a depth-first search on deeply nested data structures for
|
26
|
+
# a key and returns all occurrences of the key.
|
27
|
+
#
|
28
|
+
# options = {
|
29
|
+
# users: [
|
30
|
+
# { location: {address: '123 Street'} },
|
31
|
+
# { location: {address: '234 Street'}}
|
32
|
+
# ]
|
33
|
+
# }
|
34
|
+
# options.extend(Hashie::Extensions::DeepFind)
|
35
|
+
# options.deep_find_all(:address) # => ['123 Street', '234 Street']
|
36
|
+
#
|
37
|
+
# class MyHash < Hash
|
38
|
+
# include Hashie::Extensions::DeepFind
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# my_hash = MyHash.new
|
42
|
+
# my_hash[:users] = [
|
43
|
+
# {location: {address: '123 Street'}},
|
44
|
+
# {location: {address: '234 Street'}}
|
45
|
+
# ]
|
46
|
+
# my_hash.deep_find_all(:address) # => ['123 Street', '234 Street']
|
47
|
+
def deep_find_all(key)
|
48
|
+
matches = _deep_find_all(key)
|
49
|
+
matches.empty? ? nil : matches
|
50
|
+
end
|
51
|
+
|
52
|
+
alias deep_select deep_find_all
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def _deep_find(key, object = self)
|
57
|
+
_deep_find_all(key, object).first
|
58
|
+
end
|
59
|
+
|
60
|
+
def _deep_find_all(key, object = self, matches = [])
|
61
|
+
deep_locate_result = DeepLocate.deep_locate(key, object).tap do |result|
|
62
|
+
result.map! { |element| element[key] }
|
63
|
+
end
|
64
|
+
|
65
|
+
matches.concat(deep_locate_result)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Hashie
|
2
|
+
module Extensions
|
3
|
+
module DeepLocate
|
4
|
+
# The module level implementation of #deep_locate, incase you do not want
|
5
|
+
# to include/extend the base datastructure. For further examples please
|
6
|
+
# see #deep_locate.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# books = [
|
10
|
+
# {
|
11
|
+
# title: "Ruby for beginners",
|
12
|
+
# pages: 120
|
13
|
+
# },
|
14
|
+
# ...
|
15
|
+
# ]
|
16
|
+
#
|
17
|
+
# DeepLocate.deep_locate -> (key, value, object) { key == :title }, books
|
18
|
+
# # => [{:title=>"Ruby for beginners", :pages=>120}, ...]
|
19
|
+
def self.deep_locate(comparator, object)
|
20
|
+
unless comparator.respond_to?(:call)
|
21
|
+
comparator = _construct_key_comparator(comparator, object)
|
22
|
+
end
|
23
|
+
|
24
|
+
_deep_locate(comparator, object)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Performs a depth-first search on deeply nested data structures for a
|
28
|
+
# given comparator callable and returns each Enumerable, for which the
|
29
|
+
# callable returns true for at least one the its elements.
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# books = [
|
33
|
+
# {
|
34
|
+
# title: "Ruby for beginners",
|
35
|
+
# pages: 120
|
36
|
+
# },
|
37
|
+
# {
|
38
|
+
# title: "CSS for intermediates",
|
39
|
+
# pages: 80
|
40
|
+
# },
|
41
|
+
# {
|
42
|
+
# title: "Collection of ruby books",
|
43
|
+
# books: [
|
44
|
+
# {
|
45
|
+
# title: "Ruby for the rest of us",
|
46
|
+
# pages: 576
|
47
|
+
# }
|
48
|
+
# ]
|
49
|
+
# }
|
50
|
+
# ]
|
51
|
+
#
|
52
|
+
# books.extend(Hashie::Extensions::DeepLocate)
|
53
|
+
#
|
54
|
+
# # for ruby 1.9 leave *no* space between the lambda rocket and the braces
|
55
|
+
# # http://ruby-journal.com/becareful-with-space-in-lambda-hash-rocket-syntax-between-ruby-1-dot-9-and-2-dot-0/
|
56
|
+
#
|
57
|
+
# books.deep_locate -> (key, value, object) { key == :title && value.include?("Ruby") }
|
58
|
+
# # => [{:title=>"Ruby for beginners", :pages=>120},
|
59
|
+
# # {:title=>"Ruby for the rest of us", :pages=>576}]
|
60
|
+
#
|
61
|
+
# books.deep_locate -> (key, value, object) { key == :pages && value <= 120 }
|
62
|
+
# # => [{:title=>"Ruby for beginners", :pages=>120},
|
63
|
+
# # {:title=>"CSS for intermediates", :pages=>80}]
|
64
|
+
def deep_locate(comparator)
|
65
|
+
Hashie::Extensions::DeepLocate.deep_locate(comparator, self)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self._construct_key_comparator(search_key, object)
|
69
|
+
if object.respond_to?(:indifferent_access?) && object.indifferent_access? ||
|
70
|
+
activesupport_indifferent?(object)
|
71
|
+
search_key = search_key.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
lambda do |non_callable_object|
|
75
|
+
->(key, _, _) { key == non_callable_object }
|
76
|
+
end.call(search_key)
|
77
|
+
end
|
78
|
+
private_class_method :_construct_key_comparator
|
79
|
+
|
80
|
+
def self._deep_locate(comparator, object, result = [])
|
81
|
+
if object.is_a?(::Enumerable)
|
82
|
+
if object.any? { |value| _match_comparator?(value, comparator, object) }
|
83
|
+
result.push object
|
84
|
+
end
|
85
|
+
|
86
|
+
(object.respond_to?(:values) ? object.values : object.entries).each do |value|
|
87
|
+
_deep_locate(comparator, value, result)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
result
|
92
|
+
end
|
93
|
+
private_class_method :_deep_locate
|
94
|
+
|
95
|
+
def self._match_comparator?(value, comparator, object)
|
96
|
+
if object.is_a?(::Hash)
|
97
|
+
key, value = value
|
98
|
+
else
|
99
|
+
key = nil
|
100
|
+
end
|
101
|
+
|
102
|
+
comparator.call(key, value, object)
|
103
|
+
end
|
104
|
+
private_class_method :_match_comparator?
|
105
|
+
|
106
|
+
def self.activesupport_indifferent?(object)
|
107
|
+
defined?(::ActiveSupport::HashWithIndifferentAccess) &&
|
108
|
+
object.is_a?(::ActiveSupport::HashWithIndifferentAccess)
|
109
|
+
end
|
110
|
+
private_class_method :activesupport_indifferent?
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|