sequel 4.7.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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