sequel 3.21.0 → 3.29.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 (200) hide show
  1. data/CHANGELOG +413 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +23 -16
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +210 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/mass_assignment.rdoc +54 -0
  8. data/doc/migration.rdoc +15 -538
  9. data/doc/model_hooks.rdoc +72 -27
  10. data/doc/opening_databases.rdoc +86 -50
  11. data/doc/prepared_statements.rdoc +40 -13
  12. data/doc/reflection.rdoc +8 -2
  13. data/doc/release_notes/3.22.0.txt +39 -0
  14. data/doc/release_notes/3.23.0.txt +172 -0
  15. data/doc/release_notes/3.24.0.txt +420 -0
  16. data/doc/release_notes/3.25.0.txt +88 -0
  17. data/doc/release_notes/3.26.0.txt +88 -0
  18. data/doc/release_notes/3.27.0.txt +82 -0
  19. data/doc/release_notes/3.28.0.txt +304 -0
  20. data/doc/release_notes/3.29.0.txt +459 -0
  21. data/doc/schema_modification.rdoc +547 -0
  22. data/doc/sharding.rdoc +7 -1
  23. data/doc/testing.rdoc +115 -0
  24. data/doc/transactions.rdoc +137 -0
  25. data/doc/virtual_rows.rdoc +2 -2
  26. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  27. data/lib/sequel/adapters/ado.rb +40 -18
  28. data/lib/sequel/adapters/amalgalite.rb +15 -7
  29. data/lib/sequel/adapters/db2.rb +175 -86
  30. data/lib/sequel/adapters/dbi.rb +15 -15
  31. data/lib/sequel/adapters/do/mysql.rb +0 -5
  32. data/lib/sequel/adapters/do/postgres.rb +0 -5
  33. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  34. data/lib/sequel/adapters/do.rb +17 -36
  35. data/lib/sequel/adapters/firebird.rb +27 -208
  36. data/lib/sequel/adapters/ibmdb.rb +448 -0
  37. data/lib/sequel/adapters/informix.rb +6 -23
  38. data/lib/sequel/adapters/jdbc/as400.rb +5 -31
  39. data/lib/sequel/adapters/jdbc/db2.rb +44 -0
  40. data/lib/sequel/adapters/jdbc/derby.rb +217 -0
  41. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  42. data/lib/sequel/adapters/jdbc/h2.rb +26 -17
  43. data/lib/sequel/adapters/jdbc/hsqldb.rb +166 -0
  44. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  45. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  46. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  47. data/lib/sequel/adapters/jdbc/mysql.rb +9 -10
  48. data/lib/sequel/adapters/jdbc/oracle.rb +67 -25
  49. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -11
  50. data/lib/sequel/adapters/jdbc/sqlite.rb +0 -5
  51. data/lib/sequel/adapters/jdbc/sqlserver.rb +41 -0
  52. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  53. data/lib/sequel/adapters/jdbc.rb +107 -38
  54. data/lib/sequel/adapters/mock.rb +315 -0
  55. data/lib/sequel/adapters/mysql.rb +78 -182
  56. data/lib/sequel/adapters/mysql2.rb +24 -23
  57. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  58. data/lib/sequel/adapters/odbc/mssql.rb +0 -5
  59. data/lib/sequel/adapters/odbc.rb +28 -9
  60. data/lib/sequel/adapters/openbase.rb +2 -4
  61. data/lib/sequel/adapters/oracle.rb +349 -51
  62. data/lib/sequel/adapters/postgres.rb +169 -21
  63. data/lib/sequel/adapters/shared/access.rb +21 -6
  64. data/lib/sequel/adapters/shared/db2.rb +288 -0
  65. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  66. data/lib/sequel/adapters/shared/informix.rb +45 -0
  67. data/lib/sequel/adapters/shared/mssql.rb +108 -65
  68. data/lib/sequel/adapters/shared/mysql.rb +56 -13
  69. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  70. data/lib/sequel/adapters/shared/oracle.rb +185 -34
  71. data/lib/sequel/adapters/shared/postgres.rb +107 -58
  72. data/lib/sequel/adapters/shared/progress.rb +0 -6
  73. data/lib/sequel/adapters/shared/sqlite.rb +168 -41
  74. data/lib/sequel/adapters/sqlite.rb +27 -18
  75. data/lib/sequel/adapters/swift/mysql.rb +9 -5
  76. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  77. data/lib/sequel/adapters/swift/sqlite.rb +6 -4
  78. data/lib/sequel/adapters/swift.rb +5 -5
  79. data/lib/sequel/adapters/tinytds.rb +152 -17
  80. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +63 -0
  81. data/lib/sequel/ast_transformer.rb +190 -0
  82. data/lib/sequel/connection_pool/threaded.rb +3 -2
  83. data/lib/sequel/connection_pool.rb +1 -1
  84. data/lib/sequel/core.rb +46 -5
  85. data/lib/sequel/database/connecting.rb +5 -6
  86. data/lib/sequel/database/dataset.rb +4 -4
  87. data/lib/sequel/database/dataset_defaults.rb +59 -1
  88. data/lib/sequel/database/logging.rb +1 -1
  89. data/lib/sequel/database/misc.rb +100 -19
  90. data/lib/sequel/database/query.rb +153 -58
  91. data/lib/sequel/database/schema_generator.rb +8 -5
  92. data/lib/sequel/database/schema_methods.rb +59 -29
  93. data/lib/sequel/dataset/actions.rb +194 -57
  94. data/lib/sequel/dataset/features.rb +81 -8
  95. data/lib/sequel/dataset/graph.rb +8 -7
  96. data/lib/sequel/dataset/misc.rb +50 -23
  97. data/lib/sequel/dataset/mutation.rb +5 -6
  98. data/lib/sequel/dataset/prepared_statements.rb +32 -15
  99. data/lib/sequel/dataset/query.rb +223 -42
  100. data/lib/sequel/dataset/sql.rb +58 -61
  101. data/lib/sequel/dataset.rb +8 -0
  102. data/lib/sequel/exceptions.rb +4 -0
  103. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  104. data/lib/sequel/extensions/migration.rb +6 -4
  105. data/lib/sequel/extensions/named_timezones.rb +5 -0
  106. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  107. data/lib/sequel/extensions/to_dot.rb +95 -83
  108. data/lib/sequel/model/associations.rb +997 -331
  109. data/lib/sequel/model/base.rb +386 -129
  110. data/lib/sequel/model/errors.rb +1 -1
  111. data/lib/sequel/model/exceptions.rb +5 -1
  112. data/lib/sequel/model.rb +15 -8
  113. data/lib/sequel/plugins/association_pks.rb +22 -4
  114. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  115. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  116. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  117. data/lib/sequel/plugins/force_encoding.rb +6 -6
  118. data/lib/sequel/plugins/identity_map.rb +114 -7
  119. data/lib/sequel/plugins/many_through_many.rb +65 -7
  120. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  121. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  122. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  123. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  124. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  125. data/lib/sequel/plugins/serialization.rb +6 -1
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  127. data/lib/sequel/plugins/sharding.rb +12 -25
  128. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  129. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  130. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  131. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  132. data/lib/sequel/sql.rb +107 -51
  133. data/lib/sequel/timezones.rb +44 -35
  134. data/lib/sequel/version.rb +1 -1
  135. data/spec/adapters/db2_spec.rb +146 -0
  136. data/spec/adapters/mssql_spec.rb +65 -29
  137. data/spec/adapters/mysql_spec.rb +122 -123
  138. data/spec/adapters/oracle_spec.rb +48 -76
  139. data/spec/adapters/postgres_spec.rb +213 -61
  140. data/spec/adapters/spec_helper.rb +6 -5
  141. data/spec/adapters/sqlite_spec.rb +35 -21
  142. data/spec/core/connection_pool_spec.rb +71 -92
  143. data/spec/core/core_sql_spec.rb +20 -31
  144. data/spec/core/database_spec.rb +729 -508
  145. data/spec/core/dataset_spec.rb +980 -1044
  146. data/spec/core/expression_filters_spec.rb +159 -42
  147. data/spec/core/mock_adapter_spec.rb +378 -0
  148. data/spec/core/object_graph_spec.rb +48 -114
  149. data/spec/core/schema_generator_spec.rb +3 -3
  150. data/spec/core/schema_spec.rb +298 -38
  151. data/spec/core/spec_helper.rb +6 -48
  152. data/spec/extensions/association_pks_spec.rb +38 -0
  153. data/spec/extensions/class_table_inheritance_spec.rb +1 -1
  154. data/spec/extensions/columns_introspection_spec.rb +91 -0
  155. data/spec/extensions/dataset_associations_spec.rb +199 -0
  156. data/spec/extensions/defaults_setter_spec.rb +64 -0
  157. data/spec/extensions/identity_map_spec.rb +162 -0
  158. data/spec/extensions/instance_hooks_spec.rb +71 -0
  159. data/spec/extensions/many_through_many_spec.rb +195 -20
  160. data/spec/extensions/migration_spec.rb +17 -17
  161. data/spec/extensions/named_timezones_spec.rb +22 -2
  162. data/spec/extensions/nested_attributes_spec.rb +4 -0
  163. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  164. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  165. data/spec/extensions/prepared_statements_spec.rb +72 -0
  166. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  167. data/spec/extensions/schema_dumper_spec.rb +2 -2
  168. data/spec/extensions/schema_spec.rb +13 -21
  169. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  170. data/spec/extensions/serialization_spec.rb +5 -8
  171. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  172. data/spec/extensions/spec_helper.rb +7 -1
  173. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  174. data/spec/extensions/to_dot_spec.rb +3 -5
  175. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  176. data/spec/extensions/xml_serializer_spec.rb +16 -4
  177. data/spec/integration/associations_test.rb +522 -21
  178. data/spec/integration/database_test.rb +4 -2
  179. data/spec/integration/dataset_test.rb +375 -62
  180. data/spec/integration/eager_loader_test.rb +19 -21
  181. data/spec/integration/model_test.rb +80 -1
  182. data/spec/integration/plugin_test.rb +304 -116
  183. data/spec/integration/prepared_statement_test.rb +200 -120
  184. data/spec/integration/schema_test.rb +161 -30
  185. data/spec/integration/spec_helper.rb +39 -30
  186. data/spec/integration/timezone_test.rb +38 -12
  187. data/spec/integration/transaction_test.rb +172 -5
  188. data/spec/integration/type_test.rb +17 -3
  189. data/spec/model/association_reflection_spec.rb +174 -7
  190. data/spec/model/associations_spec.rb +849 -661
  191. data/spec/model/base_spec.rb +255 -95
  192. data/spec/model/dataset_methods_spec.rb +7 -27
  193. data/spec/model/eager_loading_spec.rb +640 -676
  194. data/spec/model/hooks_spec.rb +309 -67
  195. data/spec/model/model_spec.rb +207 -167
  196. data/spec/model/plugins_spec.rb +24 -13
  197. data/spec/model/record_spec.rb +321 -218
  198. data/spec/model/spec_helper.rb +13 -71
  199. data/spec/model/validations_spec.rb +11 -0
  200. metadata +95 -38
@@ -1,10 +1,114 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'spec_helper.rb')
2
2
 
3
+ shared_examples_for "eager limit strategies" do
4
+ specify "eager loading one_to_one associations should work correctly" do
5
+ Artist.one_to_one :first_album, {:clone=>:first_album}.merge(@els) if @els
6
+ Artist.one_to_one :last_album, {:clone=>:last_album}.merge(@els) if @els
7
+ @album.update(:artist => @artist)
8
+ diff_album = @diff_album.call
9
+ al, ar, t = @pr.call
10
+
11
+ a = Artist.eager(:first_album, :last_album).order(:name).all
12
+ a.should == [@artist, ar]
13
+ a.first.first_album.should == @album
14
+ a.first.last_album.should == diff_album
15
+ a.last.first_album.should == nil
16
+ a.last.last_album.should == nil
17
+
18
+ # Check that no extra columns got added by the eager loading
19
+ a.first.first_album.values.should == @album.values
20
+ a.first.last_album.values.should == diff_album.values
21
+
22
+ same_album = @same_album.call
23
+ a = Artist.eager(:first_album).order(:name).all
24
+ a.should == [@artist, ar]
25
+ [@album, same_album].should include(a.first.first_album)
26
+ a.last.first_album.should == nil
27
+ end
28
+
29
+ specify "should correctly handle limits and offsets when eager loading one_to_many associations" do
30
+ Artist.one_to_many :first_two_albums, {:clone=>:first_two_albums}.merge(@els) if @els
31
+ Artist.one_to_many :second_two_albums, {:clone=>:second_two_albums}.merge(@els) if @els
32
+ Artist.one_to_many :last_two_albums, {:clone=>:last_two_albums}.merge(@els) if @els
33
+ @album.update(:artist => @artist)
34
+ middle_album = @middle_album.call
35
+ diff_album = @diff_album.call
36
+ al, ar, t = @pr.call
37
+
38
+ ars = Artist.eager(:first_two_albums, :second_two_albums, :last_two_albums).order(:name).all
39
+ ars.should == [@artist, ar]
40
+ ars.first.first_two_albums.should == [@album, middle_album]
41
+ ars.first.second_two_albums.should == [middle_album, diff_album]
42
+ ars.first.last_two_albums.should == [diff_album, middle_album]
43
+ ars.last.first_two_albums.should == []
44
+ ars.last.second_two_albums.should == []
45
+ ars.last.last_two_albums.should == []
46
+
47
+ # Check that no extra columns got added by the eager loading
48
+ ars.first.first_two_albums.map{|x| x.values}.should == [@album, middle_album].map{|x| x.values}
49
+ ars.first.second_two_albums.map{|x| x.values}.should == [middle_album, diff_album].map{|x| x.values}
50
+ ars.first.last_two_albums.map{|x| x.values}.should == [diff_album, middle_album].map{|x| x.values}
51
+ end
52
+
53
+ specify "should correctly handle limits and offsets when eager loading many_to_many associations" do
54
+ if @els == {:eager_limit_strategy=>:correlated_subquery} && Sequel.guarded?(:derby)
55
+ pending("Derby errors with correlated subqueries on many_to_many associations")
56
+ end
57
+ Album.many_to_many :first_two_tags, {:clone=>:first_two_tags}.merge(@els) if @els
58
+ Album.many_to_many :second_two_tags, {:clone=>:second_two_tags}.merge(@els) if @els
59
+ Album.many_to_many :last_two_tags, {:clone=>:last_two_tags}.merge(@els) if @els
60
+ tu, tv = @other_tags.call
61
+ al, ar, t = @pr.call
62
+
63
+ als = Album.eager(:first_two_tags, :second_two_tags, :last_two_tags).order(:name).all
64
+ als.should == [@album, al]
65
+ als.first.first_two_tags.should == [@tag, tu]
66
+ als.first.second_two_tags.should == [tu, tv]
67
+ als.first.last_two_tags.should == [tv, tu]
68
+ als.last.first_two_tags.should == []
69
+ als.last.second_two_tags.should == []
70
+ als.last.last_two_tags.should == []
71
+
72
+ # Check that no extra columns got added by the eager loading
73
+ als.first.first_two_tags.map{|x| x.values}.should == [@tag, tu].map{|x| x.values}
74
+ als.first.second_two_tags.map{|x| x.values}.should == [tu, tv].map{|x| x.values}
75
+ als.first.last_two_tags.map{|x| x.values}.should == [tv, tu].map{|x| x.values}
76
+ end
77
+
78
+ specify "should correctly handle limits and offsets when eager loading many_through_many associations" do
79
+ if @els == {:eager_limit_strategy=>:correlated_subquery} && Sequel.guarded?(:derby)
80
+ pending("Derby errors with correlated subqueries on many_through_many associations")
81
+ end
82
+ Artist.many_through_many :first_two_tags, {:clone=>:first_two_tags}.merge(@els) if @els
83
+ Artist.many_through_many :second_two_tags, {:clone=>:second_two_tags}.merge(@els) if @els
84
+ Artist.many_through_many :last_two_tags, {:clone=>:last_two_tags}.merge(@els) if @els
85
+ @album.update(:artist => @artist)
86
+ tu, tv = @other_tags.call
87
+ al, ar, t = @pr.call
88
+
89
+ ars = Artist.eager(:first_two_tags, :second_two_tags, :last_two_tags).order(:name).all
90
+ ars.should == [@artist, ar]
91
+ ars.first.first_two_tags.should == [@tag, tu]
92
+ ars.first.second_two_tags.should == [tu, tv]
93
+ ars.first.last_two_tags.should == [tv, tu]
94
+ ars.last.first_two_tags.should == []
95
+ ars.last.second_two_tags.should == []
96
+ ars.last.last_two_tags.should == []
97
+
98
+ # Check that no extra columns got added by the eager loading
99
+ ars.first.first_two_tags.map{|x| x.values}.should == [@tag, tu].map{|x| x.values}
100
+ ars.first.second_two_tags.map{|x| x.values}.should == [tu, tv].map{|x| x.values}
101
+ ars.first.last_two_tags.map{|x| x.values}.should == [tv, tu].map{|x| x.values}
102
+ end
103
+ end
104
+
3
105
  shared_examples_for "regular and composite key associations" do
4
106
  specify "should return no objects if none are associated" do
5
107
  @album.artist.should == nil
108
+ @artist.first_album.should == nil
6
109
  @artist.albums.should == []
7
110
  @album.tags.should == []
111
+ @album.alias_tags.should == []
8
112
  @tag.albums.should == []
9
113
  end
10
114
 
@@ -17,17 +121,270 @@ shared_examples_for "regular and composite key associations" do
17
121
  @tag.reload
18
122
 
19
123
  @album.artist.should == @artist
124
+ @artist.first_album.should == @album
20
125
  @artist.albums.should == [@album]
21
126
  @album.tags.should == [@tag]
127
+ @album.alias_tags.should == [@tag]
22
128
  @tag.albums.should == [@album]
23
129
  end
24
130
 
131
+ specify "should work correctly with prepared_statements_association plugin" do
132
+ @album.update(:artist => @artist)
133
+ @album.add_tag(@tag)
134
+
135
+ @album.reload
136
+ @artist.reload
137
+ @tag.reload
138
+
139
+ [Tag, Album, Artist].each{|x| x.plugin :prepared_statements_associations}
140
+ @album.artist.should == @artist
141
+ @artist.first_album.should == @album
142
+ @artist.albums.should == [@album]
143
+ @album.tags.should == [@tag]
144
+ @album.alias_tags.should == [@tag]
145
+ @tag.albums.should == [@album]
146
+ end
147
+
148
+ specify "should work correctly when filtering by associations" do
149
+ @album.update(:artist => @artist)
150
+ @album.add_tag(@tag)
151
+
152
+ Artist.filter(:albums=>@album).all.should == [@artist]
153
+ Artist.filter(:first_album=>@album).all.should == [@artist]
154
+ Album.filter(:artist=>@artist).all.should == [@album]
155
+ Album.filter(:tags=>@tag).all.should == [@album]
156
+ Album.filter(:alias_tags=>@tag).all.should == [@album]
157
+ Tag.filter(:albums=>@album).all.should == [@tag]
158
+ Album.filter(:artist=>@artist, :tags=>@tag).all.should == [@album]
159
+ @artist.albums_dataset.filter(:tags=>@tag).all.should == [@album]
160
+ end
161
+
162
+ specify "should work correctly when excluding by associations" do
163
+ @album.update(:artist => @artist)
164
+ @album.add_tag(@tag)
165
+ album, artist, tag = @pr.call
166
+
167
+ Artist.exclude(:albums=>@album).all.should == [artist]
168
+ Artist.exclude(:first_album=>@album).all.should == [artist]
169
+ Album.exclude(:artist=>@artist).all.should == [album]
170
+ Album.exclude(:tags=>@tag).all.should == [album]
171
+ Album.exclude(:alias_tags=>@tag).all.should == [album]
172
+ Tag.exclude(:albums=>@album).all.should == [tag]
173
+ Album.exclude(:artist=>@artist, :tags=>@tag).all.should == [album]
174
+ end
175
+
176
+ specify "should work correctly when filtering by multiple associations" do
177
+ album, artist, tag = @pr.call
178
+ @album.update(:artist => @artist)
179
+ @album.add_tag(@tag)
180
+
181
+ Artist.filter(:albums=>[@album, album]).all.should == [@artist]
182
+ Artist.filter(:first_album=>[@album, album]).all.should == [@artist]
183
+ Album.filter(:artist=>[@artist, artist]).all.should == [@album]
184
+ Album.filter(:tags=>[@tag, tag]).all.should == [@album]
185
+ Album.filter(:alias_tags=>[@tag, tag]).all.should == [@album]
186
+ Tag.filter(:albums=>[@album, album]).all.should == [@tag]
187
+ Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [@album]
188
+ @artist.albums_dataset.filter(:tags=>[@tag, tag]).all.should == [@album]
189
+
190
+ album.add_tag(tag)
191
+
192
+ Artist.filter(:albums=>[@album, album]).all.should == [@artist]
193
+ Artist.filter(:first_album=>[@album, album]).all.should == [@artist]
194
+ Album.filter(:artist=>[@artist, artist]).all.should == [@album]
195
+ Album.filter(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
196
+ Album.filter(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
197
+ Tag.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
198
+ Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [@album]
199
+
200
+ album.update(:artist => artist)
201
+
202
+ Artist.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
203
+ Artist.filter(:first_album=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
204
+ Album.filter(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [@album, album]
205
+ Album.filter(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
206
+ Album.filter(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
207
+ Tag.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
208
+ Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
209
+ end
210
+
211
+ specify "should work correctly when excluding by multiple associations" do
212
+ album, artist, tag = @pr.call
213
+
214
+ Artist.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
215
+ Artist.exclude(:first_album=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
216
+ Album.exclude(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [@album, album]
217
+ Album.exclude(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
218
+ Album.exclude(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
219
+ Tag.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
220
+ Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
221
+
222
+ @album.update(:artist => @artist)
223
+ @album.add_tag(@tag)
224
+
225
+ Artist.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [artist]
226
+ Artist.exclude(:first_album=>[@album, album]).all.sort_by{|x| x.pk}.should == [artist]
227
+ Album.exclude(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [album]
228
+ Album.exclude(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
229
+ Album.exclude(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
230
+ Tag.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [tag]
231
+ Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
232
+
233
+ album.add_tag(tag)
234
+
235
+ Artist.exclude(:albums=>[@album, album]).all.should == [artist]
236
+ Artist.exclude(:first_album=>[@album, album]).all.should == [artist]
237
+ Album.exclude(:artist=>[@artist, artist]).all.should == [album]
238
+ Album.exclude(:tags=>[@tag, tag]).all.should == []
239
+ Album.exclude(:alias_tags=>[@tag, tag]).all.should == []
240
+ Tag.exclude(:albums=>[@album, album]).all.should == []
241
+ Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [album]
242
+
243
+ album.update(:artist => artist)
244
+
245
+ Artist.exclude(:albums=>[@album, album]).all.should == []
246
+ Artist.exclude(:first_album=>[@album, album]).all.should == []
247
+ Album.exclude(:artist=>[@artist, artist]).all.should == []
248
+ Album.exclude(:tags=>[@tag, tag]).all.should == []
249
+ Album.exclude(:alias_tags=>[@tag, tag]).all.should == []
250
+ Tag.exclude(:albums=>[@album, album]).all.should == []
251
+ Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == []
252
+ end
253
+
254
+ specify "should work correctly when excluding by associations in regards to NULL values" do
255
+ Artist.exclude(:albums=>@album).all.should == [@artist]
256
+ Artist.exclude(:first_album=>@album).all.should == [@artist]
257
+ Album.exclude(:artist=>@artist).all.should == [@album]
258
+ Album.exclude(:tags=>@tag).all.should == [@album]
259
+ Album.exclude(:alias_tags=>@tag).all.should == [@album]
260
+ Tag.exclude(:albums=>@album).all.should == [@tag]
261
+ Album.exclude(:artist=>@artist, :tags=>@tag).all.should == [@album]
262
+
263
+ @album.update(:artist => @artist)
264
+ @artist.albums_dataset.exclude(:tags=>@tag).all.should == [@album]
265
+ end
266
+
267
+ specify "should handle NULL values in join table correctly when filtering/excluding many_to_many associations" do
268
+ @ins.call
269
+ Album.exclude(:tags=>@tag).all.should == [@album]
270
+ Album.exclude(:alias_tags=>@tag).all.should == [@album]
271
+ @album.add_tag(@tag)
272
+ Album.filter(:tags=>@tag).all.should == [@album]
273
+ Album.filter(:alias_tags=>@tag).all.should == [@album]
274
+ album, artist, tag = @pr.call
275
+ Album.exclude(:tags=>@tag).all.should == [album]
276
+ Album.exclude(:alias_tags=>@tag).all.should == [album]
277
+ Album.exclude(:tags=>tag).all.sort_by{|x| x.pk}.should == [@album, album]
278
+ Album.exclude(:alias_tags=>tag).all.sort_by{|x| x.pk}.should == [@album, album]
279
+ end
280
+
281
+ specify "should work correctly when filtering by association datasets" do
282
+ album, artist, tag = @pr.call
283
+ @album.update(:artist => @artist)
284
+ @album.add_tag(@tag)
285
+ album.add_tag(tag)
286
+ album.update(:artist => artist)
287
+
288
+ Artist.filter(:albums=>Album.dataset).all.sort_by{|x| x.pk}.should == [@artist, artist]
289
+ Artist.filter(:albums=>Album.dataset.filter(Array(Album.primary_key).zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [artist]
290
+ Artist.filter(:albums=>Album.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
291
+ Artist.filter(:first_album=>Album.dataset).all.sort_by{|x| x.pk}.should == [@artist, artist]
292
+ Artist.filter(:first_album=>Album.dataset.filter(Array(Album.primary_key).zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [artist]
293
+ Artist.filter(:first_album=>Album.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
294
+ Album.filter(:artist=>Artist.dataset).all.sort_by{|x| x.pk}.should == [@album, album]
295
+ Album.filter(:artist=>Artist.dataset.filter(Array(Artist.primary_key).zip(Array(artist.pk)))).all.sort_by{|x| x.pk}.should == [album]
296
+ Album.filter(:artist=>Artist.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
297
+ Album.filter(:tags=>Tag.dataset).all.sort_by{|x| x.pk}.should == [@album, album]
298
+ Album.filter(:tags=>Tag.dataset.filter(Array(Tag.primary_key).zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
299
+ Album.filter(:tags=>Tag.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
300
+ Album.filter(:alias_tags=>Tag.dataset).all.sort_by{|x| x.pk}.should == [@album, album]
301
+ Album.filter(:alias_tags=>Tag.dataset.filter(Array(Tag.primary_key).zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
302
+ Album.filter(:alias_tags=>Tag.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
303
+ Tag.filter(:albums=>Album.dataset).all.sort_by{|x| x.pk}.should == [@tag, tag]
304
+ Tag.filter(:albums=>Album.dataset.filter(Array(Album.primary_key).zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [tag]
305
+ Tag.filter(:albums=>Album.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
306
+ end
307
+
308
+ specify "should work correctly when excluding by association datasets" do
309
+ album, artist, tag = @pr.call
310
+ @album.update(:artist => @artist)
311
+ @album.add_tag(@tag)
312
+ album.add_tag(tag)
313
+ album.update(:artist => artist)
314
+
315
+ Artist.exclude(:albums=>Album.dataset).all.sort_by{|x| x.pk}.should == []
316
+ Artist.exclude(:albums=>Album.dataset.filter(Array(Album.primary_key).zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [@artist]
317
+ Artist.exclude(:albums=>Album.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist]
318
+ Album.exclude(:artist=>Artist.dataset).all.sort_by{|x| x.pk}.should == []
319
+ Album.exclude(:artist=>Artist.dataset.filter(Array(Artist.primary_key).zip(Array(artist.pk)))).all.sort_by{|x| x.pk}.should == [@album]
320
+ Album.exclude(:artist=>Artist.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
321
+ Album.exclude(:tags=>Tag.dataset).all.sort_by{|x| x.pk}.should == []
322
+ Album.exclude(:tags=>Tag.dataset.filter(Array(Tag.primary_key).zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
323
+ Album.exclude(:tags=>Tag.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
324
+ Album.exclude(:alias_tags=>Tag.dataset).all.sort_by{|x| x.pk}.should == []
325
+ Album.exclude(:alias_tags=>Tag.dataset.filter(Array(Tag.primary_key).zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
326
+ Album.exclude(:alias_tags=>Tag.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
327
+ Tag.exclude(:albums=>Album.dataset).all.sort_by{|x| x.pk}.should == []
328
+ Tag.exclude(:albums=>Album.dataset.filter(Array(Album.primary_key).zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [@tag]
329
+ Tag.exclude(:albums=>Album.dataset.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@tag, tag]
330
+ end
331
+
332
+ specify "should have working dataset associations" do
333
+ album, artist, tag = @pr.call
334
+
335
+ Tag.albums.all.should == []
336
+ Album.artists.all.should == []
337
+ Album.tags.all.should == []
338
+ Album.alias_tags.all.should == []
339
+ Artist.albums.all.should == []
340
+ Artist.tags.all.should == []
341
+ Artist.albums.tags.all.should == []
342
+
343
+ @album.update(:artist => @artist)
344
+ @album.add_tag(@tag)
345
+
346
+ Tag.albums.all.should == [@album]
347
+ Album.artists.all.should == [@artist]
348
+ Album.tags.all.should == [@tag]
349
+ Album.alias_tags.all.should == [@tag]
350
+ Artist.albums.all.should == [@album]
351
+ Artist.tags.all.should == [@tag]
352
+ Artist.albums.tags.all.should == [@tag]
353
+
354
+ album.add_tag(tag)
355
+ album.update(:artist => artist)
356
+
357
+ Tag.albums.order(:name).all.should == [@album, album]
358
+ Album.artists.order(:name).all.should == [@artist, artist]
359
+ Album.tags.order(:name).all.should == [@tag, tag]
360
+ Album.alias_tags.order(:name).all.should == [@tag, tag]
361
+ Artist.albums.order(:name).all.should == [@album, album]
362
+ Artist.tags.order(:name).all.should == [@tag, tag]
363
+ Artist.albums.tags.order(:name).all.should == [@tag, tag]
364
+
365
+ Tag.filter(Tag.qualified_primary_key_hash(tag.pk)).albums.all.should == [album]
366
+ Album.filter(Album.qualified_primary_key_hash(album.pk)).artists.all.should == [artist]
367
+ Album.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == [tag]
368
+ Album.filter(Album.qualified_primary_key_hash(album.pk)).alias_tags.all.should == [tag]
369
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.all.should == [album]
370
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).tags.all.should == [tag]
371
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.tags.all.should == [tag]
372
+
373
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == [tag]
374
+ Artist.filter(Artist.qualified_primary_key_hash(@artist.pk)).albums.filter(Album.qualified_primary_key_hash(@album.pk)).tags.all.should == [@tag]
375
+ Artist.filter(Artist.qualified_primary_key_hash(@artist.pk)).albums.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == []
376
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.filter(Album.qualified_primary_key_hash(@album.pk)).tags.all.should == []
377
+ end
378
+
25
379
  specify "should have remove methods work" do
26
380
  @album.update(:artist => @artist)
27
381
  @album.add_tag(@tag)
28
382
 
29
383
  @album.update(:artist => nil)
30
384
  @album.remove_tag(@tag)
385
+
386
+ @album.add_alias_tag(@tag)
387
+ @album.remove_alias_tag(@tag)
31
388
 
32
389
  @album.reload
33
390
  @artist.reload
@@ -37,6 +394,12 @@ shared_examples_for "regular and composite key associations" do
37
394
  @artist.albums.should == []
38
395
  @album.tags.should == []
39
396
  @tag.albums.should == []
397
+
398
+ @album.add_alias_tag(@tag)
399
+ @album.remove_alias_tag(@tag)
400
+
401
+ @album.reload
402
+ @album.alias_tags.should == []
40
403
  end
41
404
 
42
405
  specify "should have remove_all methods work" do
@@ -54,16 +417,24 @@ shared_examples_for "regular and composite key associations" do
54
417
  @artist.albums.should == []
55
418
  @album.tags.should == []
56
419
  @tag.albums.should == []
420
+
421
+ @album.add_alias_tag(@tag)
422
+ @album.remove_all_alias_tags
423
+
424
+ @album.reload
425
+ @album.alias_tags.should == []
57
426
  end
58
427
 
59
428
  specify "should eager load via eager correctly" do
60
429
  @album.update(:artist => @artist)
61
430
  @album.add_tag(@tag)
62
431
 
63
- a = Artist.eager(:albums=>:tags).all
432
+ a = Artist.eager(:albums=>[:tags, :alias_tags]).eager(:first_album).all
64
433
  a.should == [@artist]
65
434
  a.first.albums.should == [@album]
435
+ a.first.first_album.should == @album
66
436
  a.first.albums.first.tags.should == [@tag]
437
+ a.first.albums.first.alias_tags.should == [@tag]
67
438
 
68
439
  a = Tag.eager(:albums=>:artist).all
69
440
  a.should == [@tag]
@@ -71,14 +442,34 @@ shared_examples_for "regular and composite key associations" do
71
442
  a.first.albums.first.artist.should == @artist
72
443
  end
73
444
 
445
+ describe "with no :eager_limit_strategy" do
446
+ it_should_behave_like "eager limit strategies"
447
+ end
448
+
449
+ describe "with :eager_limit_strategy=>true" do
450
+ before do
451
+ @els = {:eager_limit_strategy=>true}
452
+ end
453
+ it_should_behave_like "eager limit strategies"
454
+ end
455
+
456
+ describe "with :eager_limit_strategy=>:window_function" do
457
+ before do
458
+ @els = {:eager_limit_strategy=>:window_function}
459
+ end
460
+ it_should_behave_like "eager limit strategies"
461
+ end if INTEGRATION_DB.dataset.supports_window_functions?
462
+
74
463
  specify "should eager load via eager_graph correctly" do
75
464
  @album.update(:artist => @artist)
76
465
  @album.add_tag(@tag)
77
466
 
78
- a = Artist.eager_graph(:albums=>:tags).all
467
+ a = Artist.eager_graph(:albums=>[:tags, :alias_tags]).eager_graph(:first_album).all
79
468
  a.should == [@artist]
80
469
  a.first.albums.should == [@album]
470
+ a.first.first_album.should == @album
81
471
  a.first.albums.first.tags.should == [@tag]
472
+ a.first.albums.first.alias_tags.should == [@tag]
82
473
 
83
474
  a = Tag.eager_graph(:albums=>:artist).all
84
475
  a.should == [@tag]
@@ -117,46 +508,82 @@ shared_examples_for "regular and composite key associations" do
117
508
  end
118
509
 
119
510
  describe "Sequel::Model Simple Associations" do
120
- before do
511
+ before(:all) do
121
512
  @db = INTEGRATION_DB
122
- @db.create_table!(:artists) do
513
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db.drop_table(t) rescue nil}
514
+ @db.create_table(:artists) do
123
515
  primary_key :id
124
516
  String :name
125
517
  end
126
- @db.create_table!(:albums) do
518
+ @db.create_table(:albums) do
127
519
  primary_key :id
128
520
  String :name
129
521
  foreign_key :artist_id, :artists
130
522
  end
131
- @db.create_table!(:tags) do
523
+ @db.create_table(:tags) do
132
524
  primary_key :id
133
525
  String :name
134
526
  end
135
- @db.create_table!(:albums_tags) do
527
+ @db.create_table(:albums_tags) do
136
528
  foreign_key :album_id, :albums
137
529
  foreign_key :tag_id, :tags
138
530
  end
531
+ end
532
+ before do
533
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db[t].delete}
139
534
  class ::Artist < Sequel::Model(@db)
140
- one_to_many :albums
535
+ plugin :dataset_associations
536
+ one_to_many :albums, :order=>:name
537
+ one_to_one :first_album, :class=>:Album, :order=>:name
538
+ one_to_one :last_album, :class=>:Album, :order=>:name.desc
539
+ one_to_many :first_two_albums, :class=>:Album, :order=>:name, :limit=>2
540
+ one_to_many :second_two_albums, :class=>:Album, :order=>:name, :limit=>[2, 1]
541
+ one_to_many :last_two_albums, :class=>:Album, :order=>:name.desc, :limit=>2
141
542
  plugin :many_through_many
142
- Artist.many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]]
543
+ many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]]
544
+ many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2
545
+ many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1]
546
+ many_through_many :last_two_tags, :clone=>:tags, :order=>:tags__name.desc, :limit=>2
143
547
  end
144
548
  class ::Album < Sequel::Model(@db)
549
+ plugin :dataset_associations
145
550
  many_to_one :artist
146
- many_to_many :tags
551
+ many_to_many :tags, :right_key=>:tag_id
552
+ many_to_many :alias_tags, :clone=>:tags, :join_table=>:albums_tags___at
553
+ many_to_many :first_two_tags, :clone=>:tags, :order=>:name, :limit=>2
554
+ many_to_many :second_two_tags, :clone=>:tags, :order=>:name, :limit=>[2, 1]
555
+ many_to_many :last_two_tags, :clone=>:tags, :order=>:name.desc, :limit=>2
147
556
  end
148
557
  class ::Tag < Sequel::Model(@db)
558
+ plugin :dataset_associations
149
559
  many_to_many :albums
150
560
  end
151
561
  @album = Album.create(:name=>'Al')
152
562
  @artist = Artist.create(:name=>'Ar')
153
563
  @tag = Tag.create(:name=>'T')
564
+ @same_album = lambda{Album.create(:name=>'Al', :artist_id=>@artist.id)}
565
+ @diff_album = lambda{Album.create(:name=>'lA', :artist_id=>@artist.id)}
566
+ @middle_album = lambda{Album.create(:name=>'Bl', :artist_id=>@artist.id)}
567
+ @other_tags = lambda{t = [Tag.create(:name=>'U'), Tag.create(:name=>'V')]; @db[:albums_tags].insert([:album_id, :tag_id], Tag.select(@album.id, :id)); t}
568
+ @pr = lambda{[Album.create(:name=>'Al2'),Artist.create(:name=>'Ar2'),Tag.create(:name=>'T2')]}
569
+ @ins = lambda{@db[:albums_tags].insert(:tag_id=>@tag.id)}
154
570
  end
155
571
  after do
156
- @db.drop_table(:albums_tags, :tags, :albums, :artists)
157
572
  [:Tag, :Album, :Artist].each{|x| Object.send(:remove_const, x)}
158
573
  end
574
+ after(:all) do
575
+ @db.drop_table(:albums_tags, :tags, :albums, :artists)
576
+ end
159
577
 
578
+ it_should_behave_like "regular and composite key associations"
579
+
580
+ describe "with :eager_limit_strategy=>:correlated_subquery" do
581
+ before do
582
+ @els = {:eager_limit_strategy=>:correlated_subquery}
583
+ end
584
+ it_should_behave_like "eager limit strategies"
585
+ end unless Sequel.guarded?(:mysql, :db2, :oracle)
586
+
160
587
  specify "should handle aliased tables when eager_graphing" do
161
588
  @album.update(:artist => @artist)
162
589
  @album.add_tag(@tag)
@@ -180,8 +607,6 @@ describe "Sequel::Model Simple Associations" do
180
607
  a.first.balbums.first.bartist.should == @artist
181
608
  end
182
609
 
183
- it_should_behave_like "regular and composite key associations"
184
-
185
610
  specify "should have add method accept hashes and create new records" do
186
611
  @artist.remove_all_albums
187
612
  Album.delete
@@ -216,6 +641,48 @@ describe "Sequel::Model Simple Associations" do
216
641
  @tag.reload.albums.should == []
217
642
  end
218
643
 
644
+ specify "should handle dynamic callbacks for regular loading" do
645
+ @artist.add_album(@album)
646
+
647
+ @artist.albums.should == [@album]
648
+ @artist.albums(proc{|ds| ds.exclude(:id=>@album.id)}).should == []
649
+ @artist.albums(proc{|ds| ds.filter(:id=>@album.id)}).should == [@album]
650
+
651
+ @album.artist.should == @artist
652
+ @album.artist(proc{|ds| ds.exclude(:id=>@artist.id)}).should == nil
653
+ @album.artist(proc{|ds| ds.filter(:id=>@artist.id)}).should == @artist
654
+
655
+ if RUBY_VERSION >= '1.8.7'
656
+ @artist.albums{|ds| ds.exclude(:id=>@album.id)}.should == []
657
+ @artist.albums{|ds| ds.filter(:id=>@album.id)}.should == [@album]
658
+ @album.artist{|ds| ds.exclude(:id=>@artist.id)}.should == nil
659
+ @album.artist{|ds| ds.filter(:id=>@artist.id)}.should == @artist
660
+ end
661
+ end
662
+
663
+ specify "should handle dynamic callbacks for eager loading via eager and eager_graph" do
664
+ @artist.add_album(@album)
665
+ @album.add_tag(@tag)
666
+ album2 = @artist.add_album(:name=>'Foo')
667
+ tag2 = album2.add_tag(:name=>'T2')
668
+
669
+ artist = Artist.eager(:albums=>:tags).all.first
670
+ artist.albums.should == [@album, album2]
671
+ artist.albums.map{|x| x.tags}.should == [[@tag], [tag2]]
672
+
673
+ artist = Artist.eager_graph(:albums=>:tags).all.first
674
+ artist.albums.should == [@album, album2]
675
+ artist.albums.map{|x| x.tags}.should == [[@tag], [tag2]]
676
+
677
+ artist = Artist.eager(:albums=>{proc{|ds| ds.where(:id=>album2.id)}=>:tags}).all.first
678
+ artist.albums.should == [album2]
679
+ artist.albums.first.tags.should == [tag2]
680
+
681
+ artist = Artist.eager_graph(:albums=>{proc{|ds| ds.where(:id=>album2.id)}=>:tags}).all.first
682
+ artist.albums.should == [album2]
683
+ artist.albums.first.tags.should == [tag2]
684
+ end
685
+
219
686
  specify "should have remove method raise an error for one_to_many records if the object isn't already associated" do
220
687
  proc{@artist.remove_album(@album.id)}.should raise_error(Sequel::Error)
221
688
  proc{@artist.remove_album(@album)}.should raise_error(Sequel::Error)
@@ -223,15 +690,16 @@ describe "Sequel::Model Simple Associations" do
223
690
  end
224
691
 
225
692
  describe "Sequel::Model Composite Key Associations" do
226
- before do
693
+ before(:all) do
227
694
  @db = INTEGRATION_DB
228
- @db.create_table!(:artists) do
695
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db.drop_table(t) rescue nil}
696
+ @db.create_table(:artists) do
229
697
  Integer :id1
230
698
  Integer :id2
231
699
  String :name
232
700
  primary_key [:id1, :id2]
233
701
  end
234
- @db.create_table!(:albums) do
702
+ @db.create_table(:albums) do
235
703
  Integer :id1
236
704
  Integer :id2
237
705
  String :name
@@ -240,13 +708,13 @@ describe "Sequel::Model Composite Key Associations" do
240
708
  foreign_key [:artist_id1, :artist_id2], :artists
241
709
  primary_key [:id1, :id2]
242
710
  end
243
- @db.create_table!(:tags) do
711
+ @db.create_table(:tags) do
244
712
  Integer :id1
245
713
  Integer :id2
246
714
  String :name
247
715
  primary_key [:id1, :id2]
248
716
  end
249
- @db.create_table!(:albums_tags) do
717
+ @db.create_table(:albums_tags) do
250
718
  Integer :album_id1
251
719
  Integer :album_id2
252
720
  Integer :tag_id1
@@ -254,20 +722,38 @@ describe "Sequel::Model Composite Key Associations" do
254
722
  foreign_key [:album_id1, :album_id2], :albums
255
723
  foreign_key [:tag_id1, :tag_id2], :tags
256
724
  end
725
+ end
726
+ before do
727
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db[t].delete}
257
728
  class ::Artist < Sequel::Model(@db)
729
+ plugin :dataset_associations
258
730
  set_primary_key :id1, :id2
259
731
  unrestrict_primary_key
260
- one_to_many :albums, :key=>[:artist_id1, :artist_id2]
732
+ one_to_many :albums, :key=>[:artist_id1, :artist_id2], :order=>:name
733
+ one_to_one :first_album, :clone=>:albums, :order=>:name
734
+ one_to_one :last_album, :clone=>:albums, :order=>:name.desc
735
+ one_to_many :first_two_albums, :clone=>:albums, :order=>:name, :limit=>2
736
+ one_to_many :second_two_albums, :clone=>:albums, :order=>:name, :limit=>[2, 1]
737
+ one_to_many :last_two_albums, :clone=>:albums, :order=>:name.desc, :limit=>2
261
738
  plugin :many_through_many
262
- Artist.many_through_many :tags, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]]
739
+ many_through_many :tags, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]]
740
+ many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2
741
+ many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1]
742
+ many_through_many :last_two_tags, :clone=>:tags, :order=>:tags__name.desc, :limit=>2
263
743
  end
264
744
  class ::Album < Sequel::Model(@db)
745
+ plugin :dataset_associations
265
746
  set_primary_key :id1, :id2
266
747
  unrestrict_primary_key
267
748
  many_to_one :artist, :key=>[:artist_id1, :artist_id2]
268
749
  many_to_many :tags, :left_key=>[:album_id1, :album_id2], :right_key=>[:tag_id1, :tag_id2]
750
+ many_to_many :alias_tags, :clone=>:tags, :join_table=>:albums_tags___at
751
+ many_to_many :first_two_tags, :clone=>:tags, :order=>:name, :limit=>2
752
+ many_to_many :second_two_tags, :clone=>:tags, :order=>:name, :limit=>[2, 1]
753
+ many_to_many :last_two_tags, :clone=>:tags, :order=>:name.desc, :limit=>2
269
754
  end
270
755
  class ::Tag < Sequel::Model(@db)
756
+ plugin :dataset_associations
271
757
  set_primary_key :id1, :id2
272
758
  unrestrict_primary_key
273
759
  many_to_many :albums, :right_key=>[:album_id1, :album_id2], :left_key=>[:tag_id1, :tag_id2]
@@ -275,14 +761,29 @@ describe "Sequel::Model Composite Key Associations" do
275
761
  @album = Album.create(:name=>'Al', :id1=>1, :id2=>2)
276
762
  @artist = Artist.create(:name=>'Ar', :id1=>3, :id2=>4)
277
763
  @tag = Tag.create(:name=>'T', :id1=>5, :id2=>6)
764
+ @same_album = lambda{Album.create(:name=>'Al', :id1=>7, :id2=>8, :artist_id1=>3, :artist_id2=>4)}
765
+ @diff_album = lambda{Album.create(:name=>'lA', :id1=>9, :id2=>10, :artist_id1=>3, :artist_id2=>4)}
766
+ @middle_album = lambda{Album.create(:name=>'Bl', :id1=>13, :id2=>14, :artist_id1=>3, :artist_id2=>4)}
767
+ @other_tags = lambda{t = [Tag.create(:name=>'U', :id1=>17, :id2=>18), Tag.create(:name=>'V', :id1=>19, :id2=>20)]; @db[:albums_tags].insert([:album_id1, :album_id2, :tag_id1, :tag_id2], Tag.select(1, 2, :id1, :id2)); t}
768
+ @pr = lambda{[Album.create(:name=>'Al2', :id1=>11, :id2=>12),Artist.create(:name=>'Ar2', :id1=>13, :id2=>14),Tag.create(:name=>'T2', :id1=>15, :id2=>16)]}
769
+ @ins = lambda{@db[:albums_tags].insert(:tag_id1=>@tag.id1, :tag_id2=>@tag.id2)}
278
770
  end
279
771
  after do
280
- @db.drop_table(:albums_tags, :tags, :albums, :artists)
281
772
  [:Tag, :Album, :Artist].each{|x| Object.send(:remove_const, x)}
282
773
  end
774
+ after(:all) do
775
+ @db.drop_table(:albums_tags, :tags, :albums, :artists)
776
+ end
283
777
 
284
778
  it_should_behave_like "regular and composite key associations"
285
779
 
780
+ describe "with :eager_limit_strategy=>:correlated_subquery" do
781
+ before do
782
+ @els = {:eager_limit_strategy=>:correlated_subquery}
783
+ end
784
+ it_should_behave_like "eager limit strategies"
785
+ end if INTEGRATION_DB.dataset.supports_multiple_column_in? && !Sequel.guarded?(:mysql, :db2, :oracle)
786
+
286
787
  specify "should have add method accept hashes and create new records" do
287
788
  @artist.remove_all_albums
288
789
  Album.delete