hashie 4.1.0 → 5.2.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +138 -53
  3. data/CONTRIBUTING.md +38 -1
  4. data/LICENSE +1 -1
  5. data/README.md +920 -205
  6. data/Rakefile +32 -1
  7. data/UPGRADING.md +56 -0
  8. data/hashie.gemspec +10 -5
  9. data/lib/hashie/array.rb +15 -12
  10. data/lib/hashie/clash.rb +59 -4
  11. data/lib/hashie/dash.rb +160 -32
  12. data/lib/hashie/extensions/active_support/core_ext/hash.rb +38 -0
  13. data/lib/hashie/extensions/array/pretty_inspect.rb +20 -0
  14. data/lib/hashie/extensions/block_access.rb +71 -0
  15. data/lib/hashie/extensions/coercion.rb +180 -26
  16. data/lib/hashie/extensions/dash/coercion.rb +34 -3
  17. data/lib/hashie/extensions/dash/indifferent_access.rb +90 -3
  18. data/lib/hashie/extensions/dash/lazy.rb +170 -0
  19. data/lib/hashie/extensions/dash/predefined_values.rb +148 -0
  20. data/lib/hashie/extensions/dash/property_translation.rb +138 -15
  21. data/lib/hashie/extensions/deep_fetch.rb +21 -9
  22. data/lib/hashie/extensions/deep_find.rb +20 -0
  23. data/lib/hashie/extensions/deep_grep.rb +69 -0
  24. data/lib/hashie/extensions/deep_locate.rb +16 -29
  25. data/lib/hashie/extensions/deep_merge.rb +16 -0
  26. data/lib/hashie/extensions/deep_merge_initializer.rb +42 -0
  27. data/lib/hashie/extensions/ignore_undeclared.rb +24 -7
  28. data/lib/hashie/extensions/indifferent_access.rb +124 -5
  29. data/lib/hashie/extensions/kernel_coercion.rb +58 -0
  30. data/lib/hashie/extensions/key_conflict_warning.rb +39 -4
  31. data/lib/hashie/extensions/key_conversion.rb +9 -0
  32. data/lib/hashie/extensions/mash/define_accessors.rb +71 -8
  33. data/lib/hashie/extensions/mash/keep_original_keys.rb +13 -9
  34. data/lib/hashie/extensions/mash/permissive_respond_to.rb +2 -0
  35. data/lib/hashie/extensions/mash/required_keys.rb +111 -0
  36. data/lib/hashie/extensions/mash/safe_assignment.rb +22 -4
  37. data/lib/hashie/extensions/mash/strict_key_access.rb +65 -0
  38. data/lib/hashie/extensions/mash/symbolize_keys.rb +11 -7
  39. data/lib/hashie/extensions/mash/transform_keys.rb +86 -0
  40. data/lib/hashie/extensions/mash/underscore_keys.rb +32 -0
  41. data/lib/hashie/extensions/merge_initializer.rb +9 -0
  42. data/lib/hashie/extensions/method_access.rb +94 -8
  43. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +28 -35
  44. data/lib/hashie/extensions/parsers/yaml_parser.rb +81 -0
  45. data/lib/hashie/extensions/persistable.rb +137 -0
  46. data/lib/hashie/extensions/pretty_inspect.rb +14 -0
  47. data/lib/hashie/extensions/ruby_version.rb +18 -1
  48. data/lib/hashie/extensions/ruby_version_check.rb +17 -0
  49. data/lib/hashie/extensions/strict_key_access.rb +59 -5
  50. data/lib/hashie/extensions/stringify_keys.rb +72 -17
  51. data/lib/hashie/extensions/symbolize_keys.rb +84 -17
  52. data/lib/hashie/hash.rb +18 -2
  53. data/lib/hashie/logger.rb +18 -1
  54. data/lib/hashie/mash.rb +314 -82
  55. data/lib/hashie/railtie.rb +27 -1
  56. data/lib/hashie/rash.rb +64 -4
  57. data/lib/hashie/trash.rb +8 -0
  58. data/lib/hashie/utils.rb +17 -6
  59. data/lib/hashie/version.rb +3 -1
  60. data/lib/hashie.rb +46 -0
  61. metadata +22 -9
@@ -1,31 +1,76 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Hashie
2
4
  module Extensions
5
+ # Extends a Hash with the ability to convert its keys to symbols.
6
+ # Keys that do not respond to #to_sym are preserved.
7
+ #
8
+ # @example Extend a pre-existing Hash object
9
+ # hash = { 'test' => 'value' }
10
+ # hash.extend(Hashie::Extensions::SymbolizeKeys)
11
+ # hash.symbolize_keys!
12
+ # hash # => { test: 'value' }
13
+ #
14
+ # @example Create a Hash class with this capability
15
+ # class SymbolizableHash < Hash
16
+ # include Hashie::Extensions::SymbolizeKeys
17
+ # end
18
+ #
19
+ # hash = SymbolizableHash.new
20
+ # hash['test'] = 'value'
21
+ # hash.symbolize_keys!
22
+ # hash # => { test: 'value' }
23
+ #
24
+ # @example Use the module function to symbolize keys in a hash
25
+ # hash = { 'test' => 'value', 123 => 'number' }
26
+ # Hashie::Extensions::SymbolizeKeys.symbolize_keys!(hash)
27
+ # hash # => { test: 'value', 123 => 'number' }
3
28
  module SymbolizeKeys
4
- # Convert all keys in the hash to symbols.
29
+ # Converts keys in the hash and nested hashes and arrays to symbols.
5
30
  #
6
- # @example
7
- # test = {'abc' => 'def'}
31
+ # @example Convert keys in place
32
+ # test = { 'abc' => [{ 'def' => 'value' }] }
33
+ # test.extend(Hashie::Extensions::SymbolizeKeys)
8
34
  # test.symbolize_keys!
9
- # test # => {:abc => 'def'}
35
+ # test # => { abc: [{ def: 'value' }] }
36
+ #
37
+ # @api public
38
+ # @return [Hash] the receiver with its keys symbolized where possible
10
39
  def symbolize_keys!
11
40
  SymbolizeKeys.symbolize_keys!(self)
12
41
  self
13
42
  end
14
43
 
15
- # Return a new hash with all keys converted
16
- # to symbols.
44
+ # Returns a shallow copy with keys recursively converted to symbols.
45
+ # Nested hashes and arrays are shared with the original and are modified.
46
+ #
47
+ # @example Copy a flat hash without modifying the original
48
+ # test = { 'abc' => 'def' }
49
+ # test.extend(Hashie::Extensions::SymbolizeKeys)
50
+ # test.symbolize_keys # => { abc: 'def' }
51
+ # test # => { 'abc' => 'def' }
52
+ #
53
+ # @example Nested values are shared, not deep copied
54
+ # test = { 'abc' => [{ 'def' => 'value' }] }
55
+ # test.extend(Hashie::Extensions::SymbolizeKeys)
56
+ # test.symbolize_keys # => { abc: [{ def: 'value' }] }
57
+ # test # => { 'abc' => [{ def: 'value' }] }
58
+ #
59
+ # @api public
60
+ # @return [Hash] a shallow copy with symbolized keys
17
61
  def symbolize_keys
18
62
  SymbolizeKeys.symbolize_keys(self)
19
63
  end
20
64
 
65
+ # @example Add recursive conversion functions to an object
66
+ # converter = Object.new.extend(Hashie::Extensions::SymbolizeKeys::ClassMethods)
67
+ # converter.symbolize_keys('user' => { 'name' => 'Ada' }) # => { user: { name: 'Ada' } }
21
68
  module ClassMethods
22
69
  # Symbolize all keys recursively within nested
23
70
  # hashes and arrays.
24
71
  # @api private
25
72
  def symbolize_keys_recursively!(object)
26
73
  case object
27
- when self.class
28
- symbolize_keys!(object)
29
74
  when ::Array
30
75
  object.each do |i|
31
76
  symbolize_keys_recursively!(i)
@@ -35,25 +80,36 @@ module Hashie
35
80
  end
36
81
  end
37
82
 
38
- # Convert all keys in hash to symbols.
83
+ # Converts keys in a hash recursively to symbols, in place.
39
84
  #
85
+ # @example Convert keys through Hashie's module function
86
+ # test = { 'abc' => 'def' }
87
+ # Hashie.symbolize_keys!(test)
88
+ # test # => { abc: 'def' }
89
+ #
90
+ # @api public
40
91
  # @param [Hash] hash
41
- # @example
42
- # test = {'abc' => 'def'}
43
- # Hashie.symbolize_keys! test
44
- # test # => {:abc => 'def'}
92
+ # @return [Hash] the passed hash with symbolized keys
45
93
  def symbolize_keys!(hash)
46
94
  hash.extend(Hashie::Extensions::SymbolizeKeys) unless hash.respond_to?(:symbolize_keys!)
47
95
  hash.keys.each do |k| # rubocop:disable Performance/HashEachMethods
48
96
  symbolize_keys_recursively!(hash[k])
49
- hash[k.to_sym] = hash.delete(k)
97
+ hash[convert_key(k)] = hash.delete(k)
50
98
  end
51
99
  hash
52
100
  end
53
101
 
54
- # Return a copy of hash with all keys converted
55
- # to symbols.
56
- # @param [::Hash] hash
102
+ # Returns a shallow copy of a hash with recursively symbolized keys.
103
+ # Nested hashes and arrays are shared with the original and are modified.
104
+ #
105
+ # @example Copy keys through Hashie's module function
106
+ # test = { 'abc' => 'def' }
107
+ # Hashie.symbolize_keys(test) # => { abc: 'def' }
108
+ # test # => { 'abc' => 'def' }
109
+ #
110
+ # @api public
111
+ # @param [Hash] hash
112
+ # @return [Hash] a shallow copy with symbolized keys
57
113
  def symbolize_keys(hash)
58
114
  copy = hash.dup
59
115
  copy.extend(Hashie::Extensions::SymbolizeKeys) unless copy.respond_to?(:symbolize_keys!)
@@ -61,6 +117,17 @@ module Hashie
61
117
  symbolize_keys!(new_hash)
62
118
  end
63
119
  end
120
+
121
+ private
122
+
123
+ # Converts a key to a symbol, if possible
124
+ #
125
+ # @api private
126
+ # @param [<K>] key the key to attempt convert to a symbol
127
+ # @return [Symbol, K]
128
+ def convert_key(key)
129
+ key.respond_to?(:to_sym) ? key.to_sym : key
130
+ end
64
131
  end
65
132
 
66
133
  class << self
data/lib/hashie/hash.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'hashie/extensions/stringify_keys'
2
4
  require 'hashie/extensions/pretty_inspect'
3
5
 
@@ -5,24 +7,34 @@ module Hashie
5
7
  # A Hashie Hash is simply a Hash that has convenience
6
8
  # functions baked in such as stringify_keys that may
7
9
  # not be available in all libraries.
10
+ #
11
+ # @example Convert keys using the built-in extensions
12
+ # Hashie::Hash[name: 'Ada'].stringify_keys # => { 'name' => 'Ada' }
8
13
  class Hash < ::Hash
9
14
  include Hashie::Extensions::PrettyInspect
10
15
  include Hashie::Extensions::StringifyKeys
11
16
 
12
17
  # Convert this hash into a Mash
18
+ #
19
+ # @example Read converted data through methods
20
+ # Hashie::Hash[name: 'Ada'].to_mash.name # => 'Ada'
13
21
  def to_mash
14
22
  ::Hashie::Mash.new(self)
15
23
  end
16
24
 
17
25
  # Converts a mash back to a hash (with stringified or symbolized keys)
26
+ #
27
+ # @example Recursively convert nested hashes and their keys
28
+ # hash = Hashie::Hash[user: Hashie::Hash[name: 'Ada']]
29
+ # hash.to_hash(stringify_keys: true) # => { 'user' => { 'name' => 'Ada' } }
18
30
  def to_hash(options = {})
19
31
  out = {}
20
32
  each_key do |k|
21
33
  assignment_key =
22
34
  if options[:stringify_keys]
23
35
  k.to_s
24
- elsif options[:symbolize_keys]
25
- k.to_s.to_sym
36
+ elsif options[:symbolize_keys] && k.respond_to?(:to_sym)
37
+ k.to_sym
26
38
  else
27
39
  k
28
40
  end
@@ -39,6 +51,10 @@ module Hashie
39
51
  end
40
52
 
41
53
  # The C generator for the json gem doesn't like mashies
54
+ #
55
+ # @example Serialize as an ordinary JSON object
56
+ # require 'json'
57
+ # JSON.parse(Hashie::Hash[name: 'Ada'].to_json) # => { 'name' => 'Ada' }
42
58
  def to_json(*args)
43
59
  to_hash.to_json(*args)
44
60
  end
data/lib/hashie/logger.rb CHANGED
@@ -1,17 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
 
3
5
  module Hashie
4
6
  # The logger that Hashie uses for reporting errors.
5
7
  #
6
8
  # @return [Logger]
9
+ #
10
+ # @example Obtain the logger used for warnings
11
+ # Hashie.logger.respond_to?(:warn) # => true
7
12
  def self.logger
8
- @logger ||= Logger.new(STDOUT)
13
+ @logger ||= Logger.new($stdout)
9
14
  end
10
15
 
11
16
  # Sets the logger that Hashie uses for reporting errors.
12
17
  #
13
18
  # @param logger [Logger] The logger to set as Hashie's logger.
14
19
  # @return [void]
20
+ #
21
+ # @example Temporarily capture warnings
22
+ # require 'stringio'
23
+ # original = Hashie.logger
24
+ # begin
25
+ # output = StringIO.new
26
+ # Hashie.logger = Logger.new(output)
27
+ # Hashie.logger.warn('example warning')
28
+ # output.string.include?('example warning') # => true
29
+ # ensure
30
+ # Hashie.logger = original
31
+ # end
15
32
  def self.logger=(logger)
16
33
  @logger = logger
17
34
  end