sequel 3.21.0 → 3.41.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 (363) hide show
  1. data/CHANGELOG +1131 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +106 -65
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +68 -52
  7. data/doc/advanced_associations.rdoc +201 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/bin_sequel.rdoc +144 -0
  10. data/doc/cheat_sheet.rdoc +21 -21
  11. data/doc/core_extensions.rdoc +374 -0
  12. data/doc/dataset_basics.rdoc +10 -10
  13. data/doc/dataset_filtering.rdoc +54 -42
  14. data/doc/mass_assignment.rdoc +56 -0
  15. data/doc/migration.rdoc +89 -557
  16. data/doc/model_hooks.rdoc +80 -29
  17. data/doc/object_model.rdoc +576 -0
  18. data/doc/opening_databases.rdoc +121 -53
  19. data/doc/prepared_statements.rdoc +41 -15
  20. data/doc/querying.rdoc +137 -56
  21. data/doc/reflection.rdoc +36 -10
  22. data/doc/release_notes/3.22.0.txt +39 -0
  23. data/doc/release_notes/3.23.0.txt +172 -0
  24. data/doc/release_notes/3.24.0.txt +420 -0
  25. data/doc/release_notes/3.25.0.txt +88 -0
  26. data/doc/release_notes/3.26.0.txt +88 -0
  27. data/doc/release_notes/3.27.0.txt +82 -0
  28. data/doc/release_notes/3.28.0.txt +304 -0
  29. data/doc/release_notes/3.29.0.txt +459 -0
  30. data/doc/release_notes/3.30.0.txt +135 -0
  31. data/doc/release_notes/3.31.0.txt +146 -0
  32. data/doc/release_notes/3.32.0.txt +202 -0
  33. data/doc/release_notes/3.33.0.txt +157 -0
  34. data/doc/release_notes/3.34.0.txt +671 -0
  35. data/doc/release_notes/3.35.0.txt +144 -0
  36. data/doc/release_notes/3.36.0.txt +245 -0
  37. data/doc/release_notes/3.37.0.txt +338 -0
  38. data/doc/release_notes/3.38.0.txt +234 -0
  39. data/doc/release_notes/3.39.0.txt +237 -0
  40. data/doc/release_notes/3.40.0.txt +73 -0
  41. data/doc/release_notes/3.41.0.txt +155 -0
  42. data/doc/schema_modification.rdoc +585 -0
  43. data/doc/sharding.rdoc +99 -8
  44. data/doc/sql.rdoc +154 -112
  45. data/doc/testing.rdoc +169 -0
  46. data/doc/thread_safety.rdoc +17 -0
  47. data/doc/transactions.rdoc +137 -0
  48. data/doc/validations.rdoc +1 -1
  49. data/doc/virtual_rows.rdoc +78 -43
  50. data/lib/sequel/adapters/ado/access.rb +334 -0
  51. data/lib/sequel/adapters/ado/mssql.rb +18 -12
  52. data/lib/sequel/adapters/ado.rb +69 -21
  53. data/lib/sequel/adapters/amalgalite.rb +17 -14
  54. data/lib/sequel/adapters/cubrid.rb +143 -0
  55. data/lib/sequel/adapters/db2.rb +178 -85
  56. data/lib/sequel/adapters/dbi.rb +19 -21
  57. data/lib/sequel/adapters/do/mysql.rb +17 -11
  58. data/lib/sequel/adapters/do/postgres.rb +2 -61
  59. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  60. data/lib/sequel/adapters/do.rb +20 -39
  61. data/lib/sequel/adapters/firebird.rb +33 -210
  62. data/lib/sequel/adapters/ibmdb.rb +452 -0
  63. data/lib/sequel/adapters/informix.rb +6 -29
  64. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  65. data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
  66. data/lib/sequel/adapters/jdbc/db2.rb +78 -0
  67. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  68. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  69. data/lib/sequel/adapters/jdbc/h2.rb +68 -31
  70. data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -0
  71. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  73. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  74. data/lib/sequel/adapters/jdbc/mysql.rb +16 -28
  75. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  76. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  77. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  78. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  79. data/lib/sequel/adapters/jdbc/sqlserver.rb +72 -0
  80. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  81. data/lib/sequel/adapters/jdbc.rb +308 -114
  82. data/lib/sequel/adapters/mock.rb +372 -0
  83. data/lib/sequel/adapters/mysql.rb +130 -230
  84. data/lib/sequel/adapters/mysql2.rb +64 -63
  85. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  86. data/lib/sequel/adapters/odbc.rb +29 -10
  87. data/lib/sequel/adapters/openbase.rb +7 -11
  88. data/lib/sequel/adapters/oracle.rb +364 -57
  89. data/lib/sequel/adapters/postgres.rb +379 -145
  90. data/lib/sequel/adapters/shared/access.rb +236 -11
  91. data/lib/sequel/adapters/shared/cubrid.rb +216 -0
  92. data/lib/sequel/adapters/shared/db2.rb +353 -0
  93. data/lib/sequel/adapters/shared/firebird.rb +231 -0
  94. data/lib/sequel/adapters/shared/informix.rb +53 -0
  95. data/lib/sequel/adapters/shared/mssql.rb +412 -116
  96. data/lib/sequel/adapters/shared/mysql.rb +396 -89
  97. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
  98. data/lib/sequel/adapters/shared/oracle.rb +262 -45
  99. data/lib/sequel/adapters/shared/postgres.rb +688 -279
  100. data/lib/sequel/adapters/shared/progress.rb +5 -8
  101. data/lib/sequel/adapters/shared/sqlite.rb +317 -96
  102. data/lib/sequel/adapters/sqlite.rb +109 -46
  103. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  104. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  105. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  106. data/lib/sequel/adapters/swift.rb +31 -27
  107. data/lib/sequel/adapters/tinytds.rb +177 -25
  108. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  109. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  110. data/lib/sequel/adapters/utils/split_alter_table.rb +36 -0
  111. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  112. data/lib/sequel/ast_transformer.rb +194 -0
  113. data/lib/sequel/connection_pool/sharded_single.rb +23 -13
  114. data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
  115. data/lib/sequel/connection_pool/single.rb +11 -3
  116. data/lib/sequel/connection_pool/threaded.rb +69 -20
  117. data/lib/sequel/connection_pool.rb +20 -18
  118. data/lib/sequel/core.rb +143 -11
  119. data/lib/sequel/database/connecting.rb +66 -29
  120. data/lib/sequel/database/dataset.rb +4 -4
  121. data/lib/sequel/database/dataset_defaults.rb +63 -2
  122. data/lib/sequel/database/logging.rb +7 -2
  123. data/lib/sequel/database/misc.rb +192 -51
  124. data/lib/sequel/database/query.rb +299 -114
  125. data/lib/sequel/database/schema_generator.rb +103 -36
  126. data/lib/sequel/database/schema_methods.rb +310 -69
  127. data/lib/sequel/dataset/actions.rb +344 -90
  128. data/lib/sequel/dataset/features.rb +101 -8
  129. data/lib/sequel/dataset/graph.rb +42 -15
  130. data/lib/sequel/dataset/misc.rb +52 -25
  131. data/lib/sequel/dataset/mutation.rb +12 -7
  132. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  133. data/lib/sequel/dataset/query.rb +322 -68
  134. data/lib/sequel/dataset/sql.rb +716 -293
  135. data/lib/sequel/dataset.rb +8 -0
  136. data/lib/sequel/exceptions.rb +4 -0
  137. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  138. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  139. data/lib/sequel/extensions/blank.rb +4 -0
  140. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  141. data/lib/sequel/extensions/connection_validator.rb +109 -0
  142. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  143. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  144. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  145. data/lib/sequel/extensions/inflector.rb +4 -0
  146. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  147. data/lib/sequel/extensions/migration.rb +100 -11
  148. data/lib/sequel/extensions/named_timezones.rb +9 -0
  149. data/lib/sequel/extensions/null_dataset.rb +94 -0
  150. data/lib/sequel/extensions/pagination.rb +4 -0
  151. data/lib/sequel/extensions/pg_array.rb +539 -0
  152. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  153. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  154. data/lib/sequel/extensions/pg_hstore.rb +334 -0
  155. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  156. data/lib/sequel/extensions/pg_inet.rb +113 -0
  157. data/lib/sequel/extensions/pg_interval.rb +191 -0
  158. data/lib/sequel/extensions/pg_json.rb +212 -0
  159. data/lib/sequel/extensions/pg_range.rb +513 -0
  160. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  161. data/lib/sequel/extensions/pg_row.rb +576 -0
  162. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  163. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  164. data/lib/sequel/extensions/pretty_table.rb +17 -72
  165. data/lib/sequel/extensions/query.rb +8 -4
  166. data/lib/sequel/extensions/query_literals.rb +79 -0
  167. data/lib/sequel/extensions/schema_caching.rb +76 -0
  168. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  169. data/lib/sequel/extensions/select_remove.rb +39 -0
  170. data/lib/sequel/extensions/server_block.rb +140 -0
  171. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  172. data/lib/sequel/extensions/sql_expr.rb +8 -110
  173. data/lib/sequel/extensions/string_date_time.rb +4 -0
  174. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  175. data/lib/sequel/extensions/to_dot.rb +99 -83
  176. data/lib/sequel/model/associations.rb +1263 -409
  177. data/lib/sequel/model/base.rb +624 -171
  178. data/lib/sequel/model/errors.rb +1 -1
  179. data/lib/sequel/model/exceptions.rb +24 -2
  180. data/lib/sequel/model/inflections.rb +1 -1
  181. data/lib/sequel/model.rb +30 -11
  182. data/lib/sequel/no_core_ext.rb +2 -0
  183. data/lib/sequel/plugins/active_model.rb +13 -1
  184. data/lib/sequel/plugins/association_pks.rb +137 -16
  185. data/lib/sequel/plugins/caching.rb +25 -18
  186. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  187. data/lib/sequel/plugins/composition.rb +44 -12
  188. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  189. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  190. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  191. data/lib/sequel/plugins/dirty.rb +214 -0
  192. data/lib/sequel/plugins/eager_each.rb +59 -0
  193. data/lib/sequel/plugins/force_encoding.rb +6 -6
  194. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  195. data/lib/sequel/plugins/identity_map.rb +134 -15
  196. data/lib/sequel/plugins/instance_filters.rb +10 -0
  197. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  198. data/lib/sequel/plugins/json_serializer.rb +77 -6
  199. data/lib/sequel/plugins/list.rb +13 -2
  200. data/lib/sequel/plugins/many_through_many.rb +103 -51
  201. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  202. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  203. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  204. data/lib/sequel/plugins/pg_row.rb +121 -0
  205. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  206. data/lib/sequel/plugins/prepared_statements.rb +168 -0
  207. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  208. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  209. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  210. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  211. data/lib/sequel/plugins/schema.rb +8 -3
  212. data/lib/sequel/plugins/serialization.rb +98 -49
  213. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  214. data/lib/sequel/plugins/sharding.rb +21 -54
  215. data/lib/sequel/plugins/single_table_inheritance.rb +57 -12
  216. data/lib/sequel/plugins/static_cache.rb +99 -0
  217. data/lib/sequel/plugins/subclasses.rb +29 -3
  218. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  219. data/lib/sequel/plugins/timestamps.rb +1 -1
  220. data/lib/sequel/plugins/touch.rb +18 -6
  221. data/lib/sequel/plugins/tree.rb +3 -3
  222. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  223. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  224. data/lib/sequel/plugins/validation_class_methods.rb +2 -1
  225. data/lib/sequel/plugins/validation_helpers.rb +57 -4
  226. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  227. data/lib/sequel/sql.rb +721 -141
  228. data/lib/sequel/timezones.rb +67 -40
  229. data/lib/sequel/version.rb +1 -1
  230. data/spec/adapters/db2_spec.rb +146 -0
  231. data/spec/adapters/firebird_spec.rb +53 -39
  232. data/spec/adapters/mssql_spec.rb +261 -66
  233. data/spec/adapters/mysql_spec.rb +597 -352
  234. data/spec/adapters/oracle_spec.rb +92 -117
  235. data/spec/adapters/postgres_spec.rb +2103 -341
  236. data/spec/adapters/spec_helper.rb +16 -6
  237. data/spec/adapters/sqlite_spec.rb +237 -76
  238. data/spec/core/connection_pool_spec.rb +270 -141
  239. data/spec/core/database_spec.rb +980 -524
  240. data/spec/core/dataset_spec.rb +1658 -1243
  241. data/spec/core/expression_filters_spec.rb +741 -334
  242. data/spec/core/mock_adapter_spec.rb +453 -0
  243. data/spec/core/object_graph_spec.rb +88 -129
  244. data/spec/core/schema_generator_spec.rb +4 -4
  245. data/spec/core/schema_spec.rb +556 -53
  246. data/spec/core/spec_helper.rb +7 -48
  247. data/spec/core_extensions_spec.rb +626 -0
  248. data/spec/extensions/active_model_spec.rb +13 -0
  249. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  250. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  251. data/spec/extensions/association_dependencies_spec.rb +15 -25
  252. data/spec/extensions/association_pks_spec.rb +227 -33
  253. data/spec/extensions/association_proxies_spec.rb +4 -4
  254. data/spec/extensions/boolean_readers_spec.rb +25 -25
  255. data/spec/extensions/caching_spec.rb +47 -51
  256. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  257. data/spec/extensions/columns_introspection_spec.rb +91 -0
  258. data/spec/extensions/composition_spec.rb +18 -13
  259. data/spec/extensions/connection_validator_spec.rb +118 -0
  260. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  261. data/spec/extensions/constraint_validations_spec.rb +316 -0
  262. data/spec/extensions/dataset_associations_spec.rb +199 -0
  263. data/spec/extensions/defaults_setter_spec.rb +88 -0
  264. data/spec/extensions/dirty_spec.rb +155 -0
  265. data/spec/extensions/eager_each_spec.rb +34 -0
  266. data/spec/extensions/eval_inspect_spec.rb +67 -0
  267. data/spec/extensions/force_encoding_spec.rb +4 -2
  268. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  269. data/spec/extensions/identity_map_spec.rb +142 -24
  270. data/spec/extensions/inflector_spec.rb +0 -4
  271. data/spec/extensions/instance_filters_spec.rb +11 -21
  272. data/spec/extensions/instance_hooks_spec.rb +72 -0
  273. data/spec/extensions/json_serializer_spec.rb +49 -12
  274. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  275. data/spec/extensions/list_spec.rb +49 -40
  276. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  277. data/spec/extensions/many_through_many_spec.rb +368 -254
  278. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  279. data/spec/extensions/migration_spec.rb +140 -35
  280. data/spec/extensions/named_timezones_spec.rb +29 -11
  281. data/spec/extensions/nested_attributes_spec.rb +268 -89
  282. data/spec/extensions/null_dataset_spec.rb +85 -0
  283. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  284. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  285. data/spec/extensions/pg_array_spec.rb +324 -0
  286. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  287. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  288. data/spec/extensions/pg_hstore_spec.rb +200 -0
  289. data/spec/extensions/pg_inet_spec.rb +47 -0
  290. data/spec/extensions/pg_interval_spec.rb +72 -0
  291. data/spec/extensions/pg_json_spec.rb +99 -0
  292. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  293. data/spec/extensions/pg_range_spec.rb +395 -0
  294. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  295. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  296. data/spec/extensions/pg_row_spec.rb +340 -0
  297. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  298. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  299. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  300. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  301. data/spec/extensions/prepared_statements_spec.rb +87 -0
  302. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  303. data/spec/extensions/pretty_table_spec.rb +7 -6
  304. data/spec/extensions/query_literals_spec.rb +167 -0
  305. data/spec/extensions/query_spec.rb +9 -3
  306. data/spec/extensions/rcte_tree_spec.rb +50 -43
  307. data/spec/extensions/schema_caching_spec.rb +41 -0
  308. data/spec/extensions/schema_dumper_spec.rb +434 -49
  309. data/spec/extensions/schema_spec.rb +30 -49
  310. data/spec/extensions/select_remove_spec.rb +38 -0
  311. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  312. data/spec/extensions/serialization_spec.rb +86 -48
  313. data/spec/extensions/server_block_spec.rb +90 -0
  314. data/spec/extensions/sharding_spec.rb +69 -143
  315. data/spec/extensions/single_table_inheritance_spec.rb +65 -29
  316. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  317. data/spec/extensions/spec_helper.rb +34 -67
  318. data/spec/extensions/split_array_nil_spec.rb +24 -0
  319. data/spec/extensions/sql_expr_spec.rb +29 -60
  320. data/spec/extensions/static_cache_spec.rb +145 -0
  321. data/spec/extensions/subclasses_spec.rb +14 -0
  322. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  323. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  324. data/spec/extensions/timestamps_spec.rb +6 -6
  325. data/spec/extensions/to_dot_spec.rb +8 -11
  326. data/spec/extensions/touch_spec.rb +53 -14
  327. data/spec/extensions/tree_spec.rb +11 -26
  328. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  329. data/spec/extensions/update_primary_key_spec.rb +30 -24
  330. data/spec/extensions/validation_class_methods_spec.rb +77 -68
  331. data/spec/extensions/validation_helpers_spec.rb +48 -37
  332. data/spec/extensions/xml_serializer_spec.rb +29 -4
  333. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  334. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  335. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  336. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  337. data/spec/integration/associations_test.rb +659 -21
  338. data/spec/integration/database_test.rb +25 -5
  339. data/spec/integration/dataset_test.rb +729 -141
  340. data/spec/integration/eager_loader_test.rb +38 -54
  341. data/spec/integration/migrator_test.rb +2 -3
  342. data/spec/integration/model_test.rb +81 -2
  343. data/spec/integration/plugin_test.rb +871 -160
  344. data/spec/integration/prepared_statement_test.rb +272 -128
  345. data/spec/integration/schema_test.rb +377 -57
  346. data/spec/integration/spec_helper.rb +49 -32
  347. data/spec/integration/timezone_test.rb +38 -12
  348. data/spec/integration/transaction_test.rb +183 -8
  349. data/spec/integration/type_test.rb +40 -8
  350. data/spec/model/association_reflection_spec.rb +207 -8
  351. data/spec/model/associations_spec.rb +1206 -693
  352. data/spec/model/base_spec.rb +334 -100
  353. data/spec/model/dataset_methods_spec.rb +45 -27
  354. data/spec/model/eager_loading_spec.rb +828 -692
  355. data/spec/model/hooks_spec.rb +355 -67
  356. data/spec/model/model_spec.rb +251 -179
  357. data/spec/model/plugins_spec.rb +24 -13
  358. data/spec/model/record_spec.rb +506 -232
  359. data/spec/model/spec_helper.rb +16 -77
  360. data/spec/model/validations_spec.rb +38 -2
  361. data/spec/rcov.opts +2 -0
  362. metadata +223 -81
  363. data/spec/core/core_sql_spec.rb +0 -451
@@ -0,0 +1,110 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
+
3
+ describe "arbtirary servers" do
4
+ before do
5
+ @db = Sequel.mock(:servers=>{})
6
+ @db.extension :arbitrary_servers
7
+ end
8
+
9
+ specify "should allow arbitrary server options using a hash" do
10
+ @db.synchronize(:host=>'host1', :database=>'db1') do |c|
11
+ c.opts[:host].should == 'host1'
12
+ c.opts[:database].should == 'db1'
13
+ end
14
+ end
15
+
16
+ specify "should not cache connections to arbitrary servers" do
17
+ x = nil
18
+ @db.synchronize(:host=>'host1', :database=>'db1') do |c|
19
+ x = c
20
+ end
21
+ @db.synchronize(:host=>'host1', :database=>'db1') do |c2|
22
+ c2.should_not equal(x)
23
+ end
24
+ end
25
+
26
+ specify "should yield same connection correctly when nesting" do
27
+ @db.synchronize(:host=>'host1', :database=>'db1') do |c|
28
+ @db.synchronize(:host=>'host1', :database=>'db1') do |c2|
29
+ c2.should equal(c)
30
+ end
31
+ end
32
+ end
33
+
34
+ specify "should disconnect when connection is finished" do
35
+ x, x1 = nil, nil
36
+ @db.meta_def(:disconnect_connection){|c| x = c}
37
+ @db.synchronize(:host=>'host1', :database=>'db1') do |c|
38
+ x1 = c
39
+ @db.synchronize(:host=>'host1', :database=>'db1') do |c2|
40
+ c2.should equal(c)
41
+ end
42
+ x.should equal(nil)
43
+ end
44
+ x.should equal(x1)
45
+ end
46
+
47
+ specify "should yield different connection correctly when nesting" do
48
+ @db.synchronize(:host=>'host1', :database=>'db1') do |c|
49
+ c.opts[:host].should == 'host1'
50
+ @db.synchronize(:host=>'host2', :database=>'db1') do |c2|
51
+ c2.opts[:host].should == 'host2'
52
+ c2.should_not equal(c)
53
+ end
54
+ end
55
+ end
56
+
57
+ specify "should respect multithreaded access" do
58
+ @db.synchronize(:host=>'host1', :database=>'db1') do |c|
59
+ Thread.new do
60
+ @db.synchronize(:host=>'host1', :database=>'db1') do |c2|
61
+ c2.should_not equal(c)
62
+ end
63
+ end.join
64
+ end
65
+ end
66
+
67
+ specify "should work correctly with server_block plugin" do
68
+ @db.extend Sequel::ServerBlock
69
+ @db.with_server(:host=>'host1', :database=>'db1') do
70
+ @db.synchronize do |c|
71
+ c.opts[:host].should == 'host1'
72
+ c.opts[:database].should == 'db1'
73
+ @db.synchronize do |c2|
74
+ c2.should equal(c)
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+
81
+ specify "should respect multithreaded access with server block plugin" do
82
+ @db.extend Sequel::ServerBlock
83
+ q, q1 = Queue.new, Queue.new
84
+
85
+ t = nil
86
+ @db[:t].all
87
+ @db.with_server(:host=>'a') do
88
+ @db[:t].all
89
+ t = Thread.new do
90
+ @db[:t].all
91
+ @db.with_server(:host=>'c') do
92
+ @db[:t].all
93
+ @db.with_server(:host=>'d'){@db[:t].all}
94
+ q.push nil
95
+ q1.pop
96
+ @db[:t].all
97
+ end
98
+ @db[:t].all
99
+ end
100
+ q.pop
101
+ @db.with_server(:host=>'b'){@db[:t].all}
102
+ @db[:t].all
103
+ end
104
+ @db[:t].all
105
+ q1.push nil
106
+ t.join
107
+ @db.sqls.should == ['SELECT * FROM t', 'SELECT * FROM t -- {:host=>"a"}', 'SELECT * FROM t', 'SELECT * FROM t -- {:host=>"c"}', 'SELECT * FROM t -- {:host=>"d"}',
108
+ 'SELECT * FROM t -- {:host=>"b"}', 'SELECT * FROM t -- {:host=>"a"}', 'SELECT * FROM t', 'SELECT * FROM t -- {:host=>"c"}', 'SELECT * FROM t']
109
+ end
110
+ end
@@ -5,11 +5,7 @@ describe "AssociationAutoreloading plugin" do
5
5
  @c = Class.new(Sequel::Model)
6
6
  @c.plugin :association_autoreloading
7
7
  @Artist = Class.new(@c).set_dataset(:artists)
8
- ds1 = @Artist.dataset
9
- def ds1.fetch_rows(s)
10
- (MODEL_DB.sqls ||= []) << s
11
- yield({:id=>2, :name=>'Ar'})
12
- end
8
+ @Artist.dataset._fetch = {:id=>2, :name=>'Ar'}
13
9
  @Album = Class.new(@c).set_dataset(:albums)
14
10
  @Artist.columns :id, :name
15
11
  @Album.columns :id, :name, :artist_id
@@ -22,18 +18,33 @@ describe "AssociationAutoreloading plugin" do
22
18
  album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
23
19
  album.artist
24
20
  MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1']
25
- MODEL_DB.reset
26
21
 
27
22
  album.artist_id = 1
28
23
  album.artist
29
24
  MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
30
25
  end
31
26
 
27
+ specify "should handle multiple many_to_one association with the same foreign key" do
28
+ @Album.many_to_one :artist2, :key=>:artist_id, :class=>@Artist
29
+ album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
30
+ album.artist
31
+ album.artist2
32
+ MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1'] * 2
33
+
34
+ album.artist
35
+ album.artist2
36
+ MODEL_DB.sqls.should == []
37
+
38
+ album.artist_id = 1
39
+ album.artist
40
+ album.artist2
41
+ MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1'] * 2
42
+ end
43
+
32
44
  specify "should not reload when value has not changed" do
33
45
  album = @Album.load(:id => 1, :name=>'Al', :artist_id=>2)
34
46
  album.artist
35
47
  MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1']
36
- MODEL_DB.reset
37
48
 
38
49
  album.artist_id = 2
39
50
  album.artist
@@ -54,7 +65,6 @@ describe "AssociationAutoreloading plugin" do
54
65
  album.artist_id = 1
55
66
  album.artist
56
67
  MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
57
- MODEL_DB.reset
58
68
 
59
69
  album.other_artist
60
70
  MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 1) LIMIT 1']
@@ -69,7 +79,6 @@ describe "AssociationAutoreloading plugin" do
69
79
  album.artist_id = 1
70
80
  album.composite_artist
71
81
  MODEL_DB.sqls.should == ["SELECT * FROM artists WHERE ((artists.id = 1) AND (artists.name = 'Al')) LIMIT 1"]
72
- MODEL_DB.reset
73
82
 
74
83
  album.name = 'Al2'
75
84
  album.composite_artist
@@ -84,7 +93,6 @@ describe "AssociationAutoreloading plugin" do
84
93
  album = salbum.load(:id => 1, :name=>'Al', :artist_id=>2)
85
94
  album.artist
86
95
  MODEL_DB.sqls.should == ['SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1']
87
- MODEL_DB.reset
88
96
 
89
97
  album.artist_id = 1
90
98
  album.artist
@@ -6,17 +6,9 @@ describe "AssociationDependencies plugin" do
6
6
  @c = Class.new(Sequel::Model)
7
7
  @c.plugin :association_dependencies
8
8
  @Artist = Class.new(@c).set_dataset(:artists)
9
- ds1 = @Artist.dataset
10
- def ds1.fetch_rows(s)
11
- (MODEL_DB.sqls ||= []) << s
12
- yield({:id=>2, :name=>'Ar'})
13
- end
9
+ @Artist.dataset._fetch = {:id=>2, :name=>'Ar'}
14
10
  @Album = Class.new(@c).set_dataset(:albums)
15
- ds1 = @Album.dataset
16
- def ds1.fetch_rows(s)
17
- (MODEL_DB.sqls ||= []) << s
18
- yield({:id=>1, :name=>'Al', :artist_id=>2})
19
- end
11
+ @Album.dataset._fetch = {:id=>1, :name=>'Al', :artist_id=>2}
20
12
  @Artist.columns :id, :name
21
13
  @Album.columns :id, :name, :artist_id
22
14
  @Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
@@ -29,55 +21,55 @@ describe "AssociationDependencies plugin" do
29
21
  specify "should allow destroying associated many_to_one associated object" do
30
22
  @Album.add_association_dependencies :artist=>:destroy
31
23
  @Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
32
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
24
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE id = 2']
33
25
  end
34
26
 
35
27
  specify "should allow deleting associated many_to_one associated object" do
36
28
  @Album.add_association_dependencies :artist=>:delete
37
29
  @Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
38
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'DELETE FROM artists WHERE (artists.id = 2)']
30
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1', 'DELETE FROM artists WHERE (artists.id = 2)']
39
31
  end
40
32
 
41
33
  specify "should allow destroying associated one_to_one associated object" do
42
34
  @Artist.add_association_dependencies :first_album=>:destroy
43
35
  @Artist.load(:id=>2, :name=>'Ar').destroy
44
- MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE ((albums.artist_id = 2) AND (position = 1)) LIMIT 1', 'DELETE FROM albums WHERE (id = 1)', 'DELETE FROM artists WHERE (id = 2)']
36
+ MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE ((albums.artist_id = 2) AND (position = 1)) LIMIT 1', 'DELETE FROM albums WHERE id = 1', 'DELETE FROM artists WHERE id = 2']
45
37
  end
46
38
 
47
39
  specify "should allow deleting associated one_to_one associated object" do
48
40
  @Artist.add_association_dependencies :first_album=>:delete
49
41
  @Artist.load(:id=>2, :name=>'Ar').destroy
50
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE ((albums.artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE (id = 2)']
42
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE ((albums.artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE id = 2']
51
43
  end
52
44
 
53
45
  specify "should allow destroying associated one_to_many objects" do
54
46
  @Artist.add_association_dependencies :albums=>:destroy
55
47
  @Artist.load(:id=>2, :name=>'Ar').destroy
56
- MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE (albums.artist_id = 2)', 'DELETE FROM albums WHERE (id = 1)', 'DELETE FROM artists WHERE (id = 2)']
48
+ MODEL_DB.sqls.should == ['SELECT * FROM albums WHERE (albums.artist_id = 2)', 'DELETE FROM albums WHERE id = 1', 'DELETE FROM artists WHERE id = 2']
57
49
  end
58
50
 
59
51
  specify "should allow deleting associated one_to_many objects" do
60
52
  @Artist.add_association_dependencies :albums=>:delete
61
53
  @Artist.load(:id=>2, :name=>'Ar').destroy
62
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (albums.artist_id = 2)', 'DELETE FROM artists WHERE (id = 2)']
54
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (albums.artist_id = 2)', 'DELETE FROM artists WHERE id = 2']
63
55
  end
64
56
 
65
57
  specify "should allow nullifying associated one_to_one objects" do
66
58
  @Artist.add_association_dependencies :first_album=>:nullify
67
59
  @Artist.load(:id=>2, :name=>'Ar').destroy
68
- MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE ((artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE (id = 2)']
60
+ MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE ((artist_id = 2) AND (position = 1))', 'DELETE FROM artists WHERE id = 2']
69
61
  end
70
62
 
71
63
  specify "should allow nullifying associated one_to_many objects" do
72
64
  @Artist.add_association_dependencies :albums=>:nullify
73
65
  @Artist.load(:id=>2, :name=>'Ar').destroy
74
- MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE (artist_id = 2)', 'DELETE FROM artists WHERE (id = 2)']
66
+ MODEL_DB.sqls.should == ['UPDATE albums SET artist_id = NULL WHERE (artist_id = 2)', 'DELETE FROM artists WHERE id = 2']
75
67
  end
76
68
 
77
69
  specify "should allow nullifying associated many_to_many associations" do
78
70
  @Artist.add_association_dependencies :other_artists=>:nullify
79
71
  @Artist.load(:id=>2, :name=>'Ar').destroy
80
- MODEL_DB.sqls.should == ['DELETE FROM aoa WHERE (l = 2)', 'DELETE FROM artists WHERE (id = 2)']
72
+ MODEL_DB.sqls.should == ['DELETE FROM aoa WHERE (l = 2)', 'DELETE FROM artists WHERE id = 2']
81
73
  end
82
74
 
83
75
  specify "should raise an error if attempting to nullify a many_to_one association" do
@@ -105,23 +97,21 @@ describe "AssociationDependencies plugin" do
105
97
  specify "should allow specifying association dependencies in the plugin call" do
106
98
  @Album.plugin :association_dependencies, :artist=>:destroy
107
99
  @Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
108
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
100
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE id = 2']
109
101
  end
110
102
 
111
103
  specify "should work with subclasses" do
112
104
  c = Class.new(@Album)
113
105
  c.add_association_dependencies :artist=>:destroy
114
106
  c.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
115
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
116
- MODEL_DB.reset
107
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE id = 2']
117
108
 
118
109
  @Album.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
119
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)']
120
- MODEL_DB.reset
110
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1']
121
111
 
122
112
  @Album.add_association_dependencies :artist=>:destroy
123
113
  c2 = Class.new(@Album)
124
114
  c2.load(:id=>1, :name=>'Al', :artist_id=>2).destroy
125
- MODEL_DB.sqls.should == ['DELETE FROM albums WHERE (id = 1)', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE (id = 2)']
115
+ MODEL_DB.sqls.should == ['DELETE FROM albums WHERE id = 1', 'SELECT * FROM artists WHERE (artists.id = 2) LIMIT 1', 'DELETE FROM artists WHERE id = 2']
126
116
  end
127
117
  end
@@ -2,42 +2,62 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "Sequel::Plugins::AssociationPks" do
4
4
  before do
5
- @db = MODEL_DB.clone
6
- mod = Module.new do
7
- def fetch_rows(sql)
8
- case sql
9
- when "SELECT id FROM albums WHERE (albums.artist_id = 1)"
10
- yield({:id=>1})
11
- yield({:id=>2})
12
- yield({:id=>3})
13
- when /SELECT tag_id FROM albums_tags WHERE \(album_id = (\d)\)/
14
- yield({:tag_id=>1}) if $1 == '1'
15
- yield({:tag_id=>2}) if $1 != '3'
16
- yield({:tag_id=>3}) if $1 == '2'
17
- end
5
+ @db = Sequel.mock(:fetch=>proc do |sql|
6
+ case sql
7
+ when "SELECT id FROM albums WHERE (albums.artist_id = 1)"
8
+ [{:id=>1}, {:id=>2}, {:id=>3}]
9
+ when /SELECT tag_id FROM albums_tags WHERE \(album_id = (\d)\)/
10
+ a = []
11
+ a << {:tag_id=>1} if $1 == '1'
12
+ a << {:tag_id=>2} if $1 != '3'
13
+ a << {:tag_id=>3} if $1 == '2'
14
+ a
15
+ when "SELECT first, last FROM vocalists WHERE (vocalists.album_id = 1)"
16
+ [{:first=>"F1", :last=>"L1"}, {:first=>"F2", :last=>"L2"}]
17
+ when /SELECT first, last FROM albums_vocalists WHERE \(album_id = (\d)\)/
18
+ a = []
19
+ a << {:first=>"F1", :last=>"L1"} if $1 == '1'
20
+ a << {:first=>"F2", :last=>"L2"} if $1 != '3'
21
+ a << {:first=>"F3", :last=>"L3"} if $1 == '2'
22
+ a
23
+ when "SELECT id FROM instruments WHERE ((instruments.first = 'F1') AND (instruments.last = 'L1'))"
24
+ [{:id=>1}, {:id=>2}]
25
+ when /SELECT instrument_id FROM vocalists_instruments WHERE \(\((?:first|last) = '?[FL1](\d)/
26
+ a = []
27
+ a << {:instrument_id=>1} if $1 == "1"
28
+ a << {:instrument_id=>2} if $1 != "3"
29
+ a << {:instrument_id=>3} if $1 == "2"
30
+ a
31
+ when "SELECT year, week FROM hits WHERE ((hits.first = 'F1') AND (hits.last = 'L1'))"
32
+ [{:year=>1997, :week=>1}, {:year=>1997, :week=>2}]
33
+ when /SELECT year, week FROM vocalists_hits WHERE \(\((?:first|last) = '?[FL1](\d)/
34
+ a = []
35
+ a << {:year=>1997, :week=>1} if $1 == "1"
36
+ a << {:year=>1997, :week=>2} if $1 != "3"
37
+ a << {:year=>1997, :week=>3} if $1 == "2"
38
+ a
18
39
  end
19
- end
20
- @db.meta_def(:dataset) do |*opts|
21
- ds = super(*opts)
22
- ds.extend mod
23
- ds
24
- end
25
- def @db.transaction(opts)
26
- execute('BEGIN')
27
- yield
28
- execute('COMMIT')
29
- end
40
+ end)
30
41
  @Artist = Class.new(Sequel::Model(@db[:artists]))
31
42
  @Artist.columns :id
32
- @Album= Class.new(Sequel::Model(@db[:albums]))
43
+ @Album = Class.new(Sequel::Model(@db[:albums]))
33
44
  @Album.columns :id, :artist_id
34
45
  @Tag = Class.new(Sequel::Model(@db[:tags]))
35
46
  @Tag.columns :id
47
+ @Vocalist = Class.new(Sequel::Model(@db[:vocalists]))
48
+ @Vocalist.columns :first, :last, :album_id
49
+ @Vocalist.set_primary_key [:first, :last]
50
+ @Instrument = Class.new(Sequel::Model(@db[:instruments]))
51
+ @Instrument.columns :id, :first, :last
52
+ @Hit = Class.new(Sequel::Model(@db[:hits]))
53
+ @Hit.columns :year, :week, :first, :last
54
+ @Hit.set_primary_key [:year, :week]
36
55
  @Artist.plugin :association_pks
37
56
  @Album.plugin :association_pks
57
+ @Vocalist.plugin :association_pks
38
58
  @Artist.one_to_many :albums, :class=>@Album, :key=>:artist_id
39
59
  @Album.many_to_many :tags, :class=>@Tag, :join_table=>:albums_tags, :left_key=>:album_id
40
- @db.reset
60
+ @db.sqls
41
61
  end
42
62
 
43
63
  specify "should return correct associated pks for one_to_many associations" do
@@ -57,10 +77,117 @@ describe "Sequel::Plugins::AssociationPks" do
57
77
  "UPDATE albums SET artist_id = NULL WHERE ((albums.artist_id = 1) AND (id NOT IN (1, 2)))"]
58
78
  end
59
79
 
80
+ specify "should use associated class's primary key for a one_to_many association" do
81
+ @Album.set_primary_key :foo
82
+ @Artist.load(:id=>1).album_pks = [1, 2]
83
+ @db.sqls.should == ["UPDATE albums SET artist_id = 1 WHERE (foo IN (1, 2))",
84
+ "UPDATE albums SET artist_id = NULL WHERE ((albums.artist_id = 1) AND (foo NOT IN (1, 2)))"]
85
+ end
86
+
60
87
  specify "should set associated pks correctly for a many_to_many association" do
61
88
  @Album.load(:id=>2).tag_pks = [1, 3]
62
- @db.sqls[0].should == "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))"
63
- @db.sqls[1].should =~ /INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, 1|1, 2)\)/
89
+ sqls = @db.sqls
90
+ sqls[0].should == "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))"
91
+ sqls[1].should == 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)'
92
+ sqls[2].should =~ /INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, 1|1, 2)\)/
93
+ sqls.length.should == 3
94
+ end
95
+
96
+ specify "should return correct right-side associated cpks for one_to_many associations" do
97
+ @Album.one_to_many :vocalists, :class=>@Vocalist, :key=>:album_id
98
+ @Album.load(:id=>1).vocalist_pks.should == [["F1", "L1"], ["F2", "L2"]]
99
+ @Album.load(:id=>2).vocalist_pks.should == []
100
+ end
101
+
102
+ specify "should return correct right-side associated cpks for many_to_many associations" do
103
+ @Album.many_to_many :vocalists, :class=>@Vocalist, :join_table=>:albums_vocalists, :left_key=>:album_id, :right_key=>[:first, :last]
104
+ @Album.load(:id=>1).vocalist_pks.should == [["F1", "L1"], ["F2", "L2"]]
105
+ @Album.load(:id=>2).vocalist_pks.should == [["F2", "L2"], ["F3", "L3"]]
106
+ @Album.load(:id=>3).vocalist_pks.should == []
107
+ end
108
+
109
+ specify "should set associated right-side cpks correctly for a one_to_many association" do
110
+ @Album.one_to_many :vocalists, :class=>@Vocalist, :key=>:album_id
111
+ @Album.load(:id=>1).vocalist_pks = [["F1", "L1"], ["F2", "L2"]]
112
+ @db.sqls.should == ["UPDATE vocalists SET album_id = 1 WHERE ((first, last) IN (('F1', 'L1'), ('F2', 'L2')))",
113
+ "UPDATE vocalists SET album_id = NULL WHERE ((vocalists.album_id = 1) AND ((first, last) NOT IN (('F1', 'L1'), ('F2', 'L2'))))"]
114
+ end
115
+
116
+ specify "should set associated right-side cpks correctly for a many_to_many association" do
117
+ @Album.many_to_many :vocalists, :class=>@Vocalist, :join_table=>:albums_vocalists, :left_key=>:album_id, :right_key=>[:first, :last]
118
+ @Album.load(:id=>2).vocalist_pks = [["F1", "L1"], ["F2", "L2"]]
119
+ sqls = @db.sqls
120
+ sqls[0].should == "DELETE FROM albums_vocalists WHERE ((album_id = 2) AND ((first, last) NOT IN (('F1', 'L1'), ('F2', 'L2'))))"
121
+ sqls[1].should == 'SELECT first, last FROM albums_vocalists WHERE (album_id = 2)'
122
+ match = sqls[2].match(/INSERT INTO albums_vocalists \((.*)\) VALUES \((.*)\)/)
123
+ Hash[match[1].split(', ').zip(match[2].split(', '))].should == {"first"=>"'F1'", "last"=>"'L1'", "album_id"=>"2"}
124
+ sqls.length.should == 3
125
+ end
126
+
127
+ specify "should return correct associated pks for left-side cpks for one_to_many associations" do
128
+ @Vocalist.one_to_many :instruments, :class=>@Instrument, :key=>[:first, :last]
129
+ @Vocalist.load(:first=>'F1', :last=>'L1').instrument_pks.should == [1, 2]
130
+ @Vocalist.load(:first=>'F2', :last=>'L2').instrument_pks.should == []
131
+ end
132
+
133
+ specify "should return correct associated pks for left-side cpks for many_to_many associations" do
134
+ @Vocalist.many_to_many :instruments, :class=>@Instrument, :join_table=>:vocalists_instruments, :left_key=>[:first, :last]
135
+ @Vocalist.load(:first=>'F1', :last=>'L1').instrument_pks.should == [1, 2]
136
+ @Vocalist.load(:first=>'F2', :last=>'L2').instrument_pks.should == [2, 3]
137
+ @Vocalist.load(:first=>'F3', :last=>'L3').instrument_pks.should == []
138
+ end
139
+
140
+ specify "should set associated pks correctly for left-side cpks for a one_to_many association" do
141
+ @Vocalist.one_to_many :instruments, :class=>@Instrument, :key=>[:first, :last]
142
+ @Vocalist.load(:first=>'F1', :last=>'L1').instrument_pks = [1, 2]
143
+ sqls = @db.sqls
144
+ sqls[0].should =~ /UPDATE instruments SET (first = 'F1', last = 'L1'|last = 'L1', first = 'F1') WHERE \(id IN \(1, 2\)\)/
145
+ sqls[1].should =~ /UPDATE instruments SET (first = NULL, last = NULL|last = NULL, first = NULL) WHERE \(\(instruments.first = 'F1'\) AND \(instruments.last = 'L1'\) AND \(id NOT IN \(1, 2\)\)\)/
146
+ sqls.length.should == 2
147
+ end
148
+
149
+ specify "should set associated pks correctly for left-side cpks for a many_to_many association" do
150
+ @Vocalist.many_to_many :instruments, :class=>@Instrument, :join_table=>:vocalists_instruments, :left_key=>[:first, :last]
151
+ @Vocalist.load(:first=>'F2', :last=>'L2').instrument_pks = [1, 2]
152
+ sqls = @db.sqls
153
+ sqls[0].should == "DELETE FROM vocalists_instruments WHERE ((first = 'F2') AND (last = 'L2') AND (instrument_id NOT IN (1, 2)))"
154
+ sqls[1].should == "SELECT instrument_id FROM vocalists_instruments WHERE ((first = 'F2') AND (last = 'L2'))"
155
+ match = sqls[2].match(/INSERT INTO vocalists_instruments \((.*)\) VALUES \((.*)\)/)
156
+ Hash[match[1].split(', ').zip(match[2].split(', '))].should == {"first"=>"'F2'", "last"=>"'L2'", "instrument_id"=>"1"}
157
+ sqls.length.should == 3
158
+ end
159
+
160
+ specify "should return correct right-side associated cpks for left-side cpks for one_to_many associations" do
161
+ @Vocalist.one_to_many :hits, :class=>@Hit, :key=>[:first, :last]
162
+ @Vocalist.load(:first=>'F1', :last=>'L1').hit_pks.should == [[1997, 1], [1997, 2]]
163
+ @Vocalist.load(:first=>'F2', :last=>'L2').hit_pks.should == []
164
+ end
165
+
166
+ specify "should return correct right-side associated cpks for left-side cpks for many_to_many associations" do
167
+ @Vocalist.many_to_many :hits, :class=>@Hit, :join_table=>:vocalists_hits, :left_key=>[:first, :last], :right_key=>[:year, :week]
168
+ @Vocalist.load(:first=>'F1', :last=>'L1').hit_pks.should == [[1997, 1], [1997, 2]]
169
+ @Vocalist.load(:first=>'F2', :last=>'L2').hit_pks.should == [[1997, 2], [1997, 3]]
170
+ @Vocalist.load(:first=>'F3', :last=>'L3').hit_pks.should == []
171
+ end
172
+
173
+ specify "should set associated right-side cpks correctly for left-side cpks for a one_to_many association" do
174
+ @Vocalist.one_to_many :hits, :class=>@Hit, :key=>[:first, :last], :order=>:week
175
+ @Vocalist.load(:first=>'F1', :last=>'L1').hit_pks = [[1997, 1], [1997, 2]]
176
+ sqls = @db.sqls
177
+ sqls[0].should =~ /UPDATE hits SET (first = 'F1', last = 'L1'|last = 'L1', first = 'F1') WHERE \(\(year, week\) IN \(\(1997, 1\), \(1997, 2\)\)\)/
178
+ sqls[1].should =~ /UPDATE hits SET (first = NULL, last = NULL|last = NULL, first = NULL) WHERE \(\(hits.first = 'F1'\) AND \(hits.last = 'L1'\) AND \(\(year, week\) NOT IN \(\(1997, 1\), \(1997, 2\)\)\)\)/
179
+ sqls.length.should == 2
180
+ end
181
+
182
+ specify "should set associated right-side cpks correctly for left-side cpks for a many_to_many association" do
183
+ @Vocalist.many_to_many :hits, :class=>@Hit, :join_table=>:vocalists_hits, :left_key=>[:first, :last], :right_key=>[:year, :week]
184
+ @Vocalist.load(:first=>'F2', :last=>'L2').hit_pks = [[1997, 1], [1997, 2]]
185
+ sqls = @db.sqls
186
+ sqls[0].should == "DELETE FROM vocalists_hits WHERE ((first = 'F2') AND (last = 'L2') AND ((year, week) NOT IN ((1997, 1), (1997, 2))))"
187
+ sqls[1].should == "SELECT year, week FROM vocalists_hits WHERE ((first = 'F2') AND (last = 'L2'))"
188
+ match = sqls[2].match(/INSERT INTO vocalists_hits \((.*)\) VALUES \((.*)\)/)
189
+ Hash[match[1].split(', ').zip(match[2].split(', '))].should == {"first"=>"'F2'", "last"=>"'L2'", "year"=>"1997", "week"=>"1"}
190
+ sqls.length.should == 3
64
191
  end
65
192
 
66
193
  specify "should use transactions if the object is configured to use transactions" do
@@ -71,15 +198,82 @@ describe "Sequel::Plugins::AssociationPks" do
71
198
  "UPDATE albums SET artist_id = 1 WHERE (id IN (1, 2))",
72
199
  "UPDATE albums SET artist_id = NULL WHERE ((albums.artist_id = 1) AND (id NOT IN (1, 2)))",
73
200
  "COMMIT"]
74
- @db.reset
75
201
 
76
202
  album = @Album.load(:id=>2)
77
203
  album.use_transactions = true
78
204
  album.tag_pks = [1, 3]
79
- @db.sqls[0].should == "BEGIN"
80
- @db.sqls[1].should == "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))"
81
- @db.sqls[2].should =~ /INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, 1|1, 2)\)/
82
- @db.sqls[3].should == "COMMIT"
205
+ sqls = @db.sqls
206
+ sqls[0].should == "BEGIN"
207
+ sqls[1].should == "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))"
208
+ sqls[2].should == 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)'
209
+ sqls[3].should =~ /INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, 1|1, 2)\)/
210
+ sqls[4].should == "COMMIT"
211
+ sqls.length.should == 5
212
+ end
213
+
214
+ specify "should automatically convert keys to numbers if the primary key is an integer for one_to_many associations" do
215
+ @Album.db_schema[:id][:type] = :integer
216
+ @Artist.load(:id=>1).album_pks = %w'1 2'
217
+ @db.sqls.should == ["UPDATE albums SET artist_id = 1 WHERE (id IN (1, 2))",
218
+ "UPDATE albums SET artist_id = NULL WHERE ((albums.artist_id = 1) AND (id NOT IN (1, 2)))"]
219
+ end
220
+
221
+ specify "should not automatically convert keys if the primary key is not an integer for one_to_many associations" do
222
+ @Album.db_schema[:id][:type] = :string
223
+ @Artist.load(:id=>1).album_pks = %w'1 2'
224
+ @db.sqls.should == ["UPDATE albums SET artist_id = 1 WHERE (id IN ('1', '2'))",
225
+ "UPDATE albums SET artist_id = NULL WHERE ((albums.artist_id = 1) AND (id NOT IN ('1', '2')))"]
226
+ end
227
+
228
+ specify "should automatically convert keys to numbers if the primary key is an integer for many_to_many associations" do
229
+ @Tag.db_schema[:id][:type] = :integer
230
+ @Album.load(:id=>2).tag_pks = %w'1 3'
231
+ sqls = @db.sqls
232
+ sqls[0].should == "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN (1, 3)))"
233
+ sqls[1].should == 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)'
234
+ sqls[2].should =~ /INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, 1|1, 2)\)/
235
+ sqls.length.should == 3
83
236
  end
84
237
 
238
+ specify "should not automatically convert keys to numbers if the primary key is an integer for many_to_many associations" do
239
+ @Tag.db_schema[:id][:type] = :string
240
+ @Album.load(:id=>2).tag_pks = %w'1 3'
241
+ sqls = @db.sqls
242
+ sqls[0].should == "DELETE FROM albums_tags WHERE ((album_id = 2) AND (tag_id NOT IN ('1', '3')))"
243
+ sqls[1].should == 'SELECT tag_id FROM albums_tags WHERE (album_id = 2)'
244
+ sqls[2].should =~ /INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, '1'|'1', 2)\)/
245
+ sqls[3].should =~ /INSERT INTO albums_tags \((album_id, tag_id|tag_id, album_id)\) VALUES \((2, '3'|'3', 2)\)/
246
+ sqls.length.should == 4
247
+ end
248
+
249
+ specify "should automatically convert keys to numbers for appropriate integer primary key for composite key associations" do
250
+ @Hit.db_schema[:year][:type] = :integer
251
+ @Hit.db_schema[:week][:type] = :integer
252
+ @Vocalist.many_to_many :hits, :class=>@Hit, :join_table=>:vocalists_hits, :left_key=>[:first, :last], :right_key=>[:year, :week]
253
+ @Vocalist.load(:first=>'F2', :last=>'L2').hit_pks = [['1997', '1'], ['1997', '2']]
254
+ sqls = @db.sqls
255
+ sqls[0].should == "DELETE FROM vocalists_hits WHERE ((first = 'F2') AND (last = 'L2') AND ((year, week) NOT IN ((1997, 1), (1997, 2))))"
256
+ sqls[1].should == "SELECT year, week FROM vocalists_hits WHERE ((first = 'F2') AND (last = 'L2'))"
257
+ match = sqls[2].match(/INSERT INTO vocalists_hits \((.*)\) VALUES \((.*)\)/)
258
+ Hash[match[1].split(', ').zip(match[2].split(', '))].should == {"first"=>"'F2'", "last"=>"'L2'", "year"=>"1997", "week"=>"1"}
259
+ sqls.length.should == 3
260
+
261
+ @Vocalist.db_schema[:first][:type] = :integer
262
+ @Vocalist.db_schema[:last][:type] = :integer
263
+ @Album.one_to_many :vocalists, :class=>@Vocalist, :key=>:album_id
264
+ @Album.load(:id=>1).vocalist_pks = [["11", "11"], ["12", "12"]]
265
+ @db.sqls.should == ["UPDATE vocalists SET album_id = 1 WHERE ((first, last) IN ((11, 11), (12, 12)))",
266
+ "UPDATE vocalists SET album_id = NULL WHERE ((vocalists.album_id = 1) AND ((first, last) NOT IN ((11, 11), (12, 12))))"]
267
+
268
+ @Album.many_to_many :vocalists, :class=>@Vocalist, :join_table=>:albums_vocalists, :left_key=>:album_id, :right_key=>[:first, :last]
269
+ @Album.load(:id=>2).vocalist_pks = [["11", "11"], ["12", "12"]]
270
+ sqls = @db.sqls
271
+ sqls[0].should == "DELETE FROM albums_vocalists WHERE ((album_id = 2) AND ((first, last) NOT IN ((11, 11), (12, 12))))"
272
+ sqls[1].should == 'SELECT first, last FROM albums_vocalists WHERE (album_id = 2)'
273
+ match = sqls[2].match(/INSERT INTO albums_vocalists \((.*)\) VALUES \((.*)\)/)
274
+ Hash[match[1].split(', ').zip(match[2].split(', '))].should == {"first"=>"11", "last"=>"11", "album_id"=>"2"}
275
+ match = sqls[3].match(/INSERT INTO albums_vocalists \((.*)\) VALUES \((.*)\)/)
276
+ Hash[match[1].split(', ').zip(match[2].split(', '))].should == {"first"=>"12", "last"=>"12", "album_id"=>"2"}
277
+ sqls.length.should == 4
278
+ end
85
279
  end
@@ -31,13 +31,13 @@ describe "Sequel::Plugins::AssociationProxies" do
31
31
 
32
32
  it "should reload the cached association if sent an array method and the reload flag was given" do
33
33
  @t.select{|x| false}.should == []
34
- Item.db.sqls.length == 1
34
+ Item.db.sqls.length.should == 1
35
35
  @t.select{|x| false}.should == []
36
- Item.db.sqls.length == 1
36
+ Item.db.sqls.length.should == 0
37
37
  @i.tags(true).select{|x| false}.should == []
38
- Item.db.sqls.length == 2
38
+ Item.db.sqls.length.should == 1
39
39
  @t.filter(:a=>1).sql.should == "SELECT tags.* FROM tags INNER JOIN items_tags ON ((items_tags.tag_id = tags.id) AND (items_tags.item_id = 1)) WHERE (a = 1)"
40
- Item.db.sqls.length == 2
40
+ Item.db.sqls.length.should == 0
41
41
  end
42
42
 
43
43
  it "should not return a proxy object for associations that do not return an array" do