hashie 1.2.0 → 2.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.
@@ -0,0 +1,21 @@
1
+ module Hashie
2
+ module Extensions
3
+ module DeepMerge
4
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
5
+ def deep_merge(other_hash)
6
+ (class << (h = dup); self; end).send :include, Hashie::Extensions::DeepMerge
7
+ h.deep_merge!(other_hash)
8
+ end
9
+
10
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
11
+ # Modifies the receiver in place.
12
+ def deep_merge!(other_hash)
13
+ other_hash.each do |k,v|
14
+ (class << (tv = self[k]); self; end).send :include, Hashie::Extensions::DeepMerge
15
+ self[k] = tv.is_a?(::Hash) && v.is_a?(::Hash) ? tv.deep_merge(v) : v
16
+ end
17
+ self
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,114 @@
1
+ module Hashie
2
+ module Extensions
3
+ # IndifferentAccess gives you the ability to not care
4
+ # whether your hash has string or symbol keys. Made famous
5
+ # in Rails for accessing query and POST parameters, this
6
+ # is a handy tool for making sure your hash has maximum
7
+ # utility.
8
+ #
9
+ # One unique feature of this mixin is that it will recursively
10
+ # inject itself into sub-hash instances without modifying
11
+ # the actual class of the sub-hash.
12
+ #
13
+ # @example
14
+ # class MyHash < Hash
15
+ # include Hashie::Extensions::MergeInitializer
16
+ # include Hashie::Extensions::IndifferentAccess
17
+ # end
18
+ #
19
+ # h = MyHash.new(:foo => 'bar', 'baz' => 'blip')
20
+ # h['foo'] # => 'bar'
21
+ # h[:foo] # => 'bar'
22
+ # h[:baz] # => 'blip'
23
+ # h['baz'] # => 'blip'
24
+ #
25
+ module IndifferentAccess
26
+ def self.included(base)
27
+ base.class_eval do
28
+ alias_method :regular_writer, :[]=
29
+ alias_method :[]=, :indifferent_writer
30
+ %w(default update fetch delete key? values_at).each do |m|
31
+ alias_method "regular_#{m}", m
32
+ alias_method m, "indifferent_#{m}"
33
+ end
34
+
35
+ %w(include? member? has_key?).each do |key_alias|
36
+ alias_method key_alias, :indifferent_key?
37
+ end
38
+ end
39
+ end
40
+
41
+ # This will inject indifferent access into an instance of
42
+ # a hash without modifying the actual class. This is what
43
+ # allows IndifferentAccess to spread to sub-hashes.
44
+ def self.inject!(hash)
45
+ (class << hash; self; end).send :include, IndifferentAccess
46
+ hash.convert!
47
+ end
48
+
49
+ # Injects indifferent access into a duplicate of the hash
50
+ # provided. See #inject!
51
+ def self.inject(hash)
52
+ inject!(hash.dup)
53
+ end
54
+
55
+ def convert_key(key)
56
+ key.to_s
57
+ end
58
+
59
+ # Iterates through the keys and values, reconverting them to
60
+ # their proper indifferent state. Used when IndifferentAccess
61
+ # is injecting itself into member hashes.
62
+ def convert!
63
+ keys.each do |k|
64
+ regular_writer convert_key(k), convert_value(self.regular_delete(k))
65
+ end
66
+ self
67
+ end
68
+
69
+ def convert_value(value)
70
+ if hash_lacking_indifference?(value)
71
+ IndifferentAccess.inject(value.dup)
72
+ elsif value.is_a?(::Array)
73
+ value.dup.replace(value.map { |e| convert_value(e) })
74
+ else
75
+ value
76
+ end
77
+ end
78
+
79
+ def indifferent_default(key = nil)
80
+ return self[convert_key(key)] if key?(key)
81
+ regular_default(key)
82
+ end
83
+
84
+ def indifferent_update(other_hash)
85
+ return regular_update(other_hash) if hash_with_indifference?(other_hash)
86
+ other_hash.each_pair do |k,v|
87
+ self[k] = v
88
+ end
89
+ end
90
+
91
+ def indifferent_writer(key, value); regular_writer convert_key(key), convert_value(value) end
92
+ def indifferent_fetch(key, *args); regular_fetch convert_key(key), *args end
93
+ def indifferent_delete(key); regular_delete convert_key(key) end
94
+ def indifferent_key?(key); regular_key? convert_key(key) end
95
+ def indifferent_values_at(*indices); indices.map{|i| self[i] } end
96
+
97
+ def indifferent_access?; true end
98
+
99
+ protected
100
+
101
+ def hash_lacking_indifference?(other)
102
+ other.is_a?(::Hash) &&
103
+ !(other.respond_to?(:indifferent_access?) &&
104
+ other.indifferent_access?)
105
+ end
106
+
107
+ def hash_with_indifference?(other)
108
+ other.is_a?(::Hash) &&
109
+ other.respond_to?(:indifferent_access?) &&
110
+ other.indifferent_access?
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,92 @@
1
+ module Hashie
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] = self.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] = self.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
+ module KeyConversion
86
+ def self.included(base)
87
+ base.send :include, SymbolizeKeys
88
+ base.send :include, StringifyKeys
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,26 @@
1
+ module Hashie
2
+ module Extensions
3
+ # The MergeInitializer is a super-simple mixin that allows
4
+ # you to initialize a subclass of Hash with another Hash
5
+ # to give you faster startup time for Hash subclasses. Note
6
+ # that you can still provide a default value as a second
7
+ # argument to the initializer.
8
+ #
9
+ # @example
10
+ # class MyHash < Hash
11
+ # include Hashie::Extensions::MergeInitializer
12
+ # end
13
+ #
14
+ # h = MyHash.new(:abc => 'def')
15
+ # h[:abc] # => 'def'
16
+ #
17
+ module MergeInitializer
18
+ def initialize(hash = {}, default = nil, &block)
19
+ default ? super(default) : super(&block)
20
+ hash.each do |key, value|
21
+ self[key] = value
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,124 @@
1
+ module Hashie
2
+ module Extensions
3
+ # MethodReader allows you to access keys of the hash
4
+ # via method calls. This gives you an OStruct like way
5
+ # to access your hash's keys. It will recognize keys
6
+ # either as strings or symbols.
7
+ #
8
+ # Note that while nil keys will be returned as nil,
9
+ # undefined keys will raise NoMethodErrors. Also note that
10
+ # #respond_to? has been patched to appropriately recognize
11
+ # key methods.
12
+ #
13
+ # @example
14
+ # class User < Hash
15
+ # include Hashie::Extensions::MethodReader
16
+ # end
17
+ #
18
+ # user = User.new
19
+ # user['first_name'] = 'Michael'
20
+ # user.first_name # => 'Michael'
21
+ #
22
+ # user[:last_name] = 'Bleigh'
23
+ # user.last_name # => 'Bleigh'
24
+ #
25
+ # user[:birthday] = nil
26
+ # user.birthday # => nil
27
+ #
28
+ # user.not_declared # => NoMethodError
29
+ module MethodReader
30
+ def respond_to?(name, include_private = false)
31
+ return true if key?(name.to_s) || key?(name.to_sym)
32
+ super
33
+ end
34
+
35
+ def method_missing(name, *args)
36
+ return self[name.to_s] if key?(name.to_s)
37
+ return self[name.to_sym] if key?(name.to_sym)
38
+ super
39
+ end
40
+ end
41
+
42
+ # MethodWriter gives you #key_name= shortcuts for
43
+ # writing to your hash. Keys are written as strings,
44
+ # override #convert_key if you would like to have symbols
45
+ # or something else.
46
+ #
47
+ # Note that MethodWriter also overrides #respond_to such
48
+ # that any #method_name= will respond appropriately as true.
49
+ #
50
+ # @example
51
+ # class MyHash < Hash
52
+ # include Hashie::Extensions::MethodWriter
53
+ # end
54
+ #
55
+ # h = MyHash.new
56
+ # h.awesome = 'sauce'
57
+ # h['awesome'] # => 'sauce'
58
+ #
59
+ module MethodWriter
60
+ def respond_to?(name, include_private = false)
61
+ return true if name.to_s =~ /=$/
62
+ super
63
+ end
64
+
65
+ def method_missing(name, *args)
66
+ if args.size == 1 && name.to_s =~ /(.*)=$/
67
+ return self[convert_key($1)] = args.first
68
+ end
69
+
70
+ super
71
+ end
72
+
73
+ def convert_key(key)
74
+ key.to_s
75
+ end
76
+ end
77
+
78
+ # MethodQuery gives you the ability to check for the truthiness
79
+ # of a key via method calls. Note that it will return false if
80
+ # the key is set to a non-truthful value, not if the key isn't
81
+ # set at all. Use #key? for checking if a key has been set.
82
+ #
83
+ # MethodQuery will check against both string and symbol names
84
+ # of the method for existing keys. It also patches #respond_to
85
+ # to appropriately detect the query methods.
86
+ #
87
+ # @example
88
+ # class MyHash < Hash
89
+ # include Hashie::Extensions::MethodQuery
90
+ # end
91
+ #
92
+ # h = MyHash.new
93
+ # h['abc'] = 123
94
+ # h.abc? # => true
95
+ # h['def'] = nil
96
+ # h.def? # => false
97
+ # h.hji? # => NoMethodError
98
+ module MethodQuery
99
+ def respond_to?(name, include_private = false)
100
+ return true if name.to_s =~ /(.*)\?$/ && (key?($1) || key?($1.to_sym))
101
+ super
102
+ end
103
+
104
+ def method_missing(name, *args)
105
+ if args.empty? && name.to_s =~ /(.*)\?$/ && (key?($1) || key?($1.to_sym))
106
+ return self[$1] || self[$1.to_sym]
107
+ end
108
+
109
+ super
110
+ end
111
+ end
112
+
113
+ # A macro module that will automatically include MethodReader,
114
+ # MethodWriter, and MethodQuery, giving you the ability to read,
115
+ # write, and query keys in a hash using method call shortcuts.
116
+ module MethodAccess
117
+ def self.included(base)
118
+ [MethodReader, MethodWriter, MethodQuery].each do |mod|
119
+ base.send :include, mod
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,47 @@
1
+ module Hashie
2
+ module Extensions
3
+ # The Structure extension provides facilities for declaring
4
+ # properties that a Hash can have. This provides for the
5
+ # creation of structures that still behave like hashes but
6
+ # do not allow setting non-allowed keys.
7
+ #
8
+ # @example
9
+ # class RestrictedHash < Hash
10
+ # include Hashie::Extensions::MergeInitializer
11
+ # include Hashie::Extensions::Structure
12
+ #
13
+ # key :first
14
+ # key :second, :default => 'foo'
15
+ # end
16
+ #
17
+ # h = RestrictedHash.new(:first => 1)
18
+ # h[:first] # => 1
19
+ # h[:second] # => 'foo'
20
+ # h[:third] # => ArgumentError
21
+ #
22
+ module Structure
23
+ def self.included(base)
24
+ base.extend ClassMethods
25
+ base.class_eval do
26
+ @permitted_keys = superclass.permitted_keys if superclass.respond_to?(:permitted_keys)
27
+ end
28
+ end
29
+
30
+ module ClassMethods
31
+ def key(key, options = {})
32
+ (@permitted_keys ||= []) << key
33
+
34
+ if options[:default]
35
+ (@default_values ||= {})[key] = options.delete(:default)
36
+ end
37
+
38
+ permitted_keys
39
+ end
40
+
41
+ def permitted_keys
42
+ @permitted_keys
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -4,15 +4,21 @@ module Hashie
4
4
  # A Hashie Hash is simply a Hash that has convenience
5
5
  # functions baked in such as stringify_keys that may
6
6
  # not be available in all libraries.
7
- class Hash < Hash
8
- include Hashie::HashExtensions
7
+ class Hash < ::Hash
8
+ include HashExtensions
9
9
 
10
- # Converts a mash back to a hash.
11
- def to_hash(options = {})
10
+ # Converts a mash back to a hash (with stringified keys)
11
+ def to_hash
12
12
  out = {}
13
13
  keys.each do |k|
14
- key = options[:symbolize_keys] ? k.to_sym : k.to_s
15
- out[key] = Hashie::Hash === self[k] ? self[k].to_hash : self[k]
14
+ if self[k].is_a?(Array)
15
+ out[k] ||= []
16
+ self[k].each do |array_object|
17
+ out[k] << (Hash === array_object ? array_object.to_hash : array_object)
18
+ end
19
+ else
20
+ out[k] = Hash === self[k] ? self[k].to_hash : self[k]
21
+ end
16
22
  end
17
23
  out
18
24
  end