it has a bug or feature in it. yeilding inside with_scope will put scope on the same class

migration

class CreatePeople < ActiveRecord::Migration
  def self.up
    create_table :people do |t|
      t.string :name
      t.integer :mama_id, :papa_id, :age
      t.timestamps
    end
  end

  def self.down
    drop_table :people
  end
end

model

class Person < ActiveRecord::Base
  has_one :mama, :class_name => 'Person', :foreign_key => 'mama_id'
  has_one :papa, :class_name => 'Person', :foreign_key => 'papa_id'
end

test

class PersonTest < ActiveSupport::TestCase
  test "mama_i_papa or sheep Dolly" do
    mama = Person.create!(:name=>'Mama', :age=> 40)
    papa = Person.create!(:name=>'Papa', :age=> 40)
    son = Person.create!(:name => 'Son', :age => 10, :mama => mama, :papa => papa)
    Person.find_in_batches(:conditions => "age < 12") do |group|
      group.each do |person|
        puts "I am #{person.name}"
        has_papa = false
        has_mama = false
        if person.mama
          puts "My mom is #{person.mama.name}"
          has_mama = true
        end
        if person.papa
          puts "My Dad is  #{person.papa.name}"
          has_papa = true
        end
        assert has_papa && has_mama, "and i was cloned"
      end
    end
  end
end

queries from test.log

SELECT * FROM `people` WHERE (`people`.mama_id = 3) AND (age < 12) ORDER BY people.id ASC LIMIT 1

SELECT * FROM `people` WHERE (`people`.papa_id = 3) AND (age < 12) ORDER BY people.id ASC LIMIT 1

rake test results

rake test
(in /opt/ror/edge)
/opt/ruby-enterprise-1.8.6-20090201/bin/ruby -Ilib:test "/opt/ruby-enterprise-1.8.6-20090201/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader.rb" "test/unit/person_test.rb"
Loaded suite /opt/ruby-enterprise-1.8.6-20090201/lib/ruby/gems/1.8/gems/rake-0.8.3/lib/rake/rake_test_loader
Started
I am Son
F
Finished in 0.054297 seconds.

  1) Failure:
test_mama_i_papa_or_sheep_Dolly(PersonTest)
    [/test/unit/person_test.rb:22:in `test_mama_i_papa_or_sheep_Dolly'
     /test/unit/person_test.rb:10:in `each'
     /test/unit/person_test.rb:10:in `test_mama_i_papa_or_sheep_Dolly'
     /test/unit/person_test.rb:9:in `test_mama_i_papa_or_sheep_Dolly']:
and i was cloned.
 is not true.

1 tests, 1 assertions, 1 failures, 0 errors

the reason i am posting this comment - I've been there with my implementation (I just called it cursor). this is what I came up with after fix. Scope with_scope to fetch only, do yeild outside of the cursor scope I tried to to use with_exclusive_scope, but this would remove potentially scope set before the cursor is run

module Uping
  module ActiveRecordCursor
    def cursor(conditions={})
      limit = 1000
      callback = nil
      batch_counter = 0
      if conditions.is_a?(Hash)
        limit = conditions.delete(:limit) || limit
        callback = conditions.delete(:callback)
      end
      conditions[:order]="#{table_name}.#{primary_key}" unless conditions
      rows = cursor_next_scoop(conditions, limit, 0)
      while rows.any?
        next_max_id = rows.map(&:id).max
        batch_size = rows.size
        #using map! - replace elements of the list by the result
        #of the block - this way we may give hint to GC to discard
        rows.map! { |record|
            yield record
            nil
        }
        batch_counter += 1
        callback.call(
          :count => batch_counter,
          :size => batch_size
        ) if callback;

        rows = cursor_next_scoop(conditions, limit, next_max_id)
      end
    end

    private
    def cursor_next_scoop(conditions, limit, next_max_id)
      with_scope(:find => conditions) do
        find(:all,
          :conditions => ["#{table_name}.#{primary_key} > ?", next_max_id],
          :limit => limit
        )
      end
    end
  end
end

usage

class PersonTest < ActiveSupport::TestCase
  test "mama_i_papa or sheep Dolly" do
    mama = Person.create!(:name=>'Mama', :age=> 40)
    papa = Person.create!(:name=>'Papa', :age=> 40)
    son = Person.create!(:name => 'Son', :age => 10, :mama => mama, :papa => papa)
    Person.cursor(:conditions => "age < 12") do |person|
        puts "I am #{person.name}"
        has_papa = false
        has_mama = false
        if person.mama
          puts "My mom is #{person.mama.name}"
          has_mama = true
        end
        if person.papa
          puts "My Dad is  #{person.papa.name}"
          has_papa = true
        end
        assert has_papa && has_mama, "and i was cloned"
    end
  end
end

Expected results

/opt/ror/edge/test/unit$ ruby -I../ person_cursor_test.rb
Loaded suite person_cursor_test
Started
I am Son
My mom is Mama
My Dad is  Papa
.
Finished in 0.278581 seconds.

1 tests, 1 assertions, 0 failures, 0 errors