From 1de4a7b755809fca40c1077bfc66df34a18acf53 Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Fri, 27 Jun 2008 14:44:32 -0700 Subject: [PATCH] Add the :eager_graph association option, works just like :eager except it uses #eager_graph This doesn't work perfectly yet, as the association filters don't use qualified columns. That will be fixed in a later commit. --- sequel/CHANGELOG | 2 + sequel/lib/sequel_model/associations.rb | 8 +++- sequel/lib/sequel_model/eager_loading.rb | 1 + sequel/spec/eager_loading_spec.rb | 61 ++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/sequel/CHANGELOG b/sequel/CHANGELOG index b4852643f4..f71c191853 100644 --- a/sequel/CHANGELOG +++ b/sequel/CHANGELOG @@ -1,5 +1,7 @@ === HEAD +* Add the :eager_graph association option, works just like :eager except it uses #eager_graph (jeremyevans) + * Make eager loading via #eager respect the :limit association option (jeremyevans) * Add :graph_join_table_join_type association option (jeremyevans) diff --git a/sequel/lib/sequel_model/associations.rb b/sequel/lib/sequel_model/associations.rb index 61a9d0d170..75fac74301 100644 --- a/sequel/lib/sequel_model/associations.rb +++ b/sequel/lib/sequel_model/associations.rb @@ -87,7 +87,11 @@ def all_association_reflections # - :class - The associated class or its name. If not # given, uses the association's name, which is camelized (and # singularized unless the type is :many_to_one) - # - :eager - The associations to eagerly load when loading the associated object. + # - :eager - The associations to eagerly load via EagerLoading#eager when loading the associated object(s). + # For many_to_one associations, this is ignored unless this association is + # being eagerly loaded, as it doesn't save queries unless multiple objects + # can be loaded at once. + # - :eager_graph - The associations to eagerly load via EagerLoading#eager_graph when loading the associated object(s). # For many_to_one associations, this is ignored unless this association is # being eagerly loaded, as it doesn't save queries unless multiple objects # can be loaded at once. @@ -244,6 +248,7 @@ def def_association_dataset_methods(name, opts, &block) dataset_block = opts[:block] order = opts[:order] eager = opts[:eager] + eager_graph = opts[:eager_graph] limit = opts[:limit] # If a block is given, define a helper method for it, because it takes @@ -260,6 +265,7 @@ def def_association_dataset_methods(name, opts, &block) ds = ds.order(*order) if order ds = ds.limit(*limit) if limit ds = ds.eager(eager) if eager + ds = ds.eager_graph(eager_graph) if eager_graph ds = send(helper_method, ds) if dataset_block ds end diff --git a/sequel/lib/sequel_model/eager_loading.rb b/sequel/lib/sequel_model/eager_loading.rb index d080e1f740..d887226aec 100644 --- a/sequel/lib/sequel_model/eager_loading.rb +++ b/sequel/lib/sequel_model/eager_loading.rb @@ -371,6 +371,7 @@ def eager_load(a) assoc_block = Proc.new do |d| d = d.order(*reflection[:order]) if reflection[:order] d = d.limit(*reflection[:limit]) if reflection[:limit] + d = d.eager_graph(reflection[:eager_graph]) if reflection[:eager_graph] d = d.eager(reflection[:eager]) if reflection[:eager] d = d.eager(eager_assoc[assoc_name]) if eager_assoc[assoc_name] d = reflection[:eager_block].call(d) if reflection[:eager_block] diff --git a/sequel/spec/eager_loading_spec.rb b/sequel/spec/eager_loading_spec.rb index 855ee059d5..70aaa1135b 100644 --- a/sequel/spec/eager_loading_spec.rb +++ b/sequel/spec/eager_loading_spec.rb @@ -20,6 +20,7 @@ class EagerAlbum < Sequel::Model(:albums) class EagerBand < Sequel::Model(:bands) columns :id one_to_many :albums, :class=>'EagerAlbum', :key=>:band_id, :eager=>:tracks + one_to_many :graph_albums, :class=>'EagerAlbum', :key=>:band_id, :eager_graph=>:tracks many_to_many :members, :class=>'EagerBandMember', :left_key=>:band_id, :right_key=>:member_id, :join_table=>:bm one_to_many :good_albums, :class=>'EagerAlbum', :key=>:band_id, :eager_block=>proc{|ds| ds.filter(:name=>'good')} do |ds| ds.filter(:name=>'Good') @@ -47,6 +48,10 @@ class EagerBandMember < Sequel::Model(:members) end EagerAlbum.dataset.extend(Module.new { + def columns + [:id, :band_id] + end + def fetch_rows(sql) h = if sql =~ /101/ {:id => 101, :band_id=> 101} @@ -238,6 +243,62 @@ def fetch_rows(sql) MODEL_DB.sqls.length.should == 3 end + it "should cascade eagerly loading when the :eager_graph association option is used" do + EagerAlbum.dataset.extend(Module.new { + def fetch_rows(sql) + @db << sql + yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}) + end + }) + a = EagerBand.eager(:graph_albums).all + a.should be_a_kind_of(Array) + a.size.should == 1 + a.first.should be_a_kind_of(EagerBand) + a.first.values.should == {:id => 2} + MODEL_DB.sqls.should == ['SELECT * FROM bands', + 'SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) WHERE (band_id IN (2))'] + a = a.first + a.graph_albums.should be_a_kind_of(Array) + a.graph_albums.size.should == 1 + a.graph_albums.first.should be_a_kind_of(EagerAlbum) + a.graph_albums.first.values.should == {:id => 1, :band_id => 2} + a = a.graph_albums.first + a.tracks.should be_a_kind_of(Array) + a.tracks.size.should == 1 + a.tracks.first.should be_a_kind_of(EagerTrack) + a.tracks.first.values.should == {:id => 3, :album_id => 1} + MODEL_DB.sqls.length.should == 2 + end + + it "should respect :eager_graph when lazily loading an association" do + a = EagerBand.all + a.should be_a_kind_of(Array) + a.size.should == 1 + a.first.should be_a_kind_of(EagerBand) + a.first.values.should == {:id => 2} + MODEL_DB.sqls.should == ['SELECT * FROM bands'] + a = a.first + EagerAlbum.dataset.extend(Module.new { + def fetch_rows(sql) + @db << sql + yield({:id=>1, :band_id=>2, :tracks_id=>3, :album_id=>1}) + end + }) + a.graph_albums + MODEL_DB.sqls.should == ['SELECT * FROM bands', + 'SELECT albums.id, albums.band_id, tracks.id AS tracks_id, tracks.album_id FROM albums LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id) WHERE (band_id = 2)'] + a.graph_albums.should be_a_kind_of(Array) + a.graph_albums.size.should == 1 + a.graph_albums.first.should be_a_kind_of(EagerAlbum) + a.graph_albums.first.values.should == {:id => 1, :band_id => 2} + a = a.graph_albums.first + a.tracks.should be_a_kind_of(Array) + a.tracks.size.should == 1 + a.tracks.first.should be_a_kind_of(EagerTrack) + a.tracks.first.values.should == {:id => 3, :album_id => 1} + MODEL_DB.sqls.length.should == 2 + end + it "should respect :order when eagerly loading" do a = EagerBandMember.eager(:bands).all a.should be_a_kind_of(Array)