mash 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/History.txt +9 -0
  2. data/README.txt +1 -1
  3. data/lib/mash.rb +134 -37
  4. data/spec/mash_spec.rb +34 -0
  5. metadata +3 -3
@@ -1,3 +1,12 @@
1
+ === 0.0.3 / 2008-04-19
2
+
3
+ * [] no longer defaults to a new Mash, will return nil if
4
+ * Attribute-esque method names will yield the default value if not set
5
+ * Hash extended with #to_mash and #stringify_keys
6
+ * Added #dup and #deep_merge
7
+ * Aliased the default Hash methods so they are still accessible
8
+ * Cleaned up the recursive conversion process
9
+
1
10
  === 0.0.2 / 2008-04-12
2
11
 
3
12
  * Added bang(!) method support
data/README.txt CHANGED
@@ -32,7 +32,7 @@ RubyGem:
32
32
 
33
33
  Git:
34
34
 
35
- git clone git://github.com/mbleigh/mash
35
+ git clone git://github.com/mbleigh/mash.git
36
36
 
37
37
  == LICENSE:
38
38
 
@@ -40,14 +40,14 @@
40
40
  # mash.author # => <Mash name="Michael Bleigh">
41
41
  #
42
42
  class Mash < Hash
43
- VERSION = '0.0.2'
43
+ VERSION = '0.0.3'
44
44
 
45
45
  # If you pass in an existing hash, it will
46
46
  # convert it to a Mash including recursively
47
47
  # descending into arrays and hashes, converting
48
48
  # them as well.
49
49
  def initialize(source_hash = nil)
50
- mash_a_hash(source_hash) if source_hash
50
+ deep_update(source_hash) if source_hash
51
51
  super(nil)
52
52
  end
53
53
 
@@ -55,15 +55,56 @@ class Mash < Hash
55
55
  self["id"] ? self["id"] : super
56
56
  end
57
57
 
58
- def [](key) #:nodoc:
59
- key = key.to_s
60
- return Mash.new unless key?(key)
61
- super
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)
67
+ if key.is_a?(Symbol) && key?(key)
68
+ self[key]
69
+ else
70
+ super
71
+ end
62
72
  end
63
73
 
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)
82
+ end
83
+
84
+ # Sets an attribute in the Mash. Key will be converted to
85
+ # a string before it is set.
64
86
  def []=(key,value) #:nodoc:
65
- key = key.to_s
66
- super
87
+ key = convert_key(key)
88
+ regular_writer(key,convert_value(value))
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
+
98
+ # Duplicates the current mash as a new mash.
99
+ def dup
100
+ Mash.new(self)
101
+ end
102
+
103
+ alias_method :regular_inspect, :inspect
104
+
105
+ alias_method :picky_key?, :key?
106
+ def key?(key)
107
+ picky_key?(convert_key(key))
67
108
  end
68
109
 
69
110
  # Prints out a pretty object-like string of the
@@ -76,8 +117,53 @@ class Mash < Hash
76
117
  ret << ">"
77
118
  ret
78
119
  end
120
+ alias_method :to_s, :inspect
121
+
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
79
127
 
80
- alias :to_s :inspect
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
81
167
 
82
168
  def method_missing(method_name, *args) #:nodoc:
83
169
  if (match = method_name.to_s.match(/(.*)=$/)) && args.size == 1
@@ -85,12 +171,11 @@ class Mash < Hash
85
171
  elsif (match = method_name.to_s.match(/(.*)\?$/)) && args.size == 0
86
172
  key?(match[1])
87
173
  elsif (match = method_name.to_s.match(/(.*)!$/)) && args.size == 0
88
- return self[match[1]] if key?(match[1])
89
- self[match[1]] = Mash.new
90
- elsif keys.include?(method_name.to_s)
174
+ initializing_reader(match[1])
175
+ elsif key?(method_name)
91
176
  self[method_name]
92
177
  elsif match = method_name.to_s.match(/^([a-z][a-z0-9A-Z_]+)$/)
93
- nil
178
+ default
94
179
  else
95
180
  super
96
181
  end
@@ -98,32 +183,44 @@ class Mash < Hash
98
183
 
99
184
  protected
100
185
 
101
- def mash_a_hash(hash) #:nodoc:
102
- hash.each do |k,v|
103
- case v
104
- when Hash
105
- v = Mash.new(v) if v.is_a?(Hash)
106
- when Array
107
- v = collect_mashed_hashes_in(v) if v.is_a?(Array)
108
- end
109
-
110
- # we use the method call instead of []= here so that
111
- # it can be easily overridden for custom behavior in
112
- # inheriting objects
113
- self.send "#{k.to_s}=", v
114
- end
186
+ def convert_key(key) #:nodoc:
187
+ key.to_s
115
188
  end
116
189
 
117
- def collect_mashed_hashes_in(array) #:nodoc:
118
- array.collect do |value|
119
- case value
120
- when Hash
121
- Mash.new(value)
122
- when Array
123
- collect_mashed_hashes_in(value)
124
- else
125
- value
126
- end
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
127
198
  end
128
199
  end
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
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
129
226
  end
@@ -50,6 +50,10 @@ describe Mash do
50
50
  @mash.name!.should == "Bob"
51
51
  end
52
52
 
53
+ it "#initializing_reader should return a Mash when passed a non-existent key" do
54
+ @mash.initializing_reader(:abc).is_a?(Mash).should be_true
55
+ end
56
+
53
57
  it "should allow for multi-level assignment through bang methods" do
54
58
  @mash.author!.name = "Michael Bleigh"
55
59
  @mash.author.should == Mash.new(:name => "Michael Bleigh")
@@ -57,6 +61,14 @@ describe Mash do
57
61
  @mash.author.website.should == Mash.new(:url => "http://www.mbleigh.com/")
58
62
  end
59
63
 
64
+ it "#deep_update should recursively mash mashes and hashes together" do
65
+ @mash.first_name = "Michael"
66
+ @mash.last_name = "Bleigh"
67
+ @mash.details = {:email => "michael@asf.com"}.to_mash
68
+ @mash.deep_update({:details => {:email => "michael@intridea.com"}})
69
+ @mash.details.email.should == "michael@intridea.com"
70
+ end
71
+
60
72
  context "#initialize" do
61
73
  it "should convert an existing hash to a Mash" do
62
74
  converted = Mash.new({:abc => 123, :name => "Bob"})
@@ -77,4 +89,26 @@ describe Mash do
77
89
  converted.a.last.should == 23
78
90
  end
79
91
  end
92
+ end
93
+
94
+ describe Hash do
95
+ it "should be convertible to a Mash" do
96
+ mash = {:some => "hash"}.to_mash
97
+ mash.is_a?(Mash).should be_true
98
+ mash.some.should == "hash"
99
+ end
100
+
101
+ it "#stringify_keys! should turn all keys into strings" do
102
+ hash = {:a => "hey", 123 => "bob"}
103
+ hash.stringify_keys!
104
+ hash.should == {"a" => "hey", "123" => "bob"}
105
+ end
106
+
107
+ it "#stringify_keys should return a hash with stringified keys" do
108
+ hash = {:a => "hey", 123 => "bob"}
109
+ stringified_hash = hash.stringify_keys
110
+ hash.should == {:a => "hey", 123 => "bob"}
111
+ stringified_hash.should == {"a" => "hey", "123" => "bob"}
112
+ end
113
+
80
114
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-04-12 00:00:00 -04:00
12
+ date: 2008-04-19 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -63,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
63
  requirements: []
64
64
 
65
65
  rubyforge_project: mash-hash
66
- rubygems_version: 1.1.0
66
+ rubygems_version: 1.1.1
67
67
  signing_key:
68
68
  specification_version: 2
69
69
  summary: Mash is an extended Hash that gives simple pseudo-object functionality that can be built from hashes and easily extended