<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -83,3 +83,13 @@ Jim Mulholland, jim at squeejee dot com
 
 Clinton R. Nixon, crnixon at gmail dot com
 * Ability to define and query indexes from models
+
+Nate Wiger, http://github.com/nateware
+* Optimization to first and last to close cursor and avoid expensive to_a
+* Implemented Model.update_all leveraging Mongo collection.update
+* Scoped dynamic finders to each instance, so rows with varying attributes work
+* Added row.attributes helper to enable use of ActiveRecord::Callbacks if desired
+
+
+
+</diff>
      <filename>README.rdoc</filename>
    </modified>
    <modified>
      <diff>@@ -18,6 +18,7 @@ require 'mongo/types/code'
 require 'mongo/cursor'
 require 'mongo_record/convert'
 require 'mongo_record/sql'
+#require 'active_support/core_ext' # symbolize_keys!
 
 class String
   # Convert this String to an ObjectID.
@@ -130,6 +131,7 @@ module MongoRecord
           field = field.to_sym
           unless @field_names.include?(field)
             ivar_name = &quot;@&quot; + field.to_s
+            # this is better than lambda because it's only eval'ed once
             define_method(field, lambda { instance_variable_get(ivar_name) })
             define_method(&quot;#{field}=&quot;.to_sym, lambda { |val| instance_variable_set(ivar_name, val) })
             define_method(&quot;#{field}?&quot;.to_sym, lambda {
@@ -395,7 +397,7 @@ module MongoRecord
       end
 
       def sum(column)
-        x = self.find(:all, :select=&gt;column)
+        x = all(:select =&gt; column)
         x.map {|p1| p1[column.to_sym]}.compact.inject(0) { |s,v| s += v }
       end
 
@@ -410,23 +412,28 @@ module MongoRecord
         id.is_a?(Array) ? id.each { |oid| destroy(oid) } : find(id).destroy
       end
 
-      # This translates into a db.update call from the Mongo core API.  Currently
-      # it does not support SQL-like syntax, since Mongo is fundamentally different
-      # in this regard.  As such, you must use or Mongo operators.  For convenience,
-      # a {:key =&gt; value} element is shorthand for $set:
+      # This updates all records matching the specified criteria.  It leverages the
+      # db.update call from the Mongo core API to guarantee atomicity.  You can
+      # specify either a hash for simplicity, or full Mongo API operators to the
+      # update part of the method call:
       #
+      # Person.update_all({:name =&gt; 'Bob'}, {:name =&gt; 'Fred'})
       # Person.update_all({'$set' =&gt; {:name =&gt; 'Bob'}, '$inc' =&gt; {:age =&gt; 1}}, {:name =&gt; 'Fred'})
-      # Person.update_all({:name =&gt; 'Bob', '$inc' =&gt; {:age =&gt; 1}}, {:name =&gt; 'Fred'})
-      def update_all(updates, conditions = nil)
-        raise NotImplementedError, 'update_all not implemented (Mongo does not support multiple updates at once)'
-        collection.update(criteria_from(conditions), update_fields_from(updates), :safe =&gt; true)
+      #
+      # Note that, due to a current limitation of Mongo, you can't use $inc/$set on an
+      # indexed field.  In this case, update_all will silently fail unless :safe =&gt; true
+      # is specified.
+      def update_all(updates, conditions = nil, options = {})
+        all(:conditions =&gt; conditions).each do |row|
+          collection.update(criteria_from(conditions).merge(:_id =&gt; row.id.to_oid), update_fields_from(updates), options)
+        end
       end
 
       # Destroy all objects that match +conditions+. Warning: if
       # +conditions+ is +nil+, all records in the collection will be
       # destroyed.
       def destroy_all(conditions = nil)
-        find(:all, :conditions =&gt; conditions).each { |object| object.destroy }
+        all(:conditions =&gt; conditions).each { |object| object.destroy }
       end
 
       # Deletes all records that match +condition+, which can be a
@@ -517,11 +524,19 @@ module MongoRecord
         find_one(options)
       end
 
+      # This does not work for some reason
+      # def find_one(options)
+      #   cursor = find_every(options)
+      #   one = cursor.next_object
+      #   cursor.close  # cleanup memory
+      #   one
+      # end
+
       def find_one(options)
         one = nil
-        row = find_every(options)
-        row.each{|r| one = r; break}  # short-circuit
-        row.close  # cleanup memory
+        cursor = find_every(options)
+        cursor.each{|row| one = row; break}  # short-circuit
+        cursor.close  # cleanup memory
         one
       end
 
@@ -535,7 +550,6 @@ module MongoRecord
         find_options[:offset] = options[:offset].to_i if options[:offset]
         find_options[:sort] = sort_by_from(options[:order]) if options[:order]
 
-
         cursor = collection.find(criteria, find_options)
 
         # Override cursor.next_object so it returns a new instance of this class</diff>
      <filename>lib/mongo_record/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -386,18 +386,26 @@ class MongoTest &lt; Test::Unit::TestCase
     assert_no_match(/song: The Mayor Of Simpleton/, Track.find(:all).inject('') { |str, t| str + t.to_s })
   end
 
-  # def test_update_all
-  #   Track.update_all({:track =&gt; 919}, {:artist =&gt; 'XTC'})
-  #   tracks = Track.all.collect{|r| r }#assert_equal(919, r.track) if r.artist == 'XTC' }
-  #   Track.update_all({:song =&gt; 'Batman'})
-  #   assert_no_match(/song: The Mayor Of Simpleton/, Track.find(:all).inject('') { |str, t| str + t.to_s })
-  # 
-  #   Track.delete_all(&quot;song = 'King For A Day'&quot;)
-  #   assert_no_match(/song: King For A Day/, Track.find(:all).inject('') { |str, t| str + t.to_s })
-  # 
-  #   Track.delete_all()
-  #   assert_equal 0, Track.count
-  # end
+  def test_update_all
+    Track.update_all({:track =&gt; 919}, {:artist =&gt; 'XTC'})
+    Track.all.each{|r| assert_equal(919, r.track) if r.artist == 'XTC' }
+
+    # Should fail (can't $inc/$set) - remove this test once Mongo 1.2 is out
+    error = nil
+    begin
+      Track.update_all({:song =&gt; 'Just Drums'}, {}, :safe =&gt; true)
+    rescue Mongo::OperationFailure =&gt; error
+    end
+    assert_instance_of Mongo::OperationFailure, error 
+
+    @@tracks.drop_index 'song_-1'  # otherwise update_all $set fails
+    Track.update_all({:song =&gt; 'Just Drums'}, {}, :safe =&gt; true)
+
+    assert_no_match(/song: Budapest by Blimp/, Track.all.inject('') { |str, t| str + t.to_s })
+
+    assert_equal 6, Track.count
+    Track.index [:song, :desc], true  # reindex
+  end
 
   def test_delete_all
     Track.delete_all({:artist =&gt; 'XTC'})
@@ -837,18 +845,18 @@ class MongoTest &lt; Test::Unit::TestCase
     playlist = Playlist.new
     playlist.update_attributes(opts)
 
-    # We *want* this to fail, because otherwise MongoRecord is buggy in the following
+    # We *want* the following to fail, because otherwise MongoRecord is buggy in the following
     # situation:
     #
-    #    Rails/Sinatra/etc server instance #1 does playlist.custom_field = 'foo'
-    #    Rails/Sinatra/etc instance #2 attempts to do Playlist.find_by_custom_field
+    #    Rails/Sinatra/etc server instance #1 does: playlist.custom_field = 'foo'
+    #    Rails/Sinatra/etc instance #2 attempts to do: Playlist.find_by_custom_field
     #
     # This will fail because, in previous versions of MongoRecord, the instance would callback
-    # into the class, and change its definition, then use this modified definition to determine
-    # whether dynamic finders work.
+    # into class.field(), the changing the class definition, then use this modified definition to
+    # determine whether dynamic finders work.
     #
-    # The scary thing is you'll never catch this in dev, since everything is a single instance
-    # in memory.  It will manifest as strange mysterious production bugs.  As such, we *must*
+    # The biggest issue is you'll never catch this in dev, since everything is a single instance
+    # in memory.  It will manifest as mysterious &quot;undefined method&quot; production bugs.  As such, we *must*
     # restrict dynamic accessors to only modifying the instance for each row, or else they corrupt
     # the class.  This means find_by_whatever only works for fields defined via fields()
     error = nil</diff>
      <filename>tests/test_mongo.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>aa7840a997b7167982f7bd2eaa9834b7b6b4f535</id>
    </parent>
  </parents>
  <author>
    <name>Nate Wiger</name>
    <email>nate@wiger.org</email>
  </author>
  <url>http://github.com/mongodb/mongo-activerecord-ruby/commit/46a043948012b268bd0fc32fcd37e3d34232c4fc</url>
  <id>46a043948012b268bd0fc32fcd37e3d34232c4fc</id>
  <committed-date>2009-11-06T11:42:55-08:00</committed-date>
  <authored-date>2009-11-06T11:42:55-08:00</authored-date>
  <message>implemented update_all that leverages collection.update for atomicity</message>
  <tree>4c1f5b897159af30d861494fe26dc786d115dc43</tree>
  <committer>
    <name>Nate Wiger</name>
    <email>nate@wiger.org</email>
  </committer>
</commit>
