sequel 4.7.0 → 4.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +46 -0
  3. data/README.rdoc +25 -1
  4. data/doc/active_record.rdoc +1 -1
  5. data/doc/advanced_associations.rdoc +143 -17
  6. data/doc/association_basics.rdoc +80 -59
  7. data/doc/release_notes/4.8.0.txt +175 -0
  8. data/lib/sequel/adapters/odbc.rb +1 -1
  9. data/lib/sequel/adapters/odbc/mssql.rb +4 -2
  10. data/lib/sequel/adapters/shared/postgres.rb +19 -3
  11. data/lib/sequel/adapters/shared/sqlite.rb +3 -3
  12. data/lib/sequel/ast_transformer.rb +1 -1
  13. data/lib/sequel/dataset/actions.rb +1 -1
  14. data/lib/sequel/dataset/graph.rb +23 -9
  15. data/lib/sequel/dataset/misc.rb +2 -2
  16. data/lib/sequel/dataset/sql.rb +3 -3
  17. data/lib/sequel/extensions/columns_introspection.rb +1 -1
  18. data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +1 -1
  19. data/lib/sequel/extensions/pg_array.rb +1 -1
  20. data/lib/sequel/extensions/pg_array_ops.rb +6 -0
  21. data/lib/sequel/extensions/pg_hstore_ops.rb +7 -0
  22. data/lib/sequel/extensions/pg_json_ops.rb +5 -0
  23. data/lib/sequel/extensions/query.rb +8 -2
  24. data/lib/sequel/extensions/to_dot.rb +1 -1
  25. data/lib/sequel/model/associations.rb +476 -152
  26. data/lib/sequel/plugins/class_table_inheritance.rb +11 -3
  27. data/lib/sequel/plugins/dataset_associations.rb +21 -18
  28. data/lib/sequel/plugins/many_through_many.rb +87 -20
  29. data/lib/sequel/plugins/nested_attributes.rb +12 -0
  30. data/lib/sequel/plugins/pg_array_associations.rb +31 -12
  31. data/lib/sequel/plugins/single_table_inheritance.rb +9 -1
  32. data/lib/sequel/sql.rb +1 -0
  33. data/lib/sequel/version.rb +1 -1
  34. data/spec/adapters/mssql_spec.rb +2 -2
  35. data/spec/adapters/postgres_spec.rb +7 -0
  36. data/spec/core/object_graph_spec.rb +250 -196
  37. data/spec/extensions/core_refinements_spec.rb +1 -1
  38. data/spec/extensions/dataset_associations_spec.rb +100 -6
  39. data/spec/extensions/many_through_many_spec.rb +1002 -19
  40. data/spec/extensions/nested_attributes_spec.rb +24 -0
  41. data/spec/extensions/pg_array_associations_spec.rb +17 -12
  42. data/spec/extensions/pg_array_spec.rb +4 -2
  43. data/spec/extensions/spec_helper.rb +1 -1
  44. data/spec/integration/associations_test.rb +1003 -48
  45. data/spec/integration/dataset_test.rb +12 -5
  46. data/spec/integration/prepared_statement_test.rb +1 -1
  47. data/spec/integration/type_test.rb +1 -1
  48. data/spec/model/associations_spec.rb +467 -130
  49. data/spec/model/eager_loading_spec.rb +332 -5
  50. metadata +5 -3
@@ -71,6 +71,30 @@ describe "NestedAttributes plugin" do
71
71
  ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
72
72
  end
73
73
 
74
+ it "should support creating new one_to_many and one_to_one objects with presence validations on the foreign key" do
75
+ @Album.class_eval do
76
+ plugin :validation_helpers
77
+ def validate
78
+ validates_presence :artist_id
79
+ super
80
+ end
81
+ end
82
+ a = @Artist.new({:name=>'Ar', :albums_attributes=>[{:name=>'Al'}]})
83
+ @db.sqls.should == []
84
+ a.save
85
+ check_sql_array("INSERT INTO artists (name) VALUES ('Ar')",
86
+ ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
87
+
88
+ a = @Artist.new(:name=>'Ar')
89
+ a.id = 1
90
+ a.first_album_attributes = {:name=>'Al'}
91
+ @db.sqls.should == []
92
+ a.save
93
+ check_sql_array(["INSERT INTO artists (name, id) VALUES ('Ar', 1)", "INSERT INTO artists (id, name) VALUES (1, 'Ar')"],
94
+ "UPDATE albums SET artist_id = NULL WHERE (artist_id = 1)",
95
+ ["INSERT INTO albums (artist_id, name) VALUES (1, 'Al')", "INSERT INTO albums (name, artist_id) VALUES ('Al', 1)"])
96
+ end
97
+
74
98
  it "should support creating new many_to_many objects" do
75
99
  a = @Album.new({:name=>'Al', :tags_attributes=>[{:name=>'T'}]})
76
100
  @db.sqls.should == []
@@ -69,8 +69,8 @@ describe Sequel::Model, "pg_array_associations" do
69
69
  end
70
70
 
71
71
  it "should allowing filtering by associations with :conditions" do
72
- @c1.filter(:a_tags=>@o2).sql.should == "SELECT * FROM artists WHERE ((artists.tag_ids @> ARRAY[2]) AND coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id = 2)))), 'f'))"
73
- @c2.filter(:a_artists=>@o1).sql.should == "SELECT * FROM tags WHERE ((tags.id IN (1, 2, 3)) AND (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id = 1)))))"
72
+ @c1.filter(:a_tags=>@o2).sql.should == "SELECT * FROM artists WHERE coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id = 2)))), 'f')"
73
+ @c2.filter(:a_artists=>@o1).sql.should == "SELECT * FROM tags WHERE (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id = 1))))"
74
74
  end
75
75
 
76
76
  it "should allowing excluding by associations" do
@@ -79,8 +79,8 @@ describe Sequel::Model, "pg_array_associations" do
79
79
  end
80
80
 
81
81
  it "should allowing excluding by associations with :conditions" do
82
- @c1.exclude(:a_tags=>@o2).sql.should == "SELECT * FROM artists WHERE (NOT (artists.tag_ids @> ARRAY[2]) OR NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id = 2)))), 'f') OR (artists.tag_ids IS NULL))"
83
- @c2.exclude(:a_artists=>@o1).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (1, 2, 3)) OR (tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id = 1)))) OR (tags.id IS NULL))"
82
+ @c1.exclude(:a_tags=>@o2).sql.should == "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id = 2)))), 'f') OR (artists.tag_ids IS NULL))"
83
+ @c2.exclude(:a_artists=>@o1).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id = 1)))) OR (tags.id IS NULL))"
84
84
  end
85
85
 
86
86
  it "should allowing filtering by multiple associations" do
@@ -89,8 +89,8 @@ describe Sequel::Model, "pg_array_associations" do
89
89
  end
90
90
 
91
91
  it "should allowing filtering by multiple associations with :conditions" do
92
- @c1.filter(:a_tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE ((artists.tag_ids && ARRAY[1,2]) AND coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (1, 2))))), 'f'))"
93
- @c2.filter(:a_artists=>[@c1.load(:id=>7, :tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:id=>8, :tag_ids=>Sequel.pg_array([4, 5]))]).sql.should == "SELECT * FROM tags WHERE ((tags.id IN (3, 4, 5)) AND (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (7, 8))))))"
92
+ @c1.filter(:a_tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (1, 2))))), 'f')"
93
+ @c2.filter(:a_artists=>[@c1.load(:id=>7, :tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:id=>8, :tag_ids=>Sequel.pg_array([4, 5]))]).sql.should == "SELECT * FROM tags WHERE (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (7, 8)))))"
94
94
  end
95
95
 
96
96
  it "should allowing excluding by multiple associations" do
@@ -99,8 +99,8 @@ describe Sequel::Model, "pg_array_associations" do
99
99
  end
100
100
 
101
101
  it "should allowing excluding by multiple associations with :conditions" do
102
- @c1.exclude(:a_tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE (NOT (artists.tag_ids && ARRAY[1,2]) OR NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (1, 2))))), 'f') OR (artists.tag_ids IS NULL))"
103
- @c2.exclude(:a_artists=>[@c1.load(:id=>7, :tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:id=>8, :tag_ids=>Sequel.pg_array([4, 5]))]).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (3, 4, 5)) OR (tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (7, 8))))) OR (tags.id IS NULL))"
102
+ @c1.exclude(:a_tags=>[@c2.load(:id=>1), @c2.load(:id=>2)]).sql.should == "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (1, 2))))), 'f') OR (artists.tag_ids IS NULL))"
103
+ @c2.exclude(:a_artists=>[@c1.load(:id=>7, :tag_ids=>Sequel.pg_array([3, 4])), @c1.load(:id=>8, :tag_ids=>Sequel.pg_array([4, 5]))]).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (7, 8))))) OR (tags.id IS NULL))"
104
104
  end
105
105
 
106
106
  it "should allowing filtering/excluding associations with NULL or empty values" do
@@ -122,8 +122,8 @@ describe Sequel::Model, "pg_array_associations" do
122
122
  end
123
123
 
124
124
  it "should allowing filtering by association datasets with :conditions" do
125
- @c1.filter(:a_tags=>@c2.where(:id=>1)).sql.should == "SELECT * FROM artists WHERE (coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE (id = 1))), 'f') AND coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (id = 1)))))), 'f'))"
126
- @c2.filter(:a_artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE ((tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE (id = 1))) AND (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (id = 1)))))))"
125
+ @c1.filter(:a_tags=>@c2.where(:id=>1)).sql.should == "SELECT * FROM artists WHERE coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (id = 1)))))), 'f')"
126
+ @c2.filter(:a_artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE (tags.id IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (id = 1))))))"
127
127
  end
128
128
 
129
129
  it "should allowing excluding by association datasets" do
@@ -132,8 +132,8 @@ describe Sequel::Model, "pg_array_associations" do
132
132
  end
133
133
 
134
134
  it "should allowing excluding by association datasets with :conditions" do
135
- @c1.exclude(:a_tags=>@c2.where(:id=>1)).sql.should == "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE (id = 1))), 'f') OR NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (id = 1)))))), 'f') OR (artists.tag_ids IS NULL))"
136
- @c2.exclude(:a_artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE (id = 1))) OR (tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (id = 1)))))) OR (tags.id IS NULL))"
135
+ @c1.exclude(:a_tags=>@c2.where(:id=>1)).sql.should == "SELECT * FROM artists WHERE (NOT coalesce((artists.tag_ids && (SELECT array_agg(tags.id) FROM tags WHERE ((name = 'A') AND (tags.id IS NOT NULL) AND (tags.id IN (SELECT tags.id FROM tags WHERE (id = 1)))))), 'f') OR (artists.tag_ids IS NULL))"
136
+ @c2.exclude(:a_artists=>@c1.where(:id=>1)).sql.should == "SELECT * FROM tags WHERE ((tags.id NOT IN (SELECT unnest(artists.tag_ids) FROM artists WHERE ((name = 'A') AND (artists.tag_ids IS NOT NULL) AND (artists.id IN (SELECT artists.id FROM artists WHERE (id = 1)))))) OR (tags.id IS NULL))"
137
137
  end
138
138
 
139
139
  it "filter by associations should respect key options" do
@@ -393,6 +393,11 @@ describe Sequel::Model, "pg_array_associations" do
393
393
  DB.sqls.should == []
394
394
  end
395
395
 
396
+ it "should support association_join" do
397
+ @c1.association_join(:tags).sql.should == "SELECT * FROM artists INNER JOIN tags ON (artists.tag_ids @> ARRAY[tags.id])"
398
+ @c2.association_join(:artists).sql.should == "SELECT * FROM tags INNER JOIN artists ON (artists.tag_ids @> ARRAY[tags.id])"
399
+ end
400
+
396
401
  it "should eagerly graph associations" do
397
402
  @c2.dataset._fetch = {:id=>2, :artists_id=>1, :tag_ids=>Sequel.pg_array([1,2,3])}
398
403
  @c1.dataset._fetch = {:id=>1, :tags_id=>2, :tag_ids=>Sequel.pg_array([1,2,3])}
@@ -283,8 +283,10 @@ describe "pg_array extension" do
283
283
  @db.typecast_value(:foo15_array, ['t']).should == [true]
284
284
  end
285
285
 
286
- it "should raise an error if using :scalar_oid option with unexisting scalar conversion proc" do
287
- proc{Sequel::Postgres::PGArray.register('foo', :scalar_oid=>0)}.should raise_error(Sequel::Error)
286
+ it "should not raise an error if using :scalar_oid option with unexisting scalar conversion proc" do
287
+ h = {}
288
+ Sequel::Postgres::PGArray.register('foo', :oid=>1234, :scalar_oid=>0, :type_procs=>h)
289
+ h[1234].call('{t}').should == ["t"]
288
290
  end
289
291
 
290
292
  it "should raise an error if using :converter option and a block argument" do
@@ -31,7 +31,7 @@ rescue LoadError
31
31
  end
32
32
 
33
33
  Sequel.extension :meta_def
34
- Sequel.extension :core_refinements if RUBY_VERSION >= '2.0.0' && respond_to?(:using)
34
+ Sequel.extension :core_refinements if RUBY_VERSION >= '2.0.0' && RUBY_ENGINE == 'ruby'
35
35
 
36
36
  def skip_warn(s)
37
37
  warn "Skipping test of #{s}" if ENV["SKIPPED_TEST_WARN"]
@@ -29,6 +29,40 @@ shared_examples_for "one_to_one eager limit strategies" do
29
29
  [@album, same_album].should include(a.first.first_album)
30
30
  a.last.first_album.should == nil
31
31
  end
32
+
33
+ specify "eager graphing one_to_one associations should work correctly" do
34
+ @album.update(:artist => @artist)
35
+ diff_album = @diff_album.call
36
+ ar = @pr.call[1]
37
+ ds = Artist.order(:artists__name)
38
+ limit_strategy = {:limit_strategy=>@els[:eager_limit_strategy]}
39
+
40
+ a = ds.eager_graph_with_options(:first_album, limit_strategy).all
41
+ a.should == [@artist, ar]
42
+ a.first.first_album.should == @album
43
+ a.last.first_album.should == nil
44
+ a.first.first_album.values.should == @album.values
45
+
46
+ a = ds.eager_graph_with_options(:last_album, limit_strategy).all
47
+ a = ds.eager_graph(:last_album).all
48
+ a.should == [@artist, ar]
49
+ a.first.last_album.should == diff_album
50
+ a.last.last_album.should == nil
51
+ a.first.last_album.values.should == diff_album.values
52
+
53
+ a = ds.eager_graph_with_options(:second_album, limit_strategy).all
54
+ a = ds.eager_graph(:second_album).all
55
+ a.should == [@artist, ar]
56
+ a.first.second_album.should == diff_album
57
+ a.last.second_album.should == nil
58
+ a.first.second_album.values.should == diff_album.values
59
+
60
+ same_album = @same_album.call
61
+ a = ds.eager_graph_with_options(:first_album, limit_strategy).all
62
+ a.should == [@artist, ar]
63
+ [@album, same_album].should include(a.first.first_album)
64
+ a.last.first_album.should == nil
65
+ end
32
66
  end
33
67
 
34
68
  shared_examples_for "one_to_many eager limit strategies" do
@@ -59,6 +93,88 @@ shared_examples_for "one_to_many eager limit strategies" do
59
93
  ars.first.not_first_albums.map{|x| x.values}.should == [middle_album, diff_album].map{|x| x.values}
60
94
  ars.first.last_two_albums.map{|x| x.values}.should == [diff_album, middle_album].map{|x| x.values}
61
95
  end
96
+
97
+ specify "should correctly handle limits and offsets when eager graphing one_to_many associations" do
98
+ @album.update(:artist => @artist)
99
+ middle_album = @middle_album.call
100
+ diff_album = @diff_album.call
101
+ ar = @pr.call[1]
102
+ ds = Artist.order(:artists__name)
103
+ limit_strategy = {:limit_strategy=>@els[:eager_limit_strategy]}
104
+
105
+ ars = ds.eager_graph_with_options(:first_two_albums, limit_strategy).all
106
+ ars.should == [@artist, ar]
107
+ ars.first.first_two_albums.should == [@album, middle_album]
108
+ ars.last.first_two_albums.should == []
109
+ ars.first.first_two_albums.map{|x| x.values}.should == [@album, middle_album].map{|x| x.values}
110
+
111
+ ars = ds.eager_graph_with_options(:second_two_albums, limit_strategy).all
112
+ ars.should == [@artist, ar]
113
+ ars.first.second_two_albums.should == [middle_album, diff_album]
114
+ ars.last.second_two_albums.should == []
115
+ ars.first.second_two_albums.map{|x| x.values}.should == [middle_album, diff_album].map{|x| x.values}
116
+
117
+ ars = ds.eager_graph_with_options(:not_first_albums, limit_strategy).all
118
+ ars.should == [@artist, ar]
119
+ ars.first.not_first_albums.should == [middle_album, diff_album]
120
+ ars.last.not_first_albums.should == []
121
+ ars.first.not_first_albums.map{|x| x.values}.should == [middle_album, diff_album].map{|x| x.values}
122
+
123
+ ars = ds.eager_graph_with_options(:last_two_albums, limit_strategy).all
124
+ ars.should == [@artist, ar]
125
+ ars.first.last_two_albums.should == [diff_album, middle_album]
126
+ ars.last.last_two_albums.should == []
127
+ ars.first.last_two_albums.map{|x| x.values}.should == [diff_album, middle_album].map{|x| x.values}
128
+ end
129
+ end
130
+
131
+ shared_examples_for "one_through_one eager limit strategies" do
132
+ specify "should correctly handle offsets when eager loading one_through_one associations" do
133
+ Album.one_through_one :first_tag, {:clone=>:first_tag}.merge(@els) if @els
134
+ Album.one_through_one :second_tag, {:clone=>:second_tag}.merge(@els) if @els
135
+ Album.one_through_one :last_tag, {:clone=>:last_tag}.merge(@els) if @els
136
+ tu, tv = @other_tags.call
137
+ al = @pr.call.first
138
+
139
+ als = Album.eager(:first_tag, :second_tag, :last_tag).order(:name).all
140
+ als.should == [@album, al]
141
+ als.first.first_tag.should == @tag
142
+ als.first.second_tag.should == tu
143
+ als.first.last_tag.should == tv
144
+ als.last.first_tag.should == nil
145
+ als.last.second_tag.should == nil
146
+ als.last.last_tag.should == nil
147
+
148
+ # Check that no extra columns got added by the eager loading
149
+ als.first.first_tag.values.should == @tag.values
150
+ als.first.second_tag.values.should == tu.values
151
+ als.first.last_tag.values.should == tv.values
152
+ end
153
+
154
+ specify "should correctly handle offsets when eager graphing one_through_one associations" do
155
+ tu, tv = @other_tags.call
156
+ al = @pr.call.first
157
+ ds = Album.order(:albums__name)
158
+ limit_strategy = {:limit_strategy=>@els[:eager_limit_strategy]}
159
+
160
+ als = ds.eager_graph_with_options(:first_tag, limit_strategy).all
161
+ als.should == [@album, al]
162
+ als.first.first_tag.should == @tag
163
+ als.last.first_tag.should == nil
164
+ als.first.first_tag.values.should == @tag.values
165
+
166
+ als = ds.eager_graph_with_options(:second_tag, limit_strategy).all
167
+ als.should == [@album, al]
168
+ als.first.second_tag.should == tu
169
+ als.last.second_tag.should == nil
170
+ als.first.second_tag.values.should == tu.values
171
+
172
+ als = ds.eager_graph_with_options(:last_tag, limit_strategy).all
173
+ als.should == [@album, al]
174
+ als.first.last_tag.should == tv
175
+ als.last.last_tag.should == nil
176
+ als.first.last_tag.values.should == tv.values
177
+ end
62
178
  end
63
179
 
64
180
  shared_examples_for "many_to_many eager limit strategies" do
@@ -69,6 +185,7 @@ shared_examples_for "many_to_many eager limit strategies" do
69
185
  Album.send @many_to_many_method||:many_to_many, :last_two_tags, {:clone=>:last_two_tags}.merge(@els) if @els
70
186
  tu, tv = @other_tags.call
71
187
  al = @pr.call.first
188
+ al.add_tag(tu)
72
189
 
73
190
  als = Album.eager(:first_two_tags, :second_two_tags, :not_first_tags, :last_two_tags).order(:name).all
74
191
  als.should == [@album, al]
@@ -76,9 +193,9 @@ shared_examples_for "many_to_many eager limit strategies" do
76
193
  als.first.second_two_tags.should == [tu, tv]
77
194
  als.first.not_first_tags.should == [tu, tv]
78
195
  als.first.last_two_tags.should == [tv, tu]
79
- als.last.first_two_tags.should == []
196
+ als.last.first_two_tags.should == [tu]
80
197
  als.last.second_two_tags.should == []
81
- als.last.last_two_tags.should == []
198
+ als.last.last_two_tags.should == [tu]
82
199
 
83
200
  # Check that no extra columns got added by the eager loading
84
201
  als.first.first_two_tags.map{|x| x.values}.should == [@tag, tu].map{|x| x.values}
@@ -86,6 +203,38 @@ shared_examples_for "many_to_many eager limit strategies" do
86
203
  als.first.not_first_tags.map{|x| x.values}.should == [tu, tv].map{|x| x.values}
87
204
  als.first.last_two_tags.map{|x| x.values}.should == [tv, tu].map{|x| x.values}
88
205
  end
206
+
207
+ specify "should correctly handle limits and offsets when eager loading many_to_many associations" do
208
+ tu, tv = @other_tags.call
209
+ al = @pr.call.first
210
+ al.add_tag(tu)
211
+ ds = Album.order(:albums__name)
212
+ limit_strategy = {:limit_strategy=>(@els||{})[:eager_limit_strategy]}
213
+
214
+ als = ds.eager_graph_with_options(:first_two_tags, limit_strategy).all
215
+ als.should == [@album, al]
216
+ als.first.first_two_tags.should == [@tag, tu]
217
+ als.last.first_two_tags.should == [tu]
218
+ als.first.first_two_tags.map{|x| x.values}.should == [@tag, tu].map{|x| x.values}
219
+
220
+ als = ds.eager_graph_with_options(:second_two_tags, limit_strategy).all
221
+ als.should == [@album, al]
222
+ als.first.second_two_tags.should == [tu, tv]
223
+ als.last.second_two_tags.should == []
224
+ als.first.second_two_tags.map{|x| x.values}.should == [tu, tv].map{|x| x.values}
225
+
226
+ als = ds.eager_graph_with_options(:not_first_tags, limit_strategy).all
227
+ als.should == [@album, al]
228
+ als.first.not_first_tags.should == [tu, tv]
229
+ als.last.not_first_tags.should == []
230
+ als.first.not_first_tags.map{|x| x.values}.should == [tu, tv].map{|x| x.values}
231
+
232
+ als = ds.eager_graph_with_options(:last_two_tags, limit_strategy).all
233
+ als.should == [@album, al]
234
+ als.first.last_two_tags.should == [tv, tu]
235
+ als.last.last_two_tags.should == [tu]
236
+ als.first.last_two_tags.map{|x| x.values}.should == [tv, tu].map{|x| x.values}
237
+ end
89
238
  end
90
239
 
91
240
  shared_examples_for "many_through_many eager limit strategies" do
@@ -96,7 +245,9 @@ shared_examples_for "many_through_many eager limit strategies" do
96
245
  Artist.many_through_many :last_two_tags, {:clone=>:last_two_tags}.merge(@els) if @els
97
246
  @album.update(:artist => @artist)
98
247
  tu, tv = @other_tags.call
99
- ar = @pr.call[1]
248
+ al, ar, _ = @pr.call
249
+ al.update(:artist=>ar)
250
+ al.add_tag(tu)
100
251
 
101
252
  ars = Artist.eager(:first_two_tags, :second_two_tags, :not_first_tags, :last_two_tags).order(:name).all
102
253
  ars.should == [@artist, ar]
@@ -104,9 +255,10 @@ shared_examples_for "many_through_many eager limit strategies" do
104
255
  ars.first.second_two_tags.should == [tu, tv]
105
256
  ars.first.not_first_tags.should == [tu, tv]
106
257
  ars.first.last_two_tags.should == [tv, tu]
107
- ars.last.first_two_tags.should == []
258
+ ars.last.first_two_tags.should == [tu]
108
259
  ars.last.second_two_tags.should == []
109
- ars.last.last_two_tags.should == []
260
+ ars.last.not_first_tags.should == []
261
+ ars.last.last_two_tags.should == [tu]
110
262
 
111
263
  # Check that no extra columns got added by the eager loading
112
264
  ars.first.first_two_tags.map{|x| x.values}.should == [@tag, tu].map{|x| x.values}
@@ -114,23 +266,195 @@ shared_examples_for "many_through_many eager limit strategies" do
114
266
  ars.first.not_first_tags.map{|x| x.values}.should == [tu, tv].map{|x| x.values}
115
267
  ars.first.last_two_tags.map{|x| x.values}.should == [tv, tu].map{|x| x.values}
116
268
  end
269
+
270
+ specify "should correctly handle limits and offsets when eager loading many_through_many associations" do
271
+ @album.update(:artist => @artist)
272
+ tu, tv = @other_tags.call
273
+ al, ar, _ = @pr.call
274
+ al.update(:artist=>ar)
275
+ al.add_tag(tu)
276
+ ds = Artist.order(:artists__name)
277
+ limit_strategy = {:limit_strategy=>@els[:eager_limit_strategy]}
278
+
279
+ ars = ds.eager_graph_with_options(:first_two_tags, limit_strategy).all
280
+ ars.should == [@artist, ar]
281
+ ars.first.first_two_tags.should == [@tag, tu]
282
+ ars.last.first_two_tags.should == [tu]
283
+ ars.first.first_two_tags.map{|x| x.values}.should == [@tag, tu].map{|x| x.values}
284
+
285
+ ars = ds.eager_graph_with_options(:second_two_tags, limit_strategy).all
286
+ ars.should == [@artist, ar]
287
+ ars.first.second_two_tags.should == [tu, tv]
288
+ ars.last.second_two_tags.should == []
289
+ ars.first.second_two_tags.map{|x| x.values}.should == [tu, tv].map{|x| x.values}
290
+
291
+ ars = ds.eager_graph_with_options(:not_first_tags, limit_strategy).all
292
+ ars.should == [@artist, ar]
293
+ ars.first.not_first_tags.should == [tu, tv]
294
+ ars.last.not_first_tags.should == []
295
+ ars.first.not_first_tags.map{|x| x.values}.should == [tu, tv].map{|x| x.values}
296
+
297
+ ars = ds.eager_graph_with_options(:last_two_tags, limit_strategy).all
298
+ ars.should == [@artist, ar]
299
+ ars.first.last_two_tags.should == [tv, tu]
300
+ ars.last.last_two_tags.should == [tu]
301
+ ars.first.last_two_tags.map{|x| x.values}.should == [tv, tu].map{|x| x.values}
302
+ end
303
+ end
304
+
305
+ shared_examples_for "one_through_many eager limit strategies" do
306
+ specify "should correctly handle offsets when eager loading one_through_many associations" do
307
+ Artist.one_through_many :first_tag, {:clone=>:first_tag}.merge(@els) if @els
308
+ Artist.one_through_many :second_tag, {:clone=>:second_tag}.merge(@els) if @els
309
+ Artist.one_through_many :last_tag, {:clone=>:last_tag}.merge(@els) if @els
310
+ @album.update(:artist => @artist)
311
+ tu, tv = @other_tags.call
312
+ al, ar, _ = @pr.call
313
+ al.update(:artist=>ar)
314
+ al.add_tag(tu)
315
+
316
+ ars = Artist.eager(:first_tag, :second_tag, :last_tag).order(:name).all
317
+ ars.should == [@artist, ar]
318
+ ars.first.first_tag.should == @tag
319
+ ars.first.second_tag.should == tu
320
+ ars.first.last_tag.should == tv
321
+ ars.last.first_tag.should == tu
322
+ ars.last.second_tag.should == nil
323
+ ars.last.last_tag.should == tu
324
+
325
+ # Check that no extra columns got added by the eager loading
326
+ ars.first.first_tag.values.should == @tag.values
327
+ ars.first.second_tag.values.should == tu.values
328
+ ars.first.last_tag.values.should == tv.values
329
+ end
330
+
331
+ specify "should correctly handle offsets when eager graphing one_through_many associations" do
332
+ @album.update(:artist => @artist)
333
+ tu, tv = @other_tags.call
334
+ al, ar, _ = @pr.call
335
+ al.update(:artist=>ar)
336
+ al.add_tag(tu)
337
+ ds = Artist.order(:artists__name)
338
+ limit_strategy = {:limit_strategy=>@els[:eager_limit_strategy]}
339
+
340
+ ars = ds.eager_graph_with_options(:first_tag, limit_strategy).all
341
+ ars.should == [@artist, ar]
342
+ ars.first.first_tag.should == @tag
343
+ ars.last.first_tag.should == tu
344
+ ars.first.first_tag.values.should == @tag.values
345
+
346
+ ars = ds.eager_graph_with_options(:second_tag, limit_strategy).all
347
+ ars.should == [@artist, ar]
348
+ ars.first.second_tag.should == tu
349
+ ars.last.second_tag.should == nil
350
+ ars.first.second_tag.values.should == tu.values
351
+
352
+ ars = ds.eager_graph_with_options(:last_tag, limit_strategy).all
353
+ ars.should == [@artist, ar]
354
+ ars.first.last_tag.should == tv
355
+ ars.last.last_tag.should == tu
356
+ ars.first.last_tag.values.should == tv.values
357
+ end
117
358
  end
118
359
 
119
360
  shared_examples_for "eager limit strategies" do
120
361
  it_should_behave_like "one_to_one eager limit strategies"
121
362
  it_should_behave_like "one_to_many eager limit strategies"
122
363
  it_should_behave_like "many_to_many eager limit strategies"
364
+ it_should_behave_like "one_through_one eager limit strategies"
123
365
  it_should_behave_like "many_through_many eager limit strategies"
366
+ it_should_behave_like "one_through_many eager limit strategies"
124
367
  end
125
368
 
126
369
  shared_examples_for "filtering/excluding by associations" do
370
+ specify "should handle association inner joins" do
371
+ @Artist.association_join(:albums).all.should == []
372
+ @Artist.association_join(:first_album).all.should == []
373
+ @Album.association_join(:artist).all.should == []
374
+ @Album.association_join(:tags).all.should == []
375
+ @Album.association_join(:alias_tags).all.should == []
376
+ @Tag.association_join(:albums).all.should == []
377
+ unless @no_many_through_many
378
+ @Artist.association_join(:tags).all.should == []
379
+ @Artist.association_join(:first_tag).all.should == []
380
+ end
381
+
382
+ @album.update(:artist => @artist)
383
+ @album.add_tag(@tag)
384
+
385
+ @Artist.association_join(:albums).select_all(:artists).all.should == [@artist]
386
+ @Artist.association_join(:first_album).select_all(:artists).all.should == [@artist]
387
+ @Album.association_join(:artist).select_all(:albums).all.should == [@album]
388
+ @Album.association_join(:tags).select_all(:albums).all.should == [@album]
389
+ @Album.association_join(:alias_tags).select_all(:albums).all.should == [@album]
390
+ @Tag.association_join(:albums).select_all(:tags).all.should == [@tag]
391
+ unless @no_many_through_many
392
+ @Artist.association_join(:tags).select_all(:artists).all.should == [@artist]
393
+ @Artist.association_join(:first_tag).select_all(:artists).all.should == [@artist]
394
+ end
395
+
396
+ @Artist.association_join(:albums).select_all(:albums).naked.all.should == [@album.values]
397
+ @Artist.association_join(:first_album).select_all(:first_album).naked.all.should == [@album.values]
398
+ @Album.association_join(:artist).select_all(:artist).naked.all.should == [@artist.values]
399
+ @Album.association_join(:tags).select_all(:tags).naked.all.should == [@tag.values]
400
+ @Album.association_join(:alias_tags).select_all(:alias_tags).naked.all.should == [@tag.values]
401
+ @Tag.association_join(:albums).select_all(:albums).naked.all.should == [@album.values]
402
+ unless @no_many_through_many
403
+ @Artist.association_join(:tags).select_all(:tags).naked.all.should == [@tag.values]
404
+ @Artist.association_join(:first_tag).select_all(:first_tag).naked.all.should == [@tag.values]
405
+ end
406
+ end
407
+
408
+ specify "should handle association left joins" do
409
+ @Artist.association_left_join(:albums).select_all(:artists).all.should == [@artist]
410
+ @Artist.association_left_join(:first_album).select_all(:artists).all.should == [@artist]
411
+ @Album.association_left_join(:artist).select_all(:albums).all.should == [@album]
412
+ @Album.association_left_join(:tags).select_all(:albums).all.should == [@album]
413
+ @Album.association_left_join(:alias_tags).select_all(:albums).all.should == [@album]
414
+ @Tag.association_left_join(:albums).select_all(:tags).all.should == [@tag]
415
+ unless @no_many_through_many
416
+ @Artist.association_left_join(:tags).select_all(:artists).all.should == [@artist]
417
+ @Artist.association_left_join(:first_tag).select_all(:artists).all.should == [@artist]
418
+ end
419
+
420
+ nil_hash = lambda{|obj| [obj.values.keys.inject({}){|h,k| h[k] = nil; h}]}
421
+ @Artist.association_left_join(:albums).select_all(:albums).naked.all.should == nil_hash[@album]
422
+ @Artist.association_left_join(:first_album).select_all(:first_album).naked.all.should == nil_hash[@album]
423
+ @Album.association_left_join(:artist).select_all(:artist).naked.all.should == nil_hash[@artist]
424
+ @Album.association_left_join(:tags).select_all(:tags).naked.all.should == nil_hash[@tag]
425
+ @Album.association_left_join(:alias_tags).select_all(:alias_tags).naked.all.should == nil_hash[@tag]
426
+ @Tag.association_left_join(:albums).select_all(:albums).naked.all.should == nil_hash[@album]
427
+ unless @no_many_through_many
428
+ @Artist.association_left_join(:tags).select_all(:tags).naked.all.should == nil_hash[@tag]
429
+ @Artist.association_left_join(:first_tag).select_all(:first_tag).naked.all.should == nil_hash[@tag]
430
+ end
431
+
432
+ @album.update(:artist => @artist)
433
+ @album.add_tag(@tag)
434
+
435
+
436
+ @Artist.association_left_join(:albums).select_all(:albums).naked.all.should == [@album.values]
437
+ @Artist.association_left_join(:first_album).select_all(:first_album).naked.all.should == [@album.values]
438
+ @Album.association_left_join(:artist).select_all(:artist).naked.all.should == [@artist.values]
439
+ @Album.association_left_join(:tags).select_all(:tags).naked.all.should == [@tag.values]
440
+ @Album.association_left_join(:alias_tags).select_all(:alias_tags).naked.all.should == [@tag.values]
441
+ @Tag.association_left_join(:albums).select_all(:albums).naked.all.should == [@album.values]
442
+ unless @no_many_through_many
443
+ @Artist.association_left_join(:tags).select_all(:tags).naked.all.should == [@tag.values]
444
+ @Artist.association_left_join(:first_tag).select_all(:first_tag).naked.all.should == [@tag.values]
445
+ end
446
+ end
447
+
127
448
  specify "should work correctly when filtering by associations" do
128
449
  @album.update(:artist => @artist)
129
450
  @album.add_tag(@tag)
130
451
 
131
452
  @Artist.filter(:albums=>@album).all.should == [@artist]
132
453
  @Artist.filter(:first_album=>@album).all.should == [@artist]
133
- @Artist.filter(:tags=>@tag).all.should == [@artist] unless @no_many_through_many
454
+ unless @no_many_through_many
455
+ @Artist.filter(:tags=>@tag).all.should == [@artist]
456
+ @Artist.filter(:first_tag=>@tag).all.should == [@artist]
457
+ end
134
458
  @Album.filter(:artist=>@artist).all.should == [@album]
135
459
  @Album.filter(:tags=>@tag).all.should == [@album]
136
460
  @Album.filter(:alias_tags=>@tag).all.should == [@album]
@@ -146,7 +470,10 @@ shared_examples_for "filtering/excluding by associations" do
146
470
 
147
471
  @Artist.exclude(:albums=>@album).all.should == [artist]
148
472
  @Artist.exclude(:first_album=>@album).all.should == [artist]
149
- @Artist.exclude(:tags=>@tag).all.should == [artist] unless @no_many_through_many
473
+ unless @no_many_through_many
474
+ @Artist.exclude(:tags=>@tag).all.should == [artist]
475
+ @Artist.exclude(:first_tag=>@tag).all.should == [artist]
476
+ end
150
477
  @Album.exclude(:artist=>@artist).all.should == [album]
151
478
  @Album.exclude(:tags=>@tag).all.should == [album]
152
479
  @Album.exclude(:alias_tags=>@tag).all.should == [album]
@@ -170,11 +497,21 @@ shared_examples_for "filtering/excluding by associations" do
170
497
 
171
498
  @Album.filter(:t_tags=>@tag).all.should == [@album]
172
499
  @Album.filter(:alias_t_tags=>@tag).all.should == [@album]
173
- @Artist.filter(:t_tags=>@tag).all.should == [@artist] unless @no_many_through_many
500
+ unless @no_many_through_many
501
+ @Album.filter(:t_tag=>@tag).all.should == [@album]
502
+ @Album.filter(:alias_t_tag=>@tag).all.should == [@album]
503
+ @Artist.filter(:t_tags=>@tag).all.should == [@artist]
504
+ @Artist.filter(:t_tag=>@tag).all.should == [@artist]
505
+ end
174
506
  @tag.update(:name=>'Foo')
175
507
  @Album.filter(:t_tags=>@tag).all.should == []
176
508
  @Album.filter(:alias_t_tags=>@tag).all.should == []
177
- @Artist.filter(:t_tags=>@tag).all.should == [] unless @no_many_through_many
509
+ unless @no_many_through_many
510
+ @Album.filter(:t_tag=>@tag).all.should == []
511
+ @Album.filter(:alias_t_tag=>@tag).all.should == []
512
+ @Artist.filter(:t_tags=>@tag).all.should == []
513
+ @Artist.filter(:t_tag=>@tag).all.should == []
514
+ end
178
515
  end
179
516
 
180
517
  specify "should work correctly when excluding by associations with conditions" do
@@ -193,11 +530,21 @@ shared_examples_for "filtering/excluding by associations" do
193
530
 
194
531
  @Album.exclude(:t_tags=>@tag).all.should == []
195
532
  @Album.exclude(:alias_t_tags=>@tag).all.should == []
196
- @Artist.exclude(:t_tags=>@tag).all.should == [] unless @no_many_through_many
533
+ unless @no_many_through_many
534
+ @Album.exclude(:t_tag=>@tag).all.should == []
535
+ @Album.exclude(:alias_t_tag=>@tag).all.should == []
536
+ @Artist.exclude(:t_tags=>@tag).all.should == []
537
+ @Artist.exclude(:t_tag=>@tag).all.should == []
538
+ end
197
539
  @tag.update(:name=>'Foo')
198
540
  @Album.exclude(:t_tags=>@tag).all.should == [@album]
199
541
  @Album.exclude(:alias_t_tags=>@tag).all.should == [@album]
200
- @Artist.exclude(:t_tags=>@tag).all.should == [@artist] unless @no_many_through_many
542
+ unless @no_many_through_many
543
+ @Album.exclude(:t_tag=>@tag).all.should == [@album]
544
+ @Album.exclude(:alias_t_tag=>@tag).all.should == [@album]
545
+ @Artist.exclude(:t_tags=>@tag).all.should == [@artist]
546
+ @Artist.exclude(:t_tag=>@tag).all.should == [@artist]
547
+ end
201
548
  end
202
549
 
203
550
  specify "should work correctly when filtering by multiple associations" do
@@ -213,7 +560,10 @@ shared_examples_for "filtering/excluding by associations" do
213
560
  @Tag.filter(:albums=>[@album, album]).all.should == [@tag]
214
561
  @Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [@album]
215
562
  @artist.albums_dataset.filter(:tags=>[@tag, tag]).all.should == [@album]
216
- @Artist.filter(:tags=>[@tag, tag]).all.should == [@artist] unless @no_many_through_many
563
+ unless @no_many_through_many
564
+ @Artist.filter(:tags=>[@tag, tag]).all.should == [@artist]
565
+ @Artist.filter(:first_tag=>[@tag, tag]).all.should == [@artist]
566
+ end
217
567
 
218
568
  album.add_tag(tag)
219
569
 
@@ -224,7 +574,10 @@ shared_examples_for "filtering/excluding by associations" do
224
574
  @Album.filter(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
225
575
  @Tag.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
226
576
  @Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [@album]
227
- @Artist.filter(:tags=>[@tag, tag]).all.should == [@artist] unless @no_many_through_many
577
+ unless @no_many_through_many
578
+ @Artist.filter(:tags=>[@tag, tag]).all.should == [@artist]
579
+ @Artist.filter(:first_tag=>[@tag, tag]).all.should == [@artist]
580
+ end
228
581
 
229
582
  album.update(:artist => artist)
230
583
 
@@ -235,7 +588,10 @@ shared_examples_for "filtering/excluding by associations" do
235
588
  @Album.filter(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
236
589
  @Tag.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
237
590
  @Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
238
- @Artist.filter(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@artist, artist] unless @no_many_through_many
591
+ unless @no_many_through_many
592
+ @Artist.filter(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@artist, artist]
593
+ @Artist.filter(:first_tag=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@artist, artist]
594
+ end
239
595
  end
240
596
 
241
597
  specify "should work correctly when excluding by multiple associations" do
@@ -248,7 +604,10 @@ shared_examples_for "filtering/excluding by associations" do
248
604
  @Album.exclude(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
249
605
  @Tag.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
250
606
  @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
251
- @Artist.exclude(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@artist, artist] unless @no_many_through_many
607
+ unless @no_many_through_many
608
+ @Artist.exclude(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@artist, artist]
609
+ @Artist.exclude(:first_tag=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@artist, artist]
610
+ end
252
611
 
253
612
  @album.update(:artist => @artist)
254
613
  @album.add_tag(@tag)
@@ -260,7 +619,10 @@ shared_examples_for "filtering/excluding by associations" do
260
619
  @Album.exclude(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
261
620
  @Tag.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [tag]
262
621
  @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
263
- @Artist.exclude(:tags=>[@tag, tag]).all.should == [artist] unless @no_many_through_many
622
+ unless @no_many_through_many
623
+ @Artist.exclude(:tags=>[@tag, tag]).all.should == [artist]
624
+ @Artist.exclude(:first_tag=>[@tag, tag]).all.should == [artist]
625
+ end
264
626
 
265
627
  album.add_tag(tag)
266
628
 
@@ -271,7 +633,10 @@ shared_examples_for "filtering/excluding by associations" do
271
633
  @Album.exclude(:alias_tags=>[@tag, tag]).all.should == []
272
634
  @Tag.exclude(:albums=>[@album, album]).all.should == []
273
635
  @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [album]
274
- @Artist.exclude(:tags=>[@tag, tag]).all.should == [artist] unless @no_many_through_many
636
+ unless @no_many_through_many
637
+ @Artist.exclude(:tags=>[@tag, tag]).all.should == [artist]
638
+ @Artist.exclude(:first_tag=>[@tag, tag]).all.should == [artist]
639
+ end
275
640
 
276
641
  album.update(:artist => artist)
277
642
 
@@ -282,7 +647,10 @@ shared_examples_for "filtering/excluding by associations" do
282
647
  @Album.exclude(:alias_tags=>[@tag, tag]).all.should == []
283
648
  @Tag.exclude(:albums=>[@album, album]).all.should == []
284
649
  @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == []
285
- @Artist.exclude(:tags=>[@tag, tag]).all.should == [] unless @no_many_through_many
650
+ unless @no_many_through_many
651
+ @Artist.exclude(:tags=>[@tag, tag]).all.should == []
652
+ @Artist.exclude(:first_tag=>[@tag, tag]).all.should == []
653
+ end
286
654
  end
287
655
 
288
656
  specify "should work correctly when filtering associations with conditions with multiple objects" do
@@ -315,15 +683,30 @@ shared_examples_for "filtering/excluding by associations" do
315
683
 
316
684
  @Album.filter(:t_tags=>[@tag, tag]).all.should == [@album]
317
685
  @Album.filter(:alias_t_tags=>[@tag, tag]).all.should == [@album]
318
- @Artist.filter(:t_tags=>[@tag, tag]).all.should == [artist] unless @no_many_through_many
686
+ unless @no_many_through_many
687
+ @Album.filter(:t_tag=>[@tag, tag]).all.should == [@album]
688
+ @Album.filter(:alias_t_tag=>[@tag, tag]).all.should == [@album]
689
+ @Artist.filter(:t_tags=>[@tag, tag]).all.should == [artist]
690
+ @Artist.filter(:t_tag=>[@tag, tag]).all.should == [artist]
691
+ end
319
692
  @tag.update(:name=>'Foo')
320
693
  @Album.filter(:t_tags=>[@tag, tag]).all.should == [@album]
321
694
  @Album.filter(:alias_t_tags=>[@tag, tag]).all.should == [@album]
322
- @Artist.filter(:t_tags=>[@tag, tag]).all.should == [artist] unless @no_many_through_many
695
+ unless @no_many_through_many
696
+ @Album.filter(:t_tag=>[@tag, tag]).all.should == [@album]
697
+ @Album.filter(:alias_t_tag=>[@tag, tag]).all.should == [@album]
698
+ @Artist.filter(:t_tags=>[@tag, tag]).all.should == [artist]
699
+ @Artist.filter(:t_tag=>[@tag, tag]).all.should == [artist]
700
+ end
323
701
  tag.update(:name=>'Foo')
324
702
  @Album.filter(:t_tags=>[@tag, tag]).all.should == []
325
703
  @Album.filter(:alias_t_tags=>[@tag, tag]).all.should == []
326
- @Artist.filter(:t_tags=>[@tag, tag]).all.should == [] unless @no_many_through_many
704
+ unless @no_many_through_many
705
+ @Album.filter(:t_tag=>[@tag, tag]).all.should == []
706
+ @Album.filter(:alias_t_tag=>[@tag, tag]).all.should == []
707
+ @Artist.filter(:t_tags=>[@tag, tag]).all.should == []
708
+ @Artist.filter(:t_tag=>[@tag, tag]).all.should == []
709
+ end
327
710
  end
328
711
 
329
712
  specify "should work correctly when excluding associations with conditions with multiple objects" do
@@ -357,15 +740,30 @@ shared_examples_for "filtering/excluding by associations" do
357
740
  @tag.add_album(album)
358
741
  @Album.exclude(:t_tags=>[@tag, tag]).all.should == []
359
742
  @Album.exclude(:alias_t_tags=>[@tag, tag]).all.should == []
360
- @Artist.exclude(:t_tags=>[@tag, tag]).all.should == [@artist] unless @no_many_through_many
743
+ unless @no_many_through_many
744
+ @Album.exclude(:t_tag=>[@tag, tag]).all.should == []
745
+ @Album.exclude(:alias_t_tag=>[@tag, tag]).all.should == []
746
+ @Artist.exclude(:t_tags=>[@tag, tag]).all.should == [@artist]
747
+ @Artist.exclude(:t_tag=>[@tag, tag]).all.should == [@artist]
748
+ end
361
749
  @tag.update(:name=>'Foo')
362
750
  @Album.exclude(:t_tags=>[@tag, tag]).all.should == [album]
363
751
  @Album.exclude(:alias_t_tags=>[@tag, tag]).all.should == [album]
364
- @Artist.exclude(:t_tags=>[@tag, tag]).all.should == [@artist] unless @no_many_through_many
752
+ unless @no_many_through_many
753
+ @Album.exclude(:t_tag=>[@tag, tag]).all.should == [album]
754
+ @Album.exclude(:alias_t_tag=>[@tag, tag]).all.should == [album]
755
+ @Artist.exclude(:t_tags=>[@tag, tag]).all.should == [@artist]
756
+ @Artist.exclude(:t_tag=>[@tag, tag]).all.should == [@artist]
757
+ end
365
758
  tag.update(:name=>'Foo')
366
759
  @Album.exclude(:t_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
367
760
  @Album.exclude(:alias_t_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
368
- @Artist.exclude(:t_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@artist, artist] unless @no_many_through_many
761
+ unless @no_many_through_many
762
+ @Album.exclude(:t_tag=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
763
+ @Album.exclude(:alias_t_tag=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
764
+ @Artist.exclude(:t_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@artist, artist]
765
+ @Artist.exclude(:t_tag=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@artist, artist]
766
+ end
369
767
  end
370
768
 
371
769
  specify "should work correctly when excluding by associations in regards to NULL values" do
@@ -382,7 +780,12 @@ shared_examples_for "filtering/excluding by associations" do
382
780
  @Album.exclude(:a_artist=>@artist).all.should == [@album]
383
781
  @Album.exclude(:t_tags=>@tag).all.should == [@album]
384
782
  @Album.exclude(:alias_t_tags=>@tag).all.should == [@album]
385
- @Artist.exclude(:t_tags=>@tag).all.should == [@artist] unless @no_many_through_many
783
+ unless @no_many_through_many
784
+ @Album.exclude(:t_tag=>@tag).all.should == [@album]
785
+ @Album.exclude(:alias_t_tag=>@tag).all.should == [@album]
786
+ @Artist.exclude(:t_tags=>@tag).all.should == [@artist]
787
+ @Artist.exclude(:t_tag=>@tag).all.should == [@artist]
788
+ end
386
789
 
387
790
  @album.update(:artist => @artist)
388
791
  @artist.albums_dataset.exclude(:tags=>@tag).all.should == [@album]
@@ -440,6 +843,9 @@ shared_examples_for "filtering/excluding by associations" do
440
843
  @Artist.filter(:tags=>@Tag).all.sort_by{|x| x.pk}.should == [@artist, artist]
441
844
  @Artist.filter(:tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [artist]
442
845
  @Artist.filter(:tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
846
+ @Artist.filter(:first_tag=>@Tag).all.sort_by{|x| x.pk}.should == [@artist, artist]
847
+ @Artist.filter(:first_tag=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [artist]
848
+ @Artist.filter(:first_tag=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
443
849
  end
444
850
  end
445
851
 
@@ -473,6 +879,9 @@ shared_examples_for "filtering/excluding by associations" do
473
879
  @Artist.exclude(:tags=>@Tag).all.sort_by{|x| x.pk}.should == []
474
880
  @Artist.exclude(:tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@artist]
475
881
  @Artist.exclude(:tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist]
882
+ @Artist.exclude(:first_tag=>@Tag).all.sort_by{|x| x.pk}.should == []
883
+ @Artist.exclude(:first_tag=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@artist]
884
+ @Artist.exclude(:first_tag=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist]
476
885
  end
477
886
  end
478
887
 
@@ -488,7 +897,12 @@ shared_examples_for "filtering/excluding by associations" do
488
897
  @Album.filter(:a_artist=>@Artist).all.sort_by{|x| x.pk}.should == [@album]
489
898
  @Album.filter(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [@album]
490
899
  @Album.filter(:alias_t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [@album]
491
- @Artist.filter(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [@artist] unless @no_many_through_many
900
+ unless @no_many_through_many
901
+ @Album.filter(:t_tag=>@Tag).all.sort_by{|x| x.pk}.should == [@album]
902
+ @Album.filter(:alias_t_tag=>@Tag).all.sort_by{|x| x.pk}.should == [@album]
903
+ @Artist.filter(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [@artist]
904
+ @Artist.filter(:t_tag=>@Tag).all.sort_by{|x| x.pk}.should == [@artist]
905
+ end
492
906
 
493
907
  artist.update(:name=>@artist.name)
494
908
  album.update(:name=>@album.name)
@@ -499,14 +913,24 @@ shared_examples_for "filtering/excluding by associations" do
499
913
  @Album.filter(:a_artist=>@Artist).all.sort_by{|x| x.pk}.should == [@album, album]
500
914
  @Album.filter(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [@album, album]
501
915
  @Album.filter(:alias_t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [@album, album]
502
- @Artist.filter(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [@artist, artist] unless @no_many_through_many
916
+ unless @no_many_through_many
917
+ @Album.filter(:t_tag=>@Tag).all.sort_by{|x| x.pk}.should == [@album, album]
918
+ @Album.filter(:alias_t_tag=>@Tag).all.sort_by{|x| x.pk}.should == [@album, album]
919
+ @Artist.filter(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [@artist, artist]
920
+ @Artist.filter(:t_tag=>@Tag).all.sort_by{|x| x.pk}.should == [@artist, artist]
921
+ end
503
922
 
504
923
  @Artist.filter(:a_albums=>@Album.filter(Array(Album.primary_key).map{|k| Sequel.qualify(Album.table_name, k)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [artist]
505
924
  @Artist.filter(:first_a_album=>@Album.filter(Array(Album.primary_key).map{|k| Sequel.qualify(Album.table_name, k)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [artist]
506
925
  @Album.filter(:a_artist=>@Artist.filter(Array(Artist.primary_key).map{|k| Sequel.qualify(Artist.table_name, k)}.zip(Array(artist.pk)))).all.sort_by{|x| x.pk}.should == [album]
507
926
  @Album.filter(:t_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
508
927
  @Album.filter(:alias_t_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
509
- @Artist.filter(:t_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [artist] unless @no_many_through_many
928
+ unless @no_many_through_many
929
+ @Album.filter(:t_tag=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
930
+ @Album.filter(:alias_t_tag=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
931
+ @Artist.filter(:t_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [artist]
932
+ @Artist.filter(:t_tag=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [artist]
933
+ end
510
934
 
511
935
  @Artist.filter(:a_albums=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
512
936
  @Artist.filter(:first_a_album=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
@@ -514,7 +938,13 @@ shared_examples_for "filtering/excluding by associations" do
514
938
  @Album.filter(:t_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
515
939
  @Album.filter(:t_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
516
940
  @Album.filter(:alias_t_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
517
- @Artist.filter(:t_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [] unless @no_many_through_many
941
+ unless @no_many_through_many
942
+ @Album.filter(:t_tag=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
943
+ @Album.filter(:t_tag=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
944
+ @Album.filter(:alias_t_tag=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
945
+ @Artist.filter(:t_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
946
+ @Artist.filter(:t_tag=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
947
+ end
518
948
  end
519
949
 
520
950
  specify "should work correctly when excluding by association datasets with conditions" do
@@ -529,7 +959,12 @@ shared_examples_for "filtering/excluding by associations" do
529
959
  @Album.exclude(:a_artist=>@Artist).all.sort_by{|x| x.pk}.should == [album]
530
960
  @Album.exclude(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [album]
531
961
  @Album.exclude(:alias_t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [album]
532
- @Artist.exclude(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [artist] unless @no_many_through_many
962
+ unless @no_many_through_many
963
+ @Album.exclude(:t_tag=>@Tag).all.sort_by{|x| x.pk}.should == [album]
964
+ @Album.exclude(:alias_t_tag=>@Tag).all.sort_by{|x| x.pk}.should == [album]
965
+ @Artist.exclude(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [artist]
966
+ @Artist.exclude(:t_tag=>@Tag).all.sort_by{|x| x.pk}.should == [artist]
967
+ end
533
968
 
534
969
  artist.update(:name=>@artist.name)
535
970
  album.update(:name=>@album.name)
@@ -540,23 +975,472 @@ shared_examples_for "filtering/excluding by associations" do
540
975
  @Album.exclude(:a_artist=>@Artist).all.sort_by{|x| x.pk}.should == []
541
976
  @Album.exclude(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == []
542
977
  @Album.exclude(:alias_t_tags=>@Tag).all.sort_by{|x| x.pk}.should == []
543
- @Artist.exclude(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == [] unless @no_many_through_many
978
+ unless @no_many_through_many
979
+ @Album.exclude(:t_tag=>@Tag).all.sort_by{|x| x.pk}.should == []
980
+ @Album.exclude(:alias_t_tag=>@Tag).all.sort_by{|x| x.pk}.should == []
981
+ @Artist.exclude(:t_tags=>@Tag).all.sort_by{|x| x.pk}.should == []
982
+ @Artist.exclude(:t_tag=>@Tag).all.sort_by{|x| x.pk}.should == []
983
+ end
544
984
 
545
985
  @Artist.exclude(:a_albums=>@Album.filter(Array(Album.primary_key).map{|k| Sequel.qualify(Album.table_name, k)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [@artist]
546
986
  @Artist.exclude(:first_a_album=>@Album.filter(Array(Album.primary_key).map{|k| Sequel.qualify(Album.table_name, k)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [@artist]
547
987
  @Album.exclude(:a_artist=>@Artist.filter(Array(Artist.primary_key).map{|k| Sequel.qualify(Artist.table_name, k)}.zip(Array(artist.pk)))).all.sort_by{|x| x.pk}.should == [@album]
548
988
  @Album.exclude(:t_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
549
989
  @Album.exclude(:alias_t_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
550
- @Artist.exclude(:t_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@artist] unless @no_many_through_many
990
+ unless @no_many_through_many
991
+ @Album.exclude(:t_tag=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
992
+ @Album.exclude(:alias_t_tag=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
993
+ @Artist.exclude(:t_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@artist]
994
+ @Artist.exclude(:t_tag=>@Tag.filter(Array(Tag.primary_key).map{|k| Sequel.qualify(Tag.table_name, k)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@artist]
995
+ end
551
996
 
552
997
  @Artist.exclude(:a_albums=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist]
553
998
  @Artist.exclude(:first_a_album=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist]
554
999
  @Album.exclude(:a_artist=>@Artist.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
555
1000
  @Album.exclude(:t_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
556
1001
  @Album.exclude(:alias_t_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
557
- @Artist.exclude(:t_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist] unless @no_many_through_many
1002
+ unless @no_many_through_many
1003
+ @Album.exclude(:t_tag=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
1004
+ @Album.exclude(:alias_t_tag=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
1005
+ @Artist.exclude(:t_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist]
1006
+ @Artist.exclude(:t_tag=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist]
1007
+ end
558
1008
  end
1009
+ end
559
1010
 
1011
+ shared_examples_for "filter by associations singular association limit strategies" do
1012
+ specify "filter by associations with limited one_to_one associations should work correctly" do
1013
+ Artist.one_to_one :first_album, {:clone=>:first_album}.merge(@els)
1014
+ Artist.one_to_one :last_album, {:clone=>:last_album}.merge(@els)
1015
+ Artist.one_to_one :second_album, {:clone=>:second_album}.merge(@els)
1016
+ @album.update(:artist => @artist)
1017
+ diff_album = @diff_album.call
1018
+ ar = @pr.call[1]
1019
+ ds = Artist.order(:name)
1020
+
1021
+ ds.where(:first_album=>@album).all.should == [@artist]
1022
+ ds.where(:first_album=>diff_album).all.should == []
1023
+ ds.exclude(:first_album=>@album).all.should == [ar]
1024
+ ds.exclude(:first_album=>diff_album).all.should == [@artist, ar]
1025
+
1026
+ ds.where(:second_album=>@album).all.should == []
1027
+ ds.where(:second_album=>diff_album).all.should == [@artist]
1028
+ ds.exclude(:second_album=>@album).all.should == [@artist, ar]
1029
+ ds.exclude(:second_album=>diff_album).all.should == [ar]
1030
+
1031
+ ds.where(:last_album=>@album).all.should == []
1032
+ ds.where(:last_album=>diff_album).all.should == [@artist]
1033
+ ds.exclude(:last_album=>@album).all.should == [@artist, ar]
1034
+ ds.exclude(:last_album=>diff_album).all.should == [ar]
1035
+
1036
+ Artist.one_to_one :first_album, :clone=>:first_album do |ads| ads.where(:albums__name=>diff_album.name) end
1037
+ ar.add_album(diff_album)
1038
+ ds.where(:first_album=>[@album, diff_album]).all.should == [ar]
1039
+ ds.exclude(:first_album=>[@album, diff_album]).all.should == [@artist]
1040
+ end
1041
+
1042
+ specify "dataset associations with limited one_to_one associations should work correctly" do
1043
+ Artist.one_to_one :first_album, {:clone=>:first_album}.merge(@els)
1044
+ Artist.one_to_one :last_album, {:clone=>:last_album}.merge(@els)
1045
+ Artist.one_to_one :second_album, {:clone=>:second_album}.merge(@els)
1046
+ @album.update(:artist => @artist)
1047
+ diff_album = @diff_album.call
1048
+ ar = @pr.call[1]
1049
+ ds = Artist
1050
+
1051
+ ds.where(@artist.pk_hash).first_albums.all.should == [@album]
1052
+ ds.where(@artist.pk_hash).second_albums.all.should == [diff_album]
1053
+ ds.where(@artist.pk_hash).last_albums.all.should == [diff_album]
1054
+ ds.where(ar.pk_hash).first_albums.all.should == []
1055
+ ds.where(ar.pk_hash).second_albums.all.should == []
1056
+ ds.where(ar.pk_hash).last_albums.all.should == []
1057
+
1058
+ Artist.one_to_one :first_album, :clone=>:first_album do |ads| ads.where(:albums__name=>diff_album.name) end
1059
+ ar.add_album(diff_album)
1060
+ ds.where(@artist.pk_hash).first_albums.all.should == []
1061
+ ds.where(ar.pk_hash).first_albums.all.should == [diff_album]
1062
+ end
1063
+
1064
+ specify "filter by associations with limited one_through_one associations should work correctly" do
1065
+ Album.one_through_one :first_tag, {:clone=>:first_tag}.merge(@els)
1066
+ Album.one_through_one :second_tag, {:clone=>:second_tag}.merge(@els)
1067
+ Album.one_through_one :last_tag, {:clone=>:last_tag}.merge(@els)
1068
+ tu, tv = @other_tags.call
1069
+ al = @pr.call.first
1070
+ ds = Album.order(:name)
1071
+ al.add_tag(tu)
1072
+
1073
+ ds.where(:first_tag=>@tag).all.should == [@album]
1074
+ ds.where(:first_tag=>tu).all.should == [al]
1075
+ ds.where(:first_tag=>tv).all.should == []
1076
+ ds.exclude(:first_tag=>@tag).all.should == [al]
1077
+ ds.exclude(:first_tag=>tu).all.should == [@album]
1078
+ ds.exclude(:first_tag=>tv).all.should == [@album, al]
1079
+
1080
+ ds.where(:second_tag=>@tag).all.should == []
1081
+ ds.where(:second_tag=>tu).all.should == [@album]
1082
+ ds.where(:second_tag=>tv).all.should == []
1083
+ ds.exclude(:second_tag=>@tag).all.should == [@album, al]
1084
+ ds.exclude(:second_tag=>tu).all.should == [al]
1085
+ ds.exclude(:second_tag=>tv).all.should == [@album, al]
1086
+
1087
+ ds.where(:last_tag=>@tag).all.should == []
1088
+ ds.where(:last_tag=>tu).all.should == [al]
1089
+ ds.where(:last_tag=>tv).all.should == [@album]
1090
+ ds.exclude(:last_tag=>@tag).all.should == [@album, al]
1091
+ ds.exclude(:last_tag=>tu).all.should == [@album]
1092
+ ds.exclude(:last_tag=>tv).all.should == [al]
1093
+
1094
+ Album.one_through_one :first_tag, :clone=>:first_tag do |ads| ads.where(:tags__name=>tu.name) end
1095
+ Album.one_through_one :second_tag, :clone=>:second_tag do |ads| ads.where(:tags__name=>[tu.name, tv.name]) end
1096
+
1097
+ ds.where(:first_tag=>[@tag, tu]).all.should == [@album, al]
1098
+ ds.exclude(:first_tag=>[@tag, tu]).all.should == []
1099
+
1100
+ al.add_tag(tv)
1101
+ ds.where(:second_tag=>[tv, tu]).all.should == [@album, al]
1102
+ ds.exclude(:second_tag=>[tv, tu]).all.should == []
1103
+ end
1104
+
1105
+ specify "dataset associations with limited one_through_one associations should work correctly" do
1106
+ Album.one_through_one :first_tag, {:clone=>:first_tag}.merge(@els)
1107
+ Album.one_through_one :second_tag, {:clone=>:second_tag}.merge(@els)
1108
+ Album.one_through_one :last_tag, {:clone=>:last_tag}.merge(@els)
1109
+ tu, tv = @other_tags.call
1110
+ al = @pr.call.first
1111
+ ds = Album
1112
+ al.add_tag(tu)
1113
+
1114
+ ds.where(@album.pk_hash).first_tags.all.should == [@tag]
1115
+ ds.where(@album.pk_hash).second_tags.all.should == [tu]
1116
+ ds.where(@album.pk_hash).last_tags.all.should == [tv]
1117
+ ds.where(al.pk_hash).first_tags.all.should == [tu]
1118
+ ds.where(al.pk_hash).second_tags.all.should == []
1119
+ ds.where(al.pk_hash).last_tags.all.should == [tu]
1120
+
1121
+ Album.one_through_one :first_tag, :clone=>:first_tag do |ads| ads.where(:tags__name=>tu.name) end
1122
+ Album.one_through_one :second_tag, :clone=>:second_tag do |ads| ads.where(:tags__name=>[tu.name, tv.name]) end
1123
+
1124
+ ds.where(@album.pk_hash).first_tags.all.should == [tu]
1125
+ ds.where(@album.pk_hash).second_tags.all.should == [tv]
1126
+ ds.where(al.pk_hash).first_tags.all.should == [tu]
1127
+ ds.where(al.pk_hash).second_tags.all.should == []
1128
+
1129
+ al.add_tag(tv)
1130
+ ds.where(@album.pk_hash).first_tags.all.should == [tu]
1131
+ ds.where(@album.pk_hash).second_tags.all.should == [tv]
1132
+ ds.where(al.pk_hash).first_tags.all.should == [tu]
1133
+ ds.where(al.pk_hash).second_tags.all.should == [tv]
1134
+ end
1135
+
1136
+ specify "filter by associations with limited one_through_many associations should work correctly" do
1137
+ Artist.one_through_many :first_tag, {:clone=>:first_tag}.merge(@els)
1138
+ Artist.one_through_many :second_tag, {:clone=>:second_tag}.merge(@els)
1139
+ Artist.one_through_many :last_tag, {:clone=>:last_tag}.merge(@els)
1140
+ @album.update(:artist => @artist)
1141
+ tu, tv = @other_tags.call
1142
+ al, ar, _ = @pr.call
1143
+ al.update(:artist=>ar)
1144
+ al.add_tag(tu)
1145
+ ds = Artist.order(:name)
1146
+
1147
+ ds.where(:first_tag=>@tag).all.should == [@artist]
1148
+ ds.where(:first_tag=>tu).all.should == [ar]
1149
+ ds.where(:first_tag=>tv).all.should == []
1150
+ ds.exclude(:first_tag=>@tag).all.should == [ar]
1151
+ ds.exclude(:first_tag=>tu).all.should == [@artist]
1152
+ ds.exclude(:first_tag=>tv).all.should == [@artist, ar]
1153
+
1154
+ ds.where(:second_tag=>@tag).all.should == []
1155
+ ds.where(:second_tag=>tu).all.should == [@artist]
1156
+ ds.where(:second_tag=>tv).all.should == []
1157
+ ds.exclude(:second_tag=>@tag).all.should == [@artist, ar]
1158
+ ds.exclude(:second_tag=>tu).all.should == [ar]
1159
+ ds.exclude(:second_tag=>tv).all.should == [@artist, ar]
1160
+
1161
+ ds.where(:last_tag=>@tag).all.should == []
1162
+ ds.where(:last_tag=>tu).all.should == [ar]
1163
+ ds.where(:last_tag=>tv).all.should == [@artist]
1164
+ ds.exclude(:last_tag=>@tag).all.should == [@artist, ar]
1165
+ ds.exclude(:last_tag=>tu).all.should == [@artist]
1166
+ ds.exclude(:last_tag=>tv).all.should == [ar]
1167
+
1168
+ Artist.one_through_many :first_tag, :clone=>:first_tag do |ads| ads.where(:tags__name=>tu.name) end
1169
+ Artist.one_through_many :second_tag, :clone=>:second_tag do |ads| ads.where(:tags__name=>[tu.name, tv.name]) end
1170
+
1171
+ ds.where(:first_tag=>[@tag, tu]).all.should == [@artist, ar]
1172
+ ds.exclude(:first_tag=>[@tag, tu]).all.should == []
1173
+
1174
+ al.add_tag(tv)
1175
+ ds.where(:second_tag=>[tv, tu]).all.should == [@artist, ar]
1176
+ ds.exclude(:second_tag=>[tv, tu]).all.should == []
1177
+ end
1178
+
1179
+ specify "dataset associations with limited one_through_many associations should work correctly" do
1180
+ Artist.one_through_many :first_tag, {:clone=>:first_tag}.merge(@els)
1181
+ Artist.one_through_many :second_tag, {:clone=>:second_tag}.merge(@els)
1182
+ Artist.one_through_many :last_tag, {:clone=>:last_tag}.merge(@els)
1183
+ @album.update(:artist => @artist)
1184
+ tu, tv = @other_tags.call
1185
+ al, ar, _ = @pr.call
1186
+ al.update(:artist=>ar)
1187
+ al.add_tag(tu)
1188
+ ds = Artist.order(:name)
1189
+
1190
+ ds.where(@artist.pk_hash).first_tags.all.should == [@tag]
1191
+ ds.where(@artist.pk_hash).second_tags.all.should == [tu]
1192
+ ds.where(@artist.pk_hash).last_tags.all.should == [tv]
1193
+ ds.where(ar.pk_hash).first_tags.all.should == [tu]
1194
+ ds.where(ar.pk_hash).second_tags.all.should == []
1195
+ ds.where(ar.pk_hash).last_tags.all.should == [tu]
1196
+
1197
+ Artist.one_through_many :first_tag, :clone=>:first_tag do |ads| ads.where(:tags__name=>tu.name) end
1198
+ Artist.one_through_many :second_tag, :clone=>:second_tag do |ads| ads.where(:tags__name=>[tu.name, tv.name]) end
1199
+
1200
+ ds.where(@artist.pk_hash).first_tags.all.should == [tu]
1201
+ ds.where(@artist.pk_hash).second_tags.all.should == [tv]
1202
+ ds.where(ar.pk_hash).first_tags.all.should == [tu]
1203
+ ds.where(ar.pk_hash).second_tags.all.should == []
1204
+
1205
+ al.add_tag(tv)
1206
+ ds.where(@artist.pk_hash).first_tags.all.should == [tu]
1207
+ ds.where(@artist.pk_hash).second_tags.all.should == [tv]
1208
+ ds.where(ar.pk_hash).first_tags.all.should == [tu]
1209
+ ds.where(ar.pk_hash).second_tags.all.should == [tv]
1210
+ end
1211
+ end
1212
+
1213
+ shared_examples_for "filter by associations limit strategies" do
1214
+ it_should_behave_like "filter by associations singular association limit strategies"
1215
+
1216
+ specify "filter by associations with limited one_to_many associations should work correctly" do
1217
+ Artist.one_to_many :first_two_albums, {:clone=>:first_two_albums}.merge(@els)
1218
+ Artist.one_to_many :second_two_albums, {:clone=>:second_two_albums}.merge(@els)
1219
+ Artist.one_to_many :not_first_albums, {:clone=>:not_first_albums}.merge(@els)
1220
+ Artist.one_to_many :last_two_albums, {:clone=>:last_two_albums}.merge(@els)
1221
+ @album.update(:artist => @artist)
1222
+ middle_album = @middle_album.call
1223
+ diff_album = @diff_album.call
1224
+ ar = @pr.call[1]
1225
+ ds = Artist.order(:name)
1226
+
1227
+ ds.where(:first_two_albums=>@album).all.should == [@artist]
1228
+ ds.where(:first_two_albums=>middle_album).all.should == [@artist]
1229
+ ds.where(:first_two_albums=>diff_album).all.should == []
1230
+ ds.exclude(:first_two_albums=>@album).all.should == [ar]
1231
+ ds.exclude(:first_two_albums=>middle_album).all.should == [ar]
1232
+ ds.exclude(:first_two_albums=>diff_album).all.should == [@artist, ar]
1233
+
1234
+ [:second_two_albums, :not_first_albums, :last_two_albums].each do |a|
1235
+ ds.where(a=>@album).all.should == []
1236
+ ds.where(a=>middle_album).all.should == [@artist]
1237
+ ds.where(a=>diff_album).all.should == [@artist]
1238
+ ds.exclude(a=>@album).all.should == [@artist, ar]
1239
+ ds.exclude(a=>middle_album).all.should == [ar]
1240
+ ds.exclude(a=>diff_album).all.should == [ar]
1241
+ end
1242
+
1243
+ Artist.one_to_one :first_two_albums, :clone=>:first_two_albums do |ads| ads.where(:albums__name=>diff_album.name) end
1244
+ ar.add_album(diff_album)
1245
+ ds.where(:first_two_albums=>[@album, diff_album]).all.should == [ar]
1246
+ ds.exclude(:first_two_albums=>[@album, diff_album]).all.should == [@artist]
1247
+ end
1248
+
1249
+ specify "dataset associations with limited one_to_many associations should work correctly" do
1250
+ Artist.one_to_many :first_two_albums, {:clone=>:first_two_albums}.merge(@els)
1251
+ Artist.one_to_many :second_two_albums, {:clone=>:second_two_albums}.merge(@els)
1252
+ Artist.one_to_many :not_first_albums, {:clone=>:not_first_albums}.merge(@els)
1253
+ Artist.one_to_many :last_two_albums, {:clone=>:last_two_albums}.merge(@els)
1254
+ @album.update(:artist => @artist)
1255
+ middle_album = @middle_album.call
1256
+ diff_album = @diff_album.call
1257
+ ar = @pr.call[1]
1258
+ ds = Artist.order(:name)
1259
+
1260
+ ds.where(@artist.pk_hash).first_two_albums.all.should == [@album, middle_album]
1261
+ ds.where(@artist.pk_hash).second_two_albums.all.should == [middle_album, diff_album]
1262
+ ds.where(@artist.pk_hash).not_first_albums.all.should == [middle_album, diff_album]
1263
+ ds.where(@artist.pk_hash).last_two_albums.all.should == [diff_album, middle_album]
1264
+ ds.where(ar.pk_hash).first_two_albums.all.should == []
1265
+ ds.where(ar.pk_hash).second_two_albums.all.should == []
1266
+ ds.where(ar.pk_hash).not_first_albums.all.should == []
1267
+ ds.where(ar.pk_hash).last_two_albums.all.should == []
1268
+
1269
+ Artist.one_to_one :first_two_albums, :clone=>:first_two_albums do |ads| ads.where(:albums__name=>[diff_album.name, middle_album.name]) end
1270
+ ar.add_album(diff_album)
1271
+ ds.where(@artist.pk_hash).first_two_albums.all.should == [middle_album]
1272
+ ds.where(ar.pk_hash).first_two_albums.all.should == [diff_album]
1273
+ end
1274
+
1275
+ specify "filter by associations with limited many_to_many associations should work correctly" do
1276
+ Album.send :many_to_many, :first_two_tags, {:clone=>:first_two_tags}.merge(@els)
1277
+ Album.send :many_to_many, :second_two_tags, {:clone=>:second_two_tags}.merge(@els)
1278
+ Album.send :many_to_many, :not_first_tags, {:clone=>:not_first_tags}.merge(@els)
1279
+ Album.send :many_to_many, :last_two_tags, {:clone=>:last_two_tags}.merge(@els)
1280
+ tu, tv = @other_tags.call
1281
+ al = @pr.call.first
1282
+ al.add_tag(tu)
1283
+ ds = Album.order(:name)
1284
+
1285
+ ds.where(:first_two_tags=>@tag).all.should == [@album]
1286
+ ds.where(:first_two_tags=>tu).all.should == [@album, al]
1287
+ ds.where(:first_two_tags=>tv).all.should == []
1288
+ ds.exclude(:first_two_tags=>@tag).all.should == [al]
1289
+ ds.exclude(:first_two_tags=>tu).all.should == []
1290
+ ds.exclude(:first_two_tags=>tv).all.should == [@album, al]
1291
+
1292
+ ds.where(:second_two_tags=>@tag).all.should == []
1293
+ ds.where(:second_two_tags=>tu).all.should == [@album]
1294
+ ds.where(:second_two_tags=>tv).all.should == [@album]
1295
+ ds.exclude(:second_two_tags=>@tag).all.should == [@album, al]
1296
+ ds.exclude(:second_two_tags=>tu).all.should == [al]
1297
+ ds.exclude(:second_two_tags=>tv).all.should == [al]
1298
+
1299
+ ds.where(:not_first_tags=>@tag).all.should == []
1300
+ ds.where(:not_first_tags=>tu).all.should == [@album]
1301
+ ds.where(:not_first_tags=>tv).all.should == [@album]
1302
+ ds.exclude(:not_first_tags=>@tag).all.should == [@album, al]
1303
+ ds.exclude(:not_first_tags=>tu).all.should == [al]
1304
+ ds.exclude(:not_first_tags=>tv).all.should == [al]
1305
+
1306
+ ds.where(:last_two_tags=>@tag).all.should == []
1307
+ ds.where(:last_two_tags=>tu).all.should == [@album, al]
1308
+ ds.where(:last_two_tags=>tv).all.should == [@album]
1309
+ ds.exclude(:last_two_tags=>@tag).all.should == [@album, al]
1310
+ ds.exclude(:last_two_tags=>tu).all.should == []
1311
+ ds.exclude(:last_two_tags=>tv).all.should == [al]
1312
+
1313
+ Album.many_to_many :first_two_tags, :clone=>:first_two_tags do |ads| ads.where(:tags__name=>tu.name) end
1314
+ Album.many_to_many :second_two_tags, :clone=>:second_two_tags do |ads| ads.where(:tags__name=>[tu.name, tv.name]) end
1315
+
1316
+ ds.where(:first_two_tags=>[@tag, tu]).all.should == [@album, al]
1317
+ ds.exclude(:first_two_tags=>[@tag, tu]).all.should == []
1318
+
1319
+ al.add_tag(tv)
1320
+ ds.where(:second_two_tags=>[tv, tu]).all.should == [@album, al]
1321
+ ds.exclude(:second_two_tags=>[tv, tu]).all.should == []
1322
+ end
1323
+
1324
+ specify "dataset associations with limited many_to_many associations should work correctly" do
1325
+ Album.send :many_to_many, :first_two_tags, {:clone=>:first_two_tags}.merge(@els)
1326
+ Album.send :many_to_many, :second_two_tags, {:clone=>:second_two_tags}.merge(@els)
1327
+ Album.send :many_to_many, :not_first_tags, {:clone=>:not_first_tags}.merge(@els)
1328
+ Album.send :many_to_many, :last_two_tags, {:clone=>:last_two_tags}.merge(@els)
1329
+ tu, tv = @other_tags.call
1330
+ al = @pr.call.first
1331
+ al.add_tag(tu)
1332
+ ds = Album.order(:name)
1333
+
1334
+ ds.where(@album.pk_hash).first_two_tags.all.should == [@tag, tu]
1335
+ ds.where(@album.pk_hash).second_two_tags.all.should == [tu, tv]
1336
+ ds.where(@album.pk_hash).not_first_tags.all.should == [tu, tv]
1337
+ ds.where(@album.pk_hash).last_two_tags.all.should == [tv, tu]
1338
+ ds.where(al.pk_hash).first_two_tags.all.should == [tu]
1339
+ ds.where(al.pk_hash).second_two_tags.all.should == []
1340
+ ds.where(al.pk_hash).not_first_tags.all.should == []
1341
+ ds.where(al.pk_hash).last_two_tags.all.should == [tu]
1342
+
1343
+ Album.many_to_many :first_two_tags, :clone=>:first_two_tags do |ads| ads.where(:tags__name=>tu.name) end
1344
+ Album.many_to_many :second_two_tags, :clone=>:second_two_tags do |ads| ads.where(:tags__name=>[tu.name, tv.name]) end
1345
+
1346
+ ds.where(@album.pk_hash).first_two_tags.all.should == [tu]
1347
+ ds.where(@album.pk_hash).second_two_tags.all.should == [tv]
1348
+ ds.where(al.pk_hash).first_two_tags.all.should == [tu]
1349
+ ds.where(al.pk_hash).second_two_tags.all.should == []
1350
+
1351
+ al.add_tag(tv)
1352
+ ds.where(@album.pk_hash).first_two_tags.all.should == [tu]
1353
+ ds.where(@album.pk_hash).second_two_tags.all.should == [tv]
1354
+ ds.where(al.pk_hash).first_two_tags.all.should == [tu]
1355
+ ds.where(al.pk_hash).second_two_tags.all.should == [tv]
1356
+ end
1357
+
1358
+ specify "filter by associations with limited many_through_many associations should work correctly" do
1359
+ Artist.many_through_many :first_two_tags, {:clone=>:first_two_tags}.merge(@els)
1360
+ Artist.many_through_many :second_two_tags, {:clone=>:second_two_tags}.merge(@els)
1361
+ Artist.many_through_many :not_first_tags, {:clone=>:not_first_tags}.merge(@els)
1362
+ Artist.many_through_many :last_two_tags, {:clone=>:last_two_tags}.merge(@els)
1363
+ @album.update(:artist => @artist)
1364
+ tu, tv = @other_tags.call
1365
+ al, ar, _ = @pr.call
1366
+ al.update(:artist=>ar)
1367
+ al.add_tag(tu)
1368
+ ds = Artist.order(:name)
1369
+
1370
+ ds.where(:first_two_tags=>@tag).all.should == [@artist]
1371
+ ds.where(:first_two_tags=>tu).all.should == [@artist, ar]
1372
+ ds.where(:first_two_tags=>tv).all.should == []
1373
+ ds.exclude(:first_two_tags=>@tag).all.should == [ar]
1374
+ ds.exclude(:first_two_tags=>tu).all.should == []
1375
+ ds.exclude(:first_two_tags=>tv).all.should == [@artist, ar]
1376
+
1377
+ ds.where(:second_two_tags=>@tag).all.should == []
1378
+ ds.where(:second_two_tags=>tu).all.should == [@artist]
1379
+ ds.where(:second_two_tags=>tv).all.should == [@artist]
1380
+ ds.exclude(:second_two_tags=>@tag).all.should == [@artist, ar]
1381
+ ds.exclude(:second_two_tags=>tu).all.should == [ar]
1382
+ ds.exclude(:second_two_tags=>tv).all.should == [ar]
1383
+
1384
+ ds.where(:not_first_tags=>@tag).all.should == []
1385
+ ds.where(:not_first_tags=>tu).all.should == [@artist]
1386
+ ds.where(:not_first_tags=>tv).all.should == [@artist]
1387
+ ds.exclude(:not_first_tags=>@tag).all.should == [@artist, ar]
1388
+ ds.exclude(:not_first_tags=>tu).all.should == [ar]
1389
+ ds.exclude(:not_first_tags=>tv).all.should == [ar]
1390
+
1391
+ ds.where(:last_two_tags=>@tag).all.should == []
1392
+ ds.where(:last_two_tags=>tu).all.should == [@artist, ar]
1393
+ ds.where(:last_two_tags=>tv).all.should == [@artist]
1394
+ ds.exclude(:last_two_tags=>@tag).all.should == [@artist, ar]
1395
+ ds.exclude(:last_two_tags=>tu).all.should == []
1396
+ ds.exclude(:last_two_tags=>tv).all.should == [ar]
1397
+
1398
+ Artist.many_through_many :first_two_tags, :clone=>:first_tag do |ads| ads.where(:tags__name=>tu.name) end
1399
+ Artist.many_through_many :second_two_tags, :clone=>:first_tag do |ads| ads.where(:tags__name=>[tv.name, tu.name]) end
1400
+
1401
+ ds.where(:first_two_tags=>[@tag, tu]).all.should == [@artist, ar]
1402
+ ds.exclude(:first_two_tags=>[@tag, tu]).all.should == []
1403
+
1404
+ al.add_tag(tv)
1405
+ ds.where(:second_two_tags=>[tv, tu]).all.should == [@artist, ar]
1406
+ ds.exclude(:second_two_tags=>[tv, tu]).all.should == []
1407
+ end
1408
+
1409
+ specify "dataset associations with limited many_through_many associations should work correctly" do
1410
+ Artist.many_through_many :first_two_tags, {:clone=>:first_two_tags}.merge(@els)
1411
+ Artist.many_through_many :second_two_tags, {:clone=>:second_two_tags}.merge(@els)
1412
+ Artist.many_through_many :not_first_tags, {:clone=>:not_first_tags}.merge(@els)
1413
+ Artist.many_through_many :last_two_tags, {:clone=>:last_two_tags}.merge(@els)
1414
+ @album.update(:artist => @artist)
1415
+ tu, tv = @other_tags.call
1416
+ al, ar, _ = @pr.call
1417
+ al.update(:artist=>ar)
1418
+ al.add_tag(tu)
1419
+ ds = Artist.order(:name)
1420
+
1421
+ ds.where(@artist.pk_hash).first_two_tags.all.should == [@tag, tu]
1422
+ ds.where(@artist.pk_hash).second_two_tags.all.should == [tu, tv]
1423
+ ds.where(@artist.pk_hash).not_first_tags.all.should == [tu, tv]
1424
+ ds.where(@artist.pk_hash).last_two_tags.all.should == [tv, tu]
1425
+ ds.where(ar.pk_hash).first_two_tags.all.should == [tu]
1426
+ ds.where(ar.pk_hash).second_two_tags.all.should == []
1427
+ ds.where(ar.pk_hash).not_first_tags.all.should == []
1428
+ ds.where(ar.pk_hash).last_two_tags.all.should == [tu]
1429
+
1430
+ Artist.many_through_many :first_two_tags, :clone=>:first_two_tags do |ads| ads.where(:tags__name=>tu.name) end
1431
+ Artist.many_through_many :second_two_tags, :clone=>:second_two_tags do |ads| ads.where(:tags__name=>[tu.name, tv.name]) end
1432
+
1433
+ ds.where(@artist.pk_hash).first_two_tags.all.should == [tu]
1434
+ ds.where(@artist.pk_hash).second_two_tags.all.should == [tv]
1435
+ ds.where(ar.pk_hash).first_two_tags.all.should == [tu]
1436
+ ds.where(ar.pk_hash).second_two_tags.all.should == []
1437
+
1438
+ al.add_tag(tv)
1439
+ ds.where(@artist.pk_hash).first_two_tags.all.should == [tu]
1440
+ ds.where(@artist.pk_hash).second_two_tags.all.should == [tv]
1441
+ ds.where(ar.pk_hash).first_two_tags.all.should == [tu]
1442
+ ds.where(ar.pk_hash).second_two_tags.all.should == [tv]
1443
+ end
560
1444
  end
561
1445
 
562
1446
  shared_examples_for "basic regular and composite key associations" do
@@ -610,7 +1494,11 @@ shared_examples_for "basic regular and composite key associations" do
610
1494
  Album.tags.all.should == []
611
1495
  Album.alias_tags.all.should == []
612
1496
  Artist.albums.all.should == []
613
- Artist.tags.all.should == [] unless @no_many_through_many
1497
+ unless @no_many_through_many
1498
+ Album.first_tags.all.should == []
1499
+ Artist.tags.all.should == []
1500
+ Artist.first_tags.all.should == []
1501
+ end
614
1502
  Artist.albums.tags.all.should == []
615
1503
 
616
1504
  @album.update(:artist => @artist)
@@ -621,7 +1509,11 @@ shared_examples_for "basic regular and composite key associations" do
621
1509
  Album.tags.all.should == [@tag]
622
1510
  Album.alias_tags.all.should == [@tag]
623
1511
  Artist.albums.all.should == [@album]
624
- Artist.tags.all.should == [@tag] unless @no_many_through_many
1512
+ unless @no_many_through_many
1513
+ Album.first_tags.all.should == [@tag]
1514
+ Artist.tags.all.should == [@tag]
1515
+ Artist.first_tags.all.should == [@tag]
1516
+ end
625
1517
  Artist.albums.tags.all.should == [@tag]
626
1518
 
627
1519
  album.add_tag(tag)
@@ -632,7 +1524,11 @@ shared_examples_for "basic regular and composite key associations" do
632
1524
  Album.tags.order(:name).all.should == [@tag, tag]
633
1525
  Album.alias_tags.order(:name).all.should == [@tag, tag]
634
1526
  Artist.albums.order(:name).all.should == [@album, album]
635
- Artist.tags.order(:name).all.should == [@tag, tag] unless @no_many_through_many
1527
+ unless @no_many_through_many
1528
+ Album.first_tags.order(:name).all.should == [@tag, tag]
1529
+ Artist.tags.order(:name).all.should == [@tag, tag]
1530
+ Artist.first_tags.order(:name).all.should == [@tag, tag]
1531
+ end
636
1532
  Artist.albums.tags.order(:name).all.should == [@tag, tag]
637
1533
 
638
1534
  Tag.filter(Tag.qualified_primary_key_hash(tag.pk)).albums.all.should == [album]
@@ -640,7 +1536,11 @@ shared_examples_for "basic regular and composite key associations" do
640
1536
  Album.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == [tag]
641
1537
  Album.filter(Album.qualified_primary_key_hash(album.pk)).alias_tags.all.should == [tag]
642
1538
  Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.all.should == [album]
643
- Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).tags.all.should == [tag] unless @no_many_through_many
1539
+ unless @no_many_through_many
1540
+ Album.filter(Album.qualified_primary_key_hash(album.pk)).first_tags.all.should == [tag]
1541
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).tags.all.should == [tag]
1542
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).first_tags.all.should == [tag]
1543
+ end
644
1544
  Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.tags.all.should == [tag]
645
1545
 
646
1546
  Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == [tag]
@@ -772,6 +1672,9 @@ shared_examples_for "regular and composite key associations" do
772
1672
  @els = {:eager_limit_strategy=>true}
773
1673
  end
774
1674
  it_should_behave_like "one_to_one eager limit strategies"
1675
+ it_should_behave_like "one_through_one eager limit strategies"
1676
+ it_should_behave_like "one_through_many eager limit strategies"
1677
+ it_should_behave_like "filter by associations singular association limit strategies"
775
1678
  end if DB.dataset.supports_ordered_distinct_on?
776
1679
 
777
1680
  describe "with :eager_limit_strategy=>:window_function" do
@@ -779,6 +1682,7 @@ shared_examples_for "regular and composite key associations" do
779
1682
  @els = {:eager_limit_strategy=>:window_function}
780
1683
  end
781
1684
  it_should_behave_like "eager limit strategies"
1685
+ it_should_behave_like "filter by associations limit strategies"
782
1686
  end if DB.dataset.supports_window_functions?
783
1687
 
784
1688
  specify "should work with a many_through_many association" do
@@ -809,6 +1713,35 @@ shared_examples_for "regular and composite key associations" do
809
1713
  a.first.artist.should == @artist
810
1714
  a.first.artist.tags.should == [@tag]
811
1715
  end
1716
+
1717
+ specify "should work with a one_through_many association" do
1718
+ @album.update(:artist => @artist)
1719
+ @album.add_tag(@tag)
1720
+
1721
+ @album.reload
1722
+ @artist.reload
1723
+ @tag.reload
1724
+
1725
+ @album.tags.should == [@tag]
1726
+
1727
+ a = Artist.eager(:first_tag).all
1728
+ a.should == [@artist]
1729
+ a.first.first_tag.should == @tag
1730
+
1731
+ a = Artist.eager_graph(:first_tag).all
1732
+ a.should == [@artist]
1733
+ a.first.first_tag.should == @tag
1734
+
1735
+ a = Album.eager(:artist=>:first_tag).all
1736
+ a.should == [@album]
1737
+ a.first.artist.should == @artist
1738
+ a.first.artist.first_tag.should == @tag
1739
+
1740
+ a = Album.eager_graph(:artist=>:first_tag).all
1741
+ a.should == [@album]
1742
+ a.first.artist.should == @artist
1743
+ a.first.artist.first_tag.should == @tag
1744
+ end
812
1745
  end
813
1746
 
814
1747
  describe "Sequel::Model Simple Associations" do
@@ -849,11 +1782,15 @@ describe "Sequel::Model Simple Associations" do
849
1782
  one_to_one :first_a_album, :clone=>:a_albums
850
1783
  plugin :many_through_many
851
1784
  many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]]
852
- many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2
853
- many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1]
854
- many_through_many :not_first_tags, :clone=>:tags, :order=>:tags__name, :limit=>[nil, 1]
855
- many_through_many :last_two_tags, :clone=>:tags, :order=>Sequel.desc(:tags__name), :limit=>2
1785
+ many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2, :graph_order=>:name
1786
+ many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1], :graph_order=>:name
1787
+ many_through_many :not_first_tags, :clone=>:tags, :order=>:tags__name, :limit=>[nil, 1], :graph_order=>:name
1788
+ many_through_many :last_two_tags, :clone=>:tags, :order=>Sequel.desc(:tags__name), :limit=>2, :graph_order=>Sequel.desc(:name)
856
1789
  many_through_many :t_tags, :clone=>:tags, :conditions=>{:tags__name=>'T'}
1790
+ one_through_many :first_tag, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]], :order=>:tags__name, :graph_order=>:name, :class=>:Tag
1791
+ one_through_many :second_tag, :clone=>:first_tag, :limit=>[nil, 1]
1792
+ one_through_many :last_tag, :clone=>:first_tag, :order=>Sequel.desc(:tags__name), :graph_order=>Sequel.desc(:name)
1793
+ one_through_many :t_tag, :clone=>:first_tag, :conditions=>{:tags__name=>'T'}
857
1794
  end
858
1795
  class ::Album < Sequel::Model(@db)
859
1796
  plugin :dataset_associations
@@ -867,6 +1804,11 @@ describe "Sequel::Model Simple Associations" do
867
1804
  many_to_many :last_two_tags, :clone=>:tags, :order=>Sequel.desc(:name), :limit=>2
868
1805
  many_to_many :t_tags, :clone=>:tags, :conditions=>{:name=>'T'}
869
1806
  many_to_many :alias_t_tags, :clone=>:t_tags, :join_table=>:albums_tags___at
1807
+ one_through_one :first_tag, :clone=>:tags, :order=>:name
1808
+ one_through_one :second_tag, :clone=>:first_tag, :limit=>[nil, 1]
1809
+ one_through_one :last_tag, :clone=>:tags, :order=>Sequel.desc(:name)
1810
+ one_through_one :t_tag, :clone=>:t_tags
1811
+ one_through_one :alias_t_tag, :clone=>:alias_t_tags
870
1812
  end
871
1813
  class ::Tag < Sequel::Model(@db)
872
1814
  plugin :dataset_associations
@@ -1050,7 +1992,7 @@ describe "Sequel::Model Composite Key Associations" do
1050
1992
  set_primary_key [:id1, :id2]
1051
1993
  unrestrict_primary_key
1052
1994
  one_to_many :albums, :key=>[:artist_id1, :artist_id2], :order=>:name
1053
- one_to_one :first_album, :clone=>:albums, :order=>:name
1995
+ one_to_one :first_album, :clone=>:albums
1054
1996
  one_to_one :last_album, :clone=>:albums, :order=>Sequel.desc(:name)
1055
1997
  one_to_one :second_album, :clone=>:albums, :limit=>[nil, 1]
1056
1998
  one_to_many :first_two_albums, :clone=>:albums, :order=>:name, :limit=>2
@@ -1061,11 +2003,15 @@ describe "Sequel::Model Composite Key Associations" do
1061
2003
  one_to_one :first_a_album, :clone=>:a_albums
1062
2004
  plugin :many_through_many
1063
2005
  many_through_many :tags, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]]
1064
- many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2
1065
- many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1]
1066
- many_through_many :not_first_tags, :clone=>:tags, :order=>:tags__name, :limit=>[nil, 1]
1067
- many_through_many :last_two_tags, :clone=>:tags, :order=>Sequel.desc(:tags__name), :limit=>2
2006
+ many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2, :graph_order=>:name
2007
+ many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1], :graph_order=>:name
2008
+ many_through_many :not_first_tags, :clone=>:tags, :order=>:tags__name, :limit=>[nil, 1], :graph_order=>:name
2009
+ many_through_many :last_two_tags, :clone=>:tags, :order=>Sequel.desc(:tags__name), :limit=>2, :graph_order=>Sequel.desc(:name)
1068
2010
  many_through_many :t_tags, :clone=>:tags do |ds| ds.where(:tags__name=>'T') end
2011
+ one_through_many :first_tag, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]], :order=>:tags__name, :graph_order=>:name, :class=>:Tag
2012
+ one_through_many :second_tag, :clone=>:first_tag, :limit=>[nil, 1]
2013
+ one_through_many :last_tag, :clone=>:first_tag, :order=>Sequel.desc(:tags__name), :graph_order=>Sequel.desc(:name)
2014
+ one_through_many :t_tag, :clone=>:first_tag do |ds| ds.where(:tags__name=>'T') end
1069
2015
  end
1070
2016
  class ::Album < Sequel::Model(@db)
1071
2017
  plugin :dataset_associations
@@ -1081,6 +2027,11 @@ describe "Sequel::Model Composite Key Associations" do
1081
2027
  many_to_many :last_two_tags, :clone=>:tags, :order=>Sequel.desc(:name), :limit=>2
1082
2028
  many_to_many :t_tags, :clone=>:tags do |ds| ds.where(:name=>'T') end
1083
2029
  many_to_many :alias_t_tags, :clone=>:t_tags, :join_table=>:albums_tags___at
2030
+ one_through_one :first_tag, :clone=>:tags, :order=>:name
2031
+ one_through_one :second_tag, :clone=>:first_tag, :limit=>[nil, 1]
2032
+ one_through_one :last_tag, :clone=>:tags, :order=>Sequel.desc(:name)
2033
+ one_through_one :t_tag, :clone=>:t_tags
2034
+ one_through_one :alias_t_tag, :clone=>:alias_t_tags
1084
2035
  end
1085
2036
  class ::Tag < Sequel::Model(@db)
1086
2037
  plugin :dataset_associations
@@ -1225,7 +2176,7 @@ describe "Sequel::Model pg_array_to_many" do
1225
2176
  a.remove_tag(@tag)
1226
2177
  a.save
1227
2178
  end
1228
- end if DB.database_type == :postgres && DB.adapter_scheme == :postgres && DB.server_version >= 90300
2179
+ end if DB.database_type == :postgres && [:postgres, :jdbc].include?(DB.adapter_scheme) && DB.server_version >= 90300
1229
2180
 
1230
2181
  describe "Sequel::Model many_to_pg_array" do
1231
2182
  before(:all) do
@@ -1304,7 +2255,7 @@ describe "Sequel::Model many_to_pg_array" do
1304
2255
  a.add_tag(@tag)
1305
2256
  a.remove_tag(@tag)
1306
2257
  end
1307
- end if DB.database_type == :postgres && DB.adapter_scheme == :postgres && DB.server_version >= 90300
2258
+ end if DB.database_type == :postgres && [:postgres, :jdbc].include?(DB.adapter_scheme) && DB.server_version >= 90300
1308
2259
 
1309
2260
  describe "Sequel::Model Associations with clashing column names" do
1310
2261
  before(:all) do
@@ -1334,6 +2285,7 @@ describe "Sequel::Model Associations with clashing column names" do
1334
2285
  @Foo.one_to_one :bar, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :class=>@Bar
1335
2286
  @Bar.many_to_one :foo, :key=>:obj_id, :key_column=>:object_id, :primary_key=>:object_id, :primary_key_method=>:obj_id, :class=>@Foo
1336
2287
  @Foo.many_to_many :mtmbars, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:foo_id, :right_key=>:object_id, :class=>@Bar
2288
+ @Foo.one_through_one :mtmbar, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:foo_id, :right_key=>:object_id, :class=>@Bar
1337
2289
  @Bar.many_to_many :mtmfoos, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:object_id, :right_key=>:foo_id, :class=>@Foo
1338
2290
  @foo = @Foo.create(:obj_id=>2)
1339
2291
  @bar = @Bar.create(:obj_id=>2)
@@ -1348,6 +2300,7 @@ describe "Sequel::Model Associations with clashing column names" do
1348
2300
  @Foo.first.bars.should == [@bar]
1349
2301
  @Foo.first.bar.should == @bar
1350
2302
  @Foo.first.mtmbars.should == [@bar]
2303
+ @Foo.first.mtmbar.should == @bar
1351
2304
  @Bar.first.mtmfoos.should == [@foo]
1352
2305
  end
1353
2306
 
@@ -1356,6 +2309,7 @@ describe "Sequel::Model Associations with clashing column names" do
1356
2309
  @Foo.eager(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
1357
2310
  @Foo.eager(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
1358
2311
  @Foo.eager(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
2312
+ @Foo.eager(:mtmbar).all.map{|o| [o, o.mtmbar]}.should == [[@foo, @bar]]
1359
2313
  @Bar.eager(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
1360
2314
  end
1361
2315
 
@@ -1364,6 +2318,7 @@ describe "Sequel::Model Associations with clashing column names" do
1364
2318
  @Foo.eager_graph(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
1365
2319
  @Foo.eager_graph(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
1366
2320
  @Foo.eager_graph(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
2321
+ @Foo.eager_graph(:mtmbar).all.map{|o| [o, o.mtmbar]}.should == [[@foo, @bar]]
1367
2322
  @Bar.eager_graph(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
1368
2323
  end
1369
2324