sequel 3.21.0 → 3.32.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 (234) hide show
  1. data/CHANGELOG +539 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +20 -6
  4. data/Rakefile +37 -19
  5. data/bin/sequel +1 -5
  6. data/doc/advanced_associations.rdoc +17 -0
  7. data/doc/association_basics.rdoc +293 -43
  8. data/doc/dataset_basics.rdoc +4 -4
  9. data/doc/dataset_filtering.rdoc +8 -0
  10. data/doc/mass_assignment.rdoc +54 -0
  11. data/doc/migration.rdoc +15 -538
  12. data/doc/model_hooks.rdoc +72 -27
  13. data/doc/opening_databases.rdoc +89 -50
  14. data/doc/prepared_statements.rdoc +40 -13
  15. data/doc/reflection.rdoc +8 -2
  16. data/doc/release_notes/3.22.0.txt +39 -0
  17. data/doc/release_notes/3.23.0.txt +172 -0
  18. data/doc/release_notes/3.24.0.txt +420 -0
  19. data/doc/release_notes/3.25.0.txt +88 -0
  20. data/doc/release_notes/3.26.0.txt +88 -0
  21. data/doc/release_notes/3.27.0.txt +82 -0
  22. data/doc/release_notes/3.28.0.txt +304 -0
  23. data/doc/release_notes/3.29.0.txt +459 -0
  24. data/doc/release_notes/3.30.0.txt +135 -0
  25. data/doc/release_notes/3.31.0.txt +146 -0
  26. data/doc/release_notes/3.32.0.txt +202 -0
  27. data/doc/schema_modification.rdoc +547 -0
  28. data/doc/sharding.rdoc +7 -1
  29. data/doc/testing.rdoc +129 -0
  30. data/doc/transactions.rdoc +137 -0
  31. data/doc/virtual_rows.rdoc +2 -2
  32. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  33. data/lib/sequel/adapters/ado.rb +40 -18
  34. data/lib/sequel/adapters/amalgalite.rb +17 -9
  35. data/lib/sequel/adapters/db2.rb +175 -86
  36. data/lib/sequel/adapters/dbi.rb +15 -15
  37. data/lib/sequel/adapters/do/mysql.rb +5 -7
  38. data/lib/sequel/adapters/do/postgres.rb +0 -5
  39. data/lib/sequel/adapters/do/sqlite.rb +0 -5
  40. data/lib/sequel/adapters/do.rb +17 -36
  41. data/lib/sequel/adapters/firebird.rb +27 -208
  42. data/lib/sequel/adapters/ibmdb.rb +448 -0
  43. data/lib/sequel/adapters/informix.rb +6 -23
  44. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  45. data/lib/sequel/adapters/jdbc/db2.rb +52 -0
  46. data/lib/sequel/adapters/jdbc/derby.rb +260 -0
  47. data/lib/sequel/adapters/jdbc/firebird.rb +29 -0
  48. data/lib/sequel/adapters/jdbc/h2.rb +66 -30
  49. data/lib/sequel/adapters/jdbc/hsqldb.rb +177 -0
  50. data/lib/sequel/adapters/jdbc/informix.rb +26 -0
  51. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  52. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  53. data/lib/sequel/adapters/jdbc/mysql.rb +11 -11
  54. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  55. data/lib/sequel/adapters/jdbc/postgresql.rb +9 -13
  56. data/lib/sequel/adapters/jdbc/sqlite.rb +2 -6
  57. data/lib/sequel/adapters/jdbc/sqlserver.rb +71 -0
  58. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  59. data/lib/sequel/adapters/jdbc.rb +231 -78
  60. data/lib/sequel/adapters/mock.rb +339 -0
  61. data/lib/sequel/adapters/mysql.rb +85 -184
  62. data/lib/sequel/adapters/mysql2.rb +26 -25
  63. data/lib/sequel/adapters/odbc/db2.rb +17 -0
  64. data/lib/sequel/adapters/odbc/mssql.rb +1 -6
  65. data/lib/sequel/adapters/odbc.rb +28 -9
  66. data/lib/sequel/adapters/openbase.rb +3 -5
  67. data/lib/sequel/adapters/oracle.rb +355 -53
  68. data/lib/sequel/adapters/postgres.rb +194 -33
  69. data/lib/sequel/adapters/shared/access.rb +32 -9
  70. data/lib/sequel/adapters/shared/db2.rb +328 -0
  71. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  72. data/lib/sequel/adapters/shared/informix.rb +53 -0
  73. data/lib/sequel/adapters/shared/mssql.rb +278 -107
  74. data/lib/sequel/adapters/shared/mysql.rb +203 -65
  75. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +149 -0
  76. data/lib/sequel/adapters/shared/oracle.rb +237 -46
  77. data/lib/sequel/adapters/shared/postgres.rb +214 -82
  78. data/lib/sequel/adapters/shared/progress.rb +5 -8
  79. data/lib/sequel/adapters/shared/sqlite.rb +200 -50
  80. data/lib/sequel/adapters/sqlite.rb +29 -21
  81. data/lib/sequel/adapters/swift/mysql.rb +12 -7
  82. data/lib/sequel/adapters/swift/postgres.rb +0 -5
  83. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  84. data/lib/sequel/adapters/swift.rb +5 -5
  85. data/lib/sequel/adapters/tinytds.rb +160 -19
  86. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  87. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  88. data/lib/sequel/ast_transformer.rb +190 -0
  89. data/lib/sequel/connection_pool/threaded.rb +3 -2
  90. data/lib/sequel/connection_pool.rb +1 -1
  91. data/lib/sequel/core.rb +68 -5
  92. data/lib/sequel/database/connecting.rb +5 -6
  93. data/lib/sequel/database/dataset.rb +4 -4
  94. data/lib/sequel/database/dataset_defaults.rb +59 -1
  95. data/lib/sequel/database/logging.rb +1 -1
  96. data/lib/sequel/database/misc.rb +104 -19
  97. data/lib/sequel/database/query.rb +163 -59
  98. data/lib/sequel/database/schema_generator.rb +17 -7
  99. data/lib/sequel/database/schema_methods.rb +59 -29
  100. data/lib/sequel/dataset/actions.rb +240 -76
  101. data/lib/sequel/dataset/features.rb +91 -8
  102. data/lib/sequel/dataset/graph.rb +38 -11
  103. data/lib/sequel/dataset/misc.rb +50 -23
  104. data/lib/sequel/dataset/mutation.rb +12 -7
  105. data/lib/sequel/dataset/prepared_statements.rb +59 -27
  106. data/lib/sequel/dataset/query.rb +236 -43
  107. data/lib/sequel/dataset/sql.rb +676 -267
  108. data/lib/sequel/dataset.rb +8 -0
  109. data/lib/sequel/exceptions.rb +4 -0
  110. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  111. data/lib/sequel/extensions/migration.rb +6 -4
  112. data/lib/sequel/extensions/named_timezones.rb +5 -0
  113. data/lib/sequel/extensions/query.rb +1 -1
  114. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  115. data/lib/sequel/extensions/to_dot.rb +95 -83
  116. data/lib/sequel/model/associations.rb +1081 -344
  117. data/lib/sequel/model/base.rb +420 -133
  118. data/lib/sequel/model/errors.rb +1 -1
  119. data/lib/sequel/model/exceptions.rb +5 -1
  120. data/lib/sequel/model.rb +20 -10
  121. data/lib/sequel/plugins/active_model.rb +13 -1
  122. data/lib/sequel/plugins/association_pks.rb +22 -4
  123. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  124. data/lib/sequel/plugins/composition.rb +43 -10
  125. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  126. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  127. data/lib/sequel/plugins/force_encoding.rb +6 -6
  128. data/lib/sequel/plugins/identity_map.rb +114 -7
  129. data/lib/sequel/plugins/many_through_many.rb +68 -7
  130. data/lib/sequel/plugins/nested_attributes.rb +65 -10
  131. data/lib/sequel/plugins/prepared_statements.rb +151 -0
  132. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  133. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  134. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  135. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  136. data/lib/sequel/plugins/serialization.rb +98 -49
  137. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  138. data/lib/sequel/plugins/sharding.rb +21 -54
  139. data/lib/sequel/plugins/single_table_inheritance.rb +3 -1
  140. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  141. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  142. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  143. data/lib/sequel/sql.rb +109 -52
  144. data/lib/sequel/timezones.rb +57 -38
  145. data/lib/sequel/version.rb +1 -1
  146. data/spec/adapters/db2_spec.rb +146 -0
  147. data/spec/adapters/mssql_spec.rb +135 -30
  148. data/spec/adapters/mysql_spec.rb +244 -212
  149. data/spec/adapters/oracle_spec.rb +67 -77
  150. data/spec/adapters/postgres_spec.rb +267 -78
  151. data/spec/adapters/spec_helper.rb +6 -5
  152. data/spec/adapters/sqlite_spec.rb +35 -21
  153. data/spec/core/connection_pool_spec.rb +71 -92
  154. data/spec/core/core_sql_spec.rb +21 -30
  155. data/spec/core/database_spec.rb +789 -512
  156. data/spec/core/dataset_spec.rb +1176 -1061
  157. data/spec/core/expression_filters_spec.rb +159 -42
  158. data/spec/core/mock_adapter_spec.rb +419 -0
  159. data/spec/core/object_graph_spec.rb +86 -114
  160. data/spec/core/schema_generator_spec.rb +3 -3
  161. data/spec/core/schema_spec.rb +298 -38
  162. data/spec/core/spec_helper.rb +6 -48
  163. data/spec/extensions/active_model_spec.rb +13 -0
  164. data/spec/extensions/association_autoreloading_spec.rb +1 -10
  165. data/spec/extensions/association_dependencies_spec.rb +2 -12
  166. data/spec/extensions/association_pks_spec.rb +66 -32
  167. data/spec/extensions/caching_spec.rb +23 -50
  168. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  169. data/spec/extensions/columns_introspection_spec.rb +91 -0
  170. data/spec/extensions/composition_spec.rb +18 -13
  171. data/spec/extensions/dataset_associations_spec.rb +199 -0
  172. data/spec/extensions/defaults_setter_spec.rb +64 -0
  173. data/spec/extensions/hook_class_methods_spec.rb +65 -91
  174. data/spec/extensions/identity_map_spec.rb +114 -22
  175. data/spec/extensions/instance_filters_spec.rb +11 -21
  176. data/spec/extensions/instance_hooks_spec.rb +53 -0
  177. data/spec/extensions/json_serializer_spec.rb +4 -5
  178. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  179. data/spec/extensions/list_spec.rb +17 -39
  180. data/spec/extensions/many_through_many_spec.rb +283 -250
  181. data/spec/extensions/migration_spec.rb +35 -32
  182. data/spec/extensions/named_timezones_spec.rb +23 -3
  183. data/spec/extensions/nested_attributes_spec.rb +97 -88
  184. data/spec/extensions/optimistic_locking_spec.rb +9 -20
  185. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  186. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  187. data/spec/extensions/prepared_statements_spec.rb +53 -0
  188. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  189. data/spec/extensions/pretty_table_spec.rb +1 -6
  190. data/spec/extensions/query_spec.rb +6 -0
  191. data/spec/extensions/rcte_tree_spec.rb +41 -43
  192. data/spec/extensions/schema_dumper_spec.rb +5 -8
  193. data/spec/extensions/schema_spec.rb +13 -21
  194. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  195. data/spec/extensions/serialization_spec.rb +81 -43
  196. data/spec/extensions/sharding_spec.rb +66 -140
  197. data/spec/extensions/single_table_inheritance_spec.rb +18 -29
  198. data/spec/extensions/spec_helper.rb +13 -61
  199. data/spec/extensions/sql_expr_spec.rb +20 -60
  200. data/spec/extensions/tactical_eager_loading_spec.rb +9 -19
  201. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  202. data/spec/extensions/timestamps_spec.rb +6 -6
  203. data/spec/extensions/to_dot_spec.rb +4 -7
  204. data/spec/extensions/touch_spec.rb +13 -14
  205. data/spec/extensions/tree_spec.rb +11 -26
  206. data/spec/extensions/typecast_on_load_spec.rb +1 -6
  207. data/spec/extensions/update_primary_key_spec.rb +30 -24
  208. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  209. data/spec/extensions/validation_helpers_spec.rb +16 -35
  210. data/spec/extensions/xml_serializer_spec.rb +16 -4
  211. data/spec/integration/associations_test.rb +661 -21
  212. data/spec/integration/database_test.rb +4 -2
  213. data/spec/integration/dataset_test.rb +500 -67
  214. data/spec/integration/eager_loader_test.rb +19 -21
  215. data/spec/integration/model_test.rb +80 -1
  216. data/spec/integration/plugin_test.rb +367 -119
  217. data/spec/integration/prepared_statement_test.rb +222 -125
  218. data/spec/integration/schema_test.rb +217 -39
  219. data/spec/integration/spec_helper.rb +39 -30
  220. data/spec/integration/timezone_test.rb +38 -12
  221. data/spec/integration/transaction_test.rb +182 -5
  222. data/spec/integration/type_test.rb +17 -3
  223. data/spec/model/association_reflection_spec.rb +201 -7
  224. data/spec/model/associations_spec.rb +955 -662
  225. data/spec/model/base_spec.rb +275 -95
  226. data/spec/model/dataset_methods_spec.rb +7 -27
  227. data/spec/model/eager_loading_spec.rb +677 -676
  228. data/spec/model/hooks_spec.rb +349 -67
  229. data/spec/model/model_spec.rb +222 -168
  230. data/spec/model/plugins_spec.rb +24 -13
  231. data/spec/model/record_spec.rb +381 -218
  232. data/spec/model/spec_helper.rb +13 -71
  233. data/spec/model/validations_spec.rb +11 -0
  234. metadata +117 -73
@@ -1,10 +1,300 @@
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
+
105
+ shared_examples_for "filtering/excluding by associations" do
106
+ specify "should work correctly when filtering by associations" do
107
+ @album.update(:artist => @artist)
108
+ @album.add_tag(@tag)
109
+
110
+ @Artist.filter(:albums=>@album).all.should == [@artist]
111
+ @Artist.filter(:first_album=>@album).all.should == [@artist]
112
+ @Album.filter(:artist=>@artist).all.should == [@album]
113
+ @Album.filter(:tags=>@tag).all.should == [@album]
114
+ @Album.filter(:alias_tags=>@tag).all.should == [@album]
115
+ @Tag.filter(:albums=>@album).all.should == [@tag]
116
+ @Album.filter(:artist=>@artist, :tags=>@tag).all.should == [@album]
117
+ @artist.albums_dataset.filter(:tags=>@tag).all.should == [@album]
118
+ end
119
+
120
+ specify "should work correctly when excluding by associations" do
121
+ @album.update(:artist => @artist)
122
+ @album.add_tag(@tag)
123
+ album, artist, tag = @pr.call
124
+
125
+ @Artist.exclude(:albums=>@album).all.should == [artist]
126
+ @Artist.exclude(:first_album=>@album).all.should == [artist]
127
+ @Album.exclude(:artist=>@artist).all.should == [album]
128
+ @Album.exclude(:tags=>@tag).all.should == [album]
129
+ @Album.exclude(:alias_tags=>@tag).all.should == [album]
130
+ @Tag.exclude(:albums=>@album).all.should == [tag]
131
+ @Album.exclude(:artist=>@artist, :tags=>@tag).all.should == [album]
132
+ end
133
+
134
+ specify "should work correctly when filtering by multiple associations" do
135
+ album, artist, tag = @pr.call
136
+ @album.update(:artist => @artist)
137
+ @album.add_tag(@tag)
138
+
139
+ @Artist.filter(:albums=>[@album, album]).all.should == [@artist]
140
+ @Artist.filter(:first_album=>[@album, album]).all.should == [@artist]
141
+ @Album.filter(:artist=>[@artist, artist]).all.should == [@album]
142
+ @Album.filter(:tags=>[@tag, tag]).all.should == [@album]
143
+ @Album.filter(:alias_tags=>[@tag, tag]).all.should == [@album]
144
+ @Tag.filter(:albums=>[@album, album]).all.should == [@tag]
145
+ @Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [@album]
146
+ @artist.albums_dataset.filter(:tags=>[@tag, tag]).all.should == [@album]
147
+
148
+ album.add_tag(tag)
149
+
150
+ @Artist.filter(:albums=>[@album, album]).all.should == [@artist]
151
+ @Artist.filter(:first_album=>[@album, album]).all.should == [@artist]
152
+ @Album.filter(:artist=>[@artist, artist]).all.should == [@album]
153
+ @Album.filter(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
154
+ @Album.filter(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
155
+ @Tag.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
156
+ @Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [@album]
157
+
158
+ album.update(:artist => artist)
159
+
160
+ @Artist.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
161
+ @Artist.filter(:first_album=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
162
+ @Album.filter(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [@album, album]
163
+ @Album.filter(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
164
+ @Album.filter(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
165
+ @Tag.filter(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
166
+ @Album.filter(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
167
+ end
168
+
169
+ specify "should work correctly when excluding by multiple associations" do
170
+ album, artist, tag = @pr.call
171
+
172
+ @Artist.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
173
+ @Artist.exclude(:first_album=>[@album, album]).all.sort_by{|x| x.pk}.should == [@artist, artist]
174
+ @Album.exclude(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [@album, album]
175
+ @Album.exclude(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
176
+ @Album.exclude(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
177
+ @Tag.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [@tag, tag]
178
+ @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [@album, album]
179
+
180
+ @album.update(:artist => @artist)
181
+ @album.add_tag(@tag)
182
+
183
+ @Artist.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [artist]
184
+ @Artist.exclude(:first_album=>[@album, album]).all.sort_by{|x| x.pk}.should == [artist]
185
+ @Album.exclude(:artist=>[@artist, artist]).all.sort_by{|x| x.pk}.should == [album]
186
+ @Album.exclude(:tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
187
+ @Album.exclude(:alias_tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
188
+ @Tag.exclude(:albums=>[@album, album]).all.sort_by{|x| x.pk}.should == [tag]
189
+ @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.sort_by{|x| x.pk}.should == [album]
190
+
191
+ album.add_tag(tag)
192
+
193
+ @Artist.exclude(:albums=>[@album, album]).all.should == [artist]
194
+ @Artist.exclude(:first_album=>[@album, album]).all.should == [artist]
195
+ @Album.exclude(:artist=>[@artist, artist]).all.should == [album]
196
+ @Album.exclude(:tags=>[@tag, tag]).all.should == []
197
+ @Album.exclude(:alias_tags=>[@tag, tag]).all.should == []
198
+ @Tag.exclude(:albums=>[@album, album]).all.should == []
199
+ @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == [album]
200
+
201
+ album.update(:artist => artist)
202
+
203
+ @Artist.exclude(:albums=>[@album, album]).all.should == []
204
+ @Artist.exclude(:first_album=>[@album, album]).all.should == []
205
+ @Album.exclude(:artist=>[@artist, artist]).all.should == []
206
+ @Album.exclude(:tags=>[@tag, tag]).all.should == []
207
+ @Album.exclude(:alias_tags=>[@tag, tag]).all.should == []
208
+ @Tag.exclude(:albums=>[@album, album]).all.should == []
209
+ @Album.exclude(:artist=>[@artist, artist], :tags=>[@tag, tag]).all.should == []
210
+ end
211
+
212
+ specify "should work correctly when excluding by associations in regards to NULL values" do
213
+ @Artist.exclude(:albums=>@album).all.should == [@artist]
214
+ @Artist.exclude(:first_album=>@album).all.should == [@artist]
215
+ @Album.exclude(:artist=>@artist).all.should == [@album]
216
+ @Album.exclude(:tags=>@tag).all.should == [@album]
217
+ @Album.exclude(:alias_tags=>@tag).all.should == [@album]
218
+ @Tag.exclude(:albums=>@album).all.should == [@tag]
219
+ @Album.exclude(:artist=>@artist, :tags=>@tag).all.should == [@album]
220
+
221
+ @album.update(:artist => @artist)
222
+ @artist.albums_dataset.exclude(:tags=>@tag).all.should == [@album]
223
+ end
224
+
225
+ specify "should handle NULL values in join table correctly when filtering/excluding many_to_many associations" do
226
+ @ins.call
227
+ @Album.exclude(:tags=>@tag).all.should == [@album]
228
+ @Album.exclude(:alias_tags=>@tag).all.should == [@album]
229
+ @album.add_tag(@tag)
230
+ @Album.filter(:tags=>@tag).all.should == [@album]
231
+ @Album.filter(:alias_tags=>@tag).all.should == [@album]
232
+ album, artist, tag = @pr.call
233
+ @Album.exclude(:tags=>@tag).all.should == [album]
234
+ @Album.exclude(:alias_tags=>@tag).all.should == [album]
235
+ @Album.exclude(:tags=>tag).all.sort_by{|x| x.pk}.should == [@album, album]
236
+ @Album.exclude(:alias_tags=>tag).all.sort_by{|x| x.pk}.should == [@album, album]
237
+ end
238
+
239
+ specify "should work correctly when filtering by association datasets" do
240
+ album, artist, tag = @pr.call
241
+ @album.update(:artist => @artist)
242
+ @album.add_tag(@tag)
243
+ album.add_tag(tag)
244
+ album.update(:artist => artist)
245
+
246
+ @Artist.filter(:albums=>@Album).all.sort_by{|x| x.pk}.should == [@artist, artist]
247
+ @Artist.filter(:albums=>@Album.filter(Array(Album.primary_key).map{|k| k.qualify(Album.table_name)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [artist]
248
+ @Artist.filter(:albums=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
249
+ @Artist.filter(:first_album=>@Album).all.sort_by{|x| x.pk}.should == [@artist, artist]
250
+ @Artist.filter(:first_album=>@Album.filter(Array(Album.primary_key).map{|k| k.qualify(Album.table_name)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [artist]
251
+ @Artist.filter(:first_album=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
252
+ @Album.filter(:artist=>@Artist).all.sort_by{|x| x.pk}.should == [@album, album]
253
+ @Album.filter(:artist=>@Artist.filter(Array(Artist.primary_key).map{|k| k.qualify(Artist.table_name)}.zip(Array(artist.pk)))).all.sort_by{|x| x.pk}.should == [album]
254
+ @Album.filter(:artist=>@Artist.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
255
+ @Album.filter(:tags=>@Tag).all.sort_by{|x| x.pk}.should == [@album, album]
256
+ @Album.filter(:tags=>@Tag.filter(Array(Tag.primary_key).map{|k| k.qualify(Tag.table_name)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
257
+ @Album.filter(:tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
258
+ @Album.filter(:alias_tags=>@Tag).all.sort_by{|x| x.pk}.should == [@album, album]
259
+ @Album.filter(:alias_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| k.qualify(Tag.table_name)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [album]
260
+ @Album.filter(:alias_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
261
+ @Tag.filter(:albums=>@Album).all.sort_by{|x| x.pk}.should == [@tag, tag]
262
+ @Tag.filter(:albums=>@Album.filter(Array(Album.primary_key).map{|k| k.qualify(Album.table_name)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [tag]
263
+ @Tag.filter(:albums=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == []
264
+ end
265
+
266
+ specify "should work correctly when excluding by association datasets" do
267
+ album, artist, tag = @pr.call
268
+ @album.update(:artist => @artist)
269
+ @album.add_tag(@tag)
270
+ album.add_tag(tag)
271
+ album.update(:artist => artist)
272
+
273
+ @Artist.exclude(:albums=>@Album).all.sort_by{|x| x.pk}.should == []
274
+ @Artist.exclude(:albums=>@Album.filter(Array(Album.primary_key).map{|k| k.qualify(Album.table_name)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [@artist]
275
+ @Artist.exclude(:albums=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@artist, artist]
276
+ @Album.exclude(:artist=>@Artist).all.sort_by{|x| x.pk}.should == []
277
+ @Album.exclude(:artist=>@Artist.filter(Array(Artist.primary_key).map{|k| k.qualify(Artist.table_name)}.zip(Array(artist.pk)))).all.sort_by{|x| x.pk}.should == [@album]
278
+ @Album.exclude(:artist=>@Artist.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
279
+ @Album.exclude(:tags=>@Tag).all.sort_by{|x| x.pk}.should == []
280
+ @Album.exclude(:tags=>@Tag.filter(Array(Tag.primary_key).map{|k| k.qualify(Tag.table_name)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
281
+ @Album.exclude(:tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
282
+ @Album.exclude(:alias_tags=>@Tag).all.sort_by{|x| x.pk}.should == []
283
+ @Album.exclude(:alias_tags=>@Tag.filter(Array(Tag.primary_key).map{|k| k.qualify(Tag.table_name)}.zip(Array(tag.pk)))).all.sort_by{|x| x.pk}.should == [@album]
284
+ @Album.exclude(:alias_tags=>@Tag.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@album, album]
285
+ @Tag.exclude(:albums=>@Album).all.sort_by{|x| x.pk}.should == []
286
+ @Tag.exclude(:albums=>@Album.filter(Array(Album.primary_key).map{|k| k.qualify(Album.table_name)}.zip(Array(album.pk)))).all.sort_by{|x| x.pk}.should == [@tag]
287
+ @Tag.exclude(:albums=>@Album.filter(1=>0)).all.sort_by{|x| x.pk}.should == [@tag, tag]
288
+ end
289
+ end
290
+
3
291
  shared_examples_for "regular and composite key associations" do
4
292
  specify "should return no objects if none are associated" do
5
293
  @album.artist.should == nil
294
+ @artist.first_album.should == nil
6
295
  @artist.albums.should == []
7
296
  @album.tags.should == []
297
+ @album.alias_tags.should == []
8
298
  @tag.albums.should == []
9
299
  end
10
300
 
@@ -17,17 +307,86 @@ shared_examples_for "regular and composite key associations" do
17
307
  @tag.reload
18
308
 
19
309
  @album.artist.should == @artist
310
+ @artist.first_album.should == @album
20
311
  @artist.albums.should == [@album]
21
312
  @album.tags.should == [@tag]
313
+ @album.alias_tags.should == [@tag]
22
314
  @tag.albums.should == [@album]
23
315
  end
24
316
 
317
+ specify "should work correctly with prepared_statements_association plugin" do
318
+ @album.update(:artist => @artist)
319
+ @album.add_tag(@tag)
320
+
321
+ @album.reload
322
+ @artist.reload
323
+ @tag.reload
324
+
325
+ [Tag, Album, Artist].each{|x| x.plugin :prepared_statements_associations}
326
+ @album.artist.should == @artist
327
+ @artist.first_album.should == @album
328
+ @artist.albums.should == [@album]
329
+ @album.tags.should == [@tag]
330
+ @album.alias_tags.should == [@tag]
331
+ @tag.albums.should == [@album]
332
+ end
333
+
334
+ specify "should have working dataset associations" do
335
+ album, artist, tag = @pr.call
336
+
337
+ Tag.albums.all.should == []
338
+ Album.artists.all.should == []
339
+ Album.tags.all.should == []
340
+ Album.alias_tags.all.should == []
341
+ Artist.albums.all.should == []
342
+ Artist.tags.all.should == []
343
+ Artist.albums.tags.all.should == []
344
+
345
+ @album.update(:artist => @artist)
346
+ @album.add_tag(@tag)
347
+
348
+ Tag.albums.all.should == [@album]
349
+ Album.artists.all.should == [@artist]
350
+ Album.tags.all.should == [@tag]
351
+ Album.alias_tags.all.should == [@tag]
352
+ Artist.albums.all.should == [@album]
353
+ Artist.tags.all.should == [@tag]
354
+ Artist.albums.tags.all.should == [@tag]
355
+
356
+ album.add_tag(tag)
357
+ album.update(:artist => artist)
358
+
359
+ Tag.albums.order(:name).all.should == [@album, album]
360
+ Album.artists.order(:name).all.should == [@artist, artist]
361
+ Album.tags.order(:name).all.should == [@tag, tag]
362
+ Album.alias_tags.order(:name).all.should == [@tag, tag]
363
+ Artist.albums.order(:name).all.should == [@album, album]
364
+ Artist.tags.order(:name).all.should == [@tag, tag]
365
+ Artist.albums.tags.order(:name).all.should == [@tag, tag]
366
+
367
+ Tag.filter(Tag.qualified_primary_key_hash(tag.pk)).albums.all.should == [album]
368
+ Album.filter(Album.qualified_primary_key_hash(album.pk)).artists.all.should == [artist]
369
+ Album.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == [tag]
370
+ Album.filter(Album.qualified_primary_key_hash(album.pk)).alias_tags.all.should == [tag]
371
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.all.should == [album]
372
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).tags.all.should == [tag]
373
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.tags.all.should == [tag]
374
+
375
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == [tag]
376
+ Artist.filter(Artist.qualified_primary_key_hash(@artist.pk)).albums.filter(Album.qualified_primary_key_hash(@album.pk)).tags.all.should == [@tag]
377
+ Artist.filter(Artist.qualified_primary_key_hash(@artist.pk)).albums.filter(Album.qualified_primary_key_hash(album.pk)).tags.all.should == []
378
+ Artist.filter(Artist.qualified_primary_key_hash(artist.pk)).albums.filter(Album.qualified_primary_key_hash(@album.pk)).tags.all.should == []
379
+ end
380
+
25
381
  specify "should have remove methods work" do
26
382
  @album.update(:artist => @artist)
27
383
  @album.add_tag(@tag)
28
384
 
29
385
  @album.update(:artist => nil)
30
386
  @album.remove_tag(@tag)
387
+
388
+ @album.add_alias_tag(@tag)
389
+ @album.remove_alias_tag(@tag)
31
390
 
32
391
  @album.reload
33
392
  @artist.reload
@@ -37,6 +396,12 @@ shared_examples_for "regular and composite key associations" do
37
396
  @artist.albums.should == []
38
397
  @album.tags.should == []
39
398
  @tag.albums.should == []
399
+
400
+ @album.add_alias_tag(@tag)
401
+ @album.remove_alias_tag(@tag)
402
+
403
+ @album.reload
404
+ @album.alias_tags.should == []
40
405
  end
41
406
 
42
407
  specify "should have remove_all methods work" do
@@ -54,16 +419,24 @@ shared_examples_for "regular and composite key associations" do
54
419
  @artist.albums.should == []
55
420
  @album.tags.should == []
56
421
  @tag.albums.should == []
422
+
423
+ @album.add_alias_tag(@tag)
424
+ @album.remove_all_alias_tags
425
+
426
+ @album.reload
427
+ @album.alias_tags.should == []
57
428
  end
58
429
 
59
430
  specify "should eager load via eager correctly" do
60
431
  @album.update(:artist => @artist)
61
432
  @album.add_tag(@tag)
62
433
 
63
- a = Artist.eager(:albums=>:tags).all
434
+ a = Artist.eager(:albums=>[:tags, :alias_tags]).eager(:first_album).all
64
435
  a.should == [@artist]
65
436
  a.first.albums.should == [@album]
437
+ a.first.first_album.should == @album
66
438
  a.first.albums.first.tags.should == [@tag]
439
+ a.first.albums.first.alias_tags.should == [@tag]
67
440
 
68
441
  a = Tag.eager(:albums=>:artist).all
69
442
  a.should == [@tag]
@@ -71,14 +444,58 @@ shared_examples_for "regular and composite key associations" do
71
444
  a.first.albums.first.artist.should == @artist
72
445
  end
73
446
 
447
+ describe "when filtering/excluding by associations" do
448
+ before do
449
+ @Artist = Artist.dataset
450
+ @Album = Album.dataset
451
+ @Tag = Tag.dataset
452
+ end
453
+
454
+ it_should_behave_like "filtering/excluding by associations"
455
+ end
456
+
457
+ describe "when filtering/excluding by associations when joining" do
458
+ def self_join(c)
459
+ c.join(c.table_name.as(:b), Array(c.primary_key).zip(Array(c.primary_key))).select_all(c.table_name)
460
+ end
461
+
462
+ before do
463
+ @Artist = self_join(Artist)
464
+ @Album = self_join(Album)
465
+ @Tag = self_join(Tag)
466
+ end
467
+
468
+ it_should_behave_like "filtering/excluding by associations"
469
+ end
470
+
471
+ describe "with no :eager_limit_strategy" do
472
+ it_should_behave_like "eager limit strategies"
473
+ end
474
+
475
+ describe "with :eager_limit_strategy=>true" do
476
+ before do
477
+ @els = {:eager_limit_strategy=>true}
478
+ end
479
+ it_should_behave_like "eager limit strategies"
480
+ end
481
+
482
+ describe "with :eager_limit_strategy=>:window_function" do
483
+ before do
484
+ @els = {:eager_limit_strategy=>:window_function}
485
+ end
486
+ it_should_behave_like "eager limit strategies"
487
+ end if INTEGRATION_DB.dataset.supports_window_functions?
488
+
74
489
  specify "should eager load via eager_graph correctly" do
75
490
  @album.update(:artist => @artist)
76
491
  @album.add_tag(@tag)
77
492
 
78
- a = Artist.eager_graph(:albums=>:tags).all
493
+ a = Artist.eager_graph(:albums=>[:tags, :alias_tags]).eager_graph(:first_album).all
79
494
  a.should == [@artist]
80
495
  a.first.albums.should == [@album]
496
+ a.first.first_album.should == @album
81
497
  a.first.albums.first.tags.should == [@tag]
498
+ a.first.albums.first.alias_tags.should == [@tag]
82
499
 
83
500
  a = Tag.eager_graph(:albums=>:artist).all
84
501
  a.should == [@tag]
@@ -117,46 +534,97 @@ shared_examples_for "regular and composite key associations" do
117
534
  end
118
535
 
119
536
  describe "Sequel::Model Simple Associations" do
120
- before do
537
+ before(:all) do
121
538
  @db = INTEGRATION_DB
122
- @db.create_table!(:artists) do
539
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db.drop_table(t) rescue nil}
540
+ @db.create_table(:artists) do
123
541
  primary_key :id
124
542
  String :name
125
543
  end
126
- @db.create_table!(:albums) do
544
+ @db.create_table(:albums) do
127
545
  primary_key :id
128
546
  String :name
129
547
  foreign_key :artist_id, :artists
130
548
  end
131
- @db.create_table!(:tags) do
549
+ @db.create_table(:tags) do
132
550
  primary_key :id
133
551
  String :name
134
552
  end
135
- @db.create_table!(:albums_tags) do
553
+ @db.create_table(:albums_tags) do
136
554
  foreign_key :album_id, :albums
137
555
  foreign_key :tag_id, :tags
138
556
  end
557
+ end
558
+ before do
559
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db[t].delete}
139
560
  class ::Artist < Sequel::Model(@db)
140
- one_to_many :albums
561
+ plugin :dataset_associations
562
+ one_to_many :albums, :order=>:name
563
+ one_to_one :first_album, :class=>:Album, :order=>:name
564
+ one_to_one :last_album, :class=>:Album, :order=>:name.desc
565
+ one_to_many :first_two_albums, :class=>:Album, :order=>:name, :limit=>2
566
+ one_to_many :second_two_albums, :class=>:Album, :order=>:name, :limit=>[2, 1]
567
+ one_to_many :last_two_albums, :class=>:Album, :order=>:name.desc, :limit=>2
141
568
  plugin :many_through_many
142
- Artist.many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]]
569
+ many_through_many :tags, [[:albums, :artist_id, :id], [:albums_tags, :album_id, :tag_id]]
570
+ many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2
571
+ many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1]
572
+ many_through_many :last_two_tags, :clone=>:tags, :order=>:tags__name.desc, :limit=>2
143
573
  end
144
574
  class ::Album < Sequel::Model(@db)
575
+ plugin :dataset_associations
145
576
  many_to_one :artist
146
- many_to_many :tags
577
+ many_to_many :tags, :right_key=>:tag_id
578
+ many_to_many :alias_tags, :clone=>:tags, :join_table=>:albums_tags___at
579
+ many_to_many :first_two_tags, :clone=>:tags, :order=>:name, :limit=>2
580
+ many_to_many :second_two_tags, :clone=>:tags, :order=>:name, :limit=>[2, 1]
581
+ many_to_many :last_two_tags, :clone=>:tags, :order=>:name.desc, :limit=>2
147
582
  end
148
583
  class ::Tag < Sequel::Model(@db)
584
+ plugin :dataset_associations
149
585
  many_to_many :albums
150
586
  end
151
587
  @album = Album.create(:name=>'Al')
152
588
  @artist = Artist.create(:name=>'Ar')
153
589
  @tag = Tag.create(:name=>'T')
590
+ @same_album = lambda{Album.create(:name=>'Al', :artist_id=>@artist.id)}
591
+ @diff_album = lambda{Album.create(:name=>'lA', :artist_id=>@artist.id)}
592
+ @middle_album = lambda{Album.create(:name=>'Bl', :artist_id=>@artist.id)}
593
+ @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}
594
+ @pr = lambda{[Album.create(:name=>'Al2'),Artist.create(:name=>'Ar2'),Tag.create(:name=>'T2')]}
595
+ @ins = lambda{@db[:albums_tags].insert(:tag_id=>@tag.id)}
154
596
  end
155
597
  after do
156
- @db.drop_table(:albums_tags, :tags, :albums, :artists)
157
598
  [:Tag, :Album, :Artist].each{|x| Object.send(:remove_const, x)}
158
599
  end
600
+ after(:all) do
601
+ @db.drop_table(:albums_tags, :tags, :albums, :artists)
602
+ end
159
603
 
604
+ it_should_behave_like "regular and composite key associations"
605
+
606
+ describe "with :eager_limit_strategy=>:correlated_subquery" do
607
+ before do
608
+ @els = {:eager_limit_strategy=>:correlated_subquery}
609
+ end
610
+ it_should_behave_like "eager limit strategies"
611
+ end unless Sequel.guarded?(:mysql, :db2, :oracle)
612
+
613
+ specify "should handle many_to_one associations with same name as :key" do
614
+ Album.def_column_alias(:artist_id_id, :artist_id)
615
+ Album.many_to_one :artist_id, :key_column =>:artist_id, :class=>Artist
616
+ @album.update(:artist_id_id => @artist.id)
617
+ @album.artist_id.should == @artist
618
+
619
+ as = Album.eager(:artist_id).all
620
+ as.should == [@album]
621
+ as.map{|a| a.artist_id}.should == [@artist]
622
+
623
+ as = Album.eager_graph(:artist_id).all
624
+ as.should == [@album]
625
+ as.map{|a| a.artist_id}.should == [@artist]
626
+ end
627
+
160
628
  specify "should handle aliased tables when eager_graphing" do
161
629
  @album.update(:artist => @artist)
162
630
  @album.add_tag(@tag)
@@ -180,8 +648,6 @@ describe "Sequel::Model Simple Associations" do
180
648
  a.first.balbums.first.bartist.should == @artist
181
649
  end
182
650
 
183
- it_should_behave_like "regular and composite key associations"
184
-
185
651
  specify "should have add method accept hashes and create new records" do
186
652
  @artist.remove_all_albums
187
653
  Album.delete
@@ -216,6 +682,48 @@ describe "Sequel::Model Simple Associations" do
216
682
  @tag.reload.albums.should == []
217
683
  end
218
684
 
685
+ specify "should handle dynamic callbacks for regular loading" do
686
+ @artist.add_album(@album)
687
+
688
+ @artist.albums.should == [@album]
689
+ @artist.albums(proc{|ds| ds.exclude(:id=>@album.id)}).should == []
690
+ @artist.albums(proc{|ds| ds.filter(:id=>@album.id)}).should == [@album]
691
+
692
+ @album.artist.should == @artist
693
+ @album.artist(proc{|ds| ds.exclude(:id=>@artist.id)}).should == nil
694
+ @album.artist(proc{|ds| ds.filter(:id=>@artist.id)}).should == @artist
695
+
696
+ if RUBY_VERSION >= '1.8.7'
697
+ @artist.albums{|ds| ds.exclude(:id=>@album.id)}.should == []
698
+ @artist.albums{|ds| ds.filter(:id=>@album.id)}.should == [@album]
699
+ @album.artist{|ds| ds.exclude(:id=>@artist.id)}.should == nil
700
+ @album.artist{|ds| ds.filter(:id=>@artist.id)}.should == @artist
701
+ end
702
+ end
703
+
704
+ specify "should handle dynamic callbacks for eager loading via eager and eager_graph" do
705
+ @artist.add_album(@album)
706
+ @album.add_tag(@tag)
707
+ album2 = @artist.add_album(:name=>'Foo')
708
+ tag2 = album2.add_tag(:name=>'T2')
709
+
710
+ artist = Artist.eager(:albums=>:tags).all.first
711
+ artist.albums.should == [@album, album2]
712
+ artist.albums.map{|x| x.tags}.should == [[@tag], [tag2]]
713
+
714
+ artist = Artist.eager_graph(:albums=>:tags).all.first
715
+ artist.albums.should == [@album, album2]
716
+ artist.albums.map{|x| x.tags}.should == [[@tag], [tag2]]
717
+
718
+ artist = Artist.eager(:albums=>{proc{|ds| ds.where(:id=>album2.id)}=>:tags}).all.first
719
+ artist.albums.should == [album2]
720
+ artist.albums.first.tags.should == [tag2]
721
+
722
+ artist = Artist.eager_graph(:albums=>{proc{|ds| ds.where(:id=>album2.id)}=>:tags}).all.first
723
+ artist.albums.should == [album2]
724
+ artist.albums.first.tags.should == [tag2]
725
+ end
726
+
219
727
  specify "should have remove method raise an error for one_to_many records if the object isn't already associated" do
220
728
  proc{@artist.remove_album(@album.id)}.should raise_error(Sequel::Error)
221
729
  proc{@artist.remove_album(@album)}.should raise_error(Sequel::Error)
@@ -223,15 +731,16 @@ describe "Sequel::Model Simple Associations" do
223
731
  end
224
732
 
225
733
  describe "Sequel::Model Composite Key Associations" do
226
- before do
734
+ before(:all) do
227
735
  @db = INTEGRATION_DB
228
- @db.create_table!(:artists) do
736
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db.drop_table(t) rescue nil}
737
+ @db.create_table(:artists) do
229
738
  Integer :id1
230
739
  Integer :id2
231
740
  String :name
232
741
  primary_key [:id1, :id2]
233
742
  end
234
- @db.create_table!(:albums) do
743
+ @db.create_table(:albums) do
235
744
  Integer :id1
236
745
  Integer :id2
237
746
  String :name
@@ -240,13 +749,13 @@ describe "Sequel::Model Composite Key Associations" do
240
749
  foreign_key [:artist_id1, :artist_id2], :artists
241
750
  primary_key [:id1, :id2]
242
751
  end
243
- @db.create_table!(:tags) do
752
+ @db.create_table(:tags) do
244
753
  Integer :id1
245
754
  Integer :id2
246
755
  String :name
247
756
  primary_key [:id1, :id2]
248
757
  end
249
- @db.create_table!(:albums_tags) do
758
+ @db.create_table(:albums_tags) do
250
759
  Integer :album_id1
251
760
  Integer :album_id2
252
761
  Integer :tag_id1
@@ -254,20 +763,38 @@ describe "Sequel::Model Composite Key Associations" do
254
763
  foreign_key [:album_id1, :album_id2], :albums
255
764
  foreign_key [:tag_id1, :tag_id2], :tags
256
765
  end
766
+ end
767
+ before do
768
+ [:albums_tags, :tags, :albums, :artists].each{|t| @db[t].delete}
257
769
  class ::Artist < Sequel::Model(@db)
770
+ plugin :dataset_associations
258
771
  set_primary_key :id1, :id2
259
772
  unrestrict_primary_key
260
- one_to_many :albums, :key=>[:artist_id1, :artist_id2]
773
+ one_to_many :albums, :key=>[:artist_id1, :artist_id2], :order=>:name
774
+ one_to_one :first_album, :clone=>:albums, :order=>:name
775
+ one_to_one :last_album, :clone=>:albums, :order=>:name.desc
776
+ one_to_many :first_two_albums, :clone=>:albums, :order=>:name, :limit=>2
777
+ one_to_many :second_two_albums, :clone=>:albums, :order=>:name, :limit=>[2, 1]
778
+ one_to_many :last_two_albums, :clone=>:albums, :order=>:name.desc, :limit=>2
261
779
  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]]]
780
+ many_through_many :tags, [[:albums, [:artist_id1, :artist_id2], [:id1, :id2]], [:albums_tags, [:album_id1, :album_id2], [:tag_id1, :tag_id2]]]
781
+ many_through_many :first_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>2
782
+ many_through_many :second_two_tags, :clone=>:tags, :order=>:tags__name, :limit=>[2, 1]
783
+ many_through_many :last_two_tags, :clone=>:tags, :order=>:tags__name.desc, :limit=>2
263
784
  end
264
785
  class ::Album < Sequel::Model(@db)
786
+ plugin :dataset_associations
265
787
  set_primary_key :id1, :id2
266
788
  unrestrict_primary_key
267
789
  many_to_one :artist, :key=>[:artist_id1, :artist_id2]
268
790
  many_to_many :tags, :left_key=>[:album_id1, :album_id2], :right_key=>[:tag_id1, :tag_id2]
791
+ many_to_many :alias_tags, :clone=>:tags, :join_table=>:albums_tags___at
792
+ many_to_many :first_two_tags, :clone=>:tags, :order=>:name, :limit=>2
793
+ many_to_many :second_two_tags, :clone=>:tags, :order=>:name, :limit=>[2, 1]
794
+ many_to_many :last_two_tags, :clone=>:tags, :order=>:name.desc, :limit=>2
269
795
  end
270
796
  class ::Tag < Sequel::Model(@db)
797
+ plugin :dataset_associations
271
798
  set_primary_key :id1, :id2
272
799
  unrestrict_primary_key
273
800
  many_to_many :albums, :right_key=>[:album_id1, :album_id2], :left_key=>[:tag_id1, :tag_id2]
@@ -275,14 +802,29 @@ describe "Sequel::Model Composite Key Associations" do
275
802
  @album = Album.create(:name=>'Al', :id1=>1, :id2=>2)
276
803
  @artist = Artist.create(:name=>'Ar', :id1=>3, :id2=>4)
277
804
  @tag = Tag.create(:name=>'T', :id1=>5, :id2=>6)
805
+ @same_album = lambda{Album.create(:name=>'Al', :id1=>7, :id2=>8, :artist_id1=>3, :artist_id2=>4)}
806
+ @diff_album = lambda{Album.create(:name=>'lA', :id1=>9, :id2=>10, :artist_id1=>3, :artist_id2=>4)}
807
+ @middle_album = lambda{Album.create(:name=>'Bl', :id1=>13, :id2=>14, :artist_id1=>3, :artist_id2=>4)}
808
+ @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}
809
+ @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)]}
810
+ @ins = lambda{@db[:albums_tags].insert(:tag_id1=>@tag.id1, :tag_id2=>@tag.id2)}
278
811
  end
279
812
  after do
280
- @db.drop_table(:albums_tags, :tags, :albums, :artists)
281
813
  [:Tag, :Album, :Artist].each{|x| Object.send(:remove_const, x)}
282
814
  end
815
+ after(:all) do
816
+ @db.drop_table(:albums_tags, :tags, :albums, :artists)
817
+ end
283
818
 
284
819
  it_should_behave_like "regular and composite key associations"
285
820
 
821
+ describe "with :eager_limit_strategy=>:correlated_subquery" do
822
+ before do
823
+ @els = {:eager_limit_strategy=>:correlated_subquery}
824
+ end
825
+ it_should_behave_like "eager limit strategies"
826
+ end if INTEGRATION_DB.dataset.supports_multiple_column_in? && !Sequel.guarded?(:mysql, :db2, :oracle)
827
+
286
828
  specify "should have add method accept hashes and create new records" do
287
829
  @artist.remove_all_albums
288
830
  Album.delete
@@ -322,3 +864,101 @@ describe "Sequel::Model Composite Key Associations" do
322
864
  proc{@artist.remove_album(@album)}.should raise_error(Sequel::Error)
323
865
  end
324
866
  end
867
+
868
+ describe "Sequel::Model Associations with clashing column names" do
869
+ before(:all) do
870
+ @db = INTEGRATION_DB
871
+ [:bars_foos, :bars, :foos].each{|t| @db.drop_table(t) rescue nil}
872
+ @db.create_table(:foos) do
873
+ primary_key :id
874
+ Integer :object_id
875
+ end
876
+ @db.create_table(:bars) do
877
+ primary_key :id
878
+ Integer :object_id
879
+ end
880
+ @db.create_table(:bars_foos) do
881
+ Integer :foo_id
882
+ Integer :object_id
883
+ primary_key [:foo_id, :object_id]
884
+ end
885
+ end
886
+ before do
887
+ [:bars_foos, :bars, :foos].each{|t| @db[t].delete}
888
+ @Foo = Class.new(Sequel::Model(:foos))
889
+ @Bar = Class.new(Sequel::Model(:bars))
890
+ @Foo.def_column_alias(:obj_id, :object_id)
891
+ @Bar.def_column_alias(:obj_id, :object_id)
892
+ @Foo.one_to_many :bars, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :eager_loader_key=>:object_id, :class=>@Bar
893
+ @Foo.one_to_one :bar, :primary_key=>:obj_id, :primary_key_column=>:object_id, :key=>:object_id, :key_method=>:obj_id, :eager_loader_key=>:object_id, :class=>@Bar
894
+ @Bar.many_to_one :foo, :key=>:obj_id, :key_column=>:object_id, :primary_key=>:object_id, :primary_key_method=>:obj_id, :class=>@Foo
895
+ @Foo.many_to_many :mtmbars, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:foo_id, :right_key=>:object_id, :eager_loader_key=>:object_id, :class=>@Bar
896
+ @Bar.many_to_many :mtmfoos, :join_table=>:bars_foos, :left_primary_key=>:obj_id, :left_primary_key_column=>:object_id, :right_primary_key=>:object_id, :right_primary_key_method=>:obj_id, :left_key=>:object_id, :right_key=>:foo_id, :eager_loader_key=>:object_id, :class=>@Foo
897
+ @foo = @Foo.create(:obj_id=>2)
898
+ @bar = @Bar.create(:obj_id=>2)
899
+ @Foo.db[:bars_foos].insert(2, 2)
900
+ end
901
+ after(:all) do
902
+ @db.drop_table(:bars_foos, :bars, :foos)
903
+ end
904
+
905
+ it "should have working regular association methods" do
906
+ @Bar.first.foo.should == @foo
907
+ @Foo.first.bars.should == [@bar]
908
+ @Foo.first.bar.should == @bar
909
+ @Foo.first.mtmbars.should == [@bar]
910
+ @Bar.first.mtmfoos.should == [@foo]
911
+ end
912
+
913
+ it "should have working eager loading methods" do
914
+ @Bar.eager(:foo).all.map{|o| [o, o.foo]}.should == [[@bar, @foo]]
915
+ @Foo.eager(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
916
+ @Foo.eager(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
917
+ @Foo.eager(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
918
+ @Bar.eager(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
919
+ end
920
+
921
+ it "should have working eager graphing methods" do
922
+ @Bar.eager_graph(:foo).all.map{|o| [o, o.foo]}.should == [[@bar, @foo]]
923
+ @Foo.eager_graph(:bars).all.map{|o| [o, o.bars]}.should == [[@foo, [@bar]]]
924
+ @Foo.eager_graph(:bar).all.map{|o| [o, o.bar]}.should == [[@foo, @bar]]
925
+ @Foo.eager_graph(:mtmbars).all.map{|o| [o, o.mtmbars]}.should == [[@foo, [@bar]]]
926
+ @Bar.eager_graph(:mtmfoos).all.map{|o| [o, o.mtmfoos]}.should == [[@bar, [@foo]]]
927
+ end
928
+
929
+ it "should have working modification methods" do
930
+ b = @Bar.create(:obj_id=>3)
931
+ f = @Foo.create(:obj_id=>3)
932
+
933
+ @bar.foo = f
934
+ @bar.obj_id.should == 3
935
+ @foo.bar = @bar
936
+ @bar.obj_id.should == 2
937
+
938
+ @foo.add_bar(b)
939
+ @foo.bars.sort_by{|x| x.obj_id}.should == [@bar, b]
940
+ @foo.remove_bar(b)
941
+ @foo.bars.should == [@bar]
942
+ @foo.remove_all_bars
943
+ @foo.bars.should == []
944
+
945
+ @bar.refresh.update(:obj_id=>2)
946
+ b.refresh.update(:obj_id=>3)
947
+ @foo.mtmbars.should == [@bar]
948
+ @foo.remove_all_mtmbars
949
+ @foo.mtmbars.should == []
950
+ @foo.add_mtmbar(b)
951
+ @foo.mtmbars.should == [b]
952
+ @foo.remove_mtmbar(b)
953
+ @foo.mtmbars.should == []
954
+
955
+ @bar.add_mtmfoo(f)
956
+ @bar.mtmfoos.should == [f]
957
+ @bar.remove_all_mtmfoos
958
+ @bar.mtmfoos.should == []
959
+ @bar.add_mtmfoo(f)
960
+ @bar.mtmfoos.should == [f]
961
+ @bar.remove_mtmfoo(f)
962
+ @bar.mtmfoos.should == []
963
+ end
964
+ end