|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
1 |
# Mash allows you to create pseudo-objects that have method-like |
| |
2 |
# accessors for hash keys. This is useful for such implementations |
| |
3 |
# as an API-accessing library that wants to fake robust objects |
| |
4 |
# without the overhead of actually doing so. Think of it as OpenStruct |
| |
5 |
# with some additional goodies. |
| |
6 |
# |
|
8d5f3861
»
|
mbleigh |
2008-04-12 |
Adding bang method support ... |
7 |
# A Mash will look at the methods you pass it and perform operations |
| |
8 |
# based on the following rules: |
| |
9 |
# |
| |
10 |
# * No punctuation: Returns the value of the hash for that key, or nil if none exists. |
| |
11 |
# * Assignment (<tt>=</tt>): Sets the attribute of the given method name. |
| |
12 |
# * Existence (<tt>?</tt>): Returns true or false depending on whether that key has been set. |
| |
13 |
# * Bang (<tt>!</tt>): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes. |
| |
14 |
# |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
15 |
# == Basic Example |
| |
16 |
# |
| |
17 |
# mash = Mash.new |
| |
18 |
# mash.name? # => false |
| |
19 |
# mash.name = "Bob" |
| |
20 |
# mash.name # => "Bob" |
| |
21 |
# mash.name? # => true |
| |
22 |
# |
| |
23 |
# == Hash Conversion Example |
| |
24 |
# |
| |
25 |
# hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 44, :h => 29}, 12]} |
| |
26 |
# mash = Mash.new(hash) |
| |
27 |
# mash.a.b # => 23 |
| |
28 |
# mash.a.d.e # => "abc" |
| |
29 |
# mash.f.first.g # => 44 |
| |
30 |
# mash.f.last # => 12 |
| |
31 |
# |
|
8d5f3861
»
|
mbleigh |
2008-04-12 |
Adding bang method support ... |
32 |
# == Bang Example |
| |
33 |
# |
| |
34 |
# mash = Mash.new |
| |
35 |
# mash.author # => nil |
| |
36 |
# mash.author! # => <Mash> |
| |
37 |
# |
| |
38 |
# mash = Mash.new |
| |
39 |
# mash.author!.name = "Michael Bleigh" |
| |
40 |
# mash.author # => <Mash name="Michael Bleigh"> |
| |
41 |
# |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
42 |
class Mash < Hash |
|
76360801
»
|
mbleigh |
2008-04-16 |
[0.0.3] - added aliases to ... |
43 |
VERSION = '0.0.3' |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
44 |
|
| |
45 |
# If you pass in an existing hash, it will |
| |
46 |
# convert it to a Mash including recursively |
| |
47 |
# descending into arrays and hashes, converting |
| |
48 |
# them as well. |
| |
49 |
def initialize(source_hash = nil) |
|
63d23cf2
»
|
mbleigh |
2008-04-19 |
- Substantial cleanup of ex...  |
50 |
deep_update(source_hash) if source_hash |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
51 |
super(nil) |
| |
52 |
end |
| |
53 |
|
| |
54 |
def id #:nodoc: |
| |
55 |
self["id"] ? self["id"] : super |
| |
56 |
end |
| |
57 |
|
|
63d23cf2
»
|
mbleigh |
2008-04-19 |
- Substantial cleanup of ex...  |
58 |
# Borrowed from Merb's Mash object. |
| |
59 |
# |
| |
60 |
# ==== Parameters |
| |
61 |
# key<Object>:: The default value for the mash. Defaults to nil. |
| |
62 |
# |
| |
63 |
# ==== Alternatives |
| |
64 |
# If key is a Symbol and it is a key in the mash, then the default value will |
| |
65 |
# be set to the value matching the key. |
| |
66 |
def default(key = nil) |
|
66eaeec9
»
|
mbleigh |
2008-04-19 |
Updated history, yield defa... |
67 |
if key.is_a?(Symbol) && key?(key) |
|
63d23cf2
»
|
mbleigh |
2008-04-19 |
- Substantial cleanup of ex...  |
68 |
self[key] |
| |
69 |
else |
| |
70 |
super |
| |
71 |
end |
| |
72 |
end |
| |
73 |
|
|
76360801
»
|
mbleigh |
2008-04-16 |
[0.0.3] - added aliases to ... |
74 |
alias_method :regular_reader, :[] |
| |
75 |
alias_method :regular_writer, :[]= |
| |
76 |
|
| |
77 |
# Retrieves an attribute set in the Mash. Will convert |
| |
78 |
# any key passed in to a string before retrieving. |
| |
79 |
def [](key) |
| |
80 |
key = convert_key(key) |
| |
81 |
regular_reader(key) |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
82 |
end |
| |
83 |
|
|
76360801
»
|
mbleigh |
2008-04-16 |
[0.0.3] - added aliases to ... |
84 |
# Sets an attribute in the Mash. Key will be converted to |
| |
85 |
# a string before it is set. |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
86 |
def []=(key,value) #:nodoc: |
|
63d23cf2
»
|
mbleigh |
2008-04-19 |
- Substantial cleanup of ex...  |
87 |
key = convert_key(key) |
| |
88 |
regular_writer(key,convert_value(value)) |
|
76360801
»
|
mbleigh |
2008-04-16 |
[0.0.3] - added aliases to ... |
89 |
end |
| |
90 |
|
| |
91 |
# This is the bang method reader, it will return a new Mash |
| |
92 |
# if there isn't a value already assigned to the key requested. |
| |
93 |
def initializing_reader(key) |
| |
94 |
return self[key] if key?(key) |
| |
95 |
self[key] = Mash.new |
| |
96 |
end |
| |
97 |
|
|
63d23cf2
»
|
mbleigh |
2008-04-19 |
- Substantial cleanup of ex...  |
98 |
# Duplicates the current mash as a new mash. |
| |
99 |
def dup |
| |
100 |
Mash.new(self) |
| |
101 |
end |
| |
102 |
|
|
76360801
»
|
mbleigh |
2008-04-16 |
[0.0.3] - added aliases to ... |
103 |
alias_method :regular_inspect, :inspect |
| |
104 |
|
| |
105 |
alias_method :picky_key?, :key? |
| |
106 |
def key?(key) |
| |
107 |
picky_key?(convert_key(key)) |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
108 |
end |
| |
109 |
|
| |
110 |
# Prints out a pretty object-like string of the |
| |
111 |
# defined attributes. |
| |
112 |
def inspect |
| |
113 |
ret = "<#{self.class.to_s}" |
| |
114 |
keys.sort.each do |key| |
| |
115 |
ret << " #{key}=#{self[key].inspect}" |
| |
116 |
end |
| |
117 |
ret << ">" |
| |
118 |
ret |
| |
119 |
end |
|
76360801
»
|
mbleigh |
2008-04-16 |
[0.0.3] - added aliases to ... |
120 |
alias_method :to_s, :inspect |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
121 |
|
|
63d23cf2
»
|
mbleigh |
2008-04-19 |
- Substantial cleanup of ex...  |
122 |
# Performs a deep_update on a duplicate of the |
| |
123 |
# current mash. |
| |
124 |
def deep_merge(other_hash) |
| |
125 |
dup.deep_merge!(other_hash) |
| |
126 |
end |
| |
127 |
|
| |
128 |
# Recursively merges this mash with the passed |
| |
129 |
# in hash, merging each hash in the hierarchy. |
| |
130 |
def deep_update(other_hash) |
| |
131 |
stringified_hash = other_hash.stringify_keys |
| |
132 |
stringified_hash.each_pair do |k,v| |
| |
133 |
k = convert_key(k) |
| |
134 |
self[k] = self[k].to_mash if self[k].is_a?(Hash) unless self[k].is_a?(Mash) |
| |
135 |
if self[k].is_a?(Hash) && stringified_hash[k].is_a?(Hash) |
| |
136 |
self[k].deep_merge!(stringified_hash[k]) |
| |
137 |
else |
| |
138 |
self.send(k + "=", convert_value(stringified_hash[k])) |
| |
139 |
end |
| |
140 |
end |
| |
141 |
end |
| |
142 |
alias_method :deep_merge!, :deep_update |
| |
143 |
|
| |
144 |
# ==== Parameters |
| |
145 |
# other_hash<Hash>:: |
| |
146 |
# A hash to update values in the mash with. Keys will be |
| |
147 |
# stringified and Hashes will be converted to Mashes. |
| |
148 |
# |
| |
149 |
# ==== Returns |
| |
150 |
# Mash:: The updated mash. |
| |
151 |
def update(other_hash) |
| |
152 |
other_hash.each_pair do |key, value| |
| |
153 |
if respond_to?(convert_key(key) + "=") |
| |
154 |
self.send(convert_key(key) + "=", convert_value(value)) |
| |
155 |
else |
| |
156 |
regular_writer(convert_key(key), convert_value(value)) |
| |
157 |
end |
| |
158 |
end |
| |
159 |
self |
| |
160 |
end |
| |
161 |
alias_method :merge!, :update |
| |
162 |
|
| |
163 |
# Converts a mash back to a hash (with stringified keys) |
| |
164 |
def to_hash |
| |
165 |
Hash.new(default).merge(self) |
| |
166 |
end |
| |
167 |
|
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
168 |
def method_missing(method_name, *args) #:nodoc: |
| |
169 |
if (match = method_name.to_s.match(/(.*)=$/)) && args.size == 1 |
| |
170 |
self[match[1]] = args.first |
| |
171 |
elsif (match = method_name.to_s.match(/(.*)\?$/)) && args.size == 0 |
| |
172 |
key?(match[1]) |
|
8d5f3861
»
|
mbleigh |
2008-04-12 |
Adding bang method support ... |
173 |
elsif (match = method_name.to_s.match(/(.*)!$/)) && args.size == 0 |
|
76360801
»
|
mbleigh |
2008-04-16 |
[0.0.3] - added aliases to ... |
174 |
initializing_reader(match[1]) |
|
66eaeec9
»
|
mbleigh |
2008-04-19 |
Updated history, yield defa... |
175 |
elsif key?(method_name) |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
176 |
self[method_name] |
| |
177 |
elsif match = method_name.to_s.match(/^([a-z][a-z0-9A-Z_]+)$/) |
|
66eaeec9
»
|
mbleigh |
2008-04-19 |
Updated history, yield defa... |
178 |
default |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
179 |
else |
| |
180 |
super |
| |
181 |
end |
| |
182 |
end |
| |
183 |
|
|
8d5f3861
»
|
mbleigh |
2008-04-12 |
Adding bang method support ... |
184 |
protected |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
185 |
|
|
76360801
»
|
mbleigh |
2008-04-16 |
[0.0.3] - added aliases to ... |
186 |
def convert_key(key) #:nodoc: |
| |
187 |
key.to_s |
| |
188 |
end |
| |
189 |
|
|
63d23cf2
»
|
mbleigh |
2008-04-19 |
- Substantial cleanup of ex...  |
190 |
def convert_value(value) #:nodoc: |
| |
191 |
case value |
| |
192 |
when Hash |
| |
193 |
value.is_a?(Mash) ? value : value.to_mash |
| |
194 |
when Array |
| |
195 |
value.collect{ |e| convert_value(e) } |
| |
196 |
else |
| |
197 |
value |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
198 |
end |
| |
199 |
end |
|
76360801
»
|
mbleigh |
2008-04-16 |
[0.0.3] - added aliases to ... |
200 |
end |
| |
201 |
|
| |
202 |
class Hash |
| |
203 |
# Returns a new Mash initialized from this Hash. |
| |
204 |
def to_mash |
| |
205 |
mash = Mash.new(self) |
| |
206 |
mash.default = default |
| |
207 |
mash |
| |
208 |
end |
|
63d23cf2
»
|
mbleigh |
2008-04-19 |
- Substantial cleanup of ex...  |
209 |
|
| |
210 |
# Returns a duplicate of the current hash with |
| |
211 |
# all of the keys converted to strings. |
| |
212 |
def stringify_keys |
| |
213 |
dup.stringify_keys! |
| |
214 |
end |
| |
215 |
|
| |
216 |
# Converts all of the keys to strings |
| |
217 |
def stringify_keys! |
| |
218 |
keys.each{|k| |
| |
219 |
v = delete(k) |
| |
220 |
self[k.to_s] = v |
| |
221 |
v.stringify_keys! if v.is_a?(Hash) |
| |
222 |
v.each{|p| p.stringify_keys! if p.is_a?(Hash)} if v.is_a?(Array) |
| |
223 |
} |
| |
224 |
self |
| |
225 |
end |
|
b7d18541
»
|
mbleigh |
2008-04-12 |
Initial import. |
226 |
end |