hashie 1.2.0 → 2.0.0

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