hashie 3.4.2 → 5.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 (73) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +518 -122
  3. data/CONTRIBUTING.md +24 -7
  4. data/LICENSE +1 -1
  5. data/README.md +455 -48
  6. data/Rakefile +18 -1
  7. data/UPGRADING.md +157 -7
  8. data/hashie.gemspec +14 -7
  9. data/lib/hashie/array.rb +21 -0
  10. data/lib/hashie/clash.rb +24 -12
  11. data/lib/hashie/dash.rb +56 -31
  12. data/lib/hashie/extensions/active_support/core_ext/hash.rb +14 -0
  13. data/lib/hashie/extensions/array/pretty_inspect.rb +19 -0
  14. data/lib/hashie/extensions/coercion.rb +91 -52
  15. data/lib/hashie/extensions/dash/coercion.rb +25 -0
  16. data/lib/hashie/extensions/dash/indifferent_access.rb +30 -1
  17. data/lib/hashie/extensions/dash/predefined_values.rb +88 -0
  18. data/lib/hashie/extensions/dash/property_translation.rb +59 -30
  19. data/lib/hashie/extensions/deep_fetch.rb +5 -3
  20. data/lib/hashie/extensions/deep_find.rb +14 -5
  21. data/lib/hashie/extensions/deep_locate.rb +40 -21
  22. data/lib/hashie/extensions/deep_merge.rb +28 -10
  23. data/lib/hashie/extensions/ignore_undeclared.rb +6 -4
  24. data/lib/hashie/extensions/indifferent_access.rb +49 -8
  25. data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
  26. data/lib/hashie/extensions/mash/define_accessors.rb +90 -0
  27. data/lib/hashie/extensions/mash/keep_original_keys.rb +53 -0
  28. data/lib/hashie/extensions/mash/permissive_respond_to.rb +61 -0
  29. data/lib/hashie/extensions/mash/safe_assignment.rb +3 -1
  30. data/lib/hashie/extensions/mash/symbolize_keys.rb +38 -0
  31. data/lib/hashie/extensions/method_access.rb +77 -19
  32. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +29 -5
  33. data/lib/hashie/extensions/ruby_version.rb +60 -0
  34. data/lib/hashie/extensions/ruby_version_check.rb +21 -0
  35. data/lib/hashie/extensions/strict_key_access.rb +77 -0
  36. data/lib/hashie/extensions/stringify_keys.rb +8 -5
  37. data/lib/hashie/extensions/symbolize_keys.rb +21 -7
  38. data/lib/hashie/hash.rb +18 -11
  39. data/lib/hashie/logger.rb +18 -0
  40. data/lib/hashie/mash.rb +196 -55
  41. data/lib/hashie/railtie.rb +21 -0
  42. data/lib/hashie/rash.rb +7 -7
  43. data/lib/hashie/utils.rb +44 -0
  44. data/lib/hashie/version.rb +1 -1
  45. data/lib/hashie.rb +34 -16
  46. metadata +30 -79
  47. data/spec/hashie/clash_spec.rb +0 -48
  48. data/spec/hashie/dash_spec.rb +0 -513
  49. data/spec/hashie/extensions/autoload_spec.rb +0 -24
  50. data/spec/hashie/extensions/coercion_spec.rb +0 -625
  51. data/spec/hashie/extensions/dash/indifferent_access_spec.rb +0 -84
  52. data/spec/hashie/extensions/deep_fetch_spec.rb +0 -97
  53. data/spec/hashie/extensions/deep_find_spec.rb +0 -45
  54. data/spec/hashie/extensions/deep_locate_spec.rb +0 -124
  55. data/spec/hashie/extensions/deep_merge_spec.rb +0 -45
  56. data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -46
  57. data/spec/hashie/extensions/indifferent_access_spec.rb +0 -219
  58. data/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb +0 -208
  59. data/spec/hashie/extensions/key_conversion_spec.rb +0 -12
  60. data/spec/hashie/extensions/mash/safe_assignment_spec.rb +0 -23
  61. data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
  62. data/spec/hashie/extensions/method_access_spec.rb +0 -184
  63. data/spec/hashie/extensions/stringify_keys_spec.rb +0 -101
  64. data/spec/hashie/extensions/symbolize_keys_spec.rb +0 -106
  65. data/spec/hashie/hash_spec.rb +0 -84
  66. data/spec/hashie/mash_spec.rb +0 -683
  67. data/spec/hashie/parsers/yaml_erb_parser_spec.rb +0 -29
  68. data/spec/hashie/rash_spec.rb +0 -77
  69. data/spec/hashie/trash_spec.rb +0 -268
  70. data/spec/hashie/version_spec.rb +0 -7
  71. data/spec/spec_helper.rb +0 -15
  72. data/spec/support/module_context.rb +0 -11
  73. data/spec/support/ruby_version.rb +0 -10
@@ -0,0 +1,60 @@
1
+ # Copyright (c) Chad Fowler, Rich Kilmer, Jim Weirich and others.
2
+ # Portions copyright (c) Engine Yard and Andre Arko
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+
15
+ module Hashie
16
+ module Extensions
17
+ class RubyVersion
18
+ include Comparable
19
+
20
+ attr_accessor :segments
21
+
22
+ def initialize(version)
23
+ @segments = split_to_segments(version)
24
+ end
25
+
26
+ def <=>(other)
27
+ lhsegments = segments
28
+ rhsegments = other.segments
29
+
30
+ lhsize = lhsegments.size
31
+ rhsize = rhsegments.size
32
+ limit = (lhsize > rhsize ? lhsize : rhsize) - 1
33
+
34
+ i = 0
35
+
36
+ while i <= limit
37
+ lhs = lhsegments[i] || 0
38
+ rhs = rhsegments[i] || 0
39
+ i += 1
40
+
41
+ next if lhs == rhs
42
+ return -1 if lhs.is_a?(String) && rhs.is_a?(Numeric)
43
+ return 1 if lhs.is_a?(Numeric) && rhs.is_a?(String)
44
+
45
+ return lhs <=> rhs
46
+ end
47
+
48
+ 0
49
+ end
50
+
51
+ private
52
+
53
+ def split_to_segments(version)
54
+ version.scan(/[0-9]+|[a-z]+/i).map do |segment|
55
+ /^\d+$/ =~ segment ? segment.to_i : segment
56
+ end.freeze
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,21 @@
1
+ require 'hashie/extensions/ruby_version'
2
+
3
+ module Hashie
4
+ module Extensions
5
+ module RubyVersionCheck
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ def with_minimum_ruby(version)
12
+ yield if with_minimum_ruby?(version)
13
+ end
14
+
15
+ def with_minimum_ruby?(version)
16
+ RubyVersion.new(RUBY_VERSION) >= RubyVersion.new(version)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,77 @@
1
+ module Hashie
2
+ module Extensions
3
+ # SRP: This extension will fail an error whenever a key is accessed
4
+ # that does not exist in the hash.
5
+ #
6
+ # EXAMPLE:
7
+ #
8
+ # class StrictKeyAccessHash < Hash
9
+ # include Hashie::Extensions::StrictKeyAccess
10
+ # end
11
+ #
12
+ # >> hash = StrictKeyAccessHash[foo: "bar"]
13
+ # => {:foo=>"bar"}
14
+ # >> hash[:foo]
15
+ # => "bar"
16
+ # >> hash[:cow]
17
+ # KeyError: key not found: :cow
18
+ #
19
+ # NOTE: For googlers coming from Python to Ruby, this extension makes a Hash
20
+ # behave more like a "Dictionary".
21
+ #
22
+ module StrictKeyAccess
23
+ class DefaultError < StandardError
24
+ def initialize
25
+ super('Setting or using a default with Hashie::Extensions::StrictKeyAccess'\
26
+ ' does not make sense'
27
+ )
28
+ end
29
+ end
30
+
31
+ # NOTE: Defaults don't make any sense with a StrictKeyAccess.
32
+ # NOTE: When key lookup fails a KeyError is raised.
33
+ #
34
+ # Normal:
35
+ #
36
+ # >> a = Hash.new(123)
37
+ # => {}
38
+ # >> a["noes"]
39
+ # => 123
40
+ #
41
+ # With StrictKeyAccess:
42
+ #
43
+ # >> a = StrictKeyAccessHash.new(123)
44
+ # => {}
45
+ # >> a["noes"]
46
+ # KeyError: key not found: "noes"
47
+ #
48
+ def [](key)
49
+ fetch(key)
50
+ end
51
+
52
+ def default(_ = nil)
53
+ raise DefaultError
54
+ end
55
+
56
+ def default=(_)
57
+ raise DefaultError
58
+ end
59
+
60
+ def default_proc
61
+ raise DefaultError
62
+ end
63
+
64
+ def default_proc=(_)
65
+ raise DefaultError
66
+ end
67
+
68
+ def key(value)
69
+ super.tap do |result|
70
+ if result.nil? && (!key?(result) || self[result] != value)
71
+ raise KeyError, "key not found with value of #{value.inspect}"
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -15,7 +15,7 @@ module Hashie
15
15
  # Return a new hash with all keys converted
16
16
  # to strings.
17
17
  def stringify_keys
18
- dup.stringify_keys!
18
+ StringifyKeys.stringify_keys(self)
19
19
  end
20
20
 
21
21
  module ClassMethods
@@ -25,7 +25,7 @@ module Hashie
25
25
  def stringify_keys_recursively!(object)
26
26
  case object
27
27
  when self.class
28
- object.stringify_keys!
28
+ stringify_keys!(object)
29
29
  when ::Array
30
30
  object.each do |i|
31
31
  stringify_keys_recursively!(i)
@@ -43,7 +43,8 @@ module Hashie
43
43
  # test.stringify_keys!
44
44
  # test # => {'abc' => 'def'}
45
45
  def stringify_keys!(hash)
46
- hash.keys.each do |k|
46
+ hash.extend(Hashie::Extensions::StringifyKeys) unless hash.respond_to?(:stringify_keys!)
47
+ hash.keys.each do |k| # rubocop:disable Performance/HashEachMethods
47
48
  stringify_keys_recursively!(hash[k])
48
49
  hash[k.to_s] = hash.delete(k)
49
50
  end
@@ -54,8 +55,10 @@ module Hashie
54
55
  # to strings.
55
56
  # @param [::Hash] hash
56
57
  def stringify_keys(hash)
57
- hash.dup.tap do | new_hash |
58
- stringify_keys! new_hash
58
+ copy = hash.dup
59
+ copy.extend(Hashie::Extensions::StringifyKeys) unless copy.respond_to?(:stringify_keys!)
60
+ copy.tap do |new_hash|
61
+ stringify_keys!(new_hash)
59
62
  end
60
63
  end
61
64
  end
@@ -15,7 +15,7 @@ module Hashie
15
15
  # Return a new hash with all keys converted
16
16
  # to symbols.
17
17
  def symbolize_keys
18
- dup.symbolize_keys!
18
+ SymbolizeKeys.symbolize_keys(self)
19
19
  end
20
20
 
21
21
  module ClassMethods
@@ -23,9 +23,9 @@ module Hashie
23
23
  # hashes and arrays.
24
24
  # @api private
25
25
  def symbolize_keys_recursively!(object)
26
- object.symbolize_keys! if object.respond_to? :symbolize_keys!
27
-
28
26
  case object
27
+ when self.class
28
+ symbolize_keys!(object)
29
29
  when ::Array
30
30
  object.each do |i|
31
31
  symbolize_keys_recursively!(i)
@@ -43,9 +43,10 @@ module Hashie
43
43
  # Hashie.symbolize_keys! test
44
44
  # test # => {:abc => 'def'}
45
45
  def symbolize_keys!(hash)
46
- hash.keys.each do |k|
46
+ hash.extend(Hashie::Extensions::SymbolizeKeys) unless hash.respond_to?(:symbolize_keys!)
47
+ hash.keys.each do |k| # rubocop:disable Performance/HashEachMethods
47
48
  symbolize_keys_recursively!(hash[k])
48
- hash[k.to_sym] = hash.delete(k)
49
+ hash[convert_key(k)] = hash.delete(k)
49
50
  end
50
51
  hash
51
52
  end
@@ -54,10 +55,23 @@ module Hashie
54
55
  # to symbols.
55
56
  # @param [::Hash] hash
56
57
  def symbolize_keys(hash)
57
- hash.dup.tap do | new_hash |
58
- symbolize_keys! new_hash
58
+ copy = hash.dup
59
+ copy.extend(Hashie::Extensions::SymbolizeKeys) unless copy.respond_to?(:symbolize_keys!)
60
+ copy.tap do |new_hash|
61
+ symbolize_keys!(new_hash)
59
62
  end
60
63
  end
64
+
65
+ private
66
+
67
+ # Converts a key to a symbol, if possible
68
+ #
69
+ # @api private
70
+ # @param [<K>] key the key to attempt convert to a symbol
71
+ # @return [Symbol, K]
72
+ def convert_key(key)
73
+ key.respond_to?(:to_sym) ? key.to_sym : key
74
+ end
61
75
  end
62
76
 
63
77
  class << self
data/lib/hashie/hash.rb CHANGED
@@ -17,21 +17,22 @@ 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|
21
- assignment_key = if options[:stringify_keys]
22
- k.to_s
23
- elsif options[:symbolize_keys]
24
- k.to_s.to_sym
25
- else
26
- k
27
- end
20
+ each_key do |k|
21
+ assignment_key =
22
+ if options[:stringify_keys]
23
+ k.to_s
24
+ elsif options[:symbolize_keys] && k.respond_to?(:to_sym)
25
+ k.to_sym
26
+ else
27
+ k
28
+ end
28
29
  if self[k].is_a?(Array)
29
30
  out[assignment_key] ||= []
30
31
  self[k].each do |array_object|
31
- out[assignment_key] << (Hash === array_object ? flexibly_convert_to_hash(array_object, options) : array_object)
32
+ out[assignment_key] << maybe_convert_to_hash(array_object, options)
32
33
  end
33
34
  else
34
- out[assignment_key] = (Hash === self[k] || self[k].respond_to?(:to_hash)) ? flexibly_convert_to_hash(self[k], options) : self[k]
35
+ out[assignment_key] = maybe_convert_to_hash(self[k], options)
35
36
  end
36
37
  end
37
38
  out
@@ -44,8 +45,14 @@ module Hashie
44
45
 
45
46
  private
46
47
 
48
+ def maybe_convert_to_hash(object, options)
49
+ return object unless object.is_a?(Hash) || object.respond_to?(:to_hash)
50
+
51
+ flexibly_convert_to_hash(object, options)
52
+ end
53
+
47
54
  def flexibly_convert_to_hash(object, options = {})
48
- if object.method(:to_hash).arity == 0
55
+ if object.method(:to_hash).arity.zero?
49
56
  object.to_hash
50
57
  else
51
58
  object.to_hash(options)
@@ -0,0 +1,18 @@
1
+ require 'logger'
2
+
3
+ module Hashie
4
+ # The logger that Hashie uses for reporting errors.
5
+ #
6
+ # @return [Logger]
7
+ def self.logger
8
+ @logger ||= Logger.new(STDOUT)
9
+ end
10
+
11
+ # Sets the logger that Hashie uses for reporting errors.
12
+ #
13
+ # @param logger [Logger] The logger to set as Hashie's logger.
14
+ # @return [void]
15
+ def self.logger=(logger)
16
+ @logger = logger
17
+ end
18
+ end