hashie-pre 2.0.0.beta

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,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)
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)
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)
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
@@ -0,0 +1,31 @@
1
+ require 'hashie/hash_extensions'
2
+
3
+ module Hashie
4
+ # A Hashie Hash is simply a Hash that has convenience
5
+ # functions baked in such as stringify_keys that may
6
+ # not be available in all libraries.
7
+ class Hash < ::Hash
8
+ include Hashie::HashExtensions
9
+
10
+ # Converts a mash back to a hash (with stringified keys)
11
+ def to_hash
12
+ out = {}
13
+ keys.each do |k|
14
+ if self[k].is_a?(Array)
15
+ out[k] ||= []
16
+ self[k].each do |array_object|
17
+ out[k] << (Hashie::Hash === array_object ? array_object.to_hash : array_object)
18
+ end
19
+ else
20
+ out[k] = Hashie::Hash === self[k] ? self[k].to_hash : self[k]
21
+ end
22
+ end
23
+ out
24
+ end
25
+
26
+ # The C geneartor for the json gem doesn't like mashies
27
+ def to_json(*args)
28
+ to_hash.to_json(*args)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ module Hashie
2
+ module HashExtensions
3
+ def self.included(base)
4
+ # Don't tread on existing extensions of Hash by
5
+ # adding methods that are likely to exist.
6
+ %w(stringify_keys stringify_keys!).each do |hashie_method|
7
+ base.send :alias_method, hashie_method, "hashie_#{hashie_method}" unless base.instance_methods.include?(hashie_method)
8
+ end
9
+ end
10
+
11
+ # Destructively convert all of the keys of a Hash
12
+ # to their string representations.
13
+ def hashie_stringify_keys!
14
+ self.keys.each do |k|
15
+ unless String === k
16
+ self[k.to_s] = self.delete(k)
17
+ end
18
+ end
19
+ self
20
+ end
21
+
22
+ # Convert all of the keys of a Hash
23
+ # to their string representations.
24
+ def hashie_stringify_keys
25
+ self.dup.stringify_keys!
26
+ end
27
+
28
+ # Convert this hash into a Mash
29
+ def to_mash
30
+ ::Hashie::Mash.new(self)
31
+ end
32
+ end
33
+
34
+ module PrettyInspect
35
+ def self.included(base)
36
+ base.send :alias_method, :hash_inspect, :inspect
37
+ base.send :alias_method, :inspect, :hashie_inspect
38
+ end
39
+
40
+ def hashie_inspect
41
+ ret = "#<#{self.class.to_s}"
42
+ stringify_keys.keys.sort.each do |key|
43
+ ret << " #{key}=#{self[key].inspect}"
44
+ end
45
+ ret << ">"
46
+ ret
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,216 @@
1
+ require 'hashie/hash'
2
+
3
+ module Hashie
4
+ # Mash allows you to create pseudo-objects that have method-like
5
+ # accessors for hash keys. This is useful for such implementations
6
+ # as an API-accessing library that wants to fake robust objects
7
+ # without the overhead of actually doing so. Think of it as OpenStruct
8
+ # with some additional goodies.
9
+ #
10
+ # A Mash will look at the methods you pass it and perform operations
11
+ # based on the following rules:
12
+ #
13
+ # * No punctuation: Returns the value of the hash for that key, or nil if none exists.
14
+ # * Assignment (<tt>=</tt>): Sets the attribute of the given method name.
15
+ # * Existence (<tt>?</tt>): Returns true or false depending on whether that key has been set.
16
+ # * Bang (<tt>!</tt>): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes.
17
+ # * Under Bang (<tt>_</tt>): Like Bang, but returns a new Mash rather than creating a key. Used to test existance in deep Mashes.
18
+ #
19
+ # == Basic Example
20
+ #
21
+ # mash = Mash.new
22
+ # mash.name? # => false
23
+ # mash.name = "Bob"
24
+ # mash.name # => "Bob"
25
+ # mash.name? # => true
26
+ #
27
+ # == Hash Conversion Example
28
+ #
29
+ # hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 44, :h => 29}, 12]}
30
+ # mash = Mash.new(hash)
31
+ # mash.a.b # => 23
32
+ # mash.a.d.e # => "abc"
33
+ # mash.f.first.g # => 44
34
+ # mash.f.last # => 12
35
+ #
36
+ # == Bang Example
37
+ #
38
+ # mash = Mash.new
39
+ # mash.author # => nil
40
+ # mash.author! # => <Mash>
41
+ #
42
+ # mash = Mash.new
43
+ # mash.author!.name = "Michael Bleigh"
44
+ # mash.author # => <Mash name="Michael Bleigh">
45
+ #
46
+ # == Under Bang Example
47
+ #
48
+ # mash = Mash.new
49
+ # mash.author # => nil
50
+ # mash.author_ # => <Mash>
51
+ # mash.author_.name # => nil
52
+ #
53
+ # mash = Mash.new
54
+ # mash.author_.name = "Michael Bleigh" (assigned to temp object)
55
+ # mash.author # => <Mash>
56
+ #
57
+ class Mash < Hashie::Hash
58
+ include Hashie::PrettyInspect
59
+ alias_method :to_s, :inspect
60
+
61
+ # If you pass in an existing hash, it will
62
+ # convert it to a Mash including recursively
63
+ # descending into arrays and hashes, converting
64
+ # them as well.
65
+ def initialize(source_hash = nil, default = nil, &blk)
66
+ deep_update(source_hash) if source_hash
67
+ default ? super(default) : super(&blk)
68
+ end
69
+
70
+ class << self; alias [] new; end
71
+
72
+ def id #:nodoc:
73
+ key?("id") ? self["id"] : super
74
+ end
75
+
76
+ def type #:nodoc:
77
+ key?("type") ? self["type"] : super
78
+ end
79
+
80
+ alias_method :regular_reader, :[]
81
+ alias_method :regular_writer, :[]=
82
+
83
+ # Retrieves an attribute set in the Mash. Will convert
84
+ # any key passed in to a string before retrieving.
85
+ def [](key)
86
+ value = regular_reader(convert_key(key))
87
+ yield value if block_given?
88
+ value
89
+ end
90
+
91
+ # Sets an attribute in the Mash. Key will be converted to
92
+ # a string before it is set, and Hashes will be converted
93
+ # into Mashes for nesting purposes.
94
+ def []=(key,value) #:nodoc:
95
+ regular_writer(convert_key(key), convert_value(value))
96
+ end
97
+
98
+ # This is the bang method reader, it will return a new Mash
99
+ # if there isn't a value already assigned to the key requested.
100
+ def initializing_reader(key)
101
+ ck = convert_key(key)
102
+ regular_writer(ck, self.class.new) unless key?(ck)
103
+ regular_reader(ck)
104
+ end
105
+
106
+ # This is the under bang method reader, it will return a temporary new Mash
107
+ # if there isn't a value already assigned to the key requested.
108
+ def underbang_reader(key)
109
+ ck = convert_key(key)
110
+ if key?(ck)
111
+ regular_reader(ck)
112
+ else
113
+ self.class.new
114
+ end
115
+ end
116
+
117
+ def delete(key)
118
+ super(convert_key(key))
119
+ end
120
+
121
+ alias_method :regular_dup, :dup
122
+ # Duplicates the current mash as a new mash.
123
+ def dup
124
+ self.class.new(self, self.default)
125
+ end
126
+
127
+ def key?(key)
128
+ super(convert_key(key))
129
+ end
130
+ alias_method :has_key?, :key?
131
+ alias_method :include?, :key?
132
+ alias_method :member?, :key?
133
+
134
+ # Performs a deep_update on a duplicate of the
135
+ # current mash.
136
+ def deep_merge(other_hash)
137
+ dup.deep_update(other_hash)
138
+ end
139
+ alias_method :merge, :deep_merge
140
+
141
+ # Recursively merges this mash with the passed
142
+ # in hash, merging each hash in the hierarchy.
143
+ def deep_update(other_hash)
144
+ other_hash.each_pair do |k,v|
145
+ key = convert_key(k)
146
+ if regular_reader(key).is_a?(Mash) and v.is_a?(::Hash)
147
+ regular_reader(key).deep_update(v)
148
+ else
149
+ regular_writer(key, convert_value(v, true))
150
+ end
151
+ end
152
+ self
153
+ end
154
+ alias_method :deep_merge!, :deep_update
155
+ alias_method :update, :deep_update
156
+ alias_method :merge!, :update
157
+
158
+ # Performs a shallow_update on a duplicate of the current mash
159
+ def shallow_merge(other_hash)
160
+ dup.shallow_update(other_hash)
161
+ end
162
+
163
+ # Merges (non-recursively) the hash from the argument,
164
+ # changing the receiving hash
165
+ def shallow_update(other_hash)
166
+ other_hash.each_pair do |k,v|
167
+ regular_writer(convert_key(k), convert_value(v, true))
168
+ end
169
+ self
170
+ end
171
+
172
+ # Will return true if the Mash has had a key
173
+ # set in addition to normal respond_to? functionality.
174
+ def respond_to?(method_name, include_private=false)
175
+ return true if key?(method_name)
176
+ super
177
+ end
178
+
179
+ def method_missing(method_name, *args, &blk)
180
+ return self.[](method_name, &blk) if key?(method_name)
181
+ match = method_name.to_s.match(/(.*?)([?=!_]?)$/)
182
+ case match[2]
183
+ when "="
184
+ self[match[1]] = args.first
185
+ when "?"
186
+ !!self[match[1]]
187
+ when "!"
188
+ initializing_reader(match[1])
189
+ when "_"
190
+ underbang_reader(match[1])
191
+ else
192
+ default(method_name, *args, &blk)
193
+ end
194
+ end
195
+
196
+ protected
197
+
198
+ def convert_key(key) #:nodoc:
199
+ key.to_s
200
+ end
201
+
202
+ def convert_value(val, duping=false) #:nodoc:
203
+ case val
204
+ when self.class
205
+ val.dup
206
+ when ::Hash
207
+ val = val.dup if duping
208
+ self.class.new(val)
209
+ when Array
210
+ val.collect{ |e| convert_value(e) }
211
+ else
212
+ val
213
+ end
214
+ end
215
+ end
216
+ end