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
@@ -15,20 +15,34 @@ module Sequel
15
15
  # sc2.subclasses # []
16
16
  # ssc1.subclasses # []
17
17
  # c.descendents # [sc1, ssc1, sc2]
18
+ #
19
+ # You can provide a block when loading the plugin, and it will be called
20
+ # with each subclass created:
21
+ #
22
+ # a = []
23
+ # Sequel::Model.plugin(:subclasses){|sc| a << sc}
24
+ # class A < Sequel::Model; end
25
+ # class B < Sequel::Model; end
26
+ # a # => [A, B]
18
27
  module Subclasses
19
28
  # Initialize the subclasses instance variable for the model.
20
- def self.apply(model)
29
+ def self.apply(model, &block)
21
30
  model.instance_variable_set(:@subclasses, [])
31
+ model.instance_variable_set(:@on_subclass, block)
22
32
  end
23
33
 
24
34
  module ClassMethods
35
+ # Callable object that should be called with every descendent
36
+ # class created.
37
+ attr_reader :on_subclass
38
+
25
39
  # All subclasses for the current model. Does not
26
40
  # include the model itself.
27
41
  attr_reader :subclasses
28
42
 
29
43
  # All descendent classes of this model.
30
44
  def descendents
31
- subclasses.map{|x| [x] + x.descendents}.flatten
45
+ Sequel.synchronize{_descendents}
32
46
  end
33
47
 
34
48
  # Add the subclass to this model's current subclasses,
@@ -36,8 +50,20 @@ module Sequel
36
50
  # in the subclass.
37
51
  def inherited(subclass)
38
52
  super
39
- subclasses << subclass
53
+ Sequel.synchronize{subclasses << subclass}
40
54
  subclass.instance_variable_set(:@subclasses, [])
55
+ if on_subclass
56
+ subclass.instance_variable_set(:@on_subclass, on_subclass)
57
+ on_subclass.call(subclass)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ # Recursive, non-thread safe version of descendents, since
64
+ # the mutex Sequel uses isn't reentrant.
65
+ def _descendents
66
+ subclasses.map{|x| [x] + x.send(:_descendents)}.flatten
41
67
  end
42
68
  end
43
69
  end
@@ -28,19 +28,18 @@ module Sequel
28
28
  module TacticalEagerLoading
29
29
  module InstanceMethods
30
30
  # The dataset that retrieved this object, set if the object was
31
- # reteived via Dataset#all with an active identity map.
31
+ # reteived via Dataset#all.
32
32
  attr_accessor :retrieved_by
33
33
 
34
34
  # All model objects retrieved with this object, set if the object was
35
- # reteived via Dataset#all with an active identity map.
35
+ # reteived via Dataset#all.
36
36
  attr_accessor :retrieved_with
37
37
 
38
38
  private
39
39
 
40
- # If there is an active identity map and the association is not in the
41
- # associations cache and the object was reteived via Dataset#all,
42
- # eagerly load the association for all model objects retrieved with the
43
- # current object.
40
+ # If there the association is not in the associations cache and the object
41
+ # was reteived via Dataset#all, eagerly load the association for all model
42
+ # objects retrieved with the current object.
44
43
  def load_associated_objects(opts, reload=false)
45
44
  name = opts[:name]
46
45
  if !associations.include?(name) && retrieved_by
@@ -51,6 +50,7 @@ module Sequel
51
50
  # is only defined in a subclass. This particular instance can use the
52
51
  # association, but it can't be eagerly loaded as the parent class doesn't
53
52
  # have access to the association, and that's the class doing the eager loading.
53
+ nil
54
54
  end
55
55
  end
56
56
  super
@@ -60,7 +60,7 @@ module Sequel
60
60
  module DatasetMethods
61
61
  private
62
62
 
63
- # If there is an active identity map, set the reteived_with attribute for the object
63
+ # Set the reteived_with and retrieved_by attributes for the object
64
64
  # with the current dataset and array of all objects.
65
65
  def post_load(objects)
66
66
  super
@@ -19,7 +19,7 @@ module Sequel
19
19
  # # timestamp, and setting the update timestamp when creating
20
20
  # Album.plugin :timestamps, :force=>true, :update_on_create=>true
21
21
  module Timestamps
22
- # Configure the plugin by setting the avialable options. Note that
22
+ # Configure the plugin by setting the available options. Note that
23
23
  # if this method is run more than once, previous settings are ignored,
24
24
  # and it will just use the settings given or the default settings. Options:
25
25
  # * :create - The field to hold the create timestamp (default: :created_at)
@@ -52,7 +52,7 @@ module Sequel
52
52
  attr_accessor :touch_column
53
53
 
54
54
  # A hash specifying the associations to touch when instances are
55
- # updated or destroyed. Keys are association dataset method name symbols and values
55
+ # updated or destroyed. Keys are association name symbols and values
56
56
  # are column name symbols.
57
57
  attr_reader :touched_associations
58
58
 
@@ -71,8 +71,8 @@ module Sequel
71
71
  associations.flatten.each do |a|
72
72
  a = {a=>touch_column} if a.is_a?(Symbol)
73
73
  a.each do |k,v|
74
- raise(Error, "invalid association: #{k}") unless r = association_reflection(k)
75
- touched_associations[r.dataset_method] = v
74
+ raise(Error, "invalid association: #{k}") unless association_reflection(k)
75
+ touched_associations[k] = v
76
76
  end
77
77
  end
78
78
  end
@@ -113,10 +113,22 @@ module Sequel
113
113
  Sequel::CURRENT_TIMESTAMP
114
114
  end
115
115
 
116
- # Directly update the database using the association dataset for each association.
116
+ # Update the updated at field for all associated objects that should be touched.
117
117
  def touch_associations
118
- model.touched_associations.each do |meth, column|
119
- send(meth).update(column=>touch_association_value)
118
+ model.touched_associations.each do |assoc, column|
119
+ r = model.association_reflection(assoc)
120
+ next unless r.can_have_associated_objects?(self)
121
+ ds = send(r.dataset_method)
122
+
123
+ if ds.send(:joined_dataset?)
124
+ # Can't update all values at once, so update each instance individually.
125
+ # Instead if doing a simple save, update via the instance's dataset,
126
+ # to avoid going into an infinite loop in some cases.
127
+ send(r[:name]).each{|x| x.this.update(column=>touch_association_value)}
128
+ else
129
+ # Update all values at once for performance reasons.
130
+ ds.update(column=>touch_association_value)
131
+ end
120
132
  end
121
133
  end
122
134
 
@@ -4,7 +4,7 @@ module Sequel
4
4
  # treat a Model as a tree.
5
5
  #
6
6
  # A column for holding the parent key is required and is :parent_id by default.
7
- # This may be overridden by passing column name via :key
7
+ # This may be overridden by passing column name via :key.
8
8
  #
9
9
  # Optionally, a column to control order of nodes returned can be specified
10
10
  # by passing column name via :order.
@@ -98,7 +98,7 @@ module Sequel
98
98
  nodes
99
99
  end
100
100
 
101
- # Returns the root node of the tree that this node descends from
101
+ # Returns the root node of the tree that this node descends from.
102
102
  # This node is returned if it is a root node itself.
103
103
  def root
104
104
  ancestors.last || self
@@ -124,7 +124,7 @@ module Sequel
124
124
  end
125
125
  end
126
126
 
127
- # Plugin included when :single_root option is passed
127
+ # Plugin included when :single_root option is passed.
128
128
  module SingleRoot
129
129
  module ClassMethods
130
130
  # Returns the single root node.
@@ -5,7 +5,7 @@ module Sequel
5
5
  # typecast correctly (with correct being defined as how the model object
6
6
  # would typecast the same column values).
7
7
  #
8
- # This plugin modifies Model.load to call the setter methods (which typecast
8
+ # This plugin modifies Model#set_values to call the setter methods (which typecast
9
9
  # by default) for all columns given. You can either specify the columns to
10
10
  # typecast on load in the plugin call itself, or afterwards using
11
11
  # add_typecast_on_load_columns:
@@ -41,16 +41,11 @@ module Sequel
41
41
  super
42
42
  subclass.instance_variable_set(:@typecast_on_load_columns, typecast_on_load_columns.dup)
43
43
  end
44
-
45
- # Call the setter method for each of the typecast on load columns,
46
- # ensuring the model object will have the correct typecasting even
47
- # if the database doesn't typecast the columns correctly.
48
- def load(values)
49
- super.load_typecast
50
- end
51
44
  end
52
45
 
53
46
  module InstanceMethods
47
+ # Call the setter method for each of the model's typecast_on_load_columns
48
+ # with the current value, so it can be typecasted correctly.
54
49
  def load_typecast
55
50
  model.typecast_on_load_columns.each do |c|
56
51
  if v = values[c]
@@ -61,10 +56,12 @@ module Sequel
61
56
  self
62
57
  end
63
58
 
64
- private
65
-
66
- def _refresh(dataset)
67
- super.load_typecast
59
+ # Typecast values using #load_typecast when the values are retrieved from
60
+ # the database.
61
+ def set_values(values)
62
+ ret = super
63
+ load_typecast
64
+ ret
68
65
  end
69
66
  end
70
67
  end
@@ -1,6 +1,6 @@
1
1
  module Sequel
2
2
  module Plugins
3
- # The update_primary_key plugin allows you to modify an objects
3
+ # The update_primary_key plugin allows you to modify an object's
4
4
  # primary key and then save the record. Sequel does not work
5
5
  # correctly with primary key modifications by default. Sequel
6
6
  # is designed to work with surrogate primary keys that never need to be
@@ -22,7 +22,7 @@ module Sequel
22
22
  module UpdatePrimaryKey
23
23
  module ClassMethods
24
24
  # Cache the pk_hash when loading records
25
- def load(h)
25
+ def call(h)
26
26
  r = super(h)
27
27
  r.pk_hash
28
28
  r
@@ -60,7 +60,6 @@ module Sequel
60
60
 
61
61
  # Setup the validations and validation_reflections hash in the subclass.
62
62
  def inherited(subclass)
63
- super
64
63
  vr = @validation_reflections
65
64
  subclass.class_eval do
66
65
  @validation_mutex = Mutex.new
@@ -69,6 +68,7 @@ module Sequel
69
68
  vr.each{|k,v| h[k] = v.dup}
70
69
  @validation_reflections = h
71
70
  end
71
+ super
72
72
  end
73
73
 
74
74
  # Instructs the model to skip validations defined in superclasses
@@ -385,6 +385,7 @@ module Sequel
385
385
  error_field = a
386
386
  a = Array(a)
387
387
  v = Array(v)
388
+ next if v.empty? || !v.all?
388
389
  ds = o.class.filter(a.zip(v))
389
390
  num_dups = ds.count
390
391
  allow = if num_dups == 0
@@ -40,6 +40,37 @@ module Sequel
40
40
  # changing the values of the Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS hash. You
41
41
  # change change the default options on a per model basis
42
42
  # by overriding a private instance method default_validation_helpers_options.
43
+ #
44
+ # By changing the default options, you can setup internationalization of the
45
+ # error messages. For example, you would modify the default options:
46
+ #
47
+ # Sequel::Plugins::ValidationHelpers::DEFAULT_OPTIONS.merge!(
48
+ # :exact_length=>{:message=>lambda{|exact| I18n.t("errors.exact_length", :exact => exact)}},
49
+ # :integer=>{:message=>lambda{I18n.t("errors.integer")}},
50
+ # ...
51
+ # )
52
+ #
53
+ # and then use something like this in your yaml translation file:
54
+ #
55
+ # en:
56
+ # errors:
57
+ # exact_length: "is not %{exact} characters"
58
+ # integer: "is not a number"
59
+ #
60
+ # Note that if you want to support internationalization of Errors#full_messages,
61
+ # you need to override the method. Here's an example:
62
+ #
63
+ # class Sequel::Model::Errors
64
+ # ATTRIBUTE_JOINER = I18n.t('errors.joiner').freeze
65
+ # def full_messages
66
+ # inject([]) do |m, kv|
67
+ # att, errors = *kv
68
+ # att.is_a?(Array) ? Array(att).map!{|v| I18n.t("attributes.#{v}")} : att = I18n.t("attributes.#{att}")
69
+ # errors.each {|e| m << (e.is_a?(LiteralString) ? e : "#{Array(att).join(ATTRIBUTE_JOINER)} #{e}")}
70
+ # m
71
+ # end
72
+ # end
73
+ # end
43
74
  module ValidationHelpers
44
75
  # Default validation options used by Sequel. Can be modified to change the error
45
76
  # messages for all models (e.g. for internationalization), or to set certain
@@ -162,19 +193,41 @@ module Sequel
162
193
  # since it can deal with a grouping of multiple attributes.
163
194
  #
164
195
  # Possible Options:
165
- # * :message - The message to use (default: 'is already taken')
166
- # * :only_if_modified - Only check the uniqueness if the object is new or
167
- # one of the columns has been modified.
196
+ # :message :: The message to use (default: 'is already taken')
197
+ # :only_if_modified :: Only check the uniqueness if the object is new or
198
+ # one of the columns has been modified.
199
+ # :where :: A callable object where call takes three arguments, a dataset,
200
+ # the current object, and an array of columns, and should return
201
+ # a modified dataset that is filtered to include only rows with
202
+ # the same values as the current object for each column in the array.
203
+ #
204
+ # If you want to to a case insensitive uniqueness validation on a database that
205
+ # is case sensitive by default, you can use:
206
+ #
207
+ # :where=>(proc do |ds, obj, cols|
208
+ # ds.where(cols.map do |c|
209
+ # v = obj.send(c)
210
+ # v = v.downcase if v
211
+ # [Sequel.function(:lower, c), v]
212
+ # end)
213
+ # end)
168
214
  def validates_unique(*atts)
169
215
  opts = default_validation_helpers_options(:unique)
170
216
  if atts.last.is_a?(Hash)
171
217
  opts = opts.merge(atts.pop)
172
218
  end
173
219
  message = validation_error_message(opts[:message])
220
+ where = opts[:where]
174
221
  atts.each do |a|
175
222
  arr = Array(a)
176
223
  next if opts[:only_if_modified] && !new? && !arr.any?{|x| changed_columns.include?(x)}
177
- ds = model.filter(arr.map{|x| [x, send(x)]})
224
+ ds = if where
225
+ where.call(model.dataset, self, arr)
226
+ else
227
+ vals = arr.map{|x| send(x)}
228
+ next unless vals.all?
229
+ model.where(arr.zip(vals))
230
+ end
178
231
  ds = yield(ds) if block_given?
179
232
  ds = ds.exclude(pk_hash) unless new?
180
233
  errors.add(a, message) unless ds.count == 0
@@ -79,6 +79,11 @@ module Sequel
79
79
  #
80
80
  # Album.array_from_xml(Album.to_xml) # same as Album.all
81
81
  #
82
+ # If you have an existing array of model instances you want to convert to
83
+ # XML, you can call the class to_xml method with the :array option:
84
+ #
85
+ # Album.to_xml(:array=>[Album[1], Album[2]])
86
+ #
82
87
  # Usage:
83
88
  #
84
89
  # # Add XML output capability to all model subclass instances (called before loading subclasses)
@@ -205,9 +210,9 @@ module Sequel
205
210
  klass.from_xml_node(node)
206
211
  end
207
212
  elsif cols.include?(k)
208
- self[k.to_sym] = node[:nil] ? nil : node.children.first.to_s
213
+ self[k.to_sym] = node.key?('nil') ? nil : node.children.first.to_s
209
214
  elsif meths.include?("#{k}=")
210
- send("#{k}=", node[:nil] ? nil : node.children.first.to_s)
215
+ send("#{k}=", node.key?('nil') ? nil : node.children.first.to_s)
211
216
  else
212
217
  raise Error, "Entry in XML not an association or column and no setter method exists: #{k}"
213
218
  end
@@ -266,7 +271,7 @@ module Sequel
266
271
 
267
272
  name_proc = model.xml_serialize_name_proc(opts)
268
273
  x = model.xml_builder(opts)
269
- x.send(name_proc[opts.fetch(:root_name, model.send(:underscore, model.name)).to_s]) do |x1|
274
+ x.send(name_proc[opts.fetch(:root_name, model.send(:underscore, model.name).gsub('/', '__')).to_s]) do |x1|
270
275
  cols.each do |c|
271
276
  attrs = {}
272
277
  if types
@@ -316,8 +321,14 @@ module Sequel
316
321
  raise(Sequel::Error, "Dataset#to_xml") unless row_proc
317
322
  x = model.xml_builder(opts)
318
323
  name_proc = model.xml_serialize_name_proc(opts)
324
+ array = if opts[:array]
325
+ opts = opts.dup
326
+ opts.delete(:array)
327
+ else
328
+ all
329
+ end
319
330
  x.send(name_proc[opts.fetch(:array_root_name, model.send(:pluralize, model.send(:underscore, model.name))).to_s]) do |x1|
320
- all.each do |obj|
331
+ array.each do |obj|
321
332
  obj.to_xml(opts.merge(:builder=>x1))
322
333
  end
323
334
  end