hashie 3.5.7 → 3.6.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.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +91 -22
  4. data/Rakefile +2 -2
  5. data/hashie.gemspec +1 -1
  6. data/lib/hashie/clash.rb +12 -1
  7. data/lib/hashie/dash.rb +41 -21
  8. data/lib/hashie/extensions/coercion.rb +5 -5
  9. data/lib/hashie/extensions/dash/property_translation.rb +49 -26
  10. data/lib/hashie/extensions/deep_fetch.rb +1 -1
  11. data/lib/hashie/extensions/deep_find.rb +2 -2
  12. data/lib/hashie/extensions/deep_locate.rb +4 -5
  13. data/lib/hashie/extensions/deep_merge.rb +8 -9
  14. data/lib/hashie/extensions/indifferent_access.rb +7 -5
  15. data/lib/hashie/extensions/mash/keep_original_keys.rb +3 -5
  16. data/lib/hashie/extensions/mash/safe_assignment.rb +1 -1
  17. data/lib/hashie/extensions/mash/symbolize_keys.rb +1 -1
  18. data/lib/hashie/extensions/method_access.rb +47 -17
  19. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +3 -1
  20. data/lib/hashie/extensions/strict_key_access.rb +8 -9
  21. data/lib/hashie/extensions/stringify_keys.rb +1 -1
  22. data/lib/hashie/extensions/symbolize_keys.rb +1 -1
  23. data/lib/hashie/hash.rb +4 -4
  24. data/lib/hashie/mash.rb +22 -22
  25. data/lib/hashie/rash.rb +5 -5
  26. data/lib/hashie/version.rb +1 -1
  27. data/spec/hashie/array_spec.rb +1 -1
  28. data/spec/hashie/dash_spec.rb +27 -2
  29. data/spec/hashie/extensions/coercion_spec.rb +20 -12
  30. data/spec/hashie/extensions/deep_locate_spec.rb +1 -1
  31. data/spec/hashie/extensions/deep_merge_spec.rb +1 -1
  32. data/spec/hashie/extensions/indifferent_access_spec.rb +20 -7
  33. data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +5 -5
  34. data/spec/hashie/extensions/method_access_spec.rb +42 -4
  35. data/spec/hashie/extensions/stringify_keys_spec.rb +4 -4
  36. data/spec/hashie/extensions/symbolize_keys_spec.rb +3 -3
  37. data/spec/hashie/mash_spec.rb +16 -8
  38. data/spec/hashie/parsers/yaml_erb_parser_spec.rb +3 -3
  39. data/spec/hashie/rash_spec.rb +2 -2
  40. data/spec/hashie/trash_spec.rb +61 -1
  41. data/spec/integration/elasticsearch/integration_spec.rb +40 -0
  42. data/spec/integration/omniauth-oauth2/app.rb +3 -4
  43. data/spec/integration/omniauth-oauth2/some_site.rb +2 -2
  44. data/spec/integration/rails/app.rb +3 -4
  45. metadata +5 -3
@@ -19,7 +19,7 @@ module Hashie
19
19
  arg = Integer(arg) if obj.is_a? Array
20
20
  obj.fetch(arg)
21
21
  rescue ArgumentError, IndexError, NoMethodError => e
22
- break block.call(arg) if block
22
+ break yield(arg) if block
23
23
  raise UndefinedPathError, "Could not fetch path (#{args.join(' > ')}) at #{arg}", e.backtrace
24
24
  end
25
25
  end
@@ -19,7 +19,7 @@ module Hashie
19
19
  _deep_find(key)
20
20
  end
21
21
 
22
- alias_method :deep_detect, :deep_find
22
+ alias deep_detect deep_find
23
23
 
24
24
  # Performs a depth-first search on deeply nested data structures for
25
25
  # a key and returns all occurrences of the key.
@@ -40,7 +40,7 @@ module Hashie
40
40
  matches.empty? ? nil : matches
41
41
  end
42
42
 
43
- alias_method :deep_select, :deep_find_all
43
+ alias deep_select deep_find_all
44
44
 
45
45
  private
46
46
 
@@ -61,8 +61,6 @@ module Hashie
61
61
  Hashie::Extensions::DeepLocate.deep_locate(comparator, self)
62
62
  end
63
63
 
64
- private
65
-
66
64
  def self._construct_key_comparator(search_key, object)
67
65
  search_key = search_key.to_s if defined?(::ActiveSupport::HashWithIndifferentAccess) && object.is_a?(::ActiveSupport::HashWithIndifferentAccess)
68
66
  search_key = search_key.to_s if object.respond_to?(:indifferent_access?) && object.indifferent_access?
@@ -71,12 +69,11 @@ module Hashie
71
69
  ->(key, _, _) { key == non_callable_object }
72
70
  end.call(search_key)
73
71
  end
72
+ private_class_method :_construct_key_comparator
74
73
 
75
74
  def self._deep_locate(comparator, object, result = [])
76
75
  if object.is_a?(::Enumerable)
77
- if object.any? { |value| _match_comparator?(value, comparator, object) }
78
- result.push object
79
- end
76
+ result.push object if object.any? { |value| _match_comparator?(value, comparator, object) }
80
77
  (object.respond_to?(:values) ? object.values : object.entries).each do |value|
81
78
  _deep_locate(comparator, value, result)
82
79
  end
@@ -84,6 +81,7 @@ module Hashie
84
81
 
85
82
  result
86
83
  end
84
+ private_class_method :_deep_locate
87
85
 
88
86
  def self._match_comparator?(value, comparator, object)
89
87
  if object.is_a?(::Hash)
@@ -94,6 +92,7 @@ module Hashie
94
92
 
95
93
  comparator.call(key, value, object)
96
94
  end
95
+ private_class_method :_match_comparator?
97
96
  end
98
97
  end
99
98
  end
@@ -20,15 +20,14 @@ module Hashie
20
20
 
21
21
  def _recursive_merge(hash, other_hash, &block)
22
22
  other_hash.each do |k, v|
23
- hash[k] = if hash.key?(k) && hash[k].is_a?(::Hash) && v.is_a?(::Hash)
24
- _recursive_merge(hash[k], v, &block)
25
- else
26
- if hash.key?(k) && block_given?
27
- block.call(k, hash[k], v)
28
- else
29
- v.respond_to?(:deep_dup) ? v.deep_dup : v
30
- end
31
- end
23
+ hash[k] =
24
+ if hash.key?(k) && hash[k].is_a?(::Hash) && v.is_a?(::Hash)
25
+ _recursive_merge(hash[k], v, &block)
26
+ elsif hash.key?(k) && block_given?
27
+ yield(k, hash[k], v)
28
+ else
29
+ v.respond_to?(:deep_dup) ? v.deep_dup : v
30
+ end
32
31
  end
33
32
  hash
34
33
  end
@@ -32,12 +32,12 @@ module Hashie
32
32
  alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
33
33
  alias_method :[]=, :indifferent_writer
34
34
  alias_method :store, :indifferent_writer
35
- %w(default update replace fetch delete key? values_at).each do |m|
35
+ %w[default update replace fetch delete key? values_at].each do |m|
36
36
  alias_method "regular_#{m}", m unless method_defined?("regular_#{m}")
37
37
  alias_method m, "indifferent_#{m}"
38
38
  end
39
39
 
40
- %w(include? member? has_key?).each do |key_alias|
40
+ %w[include? member? has_key?].each do |key_alias|
41
41
  alias_method key_alias, :indifferent_key?
42
42
  end
43
43
 
@@ -75,7 +75,7 @@ module Hashie
75
75
  # their proper indifferent state. Used when IndifferentAccess
76
76
  # is injecting itself into member hashes.
77
77
  def convert!
78
- keys.each do |k|
78
+ keys.each do |k| # rubocop:disable Performance/HashEachMethods
79
79
  regular_writer convert_key(k), indifferent_value(regular_delete(k))
80
80
  end
81
81
  self
@@ -133,8 +133,10 @@ module Hashie
133
133
  self
134
134
  end
135
135
 
136
- def merge(*)
137
- super.convert!
136
+ def merge(*args)
137
+ result = super
138
+ IndifferentAccess.inject!(result) if hash_lacking_indifference?(result)
139
+ result.convert!
138
140
  end
139
141
 
140
142
  def merge!(*)
@@ -14,14 +14,12 @@ module Hashie
14
14
  # mash['string_key'] == mash[:string_key] #=> true
15
15
  # mash[:symbol_key] == mash['symbol_key'] #=> true
16
16
  module KeepOriginalKeys
17
- private
18
-
19
17
  def self.included(descendant)
20
- unless descendant <= Hashie::Mash
21
- fail ArgumentError, "#{descendant} is not a kind of Hashie::Mash"
22
- end
18
+ raise ArgumentError, "#{descendant} is not a kind of Hashie::Mash" unless descendant <= Hashie::Mash
23
19
  end
24
20
 
21
+ private
22
+
25
23
  # Converts the key when necessary to access the correct Mash key.
26
24
  #
27
25
  # @param [Object, String, Symbol] key the key to access.
@@ -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 !key?(key) && respond_to?(key, true)
6
+ raise ArgumentError, "The property #{key} clashes with an existing method." if !key?(key) && respond_to?(key, true)
7
7
  super
8
8
  end
9
9
 
@@ -19,7 +19,7 @@ module Hashie
19
19
  # @return [void]
20
20
  # @raise [ArgumentError] when the base class isn't a Mash
21
21
  def self.included(base)
22
- fail ArgumentError, "#{base} must descent from Hashie::Mash" unless base <= Hashie::Mash
22
+ raise ArgumentError, "#{base} must descent from Hashie::Mash" unless base <= Hashie::Mash
23
23
  end
24
24
 
25
25
  private
@@ -27,7 +27,7 @@ module Hashie
27
27
  #
28
28
  # user.not_declared # => NoMethodError
29
29
  module MethodReader
30
- def respond_to?(name, include_private = false)
30
+ def respond_to_missing?(name, include_private = false)
31
31
  return true if key?(name.to_s) || key?(name.to_sym)
32
32
  super
33
33
  end
@@ -67,15 +67,13 @@ module Hashie
67
67
  # h['awesome'] # => 'sauce'
68
68
  #
69
69
  module MethodWriter
70
- def respond_to?(name, include_private = false)
70
+ def respond_to_missing?(name, include_private = false)
71
71
  return true if name.to_s =~ /=$/
72
72
  super
73
73
  end
74
74
 
75
75
  def method_missing(name, *args)
76
- if args.size == 1 && name.to_s =~ /(.*)=$/
77
- return self[convert_key(Regexp.last_match[1])] = args.first
78
- end
76
+ return self[convert_key(Regexp.last_match[1])] = args.first if args.size == 1 && name.to_s =~ /(.*)=$/
79
77
 
80
78
  super
81
79
  end
@@ -106,7 +104,7 @@ module Hashie
106
104
  # h.def? # => false
107
105
  # h.hji? # => NoMethodError
108
106
  module MethodQuery
109
- def respond_to?(name, include_private = false)
107
+ def respond_to_missing?(name, include_private = false)
110
108
  if query_method?(name) && indifferent_key?(key_from_query_method(name))
111
109
  true
112
110
  else
@@ -156,6 +154,22 @@ module Hashie
156
154
  end
157
155
  end
158
156
 
157
+ # A module shared between MethodOverridingWriter and MethodOverridingInitializer
158
+ # to contained shared logic. This module aids in redefining existing hash methods.
159
+ module RedefineMethod
160
+ protected
161
+
162
+ def method?(name)
163
+ methods.map(&:to_s).include?(name)
164
+ end
165
+
166
+ def redefine_method(method_name)
167
+ eigenclass = class << self; self; end
168
+ eigenclass.__send__(:alias_method, "__#{method_name}", method_name)
169
+ eigenclass.__send__(:define_method, method_name, -> { self[method_name] })
170
+ end
171
+ end
172
+
159
173
  # MethodOverridingWriter gives you #key_name= shortcuts for
160
174
  # writing to your hash. It allows methods to be overridden by
161
175
  # #key_name= shortcuts and aliases those methods with two
@@ -181,6 +195,8 @@ module Hashie
181
195
  # h.__zip # => [[['awesome', 'sauce'], ['zip', 'a-dee-doo-dah']]]
182
196
  #
183
197
  module MethodOverridingWriter
198
+ include RedefineMethod
199
+
184
200
  def convert_key(key)
185
201
  key.to_s
186
202
  end
@@ -205,16 +221,6 @@ module Hashie
205
221
  def already_overridden?(name)
206
222
  method?("__#{name}")
207
223
  end
208
-
209
- def method?(name)
210
- methods.map(&:to_s).include?(name)
211
- end
212
-
213
- def redefine_method(method_name)
214
- eigenclass = class << self; self; end
215
- eigenclass.__send__(:alias_method, "__#{method_name}", method_name)
216
- eigenclass.__send__(:define_method, method_name, -> { self[method_name] })
217
- end
218
224
  end
219
225
 
220
226
  # A macro module that will automatically include MethodReader,
@@ -225,10 +231,34 @@ module Hashie
225
231
  # underscores.
226
232
  module MethodAccessWithOverride
227
233
  def self.included(base)
228
- [MethodReader, MethodOverridingWriter, MethodQuery].each do |mod|
234
+ [MethodReader, MethodOverridingWriter, MethodQuery, MethodOverridingInitializer].each do |mod|
229
235
  base.send :include, mod
230
236
  end
231
237
  end
232
238
  end
239
+
240
+ # MethodOverridingInitializer allows you to override default hash
241
+ # methods when passing in values from an existing hash. The overriden
242
+ # methods are aliased with two leading underscores.
243
+ #
244
+ # @example
245
+ # class MyHash < Hash
246
+ # include Hashie::Extensions::MethodOverridingInitializer
247
+ # end
248
+ #
249
+ # h = MyHash.new(zip: 'a-dee-doo-dah')
250
+ # h.zip # => 'a-dee-doo-dah'
251
+ # h.__zip # => [[['zip', 'a-dee-doo-dah']]]
252
+ module MethodOverridingInitializer
253
+ include RedefineMethod
254
+
255
+ def initialize(hash = {})
256
+ hash.each do |key, value|
257
+ skey = key.to_s
258
+ redefine_method(skey) if method?(skey)
259
+ self[skey] = value
260
+ end
261
+ end
262
+ end
233
263
  end
234
264
  end
@@ -1,5 +1,7 @@
1
1
  require 'yaml'
2
2
  require 'erb'
3
+ require 'pathname'
4
+
3
5
  module Hashie
4
6
  module Extensions
5
7
  module Parsers
@@ -12,7 +14,7 @@ module Hashie
12
14
  def perform
13
15
  template = ERB.new(@content)
14
16
  template.filename = @file_path
15
- YAML.load template.result
17
+ YAML.safe_load template.result
16
18
  end
17
19
 
18
20
  def self.perform(file_path)
@@ -46,27 +46,26 @@ module Hashie
46
46
  end
47
47
 
48
48
  def default(_ = nil)
49
- fail DefaultError
49
+ raise DefaultError
50
50
  end
51
51
 
52
52
  def default=(_)
53
- fail DefaultError
53
+ raise DefaultError
54
54
  end
55
55
 
56
56
  def default_proc
57
- fail DefaultError
57
+ raise DefaultError
58
58
  end
59
59
 
60
60
  def default_proc=(_)
61
- fail DefaultError
61
+ raise DefaultError
62
62
  end
63
63
 
64
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
65
+ super.tap do |result|
66
+ if result.nil? && (!key?(result) || self[result] != value)
67
+ raise KeyError, "key not found with value of #{value.inspect}"
68
+ end
70
69
  end
71
70
  end
72
71
  end
@@ -44,7 +44,7 @@ module Hashie
44
44
  # test # => {'abc' => 'def'}
45
45
  def stringify_keys!(hash)
46
46
  hash.extend(Hashie::Extensions::StringifyKeys) unless hash.respond_to?(:stringify_keys!)
47
- hash.keys.each do |k|
47
+ hash.keys.each do |k| # rubocop:disable Performance/HashEachMethods
48
48
  stringify_keys_recursively!(hash[k])
49
49
  hash[k.to_s] = hash.delete(k)
50
50
  end
@@ -44,7 +44,7 @@ module Hashie
44
44
  # test # => {:abc => 'def'}
45
45
  def symbolize_keys!(hash)
46
46
  hash.extend(Hashie::Extensions::SymbolizeKeys) unless hash.respond_to?(:symbolize_keys!)
47
- hash.keys.each do |k|
47
+ hash.keys.each do |k| # rubocop:disable Performance/HashEachMethods
48
48
  symbolize_keys_recursively!(hash[k])
49
49
  hash[k.to_sym] = hash.delete(k)
50
50
  end
@@ -17,7 +17,7 @@ module Hashie
17
17
  # Converts a mash back to a hash (with stringified or symbolized keys)
18
18
  def to_hash(options = {})
19
19
  out = {}
20
- keys.each do |k|
20
+ each_key do |k|
21
21
  assignment_key = if options[:stringify_keys]
22
22
  k.to_s
23
23
  elsif options[:symbolize_keys]
@@ -28,10 +28,10 @@ module Hashie
28
28
  if self[k].is_a?(Array)
29
29
  out[assignment_key] ||= []
30
30
  self[k].each do |array_object|
31
- out[assignment_key] << (Hash === array_object ? flexibly_convert_to_hash(array_object, options) : array_object)
31
+ out[assignment_key] << (array_object.is_a?(Hash) ? flexibly_convert_to_hash(array_object, options) : array_object)
32
32
  end
33
33
  else
34
- out[assignment_key] = (Hash === self[k] || self[k].respond_to?(:to_hash)) ? flexibly_convert_to_hash(self[k], options) : self[k]
34
+ out[assignment_key] = self[k].is_a?(Hash) || self[k].respond_to?(:to_hash) ? flexibly_convert_to_hash(self[k], options) : self[k]
35
35
  end
36
36
  end
37
37
  out
@@ -45,7 +45,7 @@ module Hashie
45
45
  private
46
46
 
47
47
  def flexibly_convert_to_hash(object, options = {})
48
- if object.method(:to_hash).arity == 0
48
+ if object.method(:to_hash).arity.zero?
49
49
  object.to_hash
50
50
  else
51
51
  object.to_hash(options)
@@ -61,7 +61,7 @@ module Hashie
61
61
  include Hashie::Extensions::PrettyInspect
62
62
  include Hashie::Extensions::RubyVersionCheck
63
63
 
64
- ALLOWED_SUFFIXES = %w(? ! = _)
64
+ ALLOWED_SUFFIXES = %w[? ! = _].freeze
65
65
 
66
66
  class CannotDisableMashWarnings < StandardError
67
67
  def initialize(message = 'You cannot disable warnings on the base Mash class. Please subclass the Mash and disable it in the subclass.')
@@ -74,7 +74,7 @@ module Hashie
74
74
  # @api semipublic
75
75
  # @return [void]
76
76
  def self.disable_warnings
77
- fail CannotDisableMashWarnings if self == Hashie::Mash
77
+ raise CannotDisableMashWarnings if self == Hashie::Mash
78
78
  @disable_warnings = true
79
79
  end
80
80
 
@@ -99,9 +99,9 @@ module Hashie
99
99
  @_mashes ||= new
100
100
 
101
101
  return @_mashes[path] if @_mashes.key?(path)
102
- fail ArgumentError, "The following file doesn't exist: #{path}" unless File.file?(path)
102
+ raise ArgumentError, "The following file doesn't exist: #{path}" unless File.file?(path)
103
103
 
104
- parser = options.fetch(:parser) { Hashie::Extensions::Parsers::YamlErbParser }
104
+ parser = options.fetch(:parser) { Hashie::Extensions::Parsers::YamlErbParser }
105
105
  @_mashes[path] = new(parser.perform(path)).freeze
106
106
  end
107
107
 
@@ -114,7 +114,7 @@ module Hashie
114
114
  end
115
115
  end
116
116
 
117
- alias_method :to_s, :inspect
117
+ alias to_s inspect
118
118
 
119
119
  # If you pass in an existing hash, it will
120
120
  # convert it to a Mash including recursively
@@ -125,10 +125,10 @@ module Hashie
125
125
  default ? super(default) : super(&blk)
126
126
  end
127
127
 
128
- class << self; alias_method :[], :new; end
128
+ class << self; alias [] new; end
129
129
 
130
- alias_method :regular_reader, :[]
131
- alias_method :regular_writer, :[]=
130
+ alias regular_reader []
131
+ alias regular_writer []=
132
132
 
133
133
  # Retrieves an attribute set in the Mash. Will convert
134
134
  # any key passed in to a string before retrieving.
@@ -149,8 +149,8 @@ module Hashie
149
149
  regular_writer(key, convert ? convert_value(value) : value)
150
150
  end
151
151
 
152
- alias_method :[], :custom_reader
153
- alias_method :[]=, :custom_writer
152
+ alias [] custom_reader
153
+ alias []= custom_writer
154
154
 
155
155
  # This is the bang method reader, it will return a new Mash
156
156
  # if there isn't a value already assigned to the key requested.
@@ -183,26 +183,26 @@ module Hashie
183
183
  super(*keys.map { |key| convert_key(key) })
184
184
  end
185
185
 
186
- alias_method :regular_dup, :dup
186
+ alias regular_dup dup
187
187
  # Duplicates the current mash as a new mash.
188
188
  def dup
189
- self.class.new(self, default)
189
+ self.class.new(self, default, &default_proc)
190
190
  end
191
191
 
192
- alias_method :regular_key?, :key?
192
+ alias regular_key? key?
193
193
  def key?(key)
194
194
  super(convert_key(key))
195
195
  end
196
- alias_method :has_key?, :key?
197
- alias_method :include?, :key?
198
- alias_method :member?, :key?
196
+ alias has_key? key?
197
+ alias include? key?
198
+ alias member? key?
199
199
 
200
200
  # Performs a deep_update on a duplicate of the
201
201
  # current mash.
202
202
  def deep_merge(other_hash, &blk)
203
203
  dup.deep_update(other_hash, &blk)
204
204
  end
205
- alias_method :merge, :deep_merge
205
+ alias merge deep_merge
206
206
 
207
207
  # Recursively merges this mash with the passed
208
208
  # in hash, merging each hash in the hierarchy.
@@ -213,15 +213,15 @@ module Hashie
213
213
  custom_reader(key).deep_update(v, &blk)
214
214
  else
215
215
  value = convert_value(v, true)
216
- value = convert_value(blk.call(key, self[k], value), true) if blk && self.key?(k)
216
+ value = convert_value(yield(key, self[k], value), true) if blk && key?(k)
217
217
  custom_writer(key, value, false)
218
218
  end
219
219
  end
220
220
  self
221
221
  end
222
- alias_method :deep_merge!, :deep_update
223
- alias_method :update, :deep_update
224
- alias_method :merge!, :update
222
+ alias deep_merge! deep_update
223
+ alias update deep_update
224
+ alias merge! update
225
225
 
226
226
  # Assigns a value to a key
227
227
  def assign_property(name, value)
@@ -263,7 +263,7 @@ module Hashie
263
263
  method_name.end_with?(*ALLOWED_SUFFIXES) && key?(method_name.chop)
264
264
  end
265
265
 
266
- def method_missing(method_name, *args, &blk)
266
+ def method_missing(method_name, *args, &blk) # rubocop:disable Style/MethodMissing
267
267
  return self.[](method_name, &blk) if key?(method_name)
268
268
  name, suffix = method_name_and_suffix(method_name)
269
269
  case suffix