<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -47,7 +47,7 @@ class Mash &lt; Hash
   # descending into arrays and hashes, converting
   # them as well.
   def initialize(source_hash = nil)
-    mash_a_hash(source_hash) if source_hash
+    deep_update(source_hash) if source_hash
     super(nil)
   end
   
@@ -55,6 +55,22 @@ class Mash &lt; Hash
     self[&quot;id&quot;] ? self[&quot;id&quot;] : super
   end
   
+  # Borrowed from Merb's Mash object.
+  #
+  # ==== Parameters
+  # key&lt;Object&gt;:: The default value for the mash. Defaults to nil.
+  #
+  # ==== Alternatives
+  # If key is a Symbol and it is a key in the mash, then the default value will
+  # be set to the value matching the key.
+  def default(key = nil) 
+    if key.is_a?(Symbol) &amp;&amp; include?(key = convert_key(key)) 
+      self[key] 
+    else 
+      super 
+    end 
+  end
+  
   alias_method :regular_reader, :[]
   alias_method :regular_writer, :[]=
   
@@ -68,8 +84,8 @@ class Mash &lt; Hash
   # Sets an attribute in the Mash. Key will be converted to
   # a string before it is set.
   def []=(key,value) #:nodoc:
-    key = key.to_s
-    regular_writer(key,value)
+    key = convert_key(key)
+    regular_writer(key,convert_value(value))
   end
   
   # This is the bang method reader, it will return a new Mash
@@ -79,6 +95,11 @@ class Mash &lt; Hash
     self[key] = Mash.new
   end
   
+  # Duplicates the current mash as a new mash.
+  def dup
+    Mash.new(self)
+  end
+  
   alias_method :regular_inspect, :inspect
   
   alias_method :picky_key?, :key?
@@ -96,9 +117,54 @@ class Mash &lt; Hash
     ret &lt;&lt; &quot;&gt;&quot;
     ret
   end
-  
   alias_method :to_s, :inspect
   
+  # Performs a deep_update on a duplicate of the
+  # current mash.
+  def deep_merge(other_hash)
+    dup.deep_merge!(other_hash)
+  end
+  
+  # Recursively merges this mash with the passed
+  # in hash, merging each hash in the hierarchy.
+  def deep_update(other_hash)
+    stringified_hash = other_hash.stringify_keys
+    stringified_hash.each_pair do |k,v|
+      k = convert_key(k)
+      self[k] = self[k].to_mash if self[k].is_a?(Hash) unless self[k].is_a?(Mash)
+      if self[k].is_a?(Hash) &amp;&amp; stringified_hash[k].is_a?(Hash)
+        self[k].deep_merge!(stringified_hash[k])
+      else
+        self.send(k + &quot;=&quot;, convert_value(stringified_hash[k]))
+      end
+    end
+  end
+  alias_method :deep_merge!, :deep_update
+  
+  # ==== Parameters
+  # other_hash&lt;Hash&gt;::
+  # A hash to update values in the mash with. Keys will be
+  # stringified and Hashes will be converted to Mashes.
+  #
+  # ==== Returns
+  # Mash:: The updated mash.
+  def update(other_hash)
+    other_hash.each_pair do |key, value|
+      if respond_to?(convert_key(key) + &quot;=&quot;)
+        self.send(convert_key(key) + &quot;=&quot;, convert_value(value))
+      else
+        regular_writer(convert_key(key), convert_value(value))
+      end
+    end
+    self
+  end
+  alias_method :merge!, :update
+  
+  # Converts a mash back to a hash (with stringified keys)
+  def to_hash
+    Hash.new(default).merge(self)
+  end
+  
   def method_missing(method_name, *args) #:nodoc:
     if (match = method_name.to_s.match(/(.*)=$/)) &amp;&amp; args.size == 1
       self[match[1]] = args.first
@@ -121,32 +187,14 @@ class Mash &lt; Hash
     key.to_s
   end
   
-  def mash_a_hash(hash) #:nodoc:
-    hash.each do |k,v|
-      case v
-        when Hash
-          v = Mash.new(v) if v.is_a?(Hash)
-        when Array
-          v = collect_mashed_hashes_in(v) if v.is_a?(Array)
-      end
-      
-      # we use the method call instead of []= here so that
-      # it can be easily overridden for custom behavior in
-      # inheriting objects
-      self.send &quot;#{k.to_s}=&quot;, v
-    end
-  end
-  
-  def collect_mashed_hashes_in(array) #:nodoc:
-    array.collect do |value|
-      case value
-        when Hash
-          Mash.new(value)
-        when Array
-          collect_mashed_hashes_in(value)
-        else
-          value
-      end
+  def convert_value(value) #:nodoc:
+    case value
+      when Hash
+        value.is_a?(Mash) ? value : value.to_mash
+      when Array
+        value.collect{ |e| convert_value(e) }
+      else
+        value
     end
   end
 end
@@ -158,4 +206,21 @@ class Hash
     mash.default = default
     mash
   end
+  
+  # Returns a duplicate of the current hash with
+  # all of the keys converted to strings.
+  def stringify_keys
+    dup.stringify_keys!
+  end
+  
+  # Converts all of the keys to strings
+  def stringify_keys!
+    keys.each{|k| 
+      v = delete(k)
+      self[k.to_s] = v
+      v.stringify_keys! if v.is_a?(Hash)
+      v.each{|p| p.stringify_keys! if p.is_a?(Hash)} if v.is_a?(Array)
+    }
+    self
+  end
 end
\ No newline at end of file</diff>
      <filename>lib/mash.rb</filename>
    </modified>
    <modified>
      <diff>@@ -61,6 +61,14 @@ describe Mash do
     @mash.author.website.should == Mash.new(:url =&gt; &quot;http://www.mbleigh.com/&quot;)
   end
   
+  it &quot;#deep_update should recursively mash mashes and hashes together&quot; do
+    @mash.first_name = &quot;Michael&quot;
+    @mash.last_name = &quot;Bleigh&quot;
+    @mash.details = {:email =&gt; &quot;michael@asf.com&quot;}.to_mash
+    @mash.deep_update({:details =&gt; {:email =&gt; &quot;michael@intridea.com&quot;}})
+    @mash.details.email.should == &quot;michael@intridea.com&quot;
+  end
+  
   context &quot;#initialize&quot; do
     it &quot;should convert an existing hash to a Mash&quot; do
       converted = Mash.new({:abc =&gt; 123, :name =&gt; &quot;Bob&quot;})
@@ -89,4 +97,18 @@ describe Hash do
     mash.is_a?(Mash).should be_true
     mash.some.should == &quot;hash&quot;
   end
+  
+  it &quot;#stringify_keys! should turn all keys into strings&quot; do
+    hash = {:a =&gt; &quot;hey&quot;, 123 =&gt; &quot;bob&quot;}
+    hash.stringify_keys!
+    hash.should == {&quot;a&quot; =&gt; &quot;hey&quot;, &quot;123&quot; =&gt; &quot;bob&quot;}
+  end
+  
+  it &quot;#stringify_keys should return a hash with stringified keys&quot; do
+    hash = {:a =&gt; &quot;hey&quot;, 123 =&gt; &quot;bob&quot;}
+    stringified_hash = hash.stringify_keys
+    hash.should == {:a =&gt; &quot;hey&quot;, 123 =&gt; &quot;bob&quot;}
+    stringified_hash.should == {&quot;a&quot; =&gt; &quot;hey&quot;, &quot;123&quot; =&gt; &quot;bob&quot;}
+  end
+  
 end
\ No newline at end of file</diff>
      <filename>spec/mash_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>763608015d03b573b90392134d5e928abf546d29</id>
    </parent>
  </parents>
  <author>
    <name>Michael Bleigh</name>
    <email>michael@intridea.com</email>
  </author>
  <url>http://github.com/mbleigh/mash/commit/63d23cf23f85898bb2ec923f75ee268d440ae5f6</url>
  <id>63d23cf23f85898bb2ec923f75ee268d440ae5f6</id>
  <committed-date>2008-04-19T05:47:14-07:00</committed-date>
  <authored-date>2008-04-19T05:47:14-07:00</authored-date>
  <message>- Substantial cleanup of existing code
- Added dup, deep_merge(!), and key
- Added stringify_keys(!) to Hash
- Used some nice tricks from the Merb Hash object</message>
  <tree>0a33a3054723b7f26354a003cee0609150c82d2c</tree>
  <committer>
    <name>Michael Bleigh</name>
    <email>michael@intridea.com</email>
  </committer>
</commit>
