sequel 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +100 -0
- data/README.rdoc +3 -3
- data/bin/sequel +102 -19
- data/doc/reflection.rdoc +83 -0
- data/doc/release_notes/3.1.0.txt +406 -0
- data/lib/sequel/adapters/ado.rb +11 -0
- data/lib/sequel/adapters/amalgalite.rb +5 -20
- data/lib/sequel/adapters/do.rb +44 -36
- data/lib/sequel/adapters/firebird.rb +29 -43
- data/lib/sequel/adapters/jdbc.rb +17 -27
- data/lib/sequel/adapters/mysql.rb +35 -40
- data/lib/sequel/adapters/odbc.rb +4 -23
- data/lib/sequel/adapters/oracle.rb +22 -19
- data/lib/sequel/adapters/postgres.rb +6 -15
- data/lib/sequel/adapters/shared/mssql.rb +1 -1
- data/lib/sequel/adapters/shared/mysql.rb +29 -10
- data/lib/sequel/adapters/shared/oracle.rb +6 -8
- data/lib/sequel/adapters/shared/postgres.rb +28 -72
- data/lib/sequel/adapters/shared/sqlite.rb +5 -3
- data/lib/sequel/adapters/sqlite.rb +5 -20
- data/lib/sequel/adapters/utils/savepoint_transactions.rb +80 -0
- data/lib/sequel/adapters/utils/unsupported.rb +0 -12
- data/lib/sequel/core.rb +12 -3
- data/lib/sequel/core_sql.rb +1 -8
- data/lib/sequel/database.rb +107 -43
- data/lib/sequel/database/schema_generator.rb +1 -0
- data/lib/sequel/database/schema_methods.rb +38 -4
- data/lib/sequel/dataset.rb +6 -0
- data/lib/sequel/dataset/convenience.rb +2 -2
- data/lib/sequel/dataset/graph.rb +2 -2
- data/lib/sequel/dataset/prepared_statements.rb +3 -8
- data/lib/sequel/dataset/sql.rb +93 -19
- data/lib/sequel/extensions/blank.rb +2 -1
- data/lib/sequel/extensions/inflector.rb +4 -3
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pretty_table.rb +4 -0
- data/lib/sequel/extensions/query.rb +4 -0
- data/lib/sequel/extensions/schema_dumper.rb +100 -24
- data/lib/sequel/extensions/string_date_time.rb +3 -4
- data/lib/sequel/model.rb +2 -1
- data/lib/sequel/model/associations.rb +96 -38
- data/lib/sequel/model/base.rb +14 -14
- data/lib/sequel/model/plugins.rb +32 -21
- data/lib/sequel/plugins/caching.rb +13 -15
- data/lib/sequel/plugins/identity_map.rb +107 -0
- data/lib/sequel/plugins/lazy_attributes.rb +65 -0
- data/lib/sequel/plugins/many_through_many.rb +188 -0
- data/lib/sequel/plugins/schema.rb +13 -0
- data/lib/sequel/plugins/serialization.rb +53 -37
- data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
- data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
- data/lib/sequel/plugins/validation_class_methods.rb +28 -7
- data/lib/sequel/plugins/validation_helpers.rb +31 -24
- data/lib/sequel/sql.rb +16 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/ado_spec.rb +47 -1
- data/spec/adapters/firebird_spec.rb +39 -36
- data/spec/adapters/mysql_spec.rb +25 -9
- data/spec/adapters/postgres_spec.rb +11 -24
- data/spec/core/database_spec.rb +54 -13
- data/spec/core/dataset_spec.rb +147 -29
- data/spec/core/object_graph_spec.rb +6 -1
- data/spec/core/schema_spec.rb +34 -0
- data/spec/core/spec_helper.rb +0 -2
- data/spec/extensions/caching_spec.rb +7 -0
- data/spec/extensions/identity_map_spec.rb +158 -0
- data/spec/extensions/lazy_attributes_spec.rb +113 -0
- data/spec/extensions/many_through_many_spec.rb +813 -0
- data/spec/extensions/migration_spec.rb +4 -4
- data/spec/extensions/schema_dumper_spec.rb +114 -13
- data/spec/extensions/schema_spec.rb +19 -3
- data/spec/extensions/serialization_spec.rb +28 -0
- data/spec/extensions/single_table_inheritance_spec.rb +25 -1
- data/spec/extensions/spec_helper.rb +2 -7
- data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
- data/spec/extensions/validation_class_methods_spec.rb +10 -5
- data/spec/integration/dataset_test.rb +39 -6
- data/spec/integration/eager_loader_test.rb +7 -7
- data/spec/integration/spec_helper.rb +0 -1
- data/spec/integration/transaction_test.rb +28 -1
- data/spec/model/association_reflection_spec.rb +29 -3
- data/spec/model/associations_spec.rb +1 -0
- data/spec/model/eager_loading_spec.rb +70 -1
- data/spec/model/plugins_spec.rb +236 -50
- data/spec/model/spec_helper.rb +0 -2
- metadata +18 -5
@@ -23,11 +23,16 @@ describe Sequel::Dataset, " graphing" do
|
|
23
23
|
ds1.opts.should_not == o1
|
24
24
|
end
|
25
25
|
|
26
|
-
it "#graph should accept a dataset
|
26
|
+
it "#graph should accept a simple dataset and pass the table to join" do
|
27
27
|
ds = @ds1.graph(@ds2, :x=>:id)
|
28
28
|
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
29
29
|
end
|
30
30
|
|
31
|
+
it "#graph should accept a complex dataset and pass it directly to join" do
|
32
|
+
ds = @ds1.graph(@ds2.filter(:x=>1), {:x=>:id})
|
33
|
+
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN (SELECT * FROM lines WHERE (x = 1)) AS lines ON (lines.x = points.id)'
|
34
|
+
end
|
35
|
+
|
31
36
|
it "#graph should accept a symbol table name as the dataset" do
|
32
37
|
ds = @ds1.graph(:lines, :x=>:id)
|
33
38
|
ds.sql.should == 'SELECT points.id, points.x, points.y, lines.id AS lines_id, lines.x AS lines_x, lines.y AS lines_y, lines.graph_id FROM points LEFT OUTER JOIN lines ON (lines.x = points.id)'
|
data/spec/core/schema_spec.rb
CHANGED
@@ -333,6 +333,15 @@ context "DB#create_table" do
|
|
333
333
|
}.should raise_error(Sequel::Error)
|
334
334
|
end
|
335
335
|
|
336
|
+
specify "should ignore errors if the database raises an error on an index creation statement and the :ignore_index_errors option is used" do
|
337
|
+
@db.meta_def(:execute_ddl){|*a| raise Sequel::DatabaseError if /blah/.match(a.first); super(*a)}
|
338
|
+
lambda{@db.create_table(:cats){Integer :id; index :blah; index :id}}.should raise_error(Sequel::DatabaseError)
|
339
|
+
@db.sqls.should == ['CREATE TABLE cats (id integer)']
|
340
|
+
@db.sqls.clear
|
341
|
+
lambda{@db.create_table(:cats, :ignore_index_errors=>true){Integer :id; index :blah; index :id}}.should_not raise_error(Sequel::DatabaseError)
|
342
|
+
@db.sqls.should == ['CREATE TABLE cats (id integer)', 'CREATE INDEX cats_id_index ON cats (id)']
|
343
|
+
end
|
344
|
+
|
336
345
|
specify "should accept multiple index definitions" do
|
337
346
|
@db.create_table(:cats) do
|
338
347
|
integer :id
|
@@ -521,6 +530,24 @@ context "DB#create_table!" do
|
|
521
530
|
end
|
522
531
|
end
|
523
532
|
|
533
|
+
context "DB#create_table?" do
|
534
|
+
before do
|
535
|
+
@db = SchemaDummyDatabase.new
|
536
|
+
end
|
537
|
+
|
538
|
+
specify "should not create the table if the table already exists" do
|
539
|
+
@db.meta_def(:table_exists?){|a| true}
|
540
|
+
@db.create_table?(:cats){|*a|}
|
541
|
+
@db.sqls.should == nil
|
542
|
+
end
|
543
|
+
|
544
|
+
specify "should create the table if the table doesn't already exist" do
|
545
|
+
@db.meta_def(:table_exists?){|a| false}
|
546
|
+
@db.create_table?(:cats){|*a|}
|
547
|
+
@db.sqls.should == ['CREATE TABLE cats ()']
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
524
551
|
context "DB#drop_table" do
|
525
552
|
before do
|
526
553
|
@db = SchemaDummyDatabase.new
|
@@ -624,6 +651,13 @@ context "DB#alter_table" do
|
|
624
651
|
@db.sqls.should == ["CREATE INDEX cats_name_index ON cats (name)"]
|
625
652
|
end
|
626
653
|
|
654
|
+
specify "should ignore errors if the database raises an error on an add_index call and the :ignore_errors option is used" do
|
655
|
+
@db.meta_def(:execute_ddl){|*a| raise Sequel::DatabaseError}
|
656
|
+
lambda{@db.add_index(:cats, :id)}.should raise_error(Sequel::DatabaseError)
|
657
|
+
lambda{@db.add_index(:cats, :id, :ignore_errors=>true)}.should_not raise_error(Sequel::DatabaseError)
|
658
|
+
@db.sqls.should == nil
|
659
|
+
end
|
660
|
+
|
627
661
|
specify "should support add_primary_key" do
|
628
662
|
@db.alter_table(:cats) do
|
629
663
|
add_primary_key :id
|
data/spec/core/spec_helper.rb
CHANGED
@@ -54,6 +54,13 @@ describe Sequel::Model, "caching" do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should take a ttl option" do
|
57
|
+
c = Class.new(Sequel::Model(:items))
|
58
|
+
c.plugin :caching, @cache, :ttl => 1234
|
59
|
+
c.cache_ttl.should == 1234
|
60
|
+
Class.new(c).cache_ttl.should == 1234
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should allow overriding the ttl option via a plugin :caching call" do
|
57
64
|
@c.plugin :caching, @cache, :ttl => 1234
|
58
65
|
@c.cache_ttl.should == 1234
|
59
66
|
Class.new(@c).cache_ttl.should == 1234
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe "Sequel::Plugins::IdentityMap" do
|
4
|
+
before do
|
5
|
+
class ::IdentityMapModel < Sequel::Model
|
6
|
+
plugin :identity_map
|
7
|
+
columns :id
|
8
|
+
ds = dataset
|
9
|
+
def ds.fetch_rows(sql)
|
10
|
+
c = @opts[:where].args.first
|
11
|
+
c = c.column if c.is_a?(Sequel::SQL::QualifiedIdentifier)
|
12
|
+
h = {c=>@opts[:where].args.last}
|
13
|
+
execute(sql)
|
14
|
+
yield h
|
15
|
+
end
|
16
|
+
end
|
17
|
+
class ::IdentityMapAlbum < ::IdentityMapModel
|
18
|
+
columns :id, :artist_id
|
19
|
+
end
|
20
|
+
class ::IdentityMapArtist < ::IdentityMapModel
|
21
|
+
end
|
22
|
+
@c = ::IdentityMapModel
|
23
|
+
@c1 = ::IdentityMapAlbum
|
24
|
+
@c2 = ::IdentityMapArtist
|
25
|
+
MODEL_DB.reset
|
26
|
+
end
|
27
|
+
after do
|
28
|
+
Object.send(:remove_const, :IdentityMapAlbum)
|
29
|
+
Object.send(:remove_const, :IdentityMapArtist)
|
30
|
+
Object.send(:remove_const, :IdentityMapModel)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "#identity_map should return a hash if an identity map is currently being used" do
|
34
|
+
@c.with_identity_map{@c.identity_map.should == {}}
|
35
|
+
end
|
36
|
+
|
37
|
+
it "#identity_map should return nil if an identity map is not currently being used" do
|
38
|
+
@c.identity_map.should == nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "#identity_map_key should be the same for the same class and pk" do
|
42
|
+
@c.identity_map_key(1).should == @c.identity_map_key(1)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "#identity_map_key should be different for a different class" do
|
46
|
+
@c1.identity_map_key(1).should_not == @c2.identity_map_key(1)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "#identity_map_key should be different for different anonymous classes" do
|
50
|
+
Class.new(@c).identity_map_key(1).should_not == Class.new(@c).identity_map_key(1)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "#identity_map_key should be different for a different pk" do
|
54
|
+
@c.identity_map_key(1).should_not == @c.identity_map_key(2)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "#identity_map_key should be different if the pk is nil" do
|
58
|
+
@c.identity_map_key(nil).should_not == @c.identity_map_key(nil)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "#load should return an object if there is no current identity map" do
|
62
|
+
o = @c.load(:id=>1)
|
63
|
+
o.should be_a_kind_of(@c)
|
64
|
+
o.values.should == {:id=>1}
|
65
|
+
end
|
66
|
+
|
67
|
+
it "#load should return an object if there is a current identity map" do
|
68
|
+
@c.with_identity_map do
|
69
|
+
o = @c.load(:id=>1)
|
70
|
+
o.should be_a_kind_of(@c)
|
71
|
+
o.values.should == {:id=>1}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "#load should should store the object in the current identity map if it isn't already there" do
|
76
|
+
@c.with_identity_map do
|
77
|
+
@c.identity_map[@c.identity_map_key(1)].should == nil
|
78
|
+
o = @c.load(:id=>1)
|
79
|
+
@c.identity_map[@c.identity_map_key(1)].should == o
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it "#load should update the record in the current identity map if new fields if it is already there" do
|
84
|
+
@c.with_identity_map do
|
85
|
+
o = @c.load(:id=>1, :a=>2)
|
86
|
+
o.values.should == {:id=>1, :a=>2}
|
87
|
+
o = @c.load(:id=>1, :b=>3)
|
88
|
+
o.values.should == {:id=>1, :a=>2, :b=>3}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it "#load should not update existing fields in the record if the record is in the current identity map" do
|
93
|
+
@c.with_identity_map do
|
94
|
+
o = @c.load(:id=>1, :a=>2)
|
95
|
+
o.values.should == {:id=>1, :a=>2}
|
96
|
+
o = @c.load(:id=>1, :a=>4)
|
97
|
+
o.values.should == {:id=>1, :a=>2}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should use the identity map as a lookup cache in Model.[] to save on database queries" do
|
102
|
+
@c.with_identity_map do
|
103
|
+
MODEL_DB.sqls.length.should == 0
|
104
|
+
o = @c[1]
|
105
|
+
MODEL_DB.sqls.length.should == 1
|
106
|
+
@c[1].should == o
|
107
|
+
MODEL_DB.sqls.length.should == 1
|
108
|
+
@c[2].should_not == o
|
109
|
+
MODEL_DB.sqls.length.should == 2
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should use the identity map as a lookup cache when retrieving many_to_one associated records" do
|
114
|
+
@c1.many_to_one :artist, :class=>@c2
|
115
|
+
@c.with_identity_map do
|
116
|
+
MODEL_DB.sqls.length.should == 0
|
117
|
+
o = @c1.load(:id=>1, :artist_id=>2)
|
118
|
+
a = o.artist
|
119
|
+
a.should be_a_kind_of(@c2)
|
120
|
+
MODEL_DB.sqls.length.should == 1
|
121
|
+
o = @c1.load(:id=>2, :artist_id=>2)
|
122
|
+
o.artist.should == a
|
123
|
+
MODEL_DB.sqls.length.should == 1
|
124
|
+
o = @c1.load(:id=>3, :artist_id=>3)
|
125
|
+
o.artist.should_not == a
|
126
|
+
MODEL_DB.sqls.length.should == 2
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should not use the identity map as a lookup cache if the assocation has a nil :key option" do
|
131
|
+
c = @c2
|
132
|
+
@c1.many_to_one :artist, :class=>@c2, :key=>nil, :dataset=>proc{c.filter(:artist_id=>artist_id)}
|
133
|
+
@c.with_identity_map do
|
134
|
+
MODEL_DB.sqls.length.should == 0
|
135
|
+
o = @c1.load(:id=>1, :artist_id=>2)
|
136
|
+
a = o.artist
|
137
|
+
a.should be_a_kind_of(@c2)
|
138
|
+
MODEL_DB.sqls.length.should == 1
|
139
|
+
o = @c1.load(:id=>2, :artist_id=>2)
|
140
|
+
o.artist.should == a
|
141
|
+
MODEL_DB.sqls.length.should == 2
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should not use the identity map as a lookup cache if the assocation's :primary_key option doesn't match the primary key of the associated class" do
|
146
|
+
@c1.many_to_one :artist, :class=>@c2, :primary_key=>:artist_id
|
147
|
+
@c.with_identity_map do
|
148
|
+
MODEL_DB.sqls.length.should == 0
|
149
|
+
o = @c1.load(:id=>1, :artist_id=>2)
|
150
|
+
a = o.artist
|
151
|
+
a.should be_a_kind_of(@c2)
|
152
|
+
MODEL_DB.sqls.length.should == 1
|
153
|
+
o = @c1.load(:id=>2, :artist_id=>2)
|
154
|
+
o.artist.should == a
|
155
|
+
MODEL_DB.sqls.length.should == 2
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe "Sequel::Plugins::LazyAttributes" do
|
4
|
+
before do
|
5
|
+
class ::LazyAttributesModel < Sequel::Model(:la)
|
6
|
+
plugin :lazy_attributes
|
7
|
+
columns :id, :name
|
8
|
+
meta_def(:columns){[:id, :name]}
|
9
|
+
lazy_attributes :name
|
10
|
+
meta_def(:columns){[:id]}
|
11
|
+
ds = dataset
|
12
|
+
def ds.fetch_rows(sql)
|
13
|
+
execute(sql)
|
14
|
+
select = @opts[:select]
|
15
|
+
where = @opts[:where]
|
16
|
+
if !where
|
17
|
+
if select.include?(:name)
|
18
|
+
yield(:id=>1, :name=>'1')
|
19
|
+
yield(:id=>2, :name=>'2')
|
20
|
+
else
|
21
|
+
yield(:id=>1)
|
22
|
+
yield(:id=>2)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
i = where.args.last
|
26
|
+
i = i.instance_variable_get(:@array) if i.is_a?(Sequel::SQL::SQLArray)
|
27
|
+
Array(i).each do |x|
|
28
|
+
if sql =~ /SELECT name FROM/
|
29
|
+
yield(:name=>x.to_s)
|
30
|
+
else
|
31
|
+
yield(:id=>x, :name=>x.to_s)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
@c = ::LazyAttributesModel
|
38
|
+
@ds = LazyAttributesModel.dataset
|
39
|
+
MODEL_DB.reset
|
40
|
+
end
|
41
|
+
after do
|
42
|
+
Object.send(:remove_const, :LazyAttributesModel)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allowing adding additional lazy attributes via plugin :lazy_attributes" do
|
46
|
+
@c.set_dataset(@ds.select(:id, :blah))
|
47
|
+
@c.dataset.sql.should == 'SELECT id, blah FROM la'
|
48
|
+
@c.plugin :lazy_attributes, :blah
|
49
|
+
@c.dataset.opts[:select].should == [:id]
|
50
|
+
@c.dataset.sql.should == 'SELECT id FROM la'
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should allowing adding additional lazy attributes via lazy_attributes" do
|
54
|
+
@c.set_dataset(@ds.select(:id, :blah))
|
55
|
+
@c.dataset.sql.should == 'SELECT id, blah FROM la'
|
56
|
+
@c.lazy_attributes :blah
|
57
|
+
@c.dataset.opts[:select].should == [:id]
|
58
|
+
@c.dataset.sql.should == 'SELECT id FROM la'
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should remove the attributes given from the SELECT columns of the model's dataset" do
|
62
|
+
@ds.opts[:select].should == [:id]
|
63
|
+
@ds.sql.should == 'SELECT id FROM la'
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should lazily load the attribute for a single model object if there is an active identity map" do
|
67
|
+
@c.with_identity_map do
|
68
|
+
m = @c.first
|
69
|
+
m.values.should == {:id=>1}
|
70
|
+
m.name.should == '1'
|
71
|
+
m.values.should == {:id=>1, :name=>'1'}
|
72
|
+
MODEL_DB.sqls.should == ['SELECT id FROM la LIMIT 1', 'SELECT name FROM la WHERE (id = 1) LIMIT 1']
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should lazily load the attribute for a single model object if there is no active identity map" do
|
77
|
+
m = @c.first
|
78
|
+
m.values.should == {:id=>1}
|
79
|
+
m.name.should == '1'
|
80
|
+
m.values.should == {:id=>1, :name=>'1'}
|
81
|
+
MODEL_DB.sqls.should == ['SELECT id FROM la LIMIT 1', 'SELECT name FROM la WHERE (id = 1) LIMIT 1']
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should not lazily load the attribute for a single model object if the value already exists" do
|
85
|
+
@c.with_identity_map do
|
86
|
+
m = @c.first
|
87
|
+
m.values.should == {:id=>1}
|
88
|
+
m[:name] = '1'
|
89
|
+
m.name.should == '1'
|
90
|
+
m.values.should == {:id=>1, :name=>'1'}
|
91
|
+
MODEL_DB.sqls.should == ['SELECT id FROM la LIMIT 1']
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should not lazily load the attribute for a single model object if it is a new record" do
|
96
|
+
@c.with_identity_map do
|
97
|
+
m = @c.new
|
98
|
+
m.values.should == {}
|
99
|
+
m.name.should == nil
|
100
|
+
MODEL_DB.sqls.should == []
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should eagerly load the attribute for all model objects reteived with it" do
|
105
|
+
@c.with_identity_map do
|
106
|
+
ms = @c.all
|
107
|
+
ms.map{|m| m.values}.should == [{:id=>1}, {:id=>2}]
|
108
|
+
ms.map{|m| m.name}.should == %w'1 2'
|
109
|
+
ms.map{|m| m.values}.should == [{:id=>1, :name=>'1'}, {:id=>2, :name=>'2'}]
|
110
|
+
MODEL_DB.sqls.should == ['SELECT id FROM la', 'SELECT id, name FROM la WHERE (id IN (1, 2))']
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,813 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
|
3
|
+
describe Sequel::Model, "many_through_many" do
|
4
|
+
before do
|
5
|
+
class ::Artist < Sequel::Model
|
6
|
+
attr_accessor :yyy
|
7
|
+
columns :id
|
8
|
+
plugin :many_through_many
|
9
|
+
end
|
10
|
+
class ::Tag < Sequel::Model
|
11
|
+
end
|
12
|
+
MODEL_DB.reset
|
13
|
+
@c1 = Artist
|
14
|
+
@c2 = Tag
|
15
|
+
@dataset = @c2.dataset
|
16
|
+
def @dataset.fetch_rows(sql)
|
17
|
+
@db << sql
|
18
|
+
yield({:id=>1})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
after do
|
22
|
+
Object.send(:remove_const, :Artist)
|
23
|
+
Object.send(:remove_const, :Tag)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should raise an error if in invalid form of through is used" do
|
27
|
+
proc{@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id]]}.should raise_error(Sequel::Error)
|
28
|
+
proc{@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], {:table=>:album_tags, :left=>:album_id}]}.should raise_error(Sequel::Error)
|
29
|
+
proc{@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], :album_tags]}.should raise_error(Sequel::Error)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should use join tables given" do
|
33
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
34
|
+
n = @c1.load(:id => 1234)
|
35
|
+
a = n.tags_dataset
|
36
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
37
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
|
38
|
+
n.tags.should == [@c2.load(:id=>1)]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should handle multiple aliasing of tables" do
|
42
|
+
class ::Album < Sequel::Model
|
43
|
+
end
|
44
|
+
@c1.many_through_many :albums, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_artists, :album_id, :artist_id], [:artists, :id, :id], [:albums_artists, :artist_id, :album_id]]
|
45
|
+
n = @c1.load(:id => 1234)
|
46
|
+
a = n.albums_dataset
|
47
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
48
|
+
a.sql.should == 'SELECT albums.* FROM albums INNER JOIN albums_artists ON (albums_artists.album_id = albums.id) INNER JOIN artists ON (artists.id = albums_artists.artist_id) INNER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.artist_id = artists.id) INNER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id) INNER JOIN albums_artists AS albums_artists_1 ON ((albums_artists_1.album_id = albums_0.id) AND (albums_artists_1.artist_id = 1234))'
|
49
|
+
n.albums.should == [Album.load(:id=>1, :x=>1)]
|
50
|
+
Object.send(:remove_const, :Album)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should use explicit class if given" do
|
54
|
+
@c1.many_through_many :albums_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>Tag
|
55
|
+
n = @c1.load(:id => 1234)
|
56
|
+
a = n.albums_tags_dataset
|
57
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
58
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
|
59
|
+
n.albums_tags.should == [@c2.load(:id=>1)]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should accept :left_primary_key and :right_primary_key option for primary keys to use in current and associated table" do
|
63
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :right_primary_key=>:tag_id, :left_primary_key=>:yyy
|
64
|
+
n = @c1.load(:id => 1234)
|
65
|
+
n.yyy = 85
|
66
|
+
a = n.tags_dataset
|
67
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
68
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.tag_id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 85))'
|
69
|
+
n.tags.should == [@c2.load(:id=>1)]
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should support a :conditions option" do
|
73
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:a=>32}
|
74
|
+
n = @c1.load(:id => 1234)
|
75
|
+
a = n.tags_dataset
|
76
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
77
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (a = 32)'
|
78
|
+
n.tags.should == [@c2.load(:id=>1)]
|
79
|
+
|
80
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>['a = ?', 42]
|
81
|
+
n = @c1.load(:id => 1234)
|
82
|
+
a = n.tags_dataset
|
83
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
84
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (a = 42)'
|
85
|
+
n.tags.should == [@c2.load(:id=>1)]
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should support an :order option" do
|
89
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :order=>:blah
|
90
|
+
n = @c1.load(:id => 1234)
|
91
|
+
a = n.tags_dataset
|
92
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
93
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) ORDER BY blah'
|
94
|
+
n.tags.should == [@c2.load(:id=>1)]
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should support an array for the :order option" do
|
98
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :order=>[:blah1, :blah2]
|
99
|
+
n = @c1.load(:id => 1234)
|
100
|
+
a = n.tags_dataset
|
101
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
102
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) ORDER BY blah1, blah2'
|
103
|
+
n.tags.should == [@c2.load(:id=>1)]
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should support a select option" do
|
107
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :select=>:blah
|
108
|
+
n = @c1.load(:id => 1234)
|
109
|
+
a = n.tags_dataset
|
110
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
111
|
+
a.sql.should == 'SELECT blah FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
|
112
|
+
n.tags.should == [@c2.load(:id=>1)]
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should support an array for the select option" do
|
116
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :select=>[:tags.*, :albums__name]
|
117
|
+
n = @c1.load(:id => 1234)
|
118
|
+
a = n.tags_dataset
|
119
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
120
|
+
a.sql.should == 'SELECT tags.*, albums.name FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))'
|
121
|
+
n.tags.should == [@c2.load(:id=>1)]
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should accept a block" do
|
125
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]] do |ds| ds.filter(:yyy=>@yyy) end
|
126
|
+
n = @c1.load(:id => 1234)
|
127
|
+
n.yyy = 85
|
128
|
+
a = n.tags_dataset
|
129
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
130
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (yyy = 85)'
|
131
|
+
n.tags.should == [@c2.load(:id=>1)]
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should allow the :order option while accepting a block" do
|
135
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :order=>:blah do |ds| ds.filter(:yyy=>@yyy) end
|
136
|
+
n = @c1.load(:id => 1234)
|
137
|
+
n.yyy = 85
|
138
|
+
a = n.tags_dataset
|
139
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
140
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) WHERE (yyy = 85) ORDER BY blah'
|
141
|
+
n.tags.should == [@c2.load(:id=>1)]
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should support a :dataset option that is used instead of the default" do
|
145
|
+
@c1.many_through_many :tags, [[:a, :b, :c]], :dataset=>proc{Tag.join(:albums_tags, [:tag_id]).join(:albums, [:album_id]).join(:albums_artists, [:album_id]).filter(:albums_artists__artist_id=>id)}
|
146
|
+
n = @c1.load(:id => 1234)
|
147
|
+
a = n.tags_dataset
|
148
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
149
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags USING (tag_id) INNER JOIN albums USING (album_id) INNER JOIN albums_artists USING (album_id) WHERE (albums_artists.artist_id = 1234)'
|
150
|
+
n.tags.should == [@c2.load(:id=>1)]
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should support a :limit option" do
|
154
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :limit=>10
|
155
|
+
n = @c1.load(:id => 1234)
|
156
|
+
a = n.tags_dataset
|
157
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
158
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) LIMIT 10'
|
159
|
+
n.tags.should == [@c2.load(:id=>1)]
|
160
|
+
|
161
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :limit=>[10, 10]
|
162
|
+
n = @c1.load(:id => 1234)
|
163
|
+
a = n.tags_dataset
|
164
|
+
a.should be_a_kind_of(Sequel::Dataset)
|
165
|
+
a.sql.should == 'SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234)) LIMIT 10 OFFSET 10'
|
166
|
+
n.tags.should == [@c2.load(:id=>1)]
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should have the :eager option affect the _dataset method" do
|
170
|
+
@c2.many_to_many :fans
|
171
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager=>:fans
|
172
|
+
@c1.load(:id => 1234).tags_dataset.opts[:eager].should == {:fans=>nil}
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should provide an array with all members of the association" do
|
176
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
177
|
+
@c1.load(:id => 1234).tags.should == [@c2.load(:id=>1)]
|
178
|
+
MODEL_DB.sqls.should == ['SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))']
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should set cached instance variable when accessed" do
|
182
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
183
|
+
n = @c1.load(:id => 1234)
|
184
|
+
n.associations[:tags].should == nil
|
185
|
+
MODEL_DB.sqls.should == []
|
186
|
+
n.tags.should == [@c2.load(:id=>1)]
|
187
|
+
MODEL_DB.sqls.should == ['SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))']
|
188
|
+
n.associations[:tags].should == n.tags
|
189
|
+
MODEL_DB.sqls.length.should == 1
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should use cached instance variable if available" do
|
193
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
194
|
+
n = @c1.load(:id => 1234)
|
195
|
+
n.associations[:tags] = []
|
196
|
+
n.tags.should == []
|
197
|
+
MODEL_DB.sqls.should == []
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should not use cached instance variable if asked to reload" do
|
201
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
202
|
+
n = @c1.load(:id => 1234)
|
203
|
+
n.associations[:tags] = []
|
204
|
+
MODEL_DB.sqls.should == []
|
205
|
+
n.tags(true).should == [@c2.load(:id=>1)]
|
206
|
+
MODEL_DB.sqls.should == ['SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))']
|
207
|
+
n.associations[:tags].should == n.tags
|
208
|
+
MODEL_DB.sqls.length.should == 1
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should not add associations methods directly to class" do
|
212
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
213
|
+
im = @c1.instance_methods.collect{|x| x.to_s}
|
214
|
+
im.should(include('tags'))
|
215
|
+
im.should(include('tags_dataset'))
|
216
|
+
im2 = @c1.instance_methods(false).collect{|x| x.to_s}
|
217
|
+
im2.should_not(include('tags'))
|
218
|
+
im2.should_not(include('tags_dataset'))
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should support after_load association callback" do
|
222
|
+
h = []
|
223
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :after_load=>:al
|
224
|
+
@c1.class_eval do
|
225
|
+
@@blah = h
|
226
|
+
def al(v)
|
227
|
+
v.each{|x| @@blah << x.pk * 20}
|
228
|
+
end
|
229
|
+
end
|
230
|
+
@c2.class_eval do
|
231
|
+
def @dataset.fetch_rows(sql)
|
232
|
+
yield({:id=>20})
|
233
|
+
yield({:id=>30})
|
234
|
+
end
|
235
|
+
end
|
236
|
+
p = @c1.load(:id=>10, :parent_id=>20)
|
237
|
+
p.tags
|
238
|
+
h.should == [400, 600]
|
239
|
+
p.tags.collect{|a| a.pk}.should == [20, 30]
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should support a :uniq option that removes duplicates from the association" do
|
243
|
+
h = []
|
244
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :uniq=>true
|
245
|
+
@c2.class_eval do
|
246
|
+
def @dataset.fetch_rows(sql)
|
247
|
+
yield({:id=>20})
|
248
|
+
yield({:id=>30})
|
249
|
+
yield({:id=>20})
|
250
|
+
yield({:id=>30})
|
251
|
+
end
|
252
|
+
end
|
253
|
+
@c1.load(:id=>10).tags.should == [@c2.load(:id=>20), @c2.load(:id=>30)]
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe 'Sequel::Plugins::ManyThroughMany::ManyThroughManyAssociationReflection' do
|
258
|
+
before do
|
259
|
+
class ::Artist < Sequel::Model
|
260
|
+
plugin :many_through_many
|
261
|
+
many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
262
|
+
end
|
263
|
+
class ::Tag < Sequel::Model
|
264
|
+
end
|
265
|
+
MODEL_DB.reset
|
266
|
+
@ar = Artist.association_reflection(:tags)
|
267
|
+
end
|
268
|
+
after do
|
269
|
+
Object.send(:remove_const, :Artist)
|
270
|
+
Object.send(:remove_const, :Tag)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "#edges should be an array of joins to make when eager graphing" do
|
274
|
+
@ar.edges.should == [{:conditions=>[], :left=>:id, :right=>:artist_id, :table=>:albums_artists, :join_type=>:left_outer, :block=>nil}, {:conditions=>[], :left=>:album_id, :right=>:id, :table=>:albums, :join_type=>:left_outer, :block=>nil}, {:conditions=>[], :left=>:id, :right=>:album_id, :table=>:albums_tags, :join_type=>:left_outer, :block=>nil}]
|
275
|
+
end
|
276
|
+
|
277
|
+
it "#reverse_edges should be an array of joins to make when lazy loading or eager loading" do
|
278
|
+
@ar.reverse_edges.should == [{:alias=>:albums_tags, :left=>:tag_id, :right=>:id, :table=>:albums_tags}, {:alias=>:albums, :left=>:id, :right=>:album_id, :table=>:albums}]
|
279
|
+
end
|
280
|
+
|
281
|
+
it "#reciprocal should be nil" do
|
282
|
+
@ar.reciprocal.should == nil
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
describe "Sequel::Plugins::ManyThroughMany eager loading methods" do
|
287
|
+
before do
|
288
|
+
class ::Artist < Sequel::Model
|
289
|
+
plugin :many_through_many
|
290
|
+
many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
|
291
|
+
many_through_many :other_tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :class=>:Tag
|
292
|
+
many_through_many :albums, [[:albums_artists, :artist_id, :album_id]]
|
293
|
+
many_through_many :artists, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_artists, :album_id, :artist_id]]
|
294
|
+
end
|
295
|
+
class ::Tag < Sequel::Model
|
296
|
+
plugin :many_through_many
|
297
|
+
many_through_many :tracks, [[:albums_tags, :tag_id, :album_id], [:albums, :id, :id]], :right_primary_key=>:album_id
|
298
|
+
end
|
299
|
+
class ::Album < Sequel::Model
|
300
|
+
end
|
301
|
+
class ::Track < Sequel::Model
|
302
|
+
end
|
303
|
+
|
304
|
+
Artist.dataset.extend(Module.new {
|
305
|
+
def columns
|
306
|
+
[:id]
|
307
|
+
end
|
308
|
+
|
309
|
+
def fetch_rows(sql)
|
310
|
+
@db << sql
|
311
|
+
h = {:id => 1}
|
312
|
+
if sql =~ /FROM artists LEFT OUTER JOIN albums_artists/
|
313
|
+
h[:tags_id] = 2
|
314
|
+
h[:albums_0_id] = 3 if sql =~ /LEFT OUTER JOIN albums AS albums_0/
|
315
|
+
h[:tracks_id] = 4 if sql =~ /LEFT OUTER JOIN tracks/
|
316
|
+
h[:other_tags_id] = 9 if sql =~ /other_tags\.id AS other_tags_id/
|
317
|
+
h[:artists_0_id] = 10 if sql =~ /artists_0\.id AS artists_0_id/
|
318
|
+
end
|
319
|
+
yield h
|
320
|
+
end
|
321
|
+
})
|
322
|
+
|
323
|
+
Tag.dataset.extend(Module.new {
|
324
|
+
def fetch_rows(sql)
|
325
|
+
@db << sql
|
326
|
+
h = {:id => 2}
|
327
|
+
if sql =~ /albums_artists.artist_id IN \(([18])\)/
|
328
|
+
h.merge!(:x_foreign_key_x=>$1.to_i)
|
329
|
+
end
|
330
|
+
h[:tag_id] = h.delete(:id) if sql =~ /albums_artists.artist_id IN \(8\)/
|
331
|
+
yield h
|
332
|
+
end
|
333
|
+
})
|
334
|
+
|
335
|
+
Album.dataset.extend(Module.new {
|
336
|
+
def fetch_rows(sql)
|
337
|
+
@db << sql
|
338
|
+
h = {:id => 3}
|
339
|
+
h.merge!(:x_foreign_key_x=>1) if sql =~ /albums_artists.artist_id IN \(1\)/
|
340
|
+
yield h
|
341
|
+
end
|
342
|
+
})
|
343
|
+
|
344
|
+
Track.dataset.extend(Module.new {
|
345
|
+
def fetch_rows(sql)
|
346
|
+
@db << sql
|
347
|
+
h = {:id => 4}
|
348
|
+
h.merge!(:x_foreign_key_x=>2) if sql =~ /albums_tags.tag_id IN \(2\)/
|
349
|
+
yield h
|
350
|
+
end
|
351
|
+
})
|
352
|
+
|
353
|
+
@c1 = Artist
|
354
|
+
MODEL_DB.reset
|
355
|
+
end
|
356
|
+
after do
|
357
|
+
[:Artist, :Tag, :Album, :Track].each{|x| Object.send(:remove_const, x)}
|
358
|
+
end
|
359
|
+
|
360
|
+
it "should eagerly load a single many_through_many association" do
|
361
|
+
a = @c1.eager(:tags).all
|
362
|
+
a.should == [@c1.load(:id=>1)]
|
363
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists', 'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
|
364
|
+
a.first.tags.should == [Tag.load(:id=>2)]
|
365
|
+
MODEL_DB.sqls.length.should == 2
|
366
|
+
end
|
367
|
+
|
368
|
+
it "should eagerly load multiple associations in a single call" do
|
369
|
+
a = @c1.eager(:tags, :albums).all
|
370
|
+
a.should == [@c1.load(:id=>1)]
|
371
|
+
MODEL_DB.sqls.length.should == 3
|
372
|
+
MODEL_DB.sqls[0].should == 'SELECT * FROM artists'
|
373
|
+
MODEL_DB.sqls[1..-1].should(include('SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
|
374
|
+
MODEL_DB.sqls[1..-1].should(include('SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
|
375
|
+
a = a.first
|
376
|
+
a.tags.should == [Tag.load(:id=>2)]
|
377
|
+
a.albums.should == [Album.load(:id=>3)]
|
378
|
+
MODEL_DB.sqls.length.should == 3
|
379
|
+
end
|
380
|
+
|
381
|
+
it "should eagerly load multiple associations in separate" do
|
382
|
+
a = @c1.eager(:tags).eager(:albums).all
|
383
|
+
a.should == [@c1.load(:id=>1)]
|
384
|
+
MODEL_DB.sqls.length.should == 3
|
385
|
+
MODEL_DB.sqls[0].should == 'SELECT * FROM artists'
|
386
|
+
MODEL_DB.sqls[1..-1].should(include('SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
|
387
|
+
MODEL_DB.sqls[1..-1].should(include('SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))'))
|
388
|
+
a = a.first
|
389
|
+
a.tags.should == [Tag.load(:id=>2)]
|
390
|
+
a.albums.should == [Album.load(:id=>3)]
|
391
|
+
MODEL_DB.sqls.length.should == 3
|
392
|
+
end
|
393
|
+
|
394
|
+
it "should allow cascading of eager loading for associations of associated models" do
|
395
|
+
a = @c1.eager(:tags=>:tracks).all
|
396
|
+
a.should == [@c1.load(:id=>1)]
|
397
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
398
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))',
|
399
|
+
'SELECT tracks.*, albums_tags.tag_id AS x_foreign_key_x FROM tracks INNER JOIN albums ON (albums.id = tracks.album_id) INNER JOIN albums_tags ON ((albums_tags.album_id = albums.id) AND (albums_tags.tag_id IN (2)))']
|
400
|
+
a = a.first
|
401
|
+
a.tags.should == [Tag.load(:id=>2)]
|
402
|
+
a.tags.first.tracks.should == [Track.load(:id=>4)]
|
403
|
+
MODEL_DB.sqls.length.should == 3
|
404
|
+
end
|
405
|
+
|
406
|
+
it "should cascade eagerly loading when the :eager association option is used" do
|
407
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager=>:tracks
|
408
|
+
a = @c1.eager(:tags).all
|
409
|
+
a.should == [@c1.load(:id=>1)]
|
410
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
411
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))',
|
412
|
+
'SELECT tracks.*, albums_tags.tag_id AS x_foreign_key_x FROM tracks INNER JOIN albums ON (albums.id = tracks.album_id) INNER JOIN albums_tags ON ((albums_tags.album_id = albums.id) AND (albums_tags.tag_id IN (2)))']
|
413
|
+
a = a.first
|
414
|
+
a.tags.should == [Tag.load(:id=>2)]
|
415
|
+
a.tags.first.tracks.should == [Track.load(:id=>4)]
|
416
|
+
MODEL_DB.sqls.length.should == 3
|
417
|
+
end
|
418
|
+
|
419
|
+
it "should respect :eager when lazily loading an association" do
|
420
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager=>:tracks
|
421
|
+
a = @c1.load(:id=>1)
|
422
|
+
a.tags.should == [Tag.load(:id=>2)]
|
423
|
+
MODEL_DB.sqls.should == ['SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1))',
|
424
|
+
'SELECT tracks.*, albums_tags.tag_id AS x_foreign_key_x FROM tracks INNER JOIN albums ON (albums.id = tracks.album_id) INNER JOIN albums_tags ON ((albums_tags.album_id = albums.id) AND (albums_tags.tag_id IN (2)))']
|
425
|
+
a.tags.first.tracks.should == [Track.load(:id=>4)]
|
426
|
+
MODEL_DB.sqls.length.should == 2
|
427
|
+
end
|
428
|
+
|
429
|
+
it "should cascade eagerly loading when the :eager_graph association option is used" do
|
430
|
+
Tag.dataset.extend(Module.new {
|
431
|
+
def columns
|
432
|
+
[:id]
|
433
|
+
end
|
434
|
+
def fetch_rows(sql)
|
435
|
+
@db << sql
|
436
|
+
yield({:id=>2, :tracks_id=>4, :x_foreign_key_x=>1})
|
437
|
+
end
|
438
|
+
})
|
439
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager_graph=>:tracks
|
440
|
+
a = @c1.eager(:tags).all
|
441
|
+
a.should == [@c1.load(:id=>1)]
|
442
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
443
|
+
'SELECT tags.id, tracks.id AS tracks_id, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) LEFT OUTER JOIN albums_tags AS albums_tags_0 ON (albums_tags_0.tag_id = tags.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_tags_0.album_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums_0.id)']
|
444
|
+
a = a.first
|
445
|
+
a.tags.should == [Tag.load(:id=>2)]
|
446
|
+
a.tags.first.tracks.should == [Track.load(:id=>4)]
|
447
|
+
MODEL_DB.sqls.length.should == 2
|
448
|
+
end
|
449
|
+
|
450
|
+
it "should respect :eager_graph when lazily loading an association" do
|
451
|
+
Tag.dataset.extend(Module.new {
|
452
|
+
def columns
|
453
|
+
[:id]
|
454
|
+
end
|
455
|
+
def fetch_rows(sql)
|
456
|
+
@db << sql
|
457
|
+
yield({:id=>2, :tracks_id=>4})
|
458
|
+
end
|
459
|
+
})
|
460
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager_graph=>:tracks
|
461
|
+
a = @c1.load(:id=>1)
|
462
|
+
a.tags
|
463
|
+
MODEL_DB.sqls.should == [ 'SELECT tags.id, tracks.id AS tracks_id FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1)) LEFT OUTER JOIN albums_tags AS albums_tags_0 ON (albums_tags_0.tag_id = tags.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_tags_0.album_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums_0.id)']
|
464
|
+
a.tags.should == [Tag.load(:id=>2)]
|
465
|
+
a.tags.first.tracks.should == [Track.load(:id=>4)]
|
466
|
+
MODEL_DB.sqls.length.should == 1
|
467
|
+
end
|
468
|
+
|
469
|
+
it "should respect :conditions when eagerly loading" do
|
470
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :conditions=>{:a=>32}
|
471
|
+
a = @c1.eager(:tags).all
|
472
|
+
a.should == [@c1.load(:id=>1)]
|
473
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
474
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) WHERE (a = 32)']
|
475
|
+
a.first.tags.should == [Tag.load(:id=>2)]
|
476
|
+
MODEL_DB.sqls.length.should == 2
|
477
|
+
end
|
478
|
+
|
479
|
+
it "should respect :order when eagerly loading" do
|
480
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :order=>:blah
|
481
|
+
a = @c1.eager(:tags).all
|
482
|
+
a.should == [@c1.load(:id=>1)]
|
483
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
484
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) ORDER BY blah']
|
485
|
+
a.first.tags.should == [Tag.load(:id=>2)]
|
486
|
+
MODEL_DB.sqls.length.should == 2
|
487
|
+
end
|
488
|
+
|
489
|
+
it "should use the association's block when eager loading by default" do
|
490
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]] do |ds| ds.filter(:a) end
|
491
|
+
a = @c1.eager(:tags).all
|
492
|
+
a.should == [@c1.load(:id=>1)]
|
493
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
494
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) WHERE a']
|
495
|
+
a.first.tags.should == [Tag.load(:id=>2)]
|
496
|
+
MODEL_DB.sqls.length.should == 2
|
497
|
+
end
|
498
|
+
|
499
|
+
it "should use the :eager_block option when eager loading if given" do
|
500
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :eager_block=>proc{|ds| ds.filter(:b)} do |ds| ds.filter(:a) end
|
501
|
+
a = @c1.eager(:tags).all
|
502
|
+
a.should == [@c1.load(:id=>1)]
|
503
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
504
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1))) WHERE b']
|
505
|
+
a.first.tags.should == [Tag.load(:id=>2)]
|
506
|
+
MODEL_DB.sqls.length.should == 2
|
507
|
+
end
|
508
|
+
|
509
|
+
it "should raise an error when attempting to eagerly load an association with the :allow_eager option set to false" do
|
510
|
+
proc{@c1.eager(:tags).all}.should_not raise_error
|
511
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :allow_eager=>false
|
512
|
+
proc{@c1.eager(:tags).all}.should raise_error(Sequel::Error)
|
513
|
+
end
|
514
|
+
|
515
|
+
it "should respect the association's :select option" do
|
516
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :select=>:tags__name
|
517
|
+
a = @c1.eager(:tags).all
|
518
|
+
a.should == [@c1.load(:id=>1)]
|
519
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
520
|
+
'SELECT tags.name, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
|
521
|
+
a.first.tags.should == [Tag.load(:id=>2)]
|
522
|
+
MODEL_DB.sqls.length.should == 2
|
523
|
+
end
|
524
|
+
|
525
|
+
it "should respect many_to_many association's :left_primary_key and :right_primary_key options" do
|
526
|
+
@c1.send(:define_method, :yyy){values[:yyy]}
|
527
|
+
@c1.dataset.extend(Module.new {
|
528
|
+
def columns
|
529
|
+
[:id, :yyy]
|
530
|
+
end
|
531
|
+
def fetch_rows(sql)
|
532
|
+
@db << sql
|
533
|
+
yield({:id=>1, :yyy=>8})
|
534
|
+
end
|
535
|
+
})
|
536
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :left_primary_key=>:yyy, :right_primary_key=>:tag_id
|
537
|
+
a = @c1.eager(:tags).all
|
538
|
+
a.should == [@c1.load(:id=>1, :yyy=>8)]
|
539
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
540
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.tag_id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (8)))']
|
541
|
+
a.first.tags.should == [Tag.load(:tag_id=>2)]
|
542
|
+
MODEL_DB.sqls.length.should == 2
|
543
|
+
end
|
544
|
+
|
545
|
+
it "should respect :after_load callbacks on associations when eager loading" do
|
546
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :after_load=>lambda{|o, as| o[:id] *= 2; as.each{|a| a[:id] *= 3}}
|
547
|
+
a = @c1.eager(:tags).all
|
548
|
+
a.should == [@c1.load(:id=>2)]
|
549
|
+
MODEL_DB.sqls.should == ['SELECT * FROM artists',
|
550
|
+
'SELECT tags.*, albums_artists.artist_id AS x_foreign_key_x FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
|
551
|
+
a.first.tags.should == [Tag.load(:id=>6)]
|
552
|
+
MODEL_DB.sqls.length.should == 2
|
553
|
+
end
|
554
|
+
|
555
|
+
it "should raise an error if called without a symbol or hash" do
|
556
|
+
proc{@c1.eager_graph(Object.new)}.should raise_error(Sequel::Error)
|
557
|
+
end
|
558
|
+
|
559
|
+
it "should eagerly graph a single many_through_many association" do
|
560
|
+
a = @c1.eager_graph(:tags).all
|
561
|
+
a.should == [@c1.load(:id=>1)]
|
562
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)']
|
563
|
+
a.first.tags.should == [Tag.load(:id=>2)]
|
564
|
+
MODEL_DB.sqls.length.should == 1
|
565
|
+
end
|
566
|
+
|
567
|
+
it "should eagerly graph multiple associations in a single call" do
|
568
|
+
a = @c1.eager_graph(:tags, :albums).all
|
569
|
+
a.should == [@c1.load(:id=>1)]
|
570
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id, albums_0.id AS albums_0_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.artist_id = artists.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id)']
|
571
|
+
a = a.first
|
572
|
+
a.tags.should == [Tag.load(:id=>2)]
|
573
|
+
a.albums.should == [Album.load(:id=>3)]
|
574
|
+
MODEL_DB.sqls.length.should == 1
|
575
|
+
end
|
576
|
+
|
577
|
+
it "should eagerly graph multiple associations in separate calls" do
|
578
|
+
a = @c1.eager_graph(:tags).eager_graph(:albums).all
|
579
|
+
a.should == [@c1.load(:id=>1)]
|
580
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id, albums_0.id AS albums_0_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.artist_id = artists.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id)']
|
581
|
+
a = a.first
|
582
|
+
a.tags.should == [Tag.load(:id=>2)]
|
583
|
+
a.albums.should == [Album.load(:id=>3)]
|
584
|
+
MODEL_DB.sqls.length.should == 1
|
585
|
+
end
|
586
|
+
|
587
|
+
it "should allow cascading of eager graphing for associations of associated models" do
|
588
|
+
a = @c1.eager_graph(:tags=>:tracks).all
|
589
|
+
a.should == [@c1.load(:id=>1)]
|
590
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id, tracks.id AS tracks_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_tags AS albums_tags_0 ON (albums_tags_0.tag_id = tags.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_tags_0.album_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums_0.id)']
|
591
|
+
a = a.first
|
592
|
+
a.tags.should == [Tag.load(:id=>2)]
|
593
|
+
a.tags.first.tracks.should == [Track.load(:id=>4)]
|
594
|
+
MODEL_DB.sqls.length.should == 1
|
595
|
+
end
|
596
|
+
|
597
|
+
it "eager graphing should eliminate duplicates caused by cartesian products" do
|
598
|
+
ds = @c1.eager_graph(:tags)
|
599
|
+
def ds.fetch_rows(sql, &block)
|
600
|
+
@db << sql
|
601
|
+
# Assume artist has 2 albums each with 2 tags
|
602
|
+
yield({:id=>1, :tags_id=>2})
|
603
|
+
yield({:id=>1, :tags_id=>3})
|
604
|
+
yield({:id=>1, :tags_id=>2})
|
605
|
+
yield({:id=>1, :tags_id=>3})
|
606
|
+
end
|
607
|
+
a = ds.all
|
608
|
+
a.should == [@c1.load(:id=>1)]
|
609
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)']
|
610
|
+
a.first.tags.should == [Tag.load(:id=>2), Tag.load(:id=>3)]
|
611
|
+
MODEL_DB.sqls.length.should == 1
|
612
|
+
end
|
613
|
+
|
614
|
+
it "should eager graph multiple associations from the same table" do
|
615
|
+
a = @c1.eager_graph(:tags, :other_tags).all
|
616
|
+
a.should == [@c1.load(:id=>1)]
|
617
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id, other_tags.id AS other_tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.artist_id = artists.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id) LEFT OUTER JOIN albums_tags AS albums_tags_0 ON (albums_tags_0.album_id = albums_0.id) LEFT OUTER JOIN tags AS other_tags ON (other_tags.id = albums_tags_0.tag_id)']
|
618
|
+
a = a.first
|
619
|
+
a.tags.should == [Tag.load(:id=>2)]
|
620
|
+
a.other_tags.should == [Tag.load(:id=>9)]
|
621
|
+
MODEL_DB.sqls.length.should == 1
|
622
|
+
end
|
623
|
+
|
624
|
+
it "should eager graph a self_referential association" do
|
625
|
+
a = @c1.eager_graph(:tags, :artists).all
|
626
|
+
a.should == [@c1.load(:id=>1)]
|
627
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id, artists_0.id AS artists_0_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.artist_id = artists.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id) LEFT OUTER JOIN albums_artists AS albums_artists_1 ON (albums_artists_1.album_id = albums_0.id) LEFT OUTER JOIN artists AS artists_0 ON (artists_0.id = albums_artists_1.artist_id)']
|
628
|
+
a = a.first
|
629
|
+
a.tags.should == [Tag.load(:id=>2)]
|
630
|
+
a.artists.should == [@c1.load(:id=>10)]
|
631
|
+
MODEL_DB.sqls.length.should == 1
|
632
|
+
end
|
633
|
+
|
634
|
+
it "eager graphing should give you a graph of tables when called without .all" do
|
635
|
+
@c1.eager_graph(:tags, :artists).first.should == {:artists=>@c1.load(:id=>1), :artists_0=>@c1.load(:id=>10), :tags=>Tag.load(:id=>2)}
|
636
|
+
end
|
637
|
+
|
638
|
+
it "should be able to use eager and eager_graph together" do
|
639
|
+
a = @c1.eager_graph(:tags).eager(:albums).all
|
640
|
+
a.should == [@c1.load(:id=>1)]
|
641
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)',
|
642
|
+
'SELECT albums.*, albums_artists.artist_id AS x_foreign_key_x FROM albums INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id IN (1)))']
|
643
|
+
a = a.first
|
644
|
+
a.tags.should == [Tag.load(:id=>2)]
|
645
|
+
a.albums.should == [Album.load(:id=>3)]
|
646
|
+
MODEL_DB.sqls.length.should == 2
|
647
|
+
end
|
648
|
+
|
649
|
+
it "should handle no associated records when eagerly graphing a single many_through_many association" do
|
650
|
+
ds = @c1.eager_graph(:tags)
|
651
|
+
def ds.fetch_rows(sql)
|
652
|
+
@db << sql
|
653
|
+
yield({:id=>1, :tags_id=>nil})
|
654
|
+
end
|
655
|
+
a = ds.all
|
656
|
+
a.should == [@c1.load(:id=>1)]
|
657
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)']
|
658
|
+
a.first.tags.should == []
|
659
|
+
MODEL_DB.sqls.length.should == 1
|
660
|
+
end
|
661
|
+
|
662
|
+
it "should handle no associated records when eagerly graphing multiple many_through_many associations" do
|
663
|
+
ds = @c1.eager_graph(:tags, :albums)
|
664
|
+
def ds.fetch_rows(sql)
|
665
|
+
@db << sql
|
666
|
+
yield({:id=>1, :tags_id=>nil, :albums_0_id=>3})
|
667
|
+
yield({:id=>1, :tags_id=>2, :albums_0_id=>nil})
|
668
|
+
yield({:id=>1, :tags_id=>5, :albums_0_id=>6})
|
669
|
+
yield({:id=>7, :tags_id=>nil, :albums_0_id=>nil})
|
670
|
+
end
|
671
|
+
a = ds.all
|
672
|
+
a.should == [@c1.load(:id=>1), @c1.load(:id=>7)]
|
673
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id, albums_0.id AS albums_0_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.artist_id = artists.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id)']
|
674
|
+
a.first.tags.should == [Tag.load(:id=>2), Tag.load(:id=>5)]
|
675
|
+
a.first.albums.should == [Album.load(:id=>3), Album.load(:id=>6)]
|
676
|
+
a.last.tags.should == []
|
677
|
+
a.last.albums.should == []
|
678
|
+
MODEL_DB.sqls.length.should == 1
|
679
|
+
end
|
680
|
+
|
681
|
+
it "should handle missing associated records when cascading eager graphing for associations of associated models" do
|
682
|
+
ds = @c1.eager_graph(:tags=>:tracks)
|
683
|
+
def ds.fetch_rows(sql)
|
684
|
+
@db << sql
|
685
|
+
yield({:id=>1, :tags_id=>2, :tracks_id=>4})
|
686
|
+
yield({:id=>1, :tags_id=>3, :tracks_id=>nil})
|
687
|
+
yield({:id=>2, :tags_id=>nil, :tracks_id=>nil})
|
688
|
+
end
|
689
|
+
a = ds.all
|
690
|
+
a.should == [@c1.load(:id=>1), @c1.load(:id=>2)]
|
691
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.id AS tags_id, tracks.id AS tracks_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_tags AS albums_tags_0 ON (albums_tags_0.tag_id = tags.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_tags_0.album_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums_0.id)']
|
692
|
+
a.last.tags.should == []
|
693
|
+
a = a.first
|
694
|
+
a.tags.should == [Tag.load(:id=>2), Tag.load(:id=>3)]
|
695
|
+
a.tags.first.tracks.should == [Track.load(:id=>4)]
|
696
|
+
a.tags.last.tracks.should == []
|
697
|
+
MODEL_DB.sqls.length.should == 1
|
698
|
+
end
|
699
|
+
|
700
|
+
it "eager graphing should respect :left_primary_key and :right_primary_key options" do
|
701
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :left_primary_key=>:yyy, :right_primary_key=>:tag_id
|
702
|
+
@c1.dataset.meta_def(:columns){[:id, :yyy]}
|
703
|
+
Tag.dataset.meta_def(:columns){[:id, :tag_id]}
|
704
|
+
ds = @c1.eager_graph(:tags)
|
705
|
+
def ds.fetch_rows(sql)
|
706
|
+
@db << sql
|
707
|
+
yield({:id=>1, :yyy=>8, :tags_id=>2, :tag_id=>4})
|
708
|
+
end
|
709
|
+
a = ds.all
|
710
|
+
a.should == [@c1.load(:id=>1, :yyy=>8)]
|
711
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, artists.yyy, tags.id AS tags_id, tags.tag_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.yyy) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.tag_id = albums_tags.tag_id)']
|
712
|
+
a.first.tags.should == [Tag.load(:id=>2, :tag_id=>4)]
|
713
|
+
MODEL_DB.sqls.length.should == 1
|
714
|
+
end
|
715
|
+
|
716
|
+
it "should respect the association's :graph_select option" do
|
717
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :graph_select=>:b
|
718
|
+
ds = @c1.eager_graph(:tags)
|
719
|
+
def ds.fetch_rows(sql)
|
720
|
+
@db << sql
|
721
|
+
yield({:id=>1, :b=>2})
|
722
|
+
end
|
723
|
+
a = ds.all
|
724
|
+
a.should == [@c1.load(:id=>1)]
|
725
|
+
MODEL_DB.sqls.should == ['SELECT artists.id, tags.b FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)']
|
726
|
+
a.first.tags.should == [Tag.load(:b=>2)]
|
727
|
+
MODEL_DB.sqls.length.should == 1
|
728
|
+
end
|
729
|
+
|
730
|
+
it "should respect the association's :graph_join_type option" do
|
731
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :graph_join_type=>:inner
|
732
|
+
@c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists INNER JOIN albums_artists ON (albums_artists.artist_id = artists.id) INNER JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) INNER JOIN tags ON (tags.id = albums_tags.tag_id)'
|
733
|
+
end
|
734
|
+
|
735
|
+
it "should respect the association's :join_type option on through" do
|
736
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id, :join_type=>:natural}, [:albums_tags, :album_id, :tag_id]], :graph_join_type=>:inner
|
737
|
+
@c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists INNER JOIN albums_artists ON (albums_artists.artist_id = artists.id) NATURAL JOIN albums ON (albums.id = albums_artists.album_id) INNER JOIN albums_tags ON (albums_tags.album_id = albums.id) INNER JOIN tags ON (tags.id = albums_tags.tag_id)'
|
738
|
+
end
|
739
|
+
|
740
|
+
it "should respect the association's :conditions option" do
|
741
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :conditions=>{:a=>32}
|
742
|
+
@c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON ((tags.id = albums_tags.tag_id) AND (tags.a = 32))'
|
743
|
+
end
|
744
|
+
|
745
|
+
it "should respect the association's :graph_conditions option" do
|
746
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :graph_conditions=>{:a=>42}
|
747
|
+
@c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON ((tags.id = albums_tags.tag_id) AND (tags.a = 42))'
|
748
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :graph_conditions=>{:a=>42}, :conditions=>{:a=>32}
|
749
|
+
@c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON ((tags.id = albums_tags.tag_id) AND (tags.a = 42))'
|
750
|
+
end
|
751
|
+
|
752
|
+
it "should respect the association's :conditions option on through" do
|
753
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id, :conditions=>{:a=>42}}, [:albums_tags, :album_id, :tag_id]]
|
754
|
+
@c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON ((albums.id = albums_artists.album_id) AND (albums.a = 42)) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)'
|
755
|
+
end
|
756
|
+
|
757
|
+
it "should respect the association's :graph_block option" do
|
758
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :graph_block=>proc{|ja,lja,js| {:active.qualify(ja)=>true}}
|
759
|
+
@c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON ((tags.id = albums_tags.tag_id) AND (tags.active IS TRUE))'
|
760
|
+
end
|
761
|
+
|
762
|
+
it "should respect the association's :block option on through" do
|
763
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id, :block=>proc{|ja,lja,js| {:active.qualify(ja)=>true}}}, [:albums_tags, :album_id, :tag_id]]
|
764
|
+
@c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON ((albums.id = albums_artists.album_id) AND (albums.active IS TRUE)) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)'
|
765
|
+
end
|
766
|
+
|
767
|
+
it "should respect the association's :graph_only_conditions option" do
|
768
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :graph_only_conditions=>{:a=>32}
|
769
|
+
@c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.a = 32)'
|
770
|
+
end
|
771
|
+
|
772
|
+
it "should respect the association's :only_conditions option on through" do
|
773
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id, :only_conditions=>{:a=>42}}, [:albums_tags, :album_id, :tag_id]]
|
774
|
+
@c1.eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.a = 42) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id)'
|
775
|
+
end
|
776
|
+
|
777
|
+
it "should create unique table aliases for all associations" do
|
778
|
+
@c1.eager_graph(:artists=>{:artists=>:artists}).sql.should == "SELECT artists.id, artists_0.id AS artists_0_id, artists_1.id AS artists_1_id, artists_2.id AS artists_2_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.album_id = albums.id) LEFT OUTER JOIN artists AS artists_0 ON (artists_0.id = albums_artists_0.artist_id) LEFT OUTER JOIN albums_artists AS albums_artists_1 ON (albums_artists_1.artist_id = artists_0.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_1.album_id) LEFT OUTER JOIN albums_artists AS albums_artists_2 ON (albums_artists_2.album_id = albums_0.id) LEFT OUTER JOIN artists AS artists_1 ON (artists_1.id = albums_artists_2.artist_id) LEFT OUTER JOIN albums_artists AS albums_artists_3 ON (albums_artists_3.artist_id = artists_1.id) LEFT OUTER JOIN albums AS albums_1 ON (albums_1.id = albums_artists_3.album_id) LEFT OUTER JOIN albums_artists AS albums_artists_4 ON (albums_artists_4.album_id = albums_1.id) LEFT OUTER JOIN artists AS artists_2 ON (artists_2.id = albums_artists_4.artist_id)"
|
779
|
+
end
|
780
|
+
|
781
|
+
it "should respect the association's :order" do
|
782
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :order=>[:blah1, :blah2]
|
783
|
+
@c1.order(:artists__blah2, :artists__blah3).eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) ORDER BY artists.blah2, artists.blah3, tags.blah1, tags.blah2'
|
784
|
+
end
|
785
|
+
|
786
|
+
it "should only qualify unqualified symbols, identifiers, or ordered versions in association's :order" do
|
787
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :order=>[:blah__id.identifier, :blah__id.identifier.desc, :blah__id.desc, :blah__id, :album_id, :album_id.desc, 1, 'RANDOM()'.lit, :a.qualify(:b)]
|
788
|
+
@c1.order(:artists__blah2, :artists__blah3).eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) ORDER BY artists.blah2, artists.blah3, tags.blah__id, tags.blah__id DESC, blah.id DESC, blah.id, tags.album_id, tags.album_id DESC, 1, RANDOM(), b.a'
|
789
|
+
end
|
790
|
+
|
791
|
+
it "should not respect the association's :order if :order_eager_graph is false" do
|
792
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :order=>[:blah1, :blah2], :order_eager_graph=>false
|
793
|
+
@c1.order(:artists__blah2, :artists__blah3).eager_graph(:tags).sql.should == 'SELECT artists.id, tags.id AS tags_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) ORDER BY artists.blah2, artists.blah3'
|
794
|
+
end
|
795
|
+
|
796
|
+
it "should add the associations :order for multiple associations" do
|
797
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :order=>[:blah1, :blah2]
|
798
|
+
@c1.many_through_many :albums, [[:albums_artists, :artist_id, :album_id]], :order=>[:blah3, :blah4]
|
799
|
+
@c1.eager_graph(:tags, :albums).sql.should == 'SELECT artists.id, tags.id AS tags_id, albums_0.id AS albums_0_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_artists AS albums_artists_0 ON (albums_artists_0.artist_id = artists.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id) ORDER BY tags.blah1, tags.blah2, albums_0.blah3, albums_0.blah4'
|
800
|
+
end
|
801
|
+
|
802
|
+
it "should add the association's :order for cascading associations" do
|
803
|
+
@c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]], :order=>[:blah1, :blah2]
|
804
|
+
Tag.many_through_many :tracks, [[:albums_tags, :tag_id, :album_id], [:albums, :id, :id]], :right_primary_key=>:album_id, :order=>[:blah3, :blah4]
|
805
|
+
@c1.eager_graph(:tags=>:tracks).sql.should == 'SELECT artists.id, tags.id AS tags_id, tracks.id AS tracks_id FROM artists LEFT OUTER JOIN albums_artists ON (albums_artists.artist_id = artists.id) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_tags AS albums_tags_0 ON (albums_tags_0.tag_id = tags.id) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_tags_0.album_id) LEFT OUTER JOIN tracks ON (tracks.album_id = albums_0.id) ORDER BY tags.blah1, tags.blah2, tracks.blah3, tracks.blah4'
|
806
|
+
end
|
807
|
+
|
808
|
+
it "should use the correct qualifier when graphing multiple tables with extra conditions" do
|
809
|
+
@c1.many_through_many :tags, [{:table=>:albums_artists, :left=>:artist_id, :right=>:album_id, :conditions=>{:a=>:b}}, {:table=>:albums, :left=>:id, :right=>:id}, [:albums_tags, :album_id, :tag_id]]
|
810
|
+
@c1.many_through_many :albums, [{:table=>:albums_artists, :left=>:artist_id, :right=>:album_id, :conditions=>{:c=>:d}}]
|
811
|
+
@c1.eager_graph(:tags, :albums).sql.should == 'SELECT artists.id, tags.id AS tags_id, albums_0.id AS albums_0_id FROM artists LEFT OUTER JOIN albums_artists ON ((albums_artists.artist_id = artists.id) AND (albums_artists.a = artists.b)) LEFT OUTER JOIN albums ON (albums.id = albums_artists.album_id) LEFT OUTER JOIN albums_tags ON (albums_tags.album_id = albums.id) LEFT OUTER JOIN tags ON (tags.id = albums_tags.tag_id) LEFT OUTER JOIN albums_artists AS albums_artists_0 ON ((albums_artists_0.artist_id = artists.id) AND (albums_artists_0.c = artists.d)) LEFT OUTER JOIN albums AS albums_0 ON (albums_0.id = albums_artists_0.album_id)'
|
812
|
+
end
|
813
|
+
end
|