sequel 3.21.0 → 3.31.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 (225) hide show
  1. data/CHANGELOG +485 -3
  2. data/README.rdoc +20 -6
  3. data/Rakefile +37 -19
  4. data/bin/sequel +1 -5
  5. data/doc/association_basics.rdoc +249 -43
  6. data/doc/dataset_basics.rdoc +4 -4
  7. data/doc/dataset_filtering.rdoc +8 -0
  8. data/doc/mass_assignment.rdoc +54 -0
  9. data/doc/migration.rdoc +15 -538
  10. data/doc/model_hooks.rdoc +72 -27
  11. data/doc/opening_databases.rdoc +89 -50
  12. data/doc/prepared_statements.rdoc +40 -13
  13. data/doc/reflection.rdoc +8 -2
  14. data/doc/release_notes/3.22.0.txt +39 -0
  15. data/doc/release_notes/3.23.0.txt +172 -0
  16. data/doc/release_notes/3.24.0.txt +420 -0
  17. data/doc/release_notes/3.25.0.txt +88 -0
  18. data/doc/release_notes/3.26.0.txt +88 -0
  19. data/doc/release_notes/3.27.0.txt +82 -0
  20. data/doc/release_notes/3.28.0.txt +304 -0
  21. data/doc/release_notes/3.29.0.txt +459 -0
  22. data/doc/release_notes/3.30.0.txt +135 -0
  23. data/doc/release_notes/3.31.0.txt +146 -0
  24. data/doc/schema_modification.rdoc +547 -0
  25. data/doc/sharding.rdoc +7 -1
  26. data/doc/testing.rdoc +129 -0
  27. data/doc/transactions.rdoc +137 -0
  28. data/doc/virtual_rows.rdoc +2 -2
  29. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  30. data/lib/sequel/adapters/ado.rb +40 -18
  31. data/lib/sequel/adapters/amalgalite.rb +17 -9
  32. data/lib/sequel/adapters/db2.rb +175 -86
  33. data/lib/sequel/adapters/dbi.rb +15 -15
  34. data/lib/sequel/adapters/do/mysql.rb +5 -7
  35. data/lib/sequel/adapters/do/postgres.rb +0 -5
  36. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  37. data/lib/sequel/adapters/do.rb +17 -36
  38. data/lib/sequel/adapters/firebird.rb +27 -208
  39. data/lib/sequel/adapters/ibmdb.rb +448 -0
  40. data/lib/sequel/adapters/informix.rb +6 -23
  41. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  42. data/lib/sequel/adapters/jdbc/db2.rb +45 -0
  43. data/lib/sequel/adapters/jdbc/derby.rb +247 -0
  44. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  45. data/lib/sequel/adapters/jdbc/h2.rb +56 -29
  46. data/lib/sequel/adapters/jdbc/hsqldb.rb +170 -0
  47. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  48. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  49. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  50. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  51. data/lib/sequel/adapters/jdbc/oracle.rb +81 -25
  52. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  53. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  54. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  55. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  56. data/lib/sequel/adapters/jdbc.rb +231 -78
  57. data/lib/sequel/adapters/mock.rb +335 -0
  58. data/lib/sequel/adapters/mysql.rb +82 -184
  59. data/lib/sequel/adapters/mysql2.rb +26 -25
  60. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  61. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  62. data/lib/sequel/adapters/odbc.rb +28 -9
  63. data/lib/sequel/adapters/openbase.rb +3 -5
  64. data/lib/sequel/adapters/oracle.rb +351 -53
  65. data/lib/sequel/adapters/postgres.rb +194 -33
  66. data/lib/sequel/adapters/shared/access.rb +32 -9
  67. data/lib/sequel/adapters/shared/db2.rb +321 -0
  68. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  69. data/lib/sequel/adapters/shared/informix.rb +53 -0
  70. data/lib/sequel/adapters/shared/mssql.rb +231 -106
  71. data/lib/sequel/adapters/shared/mysql.rb +181 -63
  72. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +231 -41
  74. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  75. data/lib/sequel/adapters/shared/progress.rb +4 -7
  76. data/lib/sequel/adapters/shared/sqlite.rb +201 -57
  77. data/lib/sequel/adapters/sqlite.rb +29 -21
  78. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  79. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  80. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  81. data/lib/sequel/adapters/swift.rb +5 -5
  82. data/lib/sequel/adapters/tinytds.rb +160 -19
  83. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  84. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  85. data/lib/sequel/ast_transformer.rb +190 -0
  86. data/lib/sequel/connection_pool/threaded.rb +3 -2
  87. data/lib/sequel/connection_pool.rb +1 -1
  88. data/lib/sequel/core.rb +46 -5
  89. data/lib/sequel/database/connecting.rb +5 -6
  90. data/lib/sequel/database/dataset.rb +4 -4
  91. data/lib/sequel/database/dataset_defaults.rb +59 -1
  92. data/lib/sequel/database/logging.rb +1 -1
  93. data/lib/sequel/database/misc.rb +104 -19
  94. data/lib/sequel/database/query.rb +153 -58
  95. data/lib/sequel/database/schema_generator.rb +17 -7
  96. data/lib/sequel/database/schema_methods.rb +59 -29
  97. data/lib/sequel/dataset/actions.rb +231 -76
  98. data/lib/sequel/dataset/features.rb +91 -8
  99. data/lib/sequel/dataset/graph.rb +38 -11
  100. data/lib/sequel/dataset/misc.rb +50 -23
  101. data/lib/sequel/dataset/mutation.rb +5 -6
  102. data/lib/sequel/dataset/prepared_statements.rb +48 -27
  103. data/lib/sequel/dataset/query.rb +236 -43
  104. data/lib/sequel/dataset/sql.rb +665 -253
  105. data/lib/sequel/dataset.rb +8 -0
  106. data/lib/sequel/exceptions.rb +4 -0
  107. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  108. data/lib/sequel/extensions/migration.rb +6 -4
  109. data/lib/sequel/extensions/named_timezones.rb +5 -0
  110. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  111. data/lib/sequel/extensions/to_dot.rb +95 -83
  112. data/lib/sequel/model/associations.rb +1020 -337
  113. data/lib/sequel/model/base.rb +414 -132
  114. data/lib/sequel/model/errors.rb +1 -1
  115. data/lib/sequel/model/exceptions.rb +5 -1
  116. data/lib/sequel/model.rb +15 -8
  117. data/lib/sequel/plugins/association_pks.rb +22 -4
  118. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  119. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  120. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  121. data/lib/sequel/plugins/force_encoding.rb +6 -6
  122. data/lib/sequel/plugins/identity_map.rb +114 -7
  123. data/lib/sequel/plugins/many_through_many.rb +65 -7
  124. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  125. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  126. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  127. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  128. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  129. data/lib/sequel/plugins/serialization.rb +88 -44
  130. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  131. data/lib/sequel/plugins/sharding.rb +21 -54
  132. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  133. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  134. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  135. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  136. data/lib/sequel/sql.rb +109 -52
  137. data/lib/sequel/timezones.rb +57 -38
  138. data/lib/sequel/version.rb +1 -1
  139. data/spec/adapters/db2_spec.rb +146 -0
  140. data/spec/adapters/mssql_spec.rb +111 -29
  141. data/spec/adapters/mysql_spec.rb +135 -123
  142. data/spec/adapters/oracle_spec.rb +49 -77
  143. data/spec/adapters/postgres_spec.rb +267 -78
  144. data/spec/adapters/spec_helper.rb +6 -5
  145. data/spec/adapters/sqlite_spec.rb +35 -21
  146. data/spec/core/connection_pool_spec.rb +71 -92
  147. data/spec/core/core_sql_spec.rb +21 -30
  148. data/spec/core/database_spec.rb +788 -512
  149. data/spec/core/dataset_spec.rb +1073 -1053
  150. data/spec/core/expression_filters_spec.rb +159 -42
  151. data/spec/core/mock_adapter_spec.rb +419 -0
  152. data/spec/core/object_graph_spec.rb +86 -114
  153. data/spec/core/schema_generator_spec.rb +3 -3
  154. data/spec/core/schema_spec.rb +298 -38
  155. data/spec/core/spec_helper.rb +6 -48
  156. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  157. data/spec/extensions/association_dependencies_spec.rb +2 -12
  158. data/spec/extensions/association_pks_spec.rb +66 -32
  159. data/spec/extensions/caching_spec.rb +23 -50
  160. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  161. data/spec/extensions/columns_introspection_spec.rb +91 -0
  162. data/spec/extensions/composition_spec.rb +18 -13
  163. data/spec/extensions/dataset_associations_spec.rb +199 -0
  164. data/spec/extensions/defaults_setter_spec.rb +64 -0
  165. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  166. data/spec/extensions/identity_map_spec.rb +114 -22
  167. data/spec/extensions/instance_filters_spec.rb +11 -21
  168. data/spec/extensions/instance_hooks_spec.rb +53 -0
  169. data/spec/extensions/json_serializer_spec.rb +4 -5
  170. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  171. data/spec/extensions/list_spec.rb +17 -39
  172. data/spec/extensions/many_through_many_spec.rb +283 -250
  173. data/spec/extensions/migration_spec.rb +35 -32
  174. data/spec/extensions/named_timezones_spec.rb +23 -3
  175. data/spec/extensions/nested_attributes_spec.rb +97 -88
  176. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  177. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  178. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  179. data/spec/extensions/prepared_statements_spec.rb +53 -0
  180. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  181. data/spec/extensions/pretty_table_spec.rb +1 -6
  182. data/spec/extensions/rcte_tree_spec.rb +41 -43
  183. data/spec/extensions/schema_dumper_spec.rb +5 -8
  184. data/spec/extensions/schema_spec.rb +13 -21
  185. data/spec/extensions/serialization_modification_detection_spec.rb +37 -0
  186. data/spec/extensions/serialization_spec.rb +72 -43
  187. data/spec/extensions/sharding_spec.rb +66 -140
  188. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  189. data/spec/extensions/spec_helper.rb +13 -61
  190. data/spec/extensions/sql_expr_spec.rb +20 -60
  191. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  192. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  193. data/spec/extensions/timestamps_spec.rb +6 -6
  194. data/spec/extensions/to_dot_spec.rb +4 -7
  195. data/spec/extensions/touch_spec.rb +13 -14
  196. data/spec/extensions/tree_spec.rb +11 -26
  197. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  198. data/spec/extensions/update_primary_key_spec.rb +30 -24
  199. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  200. data/spec/extensions/validation_helpers_spec.rb +16 -35
  201. data/spec/extensions/xml_serializer_spec.rb +16 -4
  202. data/spec/integration/associations_test.rb +537 -21
  203. data/spec/integration/database_test.rb +4 -2
  204. data/spec/integration/dataset_test.rb +461 -65
  205. data/spec/integration/eager_loader_test.rb +19 -21
  206. data/spec/integration/model_test.rb +80 -1
  207. data/spec/integration/plugin_test.rb +304 -116
  208. data/spec/integration/prepared_statement_test.rb +212 -122
  209. data/spec/integration/schema_test.rb +161 -30
  210. data/spec/integration/spec_helper.rb +39 -30
  211. data/spec/integration/timezone_test.rb +38 -12
  212. data/spec/integration/transaction_test.rb +172 -5
  213. data/spec/integration/type_test.rb +17 -3
  214. data/spec/model/association_reflection_spec.rb +201 -7
  215. data/spec/model/associations_spec.rb +865 -662
  216. data/spec/model/base_spec.rb +275 -95
  217. data/spec/model/dataset_methods_spec.rb +7 -27
  218. data/spec/model/eager_loading_spec.rb +677 -676
  219. data/spec/model/hooks_spec.rb +309 -67
  220. data/spec/model/model_spec.rb +222 -168
  221. data/spec/model/plugins_spec.rb +24 -13
  222. data/spec/model/record_spec.rb +381 -218
  223. data/spec/model/spec_helper.rb +13 -71
  224. data/spec/model/validations_spec.rb +11 -0
  225. metadata +115 -73
@@ -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,97 @@ 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
+
587
+ specify "should handle many_to_one associations with same name as :key" do
588
+ Album.def_column_alias(:artist_id_id, :artist_id)
589
+ Album.many_to_one :artist_id, :key_column =>:artist_id, :class=>Artist
590
+ @album.update(:artist_id_id => @artist.id)
591
+ @album.artist_id.should == @artist
592
+
593
+ as = Album.eager(:artist_id).all
594
+ as.should == [@album]
595
+ as.map{|a| a.artist_id}.should == [@artist]
596
+
597
+ as = Album.eager_graph(:artist_id).all
598
+ as.should == [@album]
599
+ as.map{|a| a.artist_id}.should == [@artist]
600
+ end
601
+
160
602
  specify "should handle aliased tables when eager_graphing" do
161
603
  @album.update(:artist => @artist)
162
604
  @album.add_tag(@tag)
@@ -180,8 +622,6 @@ describe "Sequel::Model Simple Associations" do
180
622
  a.first.balbums.first.bartist.should == @artist
181
623
  end
182
624
 
183
- it_should_behave_like "regular and composite key associations"
184
-
185
625
  specify "should have add method accept hashes and create new records" do
186
626
  @artist.remove_all_albums
187
627
  Album.delete
@@ -216,6 +656,48 @@ describe "Sequel::Model Simple Associations" do
216
656
  @tag.reload.albums.should == []
217
657
  end
218
658
 
659
+ specify "should handle dynamic callbacks for regular loading" do
660
+ @artist.add_album(@album)
661
+
662
+ @artist.albums.should == [@album]
663
+ @artist.albums(proc{|ds| ds.exclude(:id=>@album.id)}).should == []
664
+ @artist.albums(proc{|ds| ds.filter(:id=>@album.id)}).should == [@album]
665
+
666
+ @album.artist.should == @artist
667
+ @album.artist(proc{|ds| ds.exclude(:id=>@artist.id)}).should == nil
668
+ @album.artist(proc{|ds| ds.filter(:id=>@artist.id)}).should == @artist
669
+
670
+ if RUBY_VERSION >= '1.8.7'
671
+ @artist.albums{|ds| ds.exclude(:id=>@album.id)}.should == []
672
+ @artist.albums{|ds| ds.filter(:id=>@album.id)}.should == [@album]
673
+ @album.artist{|ds| ds.exclude(:id=>@artist.id)}.should == nil
674
+ @album.artist{|ds| ds.filter(:id=>@artist.id)}.should == @artist
675
+ end
676
+ end
677
+
678
+ specify "should handle dynamic callbacks for eager loading via eager and eager_graph" do
679
+ @artist.add_album(@album)
680
+ @album.add_tag(@tag)
681
+ album2 = @artist.add_album(:name=>'Foo')
682
+ tag2 = album2.add_tag(:name=>'T2')
683
+
684
+ artist = Artist.eager(:albums=>:tags).all.first
685
+ artist.albums.should == [@album, album2]
686
+ artist.albums.map{|x| x.tags}.should == [[@tag], [tag2]]
687
+
688
+ artist = Artist.eager_graph(:albums=>:tags).all.first
689
+ artist.albums.should == [@album, album2]
690
+ artist.albums.map{|x| x.tags}.should == [[@tag], [tag2]]
691
+
692
+ artist = Artist.eager(:albums=>{proc{|ds| ds.where(:id=>album2.id)}=>:tags}).all.first
693
+ artist.albums.should == [album2]
694
+ artist.albums.first.tags.should == [tag2]
695
+
696
+ artist = Artist.eager_graph(:albums=>{proc{|ds| ds.where(:id=>album2.id)}=>:tags}).all.first
697
+ artist.albums.should == [album2]
698
+ artist.albums.first.tags.should == [tag2]
699
+ end
700
+
219
701
  specify "should have remove method raise an error for one_to_many records if the object isn't already associated" do
220
702
  proc{@artist.remove_album(@album.id)}.should raise_error(Sequel::Error)
221
703
  proc{@artist.remove_album(@album)}.should raise_error(Sequel::Error)
@@ -223,15 +705,16 @@ describe "Sequel::Model Simple Associations" do
223
705
  end
224
706
 
225
707
  describe "Sequel::Model Composite Key Associations" do
226
- before do
708
+ before(:all) do
227
709
  @db = INTEGRATION_DB
228
- @db.create_table!(:artists) do
710
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db.drop_table(t) rescue nil}
711
+ @db.create_table(:artists) do
229
712
  Integer :id1
230
713
  Integer :id2
231
714
  String :name
232
715
  primary_key [:id1, :id2]
233
716
  end
234
- @db.create_table!(:albums) do
717
+ @db.create_table(:albums) do
235
718
  Integer :id1
236
719
  Integer :id2
237
720
  String :name
@@ -240,13 +723,13 @@ describe "Sequel::Model Composite Key Associations" do
240
723
  foreign_key [:artist_id1, :artist_id2], :artists
241
724
  primary_key [:id1, :id2]
242
725
  end
243
- @db.create_table!(:tags) do
726
+ @db.create_table(:tags) do
244
727
  Integer :id1
245
728
  Integer :id2
246
729
  String :name
247
730
  primary_key [:id1, :id2]
248
731
  end
249
- @db.create_table!(:albums_tags) do
732
+ @db.create_table(:albums_tags) do
250
733
  Integer :album_id1
251
734
  Integer :album_id2
252
735
  Integer :tag_id1
@@ -254,20 +737,38 @@ describe "Sequel::Model Composite Key Associations" do
254
737
  foreign_key [:album_id1, :album_id2], :albums
255
738
  foreign_key [:tag_id1, :tag_id2], :tags
256
739
  end
740
+ end
741
+ before do
742
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db[t].delete}
257
743
  class ::Artist < Sequel::Model(@db)
744
+ plugin :dataset_associations
258
745
  set_primary_key :id1, :id2
259
746
  unrestrict_primary_key
260
- one_to_many :albums, :key=>[:artist_id1, :artist_id2]
747
+ one_to_many :albums, :key=>[:artist_id1, :artist_id2], :order=>:name
748
+ one_to_one :first_album, :clone=>:albums, :order=>:name
749
+ one_to_one :last_album, :clone=>:albums, :order=>:name.desc
750
+ one_to_many :first_two_albums, :clone=>:albums, :order=>:name, :limit=>2
751
+ one_to_many :second_two_albums, :clone=>:albums, :order=>:name, :limit=>[2, 1]
752
+ one_to_many :last_two_albums, :clone=>:albums, :order=>:name.desc, :limit=>2
261
753
  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]]]
754
+ many_through_many :tags, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]]
755
+ many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2
756
+ many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1]
757
+ many_through_many :last_two_tags, :clone=>:tags, :order=>:tags__name.desc, :limit=>2
263
758
  end
264
759
  class ::Album < Sequel::Model(@db)
760
+ plugin :dataset_associations
265
761
  set_primary_key :id1, :id2
266
762
  unrestrict_primary_key
267
763
  many_to_one :artist, :key=>[:artist_id1, :artist_id2]
268
764
  many_to_many :tags, :left_key=>[:album_id1, :album_id2], :right_key=>[:tag_id1, :tag_id2]
765
+ many_to_many :alias_tags, :clone=>:tags, :join_table=>:albums_tags___at
766
+ many_to_many :first_two_tags, :clone=>:tags, :order=>:name, :limit=>2
767
+ many_to_many :second_two_tags, :clone=>:tags, :order=>:name, :limit=>[2, 1]
768
+ many_to_many :last_two_tags, :clone=>:tags, :order=>:name.desc, :limit=>2
269
769
  end
270
770
  class ::Tag < Sequel::Model(@db)
771
+ plugin :dataset_associations
271
772
  set_primary_key :id1, :id2
272
773
  unrestrict_primary_key
273
774
  many_to_many :albums, :right_key=>[:album_id1, :album_id2], :left_key=>[:tag_id1, :tag_id2]
@@ -275,14 +776,29 @@ describe "Sequel::Model Composite Key Associations" do
275
776
  @album = Album.create(:name=>'Al', :id1=>1, :id2=>2)
276
777
  @artist = Artist.create(:name=>'Ar', :id1=>3, :id2=>4)
277
778
  @tag = Tag.create(:name=>'T', :id1=>5, :id2=>6)
779
+ @same_album = lambda{Album.create(:name=>'Al', :id1=>7, :id2=>8, :artist_id1=>3, :artist_id2=>4)}
780
+ @diff_album = lambda{Album.create(:name=>'lA', :id1=>9, :id2=>10, :artist_id1=>3, :artist_id2=>4)}
781
+ @middle_album = lambda{Album.create(:name=>'Bl', :id1=>13, :id2=>14, :artist_id1=>3, :artist_id2=>4)}
782
+ @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}
783
+ @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)]}
784
+ @ins = lambda{@db[:albums_tags].insert(:tag_id1=>@tag.id1, :tag_id2=>@tag.id2)}
278
785
  end
279
786
  after do
280
- @db.drop_table(:albums_tags, :tags, :albums, :artists)
281
787
  [:Tag, :Album, :Artist].each{|x| Object.send(:remove_const, x)}
282
788
  end
789
+ after(:all) do
790
+ @db.drop_table(:albums_tags, :tags, :albums, :artists)
791
+ end
283
792
 
284
793
  it_should_behave_like "regular and composite key associations"
285
794
 
795
+ describe "with :eager_limit_strategy=>:correlated_subquery" do
796
+ before do
797
+ @els = {:eager_limit_strategy=>:correlated_subquery}
798
+ end
799
+ it_should_behave_like "eager limit strategies"
800
+ end if INTEGRATION_DB.dataset.supports_multiple_column_in? && !Sequel.guarded?(:mysql, :db2, :oracle)
801
+
286
802
  specify "should have add method accept hashes and create new records" do
287
803
  @artist.remove_all_albums
288
804
  Album.delete