hashie 5.2.0 → 6.0.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -0
  3. data/CONTRIBUTING.md +2 -0
  4. data/README.md +60 -10
  5. data/Rakefile +15 -0
  6. data/UPGRADING.md +100 -6
  7. data/lib/hashie/clash.rb +3 -2
  8. data/lib/hashie/dash.rb +25 -25
  9. data/lib/hashie/extensions/block_access.rb +5 -5
  10. data/lib/hashie/extensions/dash/coercion.rb +4 -4
  11. data/lib/hashie/extensions/dash/indifferent_access.rb +5 -5
  12. data/lib/hashie/extensions/dash/lazy.rb +10 -10
  13. data/lib/hashie/extensions/dash/predefined_values.rb +12 -16
  14. data/lib/hashie/extensions/dash/property_translation.rb +22 -14
  15. data/lib/hashie/extensions/deep_find.rb +4 -4
  16. data/lib/hashie/extensions/deep_grep.rb +15 -15
  17. data/lib/hashie/extensions/deep_locate.rb +3 -3
  18. data/lib/hashie/extensions/deep_merge.rb +16 -14
  19. data/lib/hashie/extensions/deep_merge_initializer.rb +3 -3
  20. data/lib/hashie/extensions/indifferent_access.rb +6 -6
  21. data/lib/hashie/extensions/mash/define_accessors.rb +3 -1
  22. data/lib/hashie/extensions/mash/keep_original_keys.rb +2 -2
  23. data/lib/hashie/extensions/mash/permissive_respond_to.rb +1 -1
  24. data/lib/hashie/extensions/mash/required_keys.rb +2 -2
  25. data/lib/hashie/extensions/mash/strict_key_access.rb +2 -2
  26. data/lib/hashie/extensions/method_access.rb +15 -15
  27. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +1 -1
  28. data/lib/hashie/extensions/parsers/yaml_parser.rb +2 -2
  29. data/lib/hashie/extensions/persistable.rb +4 -4
  30. data/lib/hashie/extensions/ruby_version.rb +2 -2
  31. data/lib/hashie/extensions/stringify_keys.rb +4 -3
  32. data/lib/hashie/extensions/symbolize_keys.rb +4 -3
  33. data/lib/hashie/hash.rb +5 -5
  34. data/lib/hashie/mash.rb +20 -16
  35. data/lib/hashie/rash.rb +2 -2
  36. data/lib/hashie/utils.rb +3 -2
  37. data/lib/hashie/version.rb +1 -1
  38. metadata +3 -3
@@ -132,7 +132,7 @@ module Hashie
132
132
  # hash.respond_to?(:name?) # => true
133
133
  # hash.respond_to?(:missing?) # => false
134
134
  def respond_to_missing?(name, include_private = false)
135
- if query_method?(name) && indifferent_key?(key_from_query_method(name))
135
+ if _query_method?(name) && _indifferent_key?(_key_from_query_method(name))
136
136
  true
137
137
  else
138
138
  super
@@ -146,9 +146,9 @@ module Hashie
146
146
  def method_missing(name, *args)
147
147
  return super unless args.empty?
148
148
 
149
- if query_method?(name)
150
- key = key_from_query_method(name)
151
- if indifferent_key?(key)
149
+ if _query_method?(name)
150
+ key = _key_from_query_method(name)
151
+ if _indifferent_key?(key)
152
152
  !!(self[key] || self[key.to_sym])
153
153
  else
154
154
  super
@@ -160,16 +160,16 @@ module Hashie
160
160
 
161
161
  private
162
162
 
163
- def indifferent_key?(name)
163
+ def _indifferent_key?(name)
164
164
  name = name.to_s
165
165
  key?(name) || key?(name.to_sym)
166
166
  end
167
167
 
168
- def key_from_query_method(query_method)
168
+ def _key_from_query_method(query_method)
169
169
  query_method.to_s[0..-2]
170
170
  end
171
171
 
172
- def query_method?(name)
172
+ def _query_method?(name)
173
173
  name.to_s.end_with?('?')
174
174
  end
175
175
  end
@@ -204,13 +204,13 @@ module Hashie
204
204
  # hash.size # => 'large'
205
205
  # hash.__size # => 1
206
206
  module RedefineMethod
207
- protected
207
+ private
208
208
 
209
- def method?(name)
209
+ def _method?(name)
210
210
  methods.map(&:to_s).include?(name)
211
211
  end
212
212
 
213
- def redefine_method(method_name)
213
+ def _redefine_method(method_name)
214
214
  eigenclass = class << self; self; end
215
215
  eigenclass.__send__(:alias_method, "__#{method_name}", method_name)
216
216
  eigenclass.__send__(:define_method, method_name, -> { self[method_name] })
@@ -262,7 +262,7 @@ module Hashie
262
262
  def method_missing(name, *args)
263
263
  if args.size == 1 && name.to_s =~ /(.*)=$/
264
264
  key = Regexp.last_match[1]
265
- redefine_method(key) if method?(key) && !already_overridden?(key)
265
+ _redefine_method(key) if _method?(key) && !_already_overridden?(key)
266
266
  return self[convert_key(key)] = args.first
267
267
  end
268
268
 
@@ -279,10 +279,10 @@ module Hashie
279
279
  super
280
280
  end
281
281
 
282
- protected
282
+ private
283
283
 
284
- def already_overridden?(name)
285
- method?("__#{name}")
284
+ def _already_overridden?(name)
285
+ _method?("__#{name}")
286
286
  end
287
287
  end
288
288
 
@@ -344,7 +344,7 @@ module Hashie
344
344
 
345
345
  hash.each do |key, value|
346
346
  skey = key.to_s
347
- redefine_method(skey) if method?(skey)
347
+ _redefine_method(skey) if _method?(skey)
348
348
  self[skey] = value
349
349
  end
350
350
  end
@@ -33,7 +33,7 @@ module Hashie
33
33
  def perform
34
34
  template = ERB.new(@content)
35
35
  template.filename = @file_path
36
- yaml_safe_load(template.result)
36
+ _yaml_safe_load(template.result)
37
37
  end
38
38
  end
39
39
  end
@@ -48,7 +48,7 @@ module Hashie
48
48
  # file.close!
49
49
  # end
50
50
  def perform
51
- yaml_safe_load(@content)
51
+ _yaml_safe_load(@content)
52
52
  end
53
53
 
54
54
  # @example Load YAML in one step
@@ -67,7 +67,7 @@ module Hashie
67
67
 
68
68
  private
69
69
 
70
- def yaml_safe_load(content)
70
+ def _yaml_safe_load(content)
71
71
  YAML.safe_load(
72
72
  content,
73
73
  permitted_classes: @options.fetch(:permitted_classes) { [] },
@@ -98,7 +98,7 @@ module Hashie
98
98
  raise ArgumentError, 'a persistence path is required' if path.nil?
99
99
 
100
100
  path = File.expand_path(File.path(path))
101
- yaml = Psych.dump(persistable_data(self))
101
+ yaml = Psych.dump(_persistable_data(self))
102
102
  Tempfile.create(['.hashie-', '.yaml'], File.dirname(path)) do |file|
103
103
  file.chmod(File.stat(path).mode & 0o777) if File.exist?(path)
104
104
  file.write(yaml)
@@ -112,7 +112,7 @@ module Hashie
112
112
 
113
113
  private
114
114
 
115
- def persistable_data(value, ancestors = [])
115
+ def _persistable_data(value, ancestors = [])
116
116
  raise ArgumentError, 'cannot persist cyclic data' if ancestors.any? { |ancestor| ancestor.equal?(value) }
117
117
 
118
118
  case value
@@ -120,10 +120,10 @@ module Hashie
120
120
  value.each_with_object({}) do |(key, child), data|
121
121
  raise TypeError, 'persisted hash keys must be strings or symbols' unless key.is_a?(String) || key.is_a?(Symbol)
122
122
 
123
- data[persistable_data(key)] = persistable_data(child, ancestors + [value])
123
+ data[_persistable_data(key)] = _persistable_data(child, ancestors + [value])
124
124
  end
125
125
  when ::Array
126
- value.map { |child| persistable_data(child, ancestors + [value]) }
126
+ value.map { |child| _persistable_data(child, ancestors + [value]) }
127
127
  when String
128
128
  value.dup
129
129
  when Symbol, Integer, Float, TrueClass, FalseClass, NilClass
@@ -32,7 +32,7 @@ module Hashie
32
32
  # version = Hashie::Extensions::RubyVersion.new('3.2.0.rc1')
33
33
  # version.segments # => [3, 2, 0, 'rc', 1]
34
34
  def initialize(version)
35
- @segments = split_to_segments(version)
35
+ @segments = _split_to_segments(version)
36
36
  end
37
37
 
38
38
  # @example Ignore trailing zeros and order prereleases before releases
@@ -67,7 +67,7 @@ module Hashie
67
67
 
68
68
  private
69
69
 
70
- def split_to_segments(version)
70
+ def _split_to_segments(version)
71
71
  version.scan(/[0-9]+|[a-z]+/i).map do |segment|
72
72
  /^\d+$/ =~ segment ? segment.to_i : segment
73
73
  end.freeze
@@ -68,16 +68,17 @@ module Hashie
68
68
  # Stringify all keys recursively within nested
69
69
  # hashes and arrays.
70
70
  # @api private
71
- def stringify_keys_recursively!(object)
71
+ def _stringify_keys_recursively!(object)
72
72
  case object
73
73
  when ::Array
74
74
  object.each do |i|
75
- stringify_keys_recursively!(i)
75
+ _stringify_keys_recursively!(i)
76
76
  end
77
77
  when ::Hash
78
78
  stringify_keys!(object)
79
79
  end
80
80
  end
81
+ private :_stringify_keys_recursively!
81
82
 
82
83
  # Converts all keys in a hash recursively to strings, in place.
83
84
  #
@@ -92,7 +93,7 @@ module Hashie
92
93
  def stringify_keys!(hash)
93
94
  hash.extend(Hashie::Extensions::StringifyKeys) unless hash.respond_to?(:stringify_keys!)
94
95
  hash.keys.each do |k| # rubocop:disable Performance/HashEachMethods
95
- stringify_keys_recursively!(hash[k])
96
+ _stringify_keys_recursively!(hash[k])
96
97
  hash[k.to_s] = hash.delete(k)
97
98
  end
98
99
  hash
@@ -69,16 +69,17 @@ module Hashie
69
69
  # Symbolize all keys recursively within nested
70
70
  # hashes and arrays.
71
71
  # @api private
72
- def symbolize_keys_recursively!(object)
72
+ def _symbolize_keys_recursively!(object)
73
73
  case object
74
74
  when ::Array
75
75
  object.each do |i|
76
- symbolize_keys_recursively!(i)
76
+ _symbolize_keys_recursively!(i)
77
77
  end
78
78
  when ::Hash
79
79
  symbolize_keys!(object)
80
80
  end
81
81
  end
82
+ private :_symbolize_keys_recursively!
82
83
 
83
84
  # Converts keys in a hash recursively to symbols, in place.
84
85
  #
@@ -93,7 +94,7 @@ module Hashie
93
94
  def symbolize_keys!(hash)
94
95
  hash.extend(Hashie::Extensions::SymbolizeKeys) unless hash.respond_to?(:symbolize_keys!)
95
96
  hash.keys.each do |k| # rubocop:disable Performance/HashEachMethods
96
- symbolize_keys_recursively!(hash[k])
97
+ _symbolize_keys_recursively!(hash[k])
97
98
  hash[convert_key(k)] = hash.delete(k)
98
99
  end
99
100
  hash
data/lib/hashie/hash.rb CHANGED
@@ -41,10 +41,10 @@ module Hashie
41
41
  if self[k].is_a?(Array)
42
42
  out[assignment_key] ||= []
43
43
  self[k].each do |array_object|
44
- out[assignment_key] << maybe_convert_to_hash(array_object, options)
44
+ out[assignment_key] << _maybe_convert_to_hash(array_object, options)
45
45
  end
46
46
  else
47
- out[assignment_key] = maybe_convert_to_hash(self[k], options)
47
+ out[assignment_key] = _maybe_convert_to_hash(self[k], options)
48
48
  end
49
49
  end
50
50
  out
@@ -61,13 +61,13 @@ module Hashie
61
61
 
62
62
  private
63
63
 
64
- def maybe_convert_to_hash(object, options)
64
+ def _maybe_convert_to_hash(object, options)
65
65
  return object unless object.is_a?(Hash) || object.respond_to?(:to_hash)
66
66
 
67
- flexibly_convert_to_hash(object, options)
67
+ _flexibly_convert_to_hash(object, options)
68
68
  end
69
69
 
70
- def flexibly_convert_to_hash(object, options = {})
70
+ def _flexibly_convert_to_hash(object, options = {})
71
71
  if object.method(:to_hash).arity.zero?
72
72
  object.to_hash
73
73
  else
data/lib/hashie/mash.rb CHANGED
@@ -72,8 +72,8 @@ module Hashie
72
72
 
73
73
  ALLOWED_SUFFIXES = %w[? ! = _].freeze
74
74
 
75
- # Loads a YAML file. The default parser evaluates ERB templates, so only
76
- # use this default with trusted files. Pass YamlParser to avoid ERB.
75
+ # Loads YAML without evaluating ERB. Pass YamlErbParser explicitly to
76
+ # evaluate templates from trusted files.
77
77
  #
78
78
  # @example Load and cache a frozen configuration from a YAML file
79
79
  # require 'tempfile'
@@ -96,7 +96,7 @@ module Hashie
96
96
  raise ArgumentError, "file not found: `#{path}`" unless File.file?(path)
97
97
 
98
98
  options = options.dup
99
- parser = options.delete(:parser) { Hashie::Extensions::Parsers::YamlErbParser }
99
+ parser = options.delete(:parser) { Hashie::Extensions::Parsers::YamlParser }
100
100
  @_mashes[path] = new(parser.perform(path, options)).freeze
101
101
  end
102
102
 
@@ -128,8 +128,8 @@ module Hashie
128
128
 
129
129
  # If you pass in an existing hash, it will
130
130
  # convert it to a Mash including recursively
131
- # descending into arrays and hashes, converting
132
- # them as well.
131
+ # descending into arrays and plain hashes, converting
132
+ # them as well. Nested custom Hash subclasses retain their identity.
133
133
  #
134
134
  # @example Recursively convert hashes nested in arrays
135
135
  # mash = Hashie::Mash.new(users: [{ name: 'Ada' }])
@@ -202,7 +202,7 @@ module Hashie
202
202
  # mash.keys # => ['user']
203
203
  # mash.user.name # => 'Ada'
204
204
  def custom_writer(key, value, convert = true) # :nodoc:
205
- log_built_in_message(key) if key.respond_to?(:to_sym) && log_collision?(key.to_sym)
205
+ _log_built_in_message(key) if key.respond_to?(:to_sym) && _log_collision?(key.to_sym)
206
206
  regular_writer(convert_key(key), convert ? convert_value(value) : value)
207
207
  end
208
208
 
@@ -410,7 +410,7 @@ module Hashie
410
410
  def _deep_update(other_hash, &blk)
411
411
  other_hash.each_pair do |k, v|
412
412
  key = convert_key(k)
413
- if v.is_a?(::Hash) && key?(key) && regular_reader(key).is_a?(Mash)
413
+ if (v.instance_of?(::Hash) || v.is_a?(Mash)) && key?(key) && regular_reader(key).is_a?(Mash)
414
414
  custom_reader(key).deep_update(v, &blk)
415
415
  else
416
416
  value = convert_value(v, true)
@@ -473,7 +473,7 @@ module Hashie
473
473
  def respond_to_missing?(method_name, *args)
474
474
  return true if key?(method_name)
475
475
 
476
- suffix = method_suffix(method_name)
476
+ suffix = _method_suffix(method_name)
477
477
  if suffix
478
478
  true
479
479
  else
@@ -500,7 +500,7 @@ module Hashie
500
500
  def method_missing(method_name, *args, &blk)
501
501
  return self.[](method_name, &blk) if key?(method_name)
502
502
 
503
- name, suffix = method_name_and_suffix(method_name)
503
+ name, suffix = _method_name_and_suffix(method_name)
504
504
  case suffix
505
505
  when '='
506
506
  assign_property(name, args.first)
@@ -581,9 +581,9 @@ module Hashie
581
581
  self.class.new(super(*string_keys))
582
582
  end
583
583
 
584
- protected
584
+ private
585
585
 
586
- def method_name_and_suffix(method_name)
586
+ def _method_name_and_suffix(method_name)
587
587
  method_name = method_name.to_s
588
588
  if method_name.end_with?(*ALLOWED_SUFFIXES)
589
589
  [method_name[0..-2], method_name[-1]]
@@ -592,11 +592,13 @@ module Hashie
592
592
  end
593
593
  end
594
594
 
595
- def method_suffix(method_name)
595
+ def _method_suffix(method_name)
596
596
  method_name = method_name.to_s
597
597
  method_name[-1] if method_name.end_with?(*ALLOWED_SUFFIXES)
598
598
  end
599
599
 
600
+ protected
601
+
600
602
  # Symbol#name returns the symbol's interned frozen string, where
601
603
  # Symbol#to_s allocates a new one on every read and write.
602
604
  def convert_key(key) # :nodoc:
@@ -609,9 +611,11 @@ module Hashie
609
611
  case val
610
612
  when self.class
611
613
  val.dup
612
- when Hash
614
+ when Mash
613
615
  duping ? val.dup : val
614
616
  when ::Hash
617
+ return val unless val.instance_of?(::Hash)
618
+
615
619
  val = val.dup if duping
616
620
  self.class.new(val)
617
621
  when ::Array
@@ -623,7 +627,7 @@ module Hashie
623
627
 
624
628
  private
625
629
 
626
- def log_built_in_message(method_key)
630
+ def _log_built_in_message(method_key)
627
631
  return if self.class.disable_warnings?(method_key)
628
632
 
629
633
  method_information = Hashie::Utils.method_information(method(method_key))
@@ -636,11 +640,11 @@ module Hashie
636
640
  )
637
641
  end
638
642
 
639
- def log_collision?(method_key)
643
+ def _log_collision?(method_key)
640
644
  return false unless method_key.is_a?(String) || method_key.is_a?(Symbol)
641
645
  return false unless respond_to?(method_key)
642
646
 
643
- _, suffix = method_name_and_suffix(method_key)
647
+ _, suffix = _method_name_and_suffix(method_key)
644
648
 
645
649
  (!suffix || suffix == '=') &&
646
650
  !self.class.disable_warnings?(method_key) &&
data/lib/hashie/rash.rb CHANGED
@@ -145,7 +145,7 @@ module Hashie
145
145
 
146
146
  case query
147
147
  when String
148
- optimize_if_necessary!
148
+ _optimize_if_necessary!
149
149
 
150
150
  # see if any of the regexps match the string
151
151
  @regexes.each do |regex|
@@ -193,7 +193,7 @@ module Hashie
193
193
 
194
194
  private
195
195
 
196
- def optimize_if_necessary!
196
+ def _optimize_if_necessary!
197
197
  return unless (@lookups += 1) >= @optimize_every
198
198
 
199
199
  @regexes = @regexes.sort_by { |regex| -@regex_counts[regex] }
data/lib/hashie/utils.rb CHANGED
@@ -37,7 +37,7 @@ module Hashie
37
37
  # Hashie::Utils.safe_dup(:name) # => :name
38
38
  def self.safe_dup(value)
39
39
  case value
40
- when Complex, FalseClass, NilClass, Rational, Method, Symbol, TrueClass, *integer_classes
40
+ when Complex, FalseClass, NilClass, Rational, Method, Symbol, TrueClass, *_integer_classes
41
41
  value
42
42
  else
43
43
  value.dup
@@ -48,8 +48,9 @@ module Hashie
48
48
  #
49
49
  # @api private
50
50
  # @return [Array<Class>]
51
- def self.integer_classes
51
+ def self._integer_classes
52
52
  @integer_classes ||= [Integer]
53
53
  end
54
+ private_class_method :_integer_classes
54
55
  end
55
56
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hashie
4
- VERSION = '5.2.0'
4
+ VERSION = '6.0.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hashie
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2026-10-23 00:00:00.000000000 Z
12
+ date: 2026-11-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: logger
@@ -117,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
117
  - !ruby/object:Gem::Version
118
118
  version: '0'
119
119
  requirements: []
120
- rubygems_version: 3.5.16
120
+ rubygems_version: 3.5.22
121
121
  signing_key:
122
122
  specification_version: 4
123
123
  summary: Your friendly neighborhood hash library.