Query objects and delayed execution
Published about 7 years ago

So this morning I got up after sleeping 3-4 hours and all I can somehow think of is having Query objects for ActiveRecord finders and delayed query execution. If done well, this could open pandora’s box of neat ways to extend ActiveRecord. So talk stops here.

Here’s the very basic first draft done under influence ( coffee ;-) ) :

  1. activerecord/lib/active_record/abstract_query.rb
    module ActiveRecord
      class AbstractRecords
        attr_reader :records, :klass, :query

    module ActiveRecord
    class AbstractRecords
    attr_reader :records, :klass, :query
delegate :connection, :instantiate, :name, :to => :klass delegate :sql, :options, :to => :query def initialize(query, klass) @query = query @klass = klass @loaded = false end def method_missing(method_id, *args, &block) load_records records.send(method_id, *args, &block) end def loaded? @loaded end private def load_records return if @loaded @records = connection.select_all(sql, "#{name} Load").collect! { |record| instantiate(record) } @records.each { |record| record.readonly! } if options[:readonly] @loaded = true end end class AbstractQuery < DelegateClass(AbstractRecords) attr_reader :sql, :options def initialize(klass, sql, options = {}) @sql = sql @options = options super(AbstractRecords.new(self, klass)) end end

end

So after this, a session from console would look something like :

>> i = Item.find :all, :limit => 10
=> #<ActiveRecord::AbstractRecords:0x19520e4 @klass=Item(id: integer, name: string, created_at: datetime, updated_at: datetime), @loaded=false, @query=#<ActiveRecord::AbstractRecords:0x19520e4 ...>>
>> i.first
  Item Load (0.000361)   SELECT * FROM `items` LIMIT 10
=> #<Item id: 1, name: "wtf", created_at: "2007-12-12 13:28:56", updated_at: "2007-12-12 13:28:56">

Notice when the query gets executed. Experimental patch can be found here