hashie 2.1.2 → 4.1.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 (71) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +524 -59
  3. data/CONTRIBUTING.md +24 -7
  4. data/README.md +781 -90
  5. data/Rakefile +19 -2
  6. data/UPGRADING.md +245 -0
  7. data/hashie.gemspec +21 -13
  8. data/lib/hashie.rb +60 -21
  9. data/lib/hashie/array.rb +21 -0
  10. data/lib/hashie/clash.rb +24 -12
  11. data/lib/hashie/dash.rb +96 -33
  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 +124 -18
  15. data/lib/hashie/extensions/dash/coercion.rb +25 -0
  16. data/lib/hashie/extensions/dash/indifferent_access.rb +56 -0
  17. data/lib/hashie/extensions/dash/property_translation.rb +191 -0
  18. data/lib/hashie/extensions/deep_fetch.rb +7 -5
  19. data/lib/hashie/extensions/deep_find.rb +69 -0
  20. data/lib/hashie/extensions/deep_locate.rb +113 -0
  21. data/lib/hashie/extensions/deep_merge.rb +35 -12
  22. data/lib/hashie/extensions/ignore_undeclared.rb +11 -5
  23. data/lib/hashie/extensions/indifferent_access.rb +28 -16
  24. data/lib/hashie/extensions/key_conflict_warning.rb +55 -0
  25. data/lib/hashie/extensions/key_conversion.rb +0 -82
  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 +18 -0
  30. data/lib/hashie/extensions/mash/symbolize_keys.rb +38 -0
  31. data/lib/hashie/extensions/method_access.rb +154 -11
  32. data/lib/hashie/extensions/parsers/yaml_erb_parser.rb +48 -0
  33. data/lib/hashie/extensions/pretty_inspect.rb +19 -0
  34. data/lib/hashie/extensions/ruby_version.rb +60 -0
  35. data/lib/hashie/extensions/ruby_version_check.rb +21 -0
  36. data/lib/hashie/extensions/strict_key_access.rb +77 -0
  37. data/lib/hashie/extensions/stringify_keys.rb +71 -0
  38. data/lib/hashie/extensions/symbolize_keys.rb +71 -0
  39. data/lib/hashie/hash.rb +27 -8
  40. data/lib/hashie/logger.rb +18 -0
  41. data/lib/hashie/mash.rb +235 -57
  42. data/lib/hashie/railtie.rb +21 -0
  43. data/lib/hashie/rash.rb +40 -16
  44. data/lib/hashie/trash.rb +2 -88
  45. data/lib/hashie/utils.rb +44 -0
  46. data/lib/hashie/version.rb +1 -1
  47. metadata +42 -81
  48. data/.gitignore +0 -9
  49. data/.rspec +0 -2
  50. data/.rubocop.yml +0 -36
  51. data/.travis.yml +0 -15
  52. data/Gemfile +0 -11
  53. data/Guardfile +0 -5
  54. data/lib/hashie/hash_extensions.rb +0 -47
  55. data/spec/hashie/clash_spec.rb +0 -48
  56. data/spec/hashie/dash_spec.rb +0 -338
  57. data/spec/hashie/extensions/coercion_spec.rb +0 -156
  58. data/spec/hashie/extensions/deep_fetch_spec.rb +0 -70
  59. data/spec/hashie/extensions/deep_merge_spec.rb +0 -22
  60. data/spec/hashie/extensions/ignore_undeclared_spec.rb +0 -23
  61. data/spec/hashie/extensions/indifferent_access_spec.rb +0 -152
  62. data/spec/hashie/extensions/key_conversion_spec.rb +0 -103
  63. data/spec/hashie/extensions/merge_initializer_spec.rb +0 -23
  64. data/spec/hashie/extensions/method_access_spec.rb +0 -121
  65. data/spec/hashie/hash_spec.rb +0 -66
  66. data/spec/hashie/mash_spec.rb +0 -467
  67. data/spec/hashie/rash_spec.rb +0 -44
  68. data/spec/hashie/trash_spec.rb +0 -193
  69. data/spec/hashie/version_spec.rb +0 -7
  70. data/spec/spec.opts +0 -3
  71. data/spec/spec_helper.rb +0 -8
@@ -2,28 +2,51 @@ module Hashie
2
2
  module Extensions
3
3
  module DeepMerge
4
4
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
5
- def deep_merge(other_hash)
6
- dup.deep_merge!(other_hash)
5
+ def deep_merge(other_hash, &block)
6
+ copy = _deep_dup(self)
7
+ copy.extend(Hashie::Extensions::DeepMerge) unless copy.respond_to?(:deep_merge!)
8
+ copy.deep_merge!(other_hash, &block)
7
9
  end
8
10
 
9
11
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
10
12
  # Modifies the receiver in place.
11
- def deep_merge!(other_hash)
12
- _recursive_merge(self, other_hash)
13
+ def deep_merge!(other_hash, &block)
14
+ return self unless other_hash.is_a?(::Hash)
15
+ _recursive_merge(self, other_hash, &block)
13
16
  self
14
17
  end
15
18
 
16
19
  private
17
20
 
18
- def _recursive_merge(hash, other_hash)
19
- if other_hash.is_a?(::Hash) && hash.is_a?(::Hash)
20
- other_hash.each do |k, v|
21
- hash[k] = hash.key?(k) ? _recursive_merge(hash[k], v) : v
22
- end
23
- hash
24
- else
25
- other_hash
21
+ def _deep_dup(hash)
22
+ copy = hash.dup
23
+
24
+ copy.each do |key, value|
25
+ copy[key] =
26
+ if value.is_a?(::Hash)
27
+ _deep_dup(value)
28
+ else
29
+ Hashie::Utils.safe_dup(value)
30
+ end
31
+ end
32
+
33
+ copy
34
+ end
35
+
36
+ def _recursive_merge(hash, other_hash, &block)
37
+ other_hash.each do |k, v|
38
+ hash[k] =
39
+ if hash.key?(k) && hash[k].is_a?(::Hash) && v.is_a?(::Hash)
40
+ _recursive_merge(hash[k], v, &block)
41
+ elsif v.is_a?(::Hash)
42
+ _recursive_merge({}, v, &block)
43
+ elsif hash.key?(k) && block_given?
44
+ yield(k, hash[k], v)
45
+ else
46
+ v.respond_to?(:deep_dup) ? v.deep_dup : v
47
+ end
26
48
  end
49
+ hash
27
50
  end
28
51
  end
29
52
  end
@@ -5,7 +5,7 @@ module Hashie
5
5
  # raising an error. This is useful when using a Trash to
6
6
  # capture a subset of a larger hash.
7
7
  #
8
- # Note that attempting to retrieve an undeclared property
8
+ # Note that attempting to retrieve or set an undeclared property
9
9
  # will still raise a NoMethodError, even if a value for
10
10
  # that property was provided at initialization.
11
11
  #
@@ -30,11 +30,17 @@ module Hashie
30
30
  # p.email # => NoMethodError
31
31
  module IgnoreUndeclared
32
32
  def initialize_attributes(attributes)
33
+ return unless attributes
34
+ klass = self.class
35
+ translations = klass.respond_to?(:translations) && klass.translations
33
36
  attributes.each_pair do |att, value|
34
- if self.class.property?(att) || (self.class.respond_to?(:translations) && self.class.translations.include?(att.to_sym))
35
- self[att] = value
36
- end
37
- end if attributes
37
+ next unless klass.property?(att) || (translations && translations.include?(att))
38
+ self[att] = value
39
+ end
40
+ end
41
+
42
+ def property_exists?(property)
43
+ self.class.property?(property)
38
44
  end
39
45
  end
40
46
  end
@@ -24,16 +24,18 @@ module Hashie
24
24
  #
25
25
  module IndifferentAccess
26
26
  def self.included(base)
27
+ Hashie::Extensions::Dash::IndifferentAccess.maybe_extend(base)
28
+
27
29
  base.class_eval do
28
- alias_method :regular_writer, :[]=
30
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
29
31
  alias_method :[]=, :indifferent_writer
30
32
  alias_method :store, :indifferent_writer
31
- %w(default update replace fetch delete key? values_at).each do |m|
32
- alias_method "regular_#{m}", m
33
+ %w[default update replace fetch delete key? values_at].each do |m|
34
+ alias_method "regular_#{m}", m unless method_defined?("regular_#{m}")
33
35
  alias_method m, "indifferent_#{m}"
34
36
  end
35
37
 
36
- %w(include? member? has_key?).each do |key_alias|
38
+ %w[include? member? has_key?].each do |key_alias|
37
39
  alias_method key_alias, :indifferent_key?
38
40
  end
39
41
 
@@ -71,17 +73,17 @@ module Hashie
71
73
  # their proper indifferent state. Used when IndifferentAccess
72
74
  # is injecting itself into member hashes.
73
75
  def convert!
74
- keys.each do |k|
75
- regular_writer convert_key(k), convert_value(regular_delete(k))
76
+ keys.each do |k| # rubocop:disable Performance/HashEachMethods
77
+ regular_writer convert_key(k), indifferent_value(regular_delete(k))
76
78
  end
77
79
  self
78
80
  end
79
81
 
80
- def convert_value(value)
82
+ def indifferent_value(value)
81
83
  if hash_lacking_indifference?(value)
82
- IndifferentAccess.inject(value.dup)
84
+ IndifferentAccess.inject!(value)
83
85
  elsif value.is_a?(::Array)
84
- value.dup.replace(value.map { |e| convert_value(e) })
86
+ value.replace(value.map { |e| indifferent_value(e) })
85
87
  else
86
88
  value
87
89
  end
@@ -100,11 +102,11 @@ module Hashie
100
102
  end
101
103
 
102
104
  def indifferent_writer(key, value)
103
- regular_writer convert_key(key), convert_value(value)
105
+ regular_writer convert_key(key), indifferent_value(value)
104
106
  end
105
107
 
106
- def indifferent_fetch(key, *args)
107
- regular_fetch convert_key(key), *args
108
+ def indifferent_fetch(key, *args, &block)
109
+ regular_fetch convert_key(key), *args, &block
108
110
  end
109
111
 
110
112
  def indifferent_delete(key)
@@ -129,18 +131,28 @@ module Hashie
129
131
  self
130
132
  end
131
133
 
134
+ def merge(*args)
135
+ result = super
136
+ IndifferentAccess.inject!(result) if hash_lacking_indifference?(result)
137
+ result.convert!
138
+ end
139
+
140
+ def merge!(*)
141
+ super.convert!
142
+ end
143
+
132
144
  protected
133
145
 
134
146
  def hash_lacking_indifference?(other)
135
147
  other.is_a?(::Hash) &&
136
- !(other.respond_to?(:indifferent_access?) &&
137
- other.indifferent_access?)
148
+ !(other.respond_to?(:indifferent_access?) &&
149
+ other.indifferent_access?)
138
150
  end
139
151
 
140
152
  def hash_with_indifference?(other)
141
153
  other.is_a?(::Hash) &&
142
- other.respond_to?(:indifferent_access?) &&
143
- other.indifferent_access?
154
+ other.respond_to?(:indifferent_access?) &&
155
+ other.indifferent_access?
144
156
  end
145
157
  end
146
158
  end
@@ -0,0 +1,55 @@
1
+ module Hashie
2
+ module Extensions
3
+ module KeyConflictWarning
4
+ class CannotDisableMashWarnings < StandardError
5
+ def initialize
6
+ super(
7
+ 'You cannot disable warnings on the base Mash class. ' \
8
+ 'Please subclass the Mash and disable it in the subclass.'
9
+ )
10
+ end
11
+ end
12
+
13
+ # Disable the logging of warnings based on keys conflicting keys/methods
14
+ #
15
+ # @api semipublic
16
+ # @return [void]
17
+ def disable_warnings(*method_keys)
18
+ raise CannotDisableMashWarnings if self == Hashie::Mash
19
+ if method_keys.any?
20
+ disabled_warnings.concat(method_keys).tap(&:flatten!).uniq!
21
+ else
22
+ disabled_warnings.clear
23
+ end
24
+
25
+ @disable_warnings = true
26
+ end
27
+
28
+ # Checks whether this class disables warnings for conflicting keys/methods
29
+ #
30
+ # @api semipublic
31
+ # @return [Boolean]
32
+ def disable_warnings?(method_key = nil)
33
+ return disabled_warnings.include?(method_key) if disabled_warnings.any? && method_key
34
+ @disable_warnings ||= false
35
+ end
36
+
37
+ # Returns an array of methods that this class disables warnings for.
38
+ #
39
+ # @api semipublic
40
+ # @return [Boolean]
41
+ def disabled_warnings
42
+ @_disabled_warnings ||= []
43
+ end
44
+
45
+ # Inheritance hook that sets class configuration when inherited.
46
+ #
47
+ # @api semipublic
48
+ # @return [void]
49
+ def inherited(subclass)
50
+ super
51
+ subclass.disable_warnings(disabled_warnings) if disable_warnings?
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,87 +1,5 @@
1
1
  module Hashie
2
2
  module Extensions
3
- module StringifyKeys
4
- # Convert all keys in the hash to strings.
5
- #
6
- # @example
7
- # test = {:abc => 'def'}
8
- # test.stringify_keys!
9
- # test # => {'abc' => 'def'}
10
- def stringify_keys!
11
- keys.each do |k|
12
- stringify_keys_recursively!(self[k])
13
- self[k.to_s] = delete(k)
14
- end
15
- self
16
- end
17
-
18
- # Return a new hash with all keys converted
19
- # to strings.
20
- def stringify_keys
21
- dup.stringify_keys!
22
- end
23
-
24
- protected
25
-
26
- # Stringify all keys recursively within nested
27
- # hashes and arrays.
28
- def stringify_keys_recursively!(object)
29
- if self.class === object
30
- object.stringify_keys!
31
- elsif ::Array === object
32
- object.each do |i|
33
- stringify_keys_recursively!(i)
34
- end
35
- object
36
- elsif object.respond_to?(:stringify_keys!)
37
- object.stringify_keys!
38
- else
39
- object
40
- end
41
- end
42
- end
43
-
44
- module SymbolizeKeys
45
- # Convert all keys in the hash to symbols.
46
- #
47
- # @example
48
- # test = {'abc' => 'def'}
49
- # test.symbolize_keys!
50
- # test # => {:abc => 'def'}
51
- def symbolize_keys!
52
- keys.each do |k|
53
- symbolize_keys_recursively!(self[k])
54
- self[k.to_sym] = delete(k)
55
- end
56
- self
57
- end
58
-
59
- # Return a new hash with all keys converted
60
- # to symbols.
61
- def symbolize_keys
62
- dup.symbolize_keys!
63
- end
64
-
65
- protected
66
-
67
- # Symbolize all keys recursively within nested
68
- # hashes and arrays.
69
- def symbolize_keys_recursively!(object)
70
- if self.class === object
71
- object.symbolize_keys!
72
- elsif ::Array === object
73
- object.each do |i|
74
- symbolize_keys_recursively!(i)
75
- end
76
- object
77
- elsif object.respond_to?(:symbolize_keys!)
78
- object.symbolize_keys!
79
- else
80
- object
81
- end
82
- end
83
- end
84
-
85
3
  module KeyConversion
86
4
  def self.included(base)
87
5
  base.send :include, SymbolizeKeys
@@ -0,0 +1,90 @@
1
+ module Hashie
2
+ module Extensions
3
+ module Mash
4
+ module DefineAccessors
5
+ def self.included(klass)
6
+ klass.class_eval do
7
+ mod = Ext.new
8
+ include mod
9
+ end
10
+ end
11
+
12
+ def self.extended(obj)
13
+ included(obj.singleton_class)
14
+ end
15
+
16
+ class Ext < Module
17
+ def initialize
18
+ mod = self
19
+ define_method(:method_missing) do |method_name, *args, &block|
20
+ key, suffix = method_name_and_suffix(method_name)
21
+ case suffix
22
+ when '='.freeze
23
+ mod.define_writer(key, method_name)
24
+ when '?'.freeze
25
+ mod.define_predicate(key, method_name)
26
+ when '!'.freeze
27
+ mod.define_initializing_reader(key, method_name)
28
+ when '_'.freeze
29
+ mod.define_underbang_reader(key, method_name)
30
+ else
31
+ mod.define_reader(key, method_name)
32
+ end
33
+ send(method_name, *args, &block)
34
+ end
35
+ end
36
+
37
+ def define_reader(key, method_name)
38
+ define_method(method_name) do |&block|
39
+ if key? method_name
40
+ self.[](method_name, &block)
41
+ else
42
+ self.[](key, &block)
43
+ end
44
+ end
45
+ end
46
+
47
+ def define_writer(key, method_name)
48
+ define_method(method_name) do |value = nil|
49
+ if key? method_name
50
+ self.[](method_name, &proc)
51
+ else
52
+ assign_property(key, value)
53
+ end
54
+ end
55
+ end
56
+
57
+ def define_predicate(key, method_name)
58
+ define_method(method_name) do
59
+ if key? method_name
60
+ self.[](method_name, &proc)
61
+ else
62
+ !!self[key]
63
+ end
64
+ end
65
+ end
66
+
67
+ def define_initializing_reader(key, method_name)
68
+ define_method(method_name) do
69
+ if key? method_name
70
+ self.[](method_name, &proc)
71
+ else
72
+ initializing_reader(key)
73
+ end
74
+ end
75
+ end
76
+
77
+ def define_underbang_reader(key, method_name)
78
+ define_method(method_name) do
79
+ if key? method_name
80
+ self.[](key, &proc)
81
+ else
82
+ underbang_reader(key)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,53 @@
1
+ module Hashie
2
+ module Extensions
3
+ module Mash
4
+ # Overrides the indifferent access of a Mash to keep keys in the
5
+ # original format given to the Mash.
6
+ #
7
+ # @example
8
+ # class KeepingMash < Hashie::Mash
9
+ # include Hashie::Extensions::Mash::KeepOriginalKeys
10
+ # end
11
+ #
12
+ # mash = KeepingMash.new(:symbol_key => :symbol, 'string_key' => 'string')
13
+ # mash.to_hash #=> { :symbol_key => :symbol, 'string_key' => 'string' }
14
+ # mash['string_key'] == mash[:string_key] #=> true
15
+ # mash[:symbol_key] == mash['symbol_key'] #=> true
16
+ module KeepOriginalKeys
17
+ def self.included(descendant)
18
+ error_message = "#{descendant} is not a kind of Hashie::Mash"
19
+ raise ArgumentError, error_message unless descendant <= Hashie::Mash
20
+ end
21
+
22
+ private
23
+
24
+ # Converts the key when necessary to access the correct Mash key.
25
+ #
26
+ # @param [Object, String, Symbol] key the key to access.
27
+ # @return [Object] the value assigned to the key.
28
+ def convert_key(key)
29
+ if regular_key?(key)
30
+ key
31
+ elsif (converted_key = __convert(key)) && regular_key?(converted_key)
32
+ converted_key
33
+ else
34
+ key
35
+ end
36
+ end
37
+
38
+ # Converts symbol/string keys to their alternative formats, but leaves
39
+ # other keys alone.
40
+ #
41
+ # @param [Object, String, Symbol] key the key to convert.
42
+ # @return [Object, String, Symbol] the converted key.
43
+ def __convert(key)
44
+ case key
45
+ when Symbol then key.to_s
46
+ when String then key.to_sym
47
+ else key
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end