hashie 3.4.2 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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