On Tue, Oct 19, 2010 at 04:16:33PM -0500, Tim Morgan wrote:
> Sure!
> 
> Here is the updated patch:
> https://rails.lighthouseapp.com/projects/8994/tickets/1812/a/727956/allow-default_scope-to-accept-a-proc2.patch

Thanks!  I've applied it and extended it a little bit so that
default_scope will take any object that responds to "call".

I'll show some code, then try to explain the benefits.  This is now possible:

  class AuthorFilter < Struct.new(:klass, :author_id)
    def call
      klass.where(:author_id => author_id)
    end
  end

  class AmazingPost < ActiveRecord::Base
    self.table_name = 'posts'
    default_scope AuthorFilter.new(self, 2)
  end

The Object form does not need to save binding like the lambda form does.  This
can lead to less memory usage and help curb "leaking objects".

Because we can use objects, it allows our default scopes to contain more
logic:  we can actually break up functionality to other methods.  For
example:

  class AuthorFilter < Struct.new(:klass, :some_value)
    def calculate_id
      ... do som complex logic to figure out the id ...
    end

    def call
      klass.where(:author_id => calculate_id)
    end
  end

We can even use modules or subclasses to extend our logic.  For example:

  module BodyFilter
    def call
      super.where(:body => 'hello')
    end
  end

  class AuthorFilter < Struct.new(:klass, :author_id)
    def call
      klass.where(:author_id => author_id)
    end
  end

  class AmazingPost < ActiveRecord::Base
    self.table_name = 'posts'
    # Produces: WHERE author_id = 2 AND body = 'hello'
    default_scope AuthorFilter.new(self, 2).extend(BodyFilter)
  end

Finally, we can more easily test any complex logic we need in this
filter:

  class FilterTest < Test::Unit::TestCase
    class DreamCatcher
      attr_reader :wheres
      def initialize; @wheres = []; end
      def where(arg); @wheres << arg; end
    end

    def test_appropriate_where_clause
      dc = DreamCatcher.new
      AuthorFilter.new(dc, 2).call
      assert_equal([{:author_id => 2}], dc.wheres)
    end
  end

I'd like to see the rest of our scope methods to allow arbitrary objects
that respond to `call` for the above reasons.  :-)

-- 
Aaron Patterson
http://tenderlovemaking.com/

Attachment: pgptfYFcnRkWr.pgp
Description: PGP signature

Reply via email to