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
@@ -5,13 +5,14 @@ describe "Sequel::Plugins::IdentityMap" do
5
5
  class ::IdentityMapModel < Sequel::Model
6
6
  plugin :identity_map
7
7
  attr_accessor :foo
8
- ds = dataset
9
- def ds.fetch_rows(sql)
10
- c = @opts[:where].args.first
11
- c = c.column if c.is_a?(Sequel::SQL::QualifiedIdentifier)
12
- h = {c=>@opts[:where].args.last}
13
- execute(sql)
14
- yield h
8
+ dataset._fetch = proc do |sql|
9
+ sql =~ /WHERE \(?(\w+\.)?(\w+) = (\d)\)?/
10
+ {$2.to_sym=>$3.to_i}
11
+ end
12
+ def self.waw_identity_map(&block) # with and without
13
+ with_identity_map(&block)
14
+ db.reset
15
+ yield
15
16
  end
16
17
  end
17
18
  class ::IdentityMapAlbum < ::IdentityMapModel
@@ -105,9 +106,9 @@ describe "Sequel::Plugins::IdentityMap" do
105
106
  MODEL_DB.sqls.length.should == 1
106
107
  o.foo = 1
107
108
  @c[1].foo.should == o.foo
108
- MODEL_DB.sqls.length.should == 1
109
+ MODEL_DB.sqls.length.should == 0
109
110
  @c[2].foo.should_not == o.foo
110
- MODEL_DB.sqls.length.should == 2
111
+ MODEL_DB.sqls.length.should == 1
111
112
  end
112
113
  end
113
114
 
@@ -118,22 +119,22 @@ describe "Sequel::Plugins::IdentityMap" do
118
119
  MODEL_DB.sqls.length.should == 1
119
120
  o.foo = 1
120
121
  @c[1].should == o
121
- MODEL_DB.sqls.length.should == 1
122
+ MODEL_DB.sqls.length.should == 0
122
123
  o.destroy
123
- MODEL_DB.sqls.length.should == 2
124
+ MODEL_DB.sqls.length.should == 1
124
125
  @c[1].foo.should_not == o.foo
125
- MODEL_DB.sqls.length.should == 3
126
+ MODEL_DB.sqls.length.should == 1
126
127
 
127
128
  MODEL_DB.reset
128
129
  o = @c[2]
129
130
  MODEL_DB.sqls.length.should == 1
130
131
  o.foo = 1
131
132
  @c[2].should == o
132
- MODEL_DB.sqls.length.should == 1
133
+ MODEL_DB.sqls.length.should == 0
133
134
  o.delete
134
- MODEL_DB.sqls.length.should == 2
135
+ MODEL_DB.sqls.length.should == 1
135
136
  @c[2].foo.should_not == o.foo
136
- MODEL_DB.sqls.length.should == 3
137
+ MODEL_DB.sqls.length.should == 1
137
138
  end
138
139
  end
139
140
 
@@ -148,10 +149,10 @@ describe "Sequel::Plugins::IdentityMap" do
148
149
  MODEL_DB.sqls.length.should == 1
149
150
  o = @c1.load(:id=>1, :another_id=>2, :artist_id=>2)
150
151
  o.artist.should == a
151
- MODEL_DB.sqls.length.should == 1
152
+ MODEL_DB.sqls.length.should == 0
152
153
  o = @c1.load(:id=>3, :another_id=>3, :artist_id=>3)
153
154
  o.artist.should_not == a
154
- MODEL_DB.sqls.length.should == 2
155
+ MODEL_DB.sqls.length.should == 1
155
156
  end
156
157
  end
157
158
 
@@ -165,10 +166,10 @@ describe "Sequel::Plugins::IdentityMap" do
165
166
  MODEL_DB.sqls.length.should == 1
166
167
  o = @c1.load(:id=>2, :artist_id=>2)
167
168
  o.artist.should == a
168
- MODEL_DB.sqls.length.should == 1
169
+ MODEL_DB.sqls.length.should == 0
169
170
  o = @c1.load(:id=>3, :artist_id=>3)
170
171
  o.artist.should_not == a
171
- MODEL_DB.sqls.length.should == 2
172
+ MODEL_DB.sqls.length.should == 1
172
173
  end
173
174
  end
174
175
 
@@ -182,9 +183,9 @@ describe "Sequel::Plugins::IdentityMap" do
182
183
  a.should be_a_kind_of(@c1)
183
184
  MODEL_DB.sqls.length.should == 1
184
185
  o.reload
185
- MODEL_DB.sqls.length.should == 2
186
+ MODEL_DB.sqls.length.should == 1
186
187
  o.artist.should == a
187
- MODEL_DB.sqls.length.should == 3
188
+ MODEL_DB.sqls.length.should == 1
188
189
  end
189
190
  end
190
191
 
@@ -199,7 +200,7 @@ describe "Sequel::Plugins::IdentityMap" do
199
200
  MODEL_DB.sqls.length.should == 1
200
201
  o = @c1.load(:id=>2, :artist_id=>2)
201
202
  o.artist.should == a
202
- MODEL_DB.sqls.length.should == 2
203
+ MODEL_DB.sqls.length.should == 1
203
204
  end
204
205
  end
205
206
 
@@ -213,7 +214,98 @@ describe "Sequel::Plugins::IdentityMap" do
213
214
  MODEL_DB.sqls.length.should == 1
214
215
  o = @c1.load(:id=>2, :artist_id=>2)
215
216
  o.artist.should == a
217
+ MODEL_DB.sqls.length.should == 1
218
+ end
219
+ end
220
+
221
+ it "should not override custom :eager_loaders for many_to_many associations" do
222
+ @c1.columns :id
223
+ @c2.columns :id
224
+ c = @c2
225
+ @c1.many_to_many :artists, :class=>@c2, :left_key=>:album_id, :right_key=>:artist_id, :join_table=>:aa, :eager_loader=>(proc do |eo|
226
+ eo[:rows].each{|object| object.associations[:artists] = [c.load(:id=>object.id)]}
227
+ end)
228
+ @c1.dataset._fetch = [{:id=>1}, {:id=>2}, {:id=>3}]
229
+
230
+ @c.waw_identity_map do
231
+ MODEL_DB.sqls.length.should == 0
232
+ a = @c1.eager(:artists).all
233
+ MODEL_DB.sqls.length.should == 1
234
+ a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
235
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1)], [@c2.load(:id=>2)], [@c2.load(:id=>3)]]
236
+ MODEL_DB.sqls.length.should == 0
237
+ end
238
+ end
239
+
240
+ it "should work correctly when eagerly loading many_to_many associations" do
241
+ @c1.columns :id
242
+ @c2.columns :id
243
+ @c1.many_to_many :artists, :class=>@c2, :left_key=>:album_id, :right_key=>:artist_id, :join_table=>:aa
244
+ @c1.dataset._fetch = [{:id=>1}, {:id=>2}, {:id=>3}]
245
+ @c2.dataset._fetch = [{:id=>1, :x_foreign_key_x=>1}, {:id=>1, :x_foreign_key_x=>2}, {:id=>2, :x_foreign_key_x=>1}, {:id=>2, :x_foreign_key_x=>2}, {:id=>3, :x_foreign_key_x=>1}, {:id=>3, :x_foreign_key_x=>1}]
246
+
247
+ @c.waw_identity_map do
248
+ MODEL_DB.sqls.length.should == 0
249
+ a = @c1.eager(:artists).all
250
+ MODEL_DB.sqls.length.should == 2
251
+ a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
252
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
253
+ MODEL_DB.sqls.length.should == 0
254
+ end
255
+ end
256
+
257
+ it "should work correctly when eagerly loading many_to_many associations with composite keys" do
258
+ @c1.columns :id, :id2
259
+ @c2.columns :id
260
+ @c1.set_primary_key :id, :id2
261
+ @c1.many_to_many :artists, :class=>@c2, :left_key=>[:album_id1, :album_id2], :right_key=>:artist_id, :join_table=>:aa
262
+ @c1.dataset._fetch = [{:id=>1, :id2=>4}, {:id=>2, :id2=>5}, {:id=>3, :id2=>6}]
263
+ @c2.dataset._fetch = [ {:id=>1, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>1, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>2, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>2, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}]
264
+
265
+ @c.waw_identity_map do
266
+ MODEL_DB.sqls.length.should == 0
267
+ a = @c1.eager(:artists).all
268
+ MODEL_DB.sqls.length.should == 2
269
+ a.should == [@c1.load(:id=>1, :id2=>4), @c1.load(:id=>2, :id2=>5), @c1.load(:id=>3, :id2=>6)]
270
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
271
+ MODEL_DB.sqls.length.should == 0
272
+ end
273
+ end
274
+
275
+ it "should work correctly when eagerly loading many_through_many associations" do
276
+ @c1.columns :id
277
+ @c2.columns :id
278
+ @c1.plugin :many_through_many
279
+ @c1.many_through_many :artists, [[:aa, :album_id, :artist_id]], :class=>@c2
280
+ @c1.dataset._fetch = [{:id=>1}, {:id=>2}, {:id=>3}]
281
+ @c2.dataset._fetch = [{:id=>1, :x_foreign_key_x=>1}, {:id=>1, :x_foreign_key_x=>2}, {:id=>2, :x_foreign_key_x=>1}, {:id=>2, :x_foreign_key_x=>2}, {:id=>3, :x_foreign_key_x=>1}, {:id=>3, :x_foreign_key_x=>1}]
282
+
283
+ @c.waw_identity_map do
284
+ MODEL_DB.sqls.length.should == 0
285
+ a = @c1.eager(:artists).all
216
286
  MODEL_DB.sqls.length.should == 2
287
+ a.should == [@c1.load(:id=>1), @c1.load(:id=>2), @c1.load(:id=>3)]
288
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
289
+ MODEL_DB.sqls.length.should == 0
290
+ end
291
+ end
292
+
293
+ it "should work correctly when eagerly loading many_to_many associations with composite keys" do
294
+ @c1.columns :id, :id2
295
+ @c2.columns :id
296
+ @c1.set_primary_key :id, :id2
297
+ @c1.plugin :many_through_many
298
+ @c1.many_through_many :artists, [[:aa, [:album_id1, :album_id2], :artist_id]], :class=>@c2
299
+ @c1.dataset._fetch = [{:id=>1, :id2=>4}, {:id=>2, :id2=>5}, {:id=>3, :id2=>6}]
300
+ @c2.dataset._fetch = [ {:id=>1, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>1, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>2, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>2, :x_foreign_key_0_x=>2, :x_foreign_key_1_x=>5}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}, {:id=>3, :x_foreign_key_0_x=>1, :x_foreign_key_1_x=>4}]
301
+
302
+ @c.waw_identity_map do
303
+ MODEL_DB.sqls.length.should == 0
304
+ a = @c1.eager(:artists).all
305
+ MODEL_DB.sqls.length.should == 2
306
+ a.should == [@c1.load(:id=>1, :id2=>4), @c1.load(:id=>2, :id2=>5), @c1.load(:id=>3, :id2=>6)]
307
+ a.map{|x| x.artists}.should == [[@c2.load(:id=>1), @c2.load(:id=>2), @c2.load(:id=>3), @c2.load(:id=>3)], [@c2.load(:id=>1), @c2.load(:id=>2)], []]
308
+ MODEL_DB.sqls.length.should == 0
217
309
  end
218
310
  end
219
311
  end
@@ -2,54 +2,44 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "instance_filters plugin" do
4
4
  before do
5
- @c = Class.new(Sequel::Model(:people)) do
6
- end
7
- @sql = sql = ''
8
- @v = v = [1]
5
+ @c = Class.new(Sequel::Model(:people))
9
6
  @c.dataset.quote_identifiers = false
10
- @c.dataset.meta_def(:update) do |opts|
11
- sql.replace(update_sql(opts))
12
- return v.first
13
- end
14
- @c.dataset.meta_def(:delete) do
15
- sql.replace(delete_sql)
16
- return v.first
17
- end
18
7
  @c.columns :id, :name, :num
19
8
  @c.plugin :instance_filters
20
9
  @p = @c.load(:id=>1, :name=>'John', :num=>1)
10
+ MODEL_DB.sqls
21
11
  end
22
12
 
23
13
  specify "should raise an error when updating a stale record" do
24
14
  @p.update(:name=>'Bob')
25
- @sql.should == "UPDATE people SET name = 'Bob' WHERE (id = 1)"
15
+ MODEL_DB.sqls.should == ["UPDATE people SET name = 'Bob' WHERE (id = 1)"]
26
16
  @p.instance_filter(:name=>'Jim')
27
- @v.replace([0])
17
+ @p.this.numrows = 0
28
18
  proc{@p.update(:name=>'Joe')}.should raise_error(Sequel::Plugins::InstanceFilters::Error)
29
- @sql.should == "UPDATE people SET name = 'Joe' WHERE ((id = 1) AND (name = 'Jim'))"
19
+ MODEL_DB.sqls.should == ["UPDATE people SET name = 'Joe' WHERE ((id = 1) AND (name = 'Jim'))"]
30
20
  end
31
21
 
32
22
  specify "should raise an error when destroying a stale record" do
33
23
  @p.destroy
34
- @sql.should == "DELETE FROM people WHERE (id = 1)"
24
+ MODEL_DB.sqls.should == ["DELETE FROM people WHERE (id = 1)"]
35
25
  @p.instance_filter(:name=>'Jim')
36
- @v.replace([0])
26
+ @p.this.numrows = 0
37
27
  proc{@p.destroy}.should raise_error(Sequel::Plugins::InstanceFilters::Error)
38
- @sql.should == "DELETE FROM people WHERE ((id = 1) AND (name = 'Jim'))"
28
+ MODEL_DB.sqls.should == ["DELETE FROM people WHERE ((id = 1) AND (name = 'Jim'))"]
39
29
  end
40
30
 
41
31
  specify "should apply all instance filters" do
42
32
  @p.instance_filter(:name=>'Jim')
43
33
  @p.instance_filter{num > 2}
44
34
  @p.update(:name=>'Bob')
45
- @sql.should == "UPDATE people SET name = 'Bob' WHERE ((id = 1) AND (name = 'Jim') AND (num > 2))"
35
+ MODEL_DB.sqls.should == ["UPDATE people SET name = 'Bob' WHERE ((id = 1) AND (name = 'Jim') AND (num > 2))"]
46
36
  end
47
37
 
48
38
  specify "should drop instance filters after updating" do
49
39
  @p.instance_filter(:name=>'Joe')
50
40
  @p.update(:name=>'Joe')
51
- @sql.should == "UPDATE people SET name = 'Joe' WHERE ((id = 1) AND (name = 'Joe'))"
41
+ MODEL_DB.sqls.should == ["UPDATE people SET name = 'Joe' WHERE ((id = 1) AND (name = 'Joe'))"]
52
42
  @p.update(:name=>'Bob')
53
- @sql.should == "UPDATE people SET name = 'Bob' WHERE (id = 1)"
43
+ MODEL_DB.sqls.should == ["UPDATE people SET name = 'Bob' WHERE (id = 1)"]
54
44
  end
55
45
  end
@@ -177,3 +177,56 @@ describe "InstanceHooks plugin" do
177
177
  @r.should == [2, 1, 4, 3]
178
178
  end
179
179
  end
180
+
181
+ describe "InstanceHooks plugin with transactions" do
182
+ before do
183
+ @db = Sequel.mock(:numrows=>1)
184
+ pr = proc{|x| r(x)}
185
+ @c = Class.new(Sequel::Model(@db[:items])) do
186
+ attr_accessor :rb
187
+ def after_save
188
+ db.execute('as')
189
+ raise Sequel::Rollback if rb
190
+ end
191
+ def after_destroy
192
+ db.execute('ad')
193
+ raise Sequel::Rollback if rb
194
+ end
195
+ end
196
+ @c.use_transactions = true
197
+ @c.plugin :instance_hooks
198
+ @o = @c.load({:id=>1})
199
+ @or = @c.load({:id=>1})
200
+ @or.rb = true
201
+ @r = []
202
+ @db.sqls
203
+ end
204
+
205
+ it "should support after_commit_hook" do
206
+ @o.after_commit_hook{@db.execute('ac1')}
207
+ @o.after_commit_hook{@db.execute('ac2')}
208
+ @o.save.should_not be_nil
209
+ @db.sqls.should == ['BEGIN', 'as', 'COMMIT', 'ac1', 'ac2']
210
+ end
211
+
212
+ it "should support after_rollback_hook" do
213
+ @or.after_rollback_hook{@db.execute('ar1')}
214
+ @or.after_rollback_hook{@db.execute('ar2')}
215
+ @or.save.should be_nil
216
+ @db.sqls.should == ['BEGIN', 'as', 'ROLLBACK', 'ar1', 'ar2']
217
+ end
218
+
219
+ it "should support after_commit_hook" do
220
+ @o.after_destroy_commit_hook{@db.execute('adc1')}
221
+ @o.after_destroy_commit_hook{@db.execute('adc2')}
222
+ @o.destroy.should_not be_nil
223
+ @db.sqls.should == ['BEGIN', "DELETE FROM items WHERE (id = 1)", 'ad', 'COMMIT', 'adc1', 'adc2']
224
+ end
225
+
226
+ it "should support after_rollback_hook" do
227
+ @or.after_destroy_rollback_hook{@db.execute('adr1')}
228
+ @or.after_destroy_rollback_hook{@db.execute('adr2')}
229
+ @or.destroy.should be_nil
230
+ @db.sqls.should == ['BEGIN', "DELETE FROM items WHERE (id = 1)", 'ad', 'ROLLBACK', 'adr1', 'adr2']
231
+ end
232
+ end
@@ -102,8 +102,8 @@ describe "Sequel::Plugins::JsonSerializer" do
102
102
  end
103
103
 
104
104
  it "should support a to_json class and dataset method" do
105
- album = @album
106
- Album.dataset.meta_def(:all){[album]}
105
+ Album.dataset._fetch = {:id=>1, :name=>'RF', :artist_id=>2}
106
+ Artist.dataset._fetch = {:id=>2, :name=>'YJM'}
107
107
  JSON.parse(Album.to_json).should == [@album]
108
108
  JSON.parse(Album.to_json(:include=>:artist)).map{|x| x.artist}.should == [@artist]
109
109
  JSON.parse(Album.dataset.to_json(:only=>:name)).should == [Album.load(:name=>@album.name)]
@@ -112,7 +112,7 @@ describe "Sequel::Plugins::JsonSerializer" do
112
112
  it "should have dataset to_json method work with naked datasets" do
113
113
  album = @album
114
114
  ds = Album.dataset.naked
115
- ds.meta_def(:all){[album.values]}
115
+ ds._fetch = {:id=>1, :name=>'RF', :artist_id=>2}
116
116
  JSON.parse(ds.to_json).should == [@album.values.inject({}){|h, (k, v)| h[k.to_s] = v; h}]
117
117
  end
118
118
 
@@ -137,8 +137,7 @@ describe "Sequel::Plugins::JsonSerializer" do
137
137
  end
138
138
 
139
139
  it "should handle the :root option to qualify a dataset of records" do
140
- album = @album
141
- Album.dataset.meta_def(:all){[album, album]}
140
+ Album.dataset._fetch = [{:id=>1, :name=>'RF'}, {:id=>1, :name=>'RF'}]
142
141
  Album.dataset.to_json(:root=>true, :only => :id).to_s.should == '{"albums":[{"album":{"id":1}},{"album":{"id":1}}]}'
143
142
  end
144
143
 
@@ -1,8 +1,9 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+ require 'yaml'
2
3
 
3
4
  describe "Sequel::Plugins::LazyAttributes" do
4
5
  before do
5
- @db = MockDatabase.new
6
+ @db = Sequel.mock
6
7
  @db.meta_def(:schema){|*a| [[:id, {:type=>:integer}], [:name,{:type=>:string}]]}
7
8
  class ::LazyAttributesModel < Sequel::Model(@db[:la])
8
9
  plugin :lazy_attributes
@@ -10,27 +11,23 @@ describe "Sequel::Plugins::LazyAttributes" do
10
11
  meta_def(:columns){[:id, :name]}
11
12
  lazy_attributes :name
12
13
  meta_def(:columns){[:id]}
13
- ds = dataset
14
- def ds.fetch_rows(sql)
15
- execute(sql)
16
- select = @opts[:select]
17
- where = @opts[:where]
18
- block = @mod_block || proc{|s| s}
19
- if !where
20
- if select.include?(:name)
21
- yield(block[:id=>1, :name=>'1'])
22
- yield(block[:id=>2, :name=>'2'])
14
+ dataset._fetch = proc do |sql|
15
+ if sql !~ /WHERE/
16
+ if sql =~ /name/
17
+ [{:id=>1, :name=>'1'}, {:id=>2, :name=>'2'}]
23
18
  else
24
- yield(:id=>1)
25
- yield(:id=>2)
19
+ [{:id=>1}, {:id=>2}]
26
20
  end
27
21
  else
28
- i = where.args.last
29
- Array(i).each do |x|
22
+ if sql =~ /id IN \(([\d, ]+)\)/
23
+ $1.split(', ')
24
+ elsif sql =~ /id = (\d)/
25
+ [$1]
26
+ end.map do |x|
30
27
  if sql =~ /SELECT name FROM/
31
- yield(block[:name=>x.to_s])
28
+ {:name=>x.to_s}
32
29
  else
33
- yield(block[:id=>x, :name=>x.to_s])
30
+ {:id=>x.to_i, :name=>x.to_s}
34
31
  end
35
32
  end
36
33
  end
@@ -38,7 +35,7 @@ describe "Sequel::Plugins::LazyAttributes" do
38
35
  end
39
36
  @c = ::LazyAttributesModel
40
37
  @ds = LazyAttributesModel.dataset
41
- @db.reset
38
+ @db.sqls
42
39
  end
43
40
  after do
44
41
  Object.send(:remove_const, :LazyAttributesModel)
@@ -136,7 +133,7 @@ describe "Sequel::Plugins::LazyAttributes" do
136
133
 
137
134
  it "should work with the serialization plugin" do
138
135
  @c.plugin :serialization, :yaml, :name
139
- @ds.instance_variable_set(:@mod_block, proc{|s| s.merge(:name=>"--- #{s[:name].to_i*3}\n")})
136
+ @ds._fetch = [[{:id=>1}, {:id=>2}], [{:id=>1, :name=>"--- 3\n"}, {:id=>2, :name=>"--- 6\n"}], [{:id=>1}], [{:name=>"--- 3\n"}]]
140
137
  @c.with_identity_map do
141
138
  ms = @ds.all
142
139
  ms.map{|m| m.values}.should == [{:id=>1}, {:id=>2}]
@@ -146,7 +143,6 @@ describe "Sequel::Plugins::LazyAttributes" do
146
143
  ms.map{|m| m.name}.should == [3,6]
147
144
  @db.sqls.should == ['SELECT id FROM la', 'SELECT id, name FROM la WHERE (id IN (1, 2))']
148
145
  end
149
- @db.reset
150
146
  @c.with_identity_map do
151
147
  m = @ds.first
152
148
  m.values.should == {:id=>1}
@@ -8,32 +8,10 @@ describe "List plugin" do
8
8
  columns :id, :position, :scope_id, :pos
9
9
  plugin :list, opts
10
10
  self.use_transactions = false
11
-
12
- class << self
13
- attr_accessor :rows
14
- end
15
-
16
- def checked_transaction(opts={})
17
- return super if @in_transaction || !use_transaction?(opts)
18
- @in_transaction = true
19
- db.execute 'BEGIN'
20
- super
21
- db.execute 'COMMIT'
22
- @in_transaction = false
23
- end
24
11
  end
25
12
  c
26
13
  end
27
14
 
28
- def y(c, *hs)
29
- c.rows = hs
30
- ds = c.dataset
31
- def ds.fetch_rows(sql)
32
- db.execute(sql)
33
- yield model.rows.shift
34
- end
35
- end
36
-
37
15
  before do
38
16
  @c = klass
39
17
  @o = @c.load(:id=>7, :position=>3)
@@ -89,18 +67,18 @@ describe "List plugin" do
89
67
  end
90
68
 
91
69
  it "should have at_position return the model object at the given position" do
92
- y(@c, :id=>1, :position=>1)
70
+ @c.dataset._fetch = {:id=>1, :position=>1}
93
71
  @o.at_position(10).should == @c.load(:id=>1, :position=>1)
94
- y(@sc, :id=>2, :position=>2, :scope_id=>5)
72
+ @sc.dataset._fetch = {:id=>2, :position=>2, :scope_id=>5}
95
73
  @so.at_position(20).should == @sc.load(:id=>2, :position=>2, :scope_id=>5)
96
74
  @db.sqls.should == ["SELECT * FROM items WHERE (position = 10) ORDER BY position LIMIT 1",
97
75
  "SELECT * FROM items WHERE ((scope_id = 5) AND (position = 20)) ORDER BY scope_id, position LIMIT 1"]
98
76
  end
99
77
 
100
78
  it "should have last_position return the last position in the list" do
101
- y(@c, :max=>10)
79
+ @c.dataset._fetch = {:max=>10}
102
80
  @o.last_position.should == 10
103
- y(@sc, :max=>20)
81
+ @sc.dataset._fetch = {:max=>20}
104
82
  @so.last_position.should == 20
105
83
  @db.sqls.should == ["SELECT max(position) FROM items LIMIT 1",
106
84
  "SELECT max(position) FROM items WHERE (scope_id = 5) LIMIT 1"]
@@ -115,7 +93,7 @@ describe "List plugin" do
115
93
  end
116
94
 
117
95
  it "should have move_down without an argument move down a single position" do
118
- y(@c, :max=>10)
96
+ @c.dataset._fetch = {:max=>10}
119
97
  @o.move_down.should == @o
120
98
  @o.position.should == 4
121
99
  @db.sqls.should == ["SELECT max(position) FROM items LIMIT 1",
@@ -124,7 +102,7 @@ describe "List plugin" do
124
102
  end
125
103
 
126
104
  it "should have move_down with an argument move down the given number of positions" do
127
- y(@c, :max=>10)
105
+ @c.dataset._fetch = {:max=>10}
128
106
  @o.move_down(3).should == @o
129
107
  @o.position.should == 6
130
108
  @db.sqls.should == ["SELECT max(position) FROM items LIMIT 1",
@@ -141,11 +119,11 @@ describe "List plugin" do
141
119
 
142
120
  it "should have move_to raise an error if an invalid target is used" do
143
121
  proc{@o.move_to(0)}.should raise_error(Sequel::Error)
144
- y(@c, :max=>10)
122
+ @c.dataset._fetch = {:max=>10}
145
123
  proc{@o.move_to(11)}.should raise_error(Sequel::Error)
146
124
  end
147
125
 
148
- it "should have move_to use a transaction is the instance is configured to use transactions" do
126
+ it "should have move_to use a transaction if the instance is configured to use transactions" do
149
127
  @o.use_transactions = true
150
128
  @o.move_to(2)
151
129
  @db.sqls.should == ["BEGIN",
@@ -167,13 +145,13 @@ describe "List plugin" do
167
145
  end
168
146
 
169
147
  it "should have move to shift entries correctly between current and target if moving down" do
170
- y(@c, :max=>10)
148
+ @c.dataset._fetch = {:max=>10}
171
149
  @o.move_to(4)
172
150
  @db.sqls[1].should == "UPDATE items SET position = (position - 1) WHERE ((position >= 4) AND (position <= 4))"
173
151
  end
174
152
 
175
153
  it "should have move_to_bottom move the item to the last position" do
176
- y(@c, :max=>10)
154
+ @c.dataset._fetch = {:max=>10}
177
155
  @o.move_to_bottom
178
156
  @db.sqls.should == ["SELECT max(position) FROM items LIMIT 1",
179
157
  "UPDATE items SET position = (position - 1) WHERE ((position >= 4) AND (position <= 10))",
@@ -201,7 +179,7 @@ describe "List plugin" do
201
179
  end
202
180
 
203
181
  it "should have move_up with a negative argument move down the given number of positions" do
204
- y(@c, :max=>10)
182
+ @c.dataset._fetch = {:max=>10}
205
183
  @o.move_up(-1).should == @o
206
184
  @o.position.should == 4
207
185
  @db.sqls.should == ["SELECT max(position) FROM items LIMIT 1",
@@ -210,19 +188,19 @@ describe "List plugin" do
210
188
  end
211
189
 
212
190
  it "should have next return the next entry in the list if not given an argument" do
213
- y(@c, :id=>9, :position=>4)
191
+ @c.dataset._fetch = {:id=>9, :position=>4}
214
192
  @o.next.should == @c.load(:id=>9, :position=>4)
215
193
  @db.sqls.should == ["SELECT * FROM items WHERE (position = 4) ORDER BY position LIMIT 1"]
216
194
  end
217
195
 
218
196
  it "should have next return the entry the given number of positions below the instance if given an argument" do
219
- y(@c, :id=>9, :position=>5)
197
+ @c.dataset._fetch = {:id=>9, :position=>5}
220
198
  @o.next(2).should == @c.load(:id=>9, :position=>5)
221
199
  @db.sqls.should == ["SELECT * FROM items WHERE (position = 5) ORDER BY position LIMIT 1"]
222
200
  end
223
201
 
224
202
  it "should have next return a previous entry if given a negative argument" do
225
- y(@c, :id=>9, :position=>2)
203
+ @c.dataset._fetch = {:id=>9, :position=>2}
226
204
  @o.next(-1).should == @c.load(:id=>9, :position=>2)
227
205
  @db.sqls.should == ["SELECT * FROM items WHERE (position = 2) ORDER BY position LIMIT 1"]
228
206
  end
@@ -232,19 +210,19 @@ describe "List plugin" do
232
210
  end
233
211
 
234
212
  it "should have prev return the previous entry in the list if not given an argument" do
235
- y(@c, :id=>9, :position=>2)
213
+ @c.dataset._fetch = {:id=>9, :position=>2}
236
214
  @o.prev.should == @c.load(:id=>9, :position=>2)
237
215
  @db.sqls.should == ["SELECT * FROM items WHERE (position = 2) ORDER BY position LIMIT 1"]
238
216
  end
239
217
 
240
218
  it "should have prev return the entry the given number of positions above the instance if given an argument" do
241
- y(@c, :id=>9, :position=>1)
219
+ @c.dataset._fetch = {:id=>9, :position=>1}
242
220
  @o.prev(2).should == @c.load(:id=>9, :position=>1)
243
221
  @db.sqls.should == ["SELECT * FROM items WHERE (position = 1) ORDER BY position LIMIT 1"]
244
222
  end
245
223
 
246
224
  it "should have prev return a following entry if given a negative argument" do
247
- y(@c, :id=>9, :position=>4)
225
+ @c.dataset._fetch = {:id=>9, :position=>4}
248
226
  @o.prev(-1).should == @c.load(:id=>9, :position=>4)
249
227
  @db.sqls.should == ["SELECT * FROM items WHERE (position = 4) ORDER BY position LIMIT 1"]
250
228
  end