1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
module Hashie
module Extensions
# IndifferentAccess gives you the ability to not care
# whether your hash has string or symbol keys. Made famous
# in Rails for accessing query and POST parameters, this
# is a handy tool for making sure your hash has maximum
# utility.
#
# One unique feature of this mixin is that it will recursively
# inject itself into sub-hash instances without modifying
# the actual class of the sub-hash.
#
# @example
# class MyHash < Hash
# include Hashie::Extensions::MergeInitializer
# include Hashie::Extensions::IndifferentAccess
# end
#
# h = MyHash.new(:foo => 'bar', 'baz' => 'blip')
# h['foo'] # => 'bar'
# h[:foo] # => 'bar'
# h[:baz] # => 'blip'
# h['baz'] # => 'blip'
#
module IndifferentAccess
def self.included(base)
Hashie::Extensions::Dash::IndifferentAccess.maybe_extend(base)
base.class_eval do
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
alias_method :[]=, :indifferent_writer
alias_method :store, :indifferent_writer
%w[default update replace fetch delete key? values_at].each do |m|
alias_method "regular_#{m}", m unless method_defined?("regular_#{m}")
alias_method m, "indifferent_#{m}"
end
%w[include? member? has_key?].each do |key_alias|
alias_method key_alias, :indifferent_key?
end
class << self
def [](*)
super.convert!
end
def try_convert(*)
(hash = super) && self[hash]
end
end
end
end
# This will inject indifferent access into an instance of
# a hash without modifying the actual class. This is what
# allows IndifferentAccess to spread to sub-hashes.
def self.inject!(hash)
(class << hash; self; end).send :include, IndifferentAccess
hash.convert!
end
# Injects indifferent access into a duplicate of the hash
# provided. See #inject!
def self.inject(hash)
inject!(hash.dup)
end
def convert_key(key)
key.to_s
end
# Iterates through the keys and values, reconverting them to
# their proper indifferent state. Used when IndifferentAccess
# is injecting itself into member hashes.
def convert!
keys.each do |k| # rubocop:disable Performance/HashEachMethods
regular_writer convert_key(k), indifferent_value(regular_delete(k))
end
self
end
def indifferent_value(value)
if hash_lacking_indifference?(value)
IndifferentAccess.inject!(value)
elsif value.is_a?(::Array)
value.replace(value.map { |e| indifferent_value(e) })
else
value
end
end
def indifferent_default(key = nil)
return self[convert_key(key)] if key?(key)
regular_default(key)
end
def indifferent_update(other_hash)
return regular_update(other_hash) if hash_with_indifference?(other_hash)
other_hash.each_pair do |k, v|
self[k] = v
end
end
def indifferent_writer(key, value)
regular_writer convert_key(key), indifferent_value(value)
end
def indifferent_fetch(key, *args, &block)
regular_fetch convert_key(key), *args, &block
end
def indifferent_delete(key)
regular_delete convert_key(key)
end
def indifferent_key?(key)
regular_key? convert_key(key)
end
def indifferent_values_at(*indices)
indices.map { |i| self[i] }
end
def indifferent_access?
true
end
def indifferent_replace(other_hash)
(keys - other_hash.keys).each { |key| delete(key) }
other_hash.each { |key, value| self[key] = value }
self
end
def merge(*args)
result = super
IndifferentAccess.inject!(result) if hash_lacking_indifference?(result)
result.convert!
end
def merge!(*)
super.convert!
end
protected
def hash_lacking_indifference?(other)
other.is_a?(::Hash) &&
!(other.respond_to?(:indifferent_access?) &&
other.indifferent_access?)
end
def hash_with_indifference?(other)
other.is_a?(::Hash) &&
other.respond_to?(:indifferent_access?) &&
other.indifferent_access?
end
end
end
end
|