module Sequel module Plugins # The tactical_eager_loading plugin allows you to eagerly load # an association for all objects retrieved from the same dataset # without calling eager on the dataset. If you attempt to load # associated objects for a record and the association for that # object is currently not cached, it assumes you want to get # the associated objects for all objects retrieved with the dataset that # retrieved the current object. # # Tactical eager loading only takes affect if you retrieved the # current object with Dataset#all, it doesn't work if you # retrieved the current object with Dataset#each. # # Basically, this allows the following code to issue only two queries: # # Album.filter{id<100}.all do |a| # a.artists # end # # Usage: # # # Make all model subclass instances use tactical eager loading (called before loading subclasses) # Sequel::Model.plugin :tactical_eager_loading # # # Make the Album class use tactical eager loading # Album.plugin :tactical_eager_loading module TacticalEagerLoading module InstanceMethods # The dataset that retrieved this object, set if the object was # reteived via Dataset#all. attr_accessor :retrieved_by # All model objects retrieved with this object, set if the object was # reteived via Dataset#all. attr_accessor :retrieved_with # Remove retrieved_by and retrieved_with when marshalling. retrieved_by # contains unmarshallable objects, and retrieved_with can be very large # and is not helpful without retrieved_by. def marshallable! @retrieved_by = nil @retrieved_with = nil super end private # If there the association is not in the associations cache and the object # was reteived via Dataset#all, eagerly load the association for all model # objects retrieved with the current object. def load_associated_objects(opts, reload=false) name = opts[:name] if !associations.include?(name) && retrieved_by && !frozen? begin retrieved_by.send(:eager_load, retrieved_with.reject{|o| o.frozen?}, name=>{}) rescue Sequel::UndefinedAssociation # This can happen if class table inheritance is used and the association # is only defined in a subclass. This particular instance can use the # association, but it can't be eagerly loaded as the parent class doesn't # have access to the association, and that's the class doing the eager loading. nil end end super end end module DatasetMethods private # Set the retrieved_with and retrieved_by attributes for the object # with the current dataset and array of all objects. def post_load(objects) super objects.each do |o| next unless o.is_a?(Sequel::Model) o.retrieved_by = self o.retrieved_with = objects end end end end end end