sequel 4.26.0 → 5.37.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 (692) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG +405 -5656
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +232 -157
  5. data/bin/sequel +32 -9
  6. data/doc/advanced_associations.rdoc +252 -188
  7. data/doc/association_basics.rdoc +231 -273
  8. data/doc/bin_sequel.rdoc +5 -3
  9. data/doc/cheat_sheet.rdoc +75 -48
  10. data/doc/code_order.rdoc +28 -10
  11. data/doc/core_extensions.rdoc +104 -63
  12. data/doc/dataset_basics.rdoc +12 -21
  13. data/doc/dataset_filtering.rdoc +99 -86
  14. data/doc/extensions.rdoc +3 -10
  15. data/doc/mass_assignment.rdoc +74 -31
  16. data/doc/migration.rdoc +72 -46
  17. data/doc/model_dataset_method_design.rdoc +129 -0
  18. data/doc/model_hooks.rdoc +15 -25
  19. data/doc/model_plugins.rdoc +12 -12
  20. data/doc/mssql_stored_procedures.rdoc +3 -3
  21. data/doc/object_model.rdoc +59 -69
  22. data/doc/opening_databases.rdoc +84 -94
  23. data/doc/postgresql.rdoc +268 -38
  24. data/doc/prepared_statements.rdoc +29 -24
  25. data/doc/querying.rdoc +184 -164
  26. data/doc/reflection.rdoc +5 -6
  27. data/doc/release_notes/5.0.0.txt +159 -0
  28. data/doc/release_notes/5.1.0.txt +31 -0
  29. data/doc/release_notes/5.10.0.txt +84 -0
  30. data/doc/release_notes/5.11.0.txt +83 -0
  31. data/doc/release_notes/5.12.0.txt +141 -0
  32. data/doc/release_notes/5.13.0.txt +27 -0
  33. data/doc/release_notes/5.14.0.txt +63 -0
  34. data/doc/release_notes/5.15.0.txt +39 -0
  35. data/doc/release_notes/5.16.0.txt +110 -0
  36. data/doc/release_notes/5.17.0.txt +31 -0
  37. data/doc/release_notes/5.18.0.txt +69 -0
  38. data/doc/release_notes/5.19.0.txt +28 -0
  39. data/doc/release_notes/5.2.0.txt +33 -0
  40. data/doc/release_notes/5.20.0.txt +89 -0
  41. data/doc/release_notes/5.21.0.txt +87 -0
  42. data/doc/release_notes/5.22.0.txt +48 -0
  43. data/doc/release_notes/5.23.0.txt +56 -0
  44. data/doc/release_notes/5.24.0.txt +56 -0
  45. data/doc/release_notes/5.25.0.txt +32 -0
  46. data/doc/release_notes/5.26.0.txt +35 -0
  47. data/doc/release_notes/5.27.0.txt +21 -0
  48. data/doc/release_notes/5.28.0.txt +16 -0
  49. data/doc/release_notes/5.29.0.txt +22 -0
  50. data/doc/release_notes/5.3.0.txt +121 -0
  51. data/doc/release_notes/5.30.0.txt +20 -0
  52. data/doc/release_notes/5.31.0.txt +148 -0
  53. data/doc/release_notes/5.32.0.txt +46 -0
  54. data/doc/release_notes/5.33.0.txt +24 -0
  55. data/doc/release_notes/5.34.0.txt +40 -0
  56. data/doc/release_notes/5.35.0.txt +56 -0
  57. data/doc/release_notes/5.36.0.txt +60 -0
  58. data/doc/release_notes/5.37.0.txt +30 -0
  59. data/doc/release_notes/5.4.0.txt +80 -0
  60. data/doc/release_notes/5.5.0.txt +61 -0
  61. data/doc/release_notes/5.6.0.txt +31 -0
  62. data/doc/release_notes/5.7.0.txt +108 -0
  63. data/doc/release_notes/5.8.0.txt +170 -0
  64. data/doc/release_notes/5.9.0.txt +99 -0
  65. data/doc/schema_modification.rdoc +102 -77
  66. data/doc/security.rdoc +160 -87
  67. data/doc/sharding.rdoc +74 -47
  68. data/doc/sql.rdoc +135 -122
  69. data/doc/testing.rdoc +34 -18
  70. data/doc/thread_safety.rdoc +2 -4
  71. data/doc/transactions.rdoc +101 -19
  72. data/doc/validations.rdoc +64 -51
  73. data/doc/virtual_rows.rdoc +90 -109
  74. data/lib/sequel.rb +3 -1
  75. data/lib/sequel/adapters/ado.rb +154 -22
  76. data/lib/sequel/adapters/ado/access.rb +21 -21
  77. data/lib/sequel/adapters/ado/mssql.rb +8 -15
  78. data/lib/sequel/adapters/amalgalite.rb +17 -25
  79. data/lib/sequel/adapters/ibmdb.rb +52 -58
  80. data/lib/sequel/adapters/jdbc.rb +149 -127
  81. data/lib/sequel/adapters/jdbc/db2.rb +32 -40
  82. data/lib/sequel/adapters/jdbc/derby.rb +56 -58
  83. data/lib/sequel/adapters/jdbc/h2.rb +40 -30
  84. data/lib/sequel/adapters/jdbc/hsqldb.rb +22 -33
  85. data/lib/sequel/adapters/jdbc/jtds.rb +4 -10
  86. data/lib/sequel/adapters/jdbc/mssql.rb +6 -12
  87. data/lib/sequel/adapters/jdbc/mysql.rb +17 -18
  88. data/lib/sequel/adapters/jdbc/oracle.rb +25 -19
  89. data/lib/sequel/adapters/jdbc/postgresql.rb +90 -69
  90. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +14 -24
  91. data/lib/sequel/adapters/jdbc/sqlite.rb +50 -12
  92. data/lib/sequel/adapters/jdbc/sqlserver.rb +36 -9
  93. data/lib/sequel/adapters/jdbc/transactions.rb +25 -39
  94. data/lib/sequel/adapters/mock.rb +104 -113
  95. data/lib/sequel/adapters/mysql.rb +42 -61
  96. data/lib/sequel/adapters/mysql2.rb +126 -35
  97. data/lib/sequel/adapters/odbc.rb +21 -28
  98. data/lib/sequel/adapters/odbc/db2.rb +3 -1
  99. data/lib/sequel/adapters/odbc/mssql.rb +11 -15
  100. data/lib/sequel/adapters/odbc/oracle.rb +11 -0
  101. data/lib/sequel/adapters/oracle.rb +62 -68
  102. data/lib/sequel/adapters/postgres.rb +257 -311
  103. data/lib/sequel/adapters/postgresql.rb +3 -1
  104. data/lib/sequel/adapters/shared/access.rb +75 -79
  105. data/lib/sequel/adapters/shared/db2.rb +96 -74
  106. data/lib/sequel/adapters/shared/mssql.rb +258 -213
  107. data/lib/sequel/adapters/shared/mysql.rb +284 -216
  108. data/lib/sequel/adapters/shared/oracle.rb +175 -60
  109. data/lib/sequel/adapters/shared/postgres.rb +829 -383
  110. data/lib/sequel/adapters/shared/sqlanywhere.rb +105 -127
  111. data/lib/sequel/adapters/shared/sqlite.rb +382 -159
  112. data/lib/sequel/adapters/sqlanywhere.rb +53 -38
  113. data/lib/sequel/adapters/sqlite.rb +111 -105
  114. data/lib/sequel/adapters/tinytds.rb +38 -46
  115. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +8 -9
  116. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +7 -5
  117. data/lib/sequel/adapters/utils/mysql_mysql2.rb +87 -0
  118. data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +56 -0
  119. data/lib/sequel/adapters/utils/replace.rb +3 -4
  120. data/lib/sequel/adapters/utils/split_alter_table.rb +2 -0
  121. data/lib/sequel/adapters/utils/stored_procedures.rb +9 -22
  122. data/lib/sequel/adapters/utils/unmodified_identifiers.rb +28 -0
  123. data/lib/sequel/ast_transformer.rb +13 -89
  124. data/lib/sequel/connection_pool.rb +54 -26
  125. data/lib/sequel/connection_pool/sharded_single.rb +19 -12
  126. data/lib/sequel/connection_pool/sharded_threaded.rb +160 -111
  127. data/lib/sequel/connection_pool/single.rb +21 -12
  128. data/lib/sequel/connection_pool/threaded.rb +137 -119
  129. data/lib/sequel/core.rb +352 -320
  130. data/lib/sequel/database.rb +19 -2
  131. data/lib/sequel/database/connecting.rb +70 -55
  132. data/lib/sequel/database/dataset.rb +15 -5
  133. data/lib/sequel/database/dataset_defaults.rb +20 -102
  134. data/lib/sequel/database/features.rb +20 -4
  135. data/lib/sequel/database/logging.rb +25 -7
  136. data/lib/sequel/database/misc.rb +132 -118
  137. data/lib/sequel/database/query.rb +51 -28
  138. data/lib/sequel/database/schema_generator.rb +188 -75
  139. data/lib/sequel/database/schema_methods.rb +161 -92
  140. data/lib/sequel/database/transactions.rb +260 -58
  141. data/lib/sequel/dataset.rb +28 -12
  142. data/lib/sequel/dataset/actions.rb +354 -170
  143. data/lib/sequel/dataset/dataset_module.rb +46 -0
  144. data/lib/sequel/dataset/features.rb +81 -34
  145. data/lib/sequel/dataset/graph.rb +82 -58
  146. data/lib/sequel/dataset/misc.rb +139 -47
  147. data/lib/sequel/dataset/placeholder_literalizer.rb +66 -26
  148. data/lib/sequel/dataset/prepared_statements.rb +188 -85
  149. data/lib/sequel/dataset/query.rb +428 -214
  150. data/lib/sequel/dataset/sql.rb +446 -339
  151. data/lib/sequel/deprecated.rb +14 -2
  152. data/lib/sequel/exceptions.rb +48 -16
  153. data/lib/sequel/extensions/_model_constraint_validations.rb +16 -0
  154. data/lib/sequel/extensions/_model_pg_row.rb +43 -0
  155. data/lib/sequel/extensions/_pretty_table.rb +10 -9
  156. data/lib/sequel/extensions/any_not_empty.rb +45 -0
  157. data/lib/sequel/extensions/arbitrary_servers.rb +15 -11
  158. data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
  159. data/lib/sequel/extensions/blank.rb +2 -0
  160. data/lib/sequel/extensions/caller_logging.rb +79 -0
  161. data/lib/sequel/extensions/columns_introspection.rb +9 -4
  162. data/lib/sequel/extensions/connection_expiration.rb +99 -0
  163. data/lib/sequel/extensions/connection_validator.rb +26 -13
  164. data/lib/sequel/extensions/constant_sql_override.rb +65 -0
  165. data/lib/sequel/extensions/constraint_validations.rb +93 -38
  166. data/lib/sequel/extensions/core_extensions.rb +45 -53
  167. data/lib/sequel/extensions/core_refinements.rb +44 -46
  168. data/lib/sequel/extensions/current_datetime_timestamp.rb +5 -4
  169. data/lib/sequel/extensions/dataset_source_alias.rb +4 -0
  170. data/lib/sequel/extensions/date_arithmetic.rb +42 -16
  171. data/lib/sequel/extensions/datetime_parse_to_time.rb +37 -0
  172. data/lib/sequel/extensions/duplicate_columns_handler.rb +94 -0
  173. data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -3
  174. data/lib/sequel/extensions/error_sql.rb +7 -3
  175. data/lib/sequel/extensions/escaped_like.rb +100 -0
  176. data/lib/sequel/extensions/eval_inspect.rb +14 -15
  177. data/lib/sequel/extensions/exclude_or_null.rb +68 -0
  178. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  179. data/lib/sequel/extensions/freeze_datasets.rb +3 -0
  180. data/lib/sequel/extensions/from_block.rb +2 -31
  181. data/lib/sequel/extensions/graph_each.rb +19 -6
  182. data/lib/sequel/extensions/identifier_mangling.rb +180 -0
  183. data/lib/sequel/extensions/implicit_subquery.rb +48 -0
  184. data/lib/sequel/extensions/index_caching.rb +109 -0
  185. data/lib/sequel/extensions/inflector.rb +8 -4
  186. data/lib/sequel/extensions/integer64.rb +32 -0
  187. data/lib/sequel/extensions/looser_typecasting.rb +19 -9
  188. data/lib/sequel/extensions/migration.rb +132 -80
  189. data/lib/sequel/extensions/mssql_emulate_lateral_with_apply.rb +4 -0
  190. data/lib/sequel/extensions/named_timezones.rb +88 -23
  191. data/lib/sequel/extensions/no_auto_literal_strings.rb +4 -0
  192. data/lib/sequel/extensions/null_dataset.rb +12 -8
  193. data/lib/sequel/extensions/pagination.rb +35 -28
  194. data/lib/sequel/extensions/pg_array.rb +227 -316
  195. data/lib/sequel/extensions/pg_array_ops.rb +19 -7
  196. data/lib/sequel/extensions/pg_enum.rb +69 -24
  197. data/lib/sequel/extensions/pg_extended_date_support.rb +250 -0
  198. data/lib/sequel/extensions/pg_hstore.rb +50 -59
  199. data/lib/sequel/extensions/pg_hstore_ops.rb +9 -3
  200. data/lib/sequel/extensions/pg_inet.rb +34 -15
  201. data/lib/sequel/extensions/pg_inet_ops.rb +5 -1
  202. data/lib/sequel/extensions/pg_interval.rb +26 -26
  203. data/lib/sequel/extensions/pg_json.rb +422 -141
  204. data/lib/sequel/extensions/pg_json_ops.rb +248 -9
  205. data/lib/sequel/extensions/pg_loose_count.rb +5 -1
  206. data/lib/sequel/extensions/pg_range.rb +162 -146
  207. data/lib/sequel/extensions/pg_range_ops.rb +10 -5
  208. data/lib/sequel/extensions/pg_row.rb +53 -87
  209. data/lib/sequel/extensions/pg_row_ops.rb +36 -13
  210. data/lib/sequel/extensions/pg_static_cache_updater.rb +6 -2
  211. data/lib/sequel/extensions/pg_timestamptz.rb +28 -0
  212. data/lib/sequel/extensions/pretty_table.rb +4 -0
  213. data/lib/sequel/extensions/query.rb +12 -7
  214. data/lib/sequel/extensions/round_timestamps.rb +6 -9
  215. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  216. data/lib/sequel/extensions/s.rb +59 -0
  217. data/lib/sequel/extensions/schema_caching.rb +14 -1
  218. data/lib/sequel/extensions/schema_dumper.rb +83 -55
  219. data/lib/sequel/extensions/select_remove.rb +8 -4
  220. data/lib/sequel/extensions/sequel_4_dataset_methods.rb +85 -0
  221. data/lib/sequel/extensions/server_block.rb +50 -17
  222. data/lib/sequel/extensions/server_logging.rb +61 -0
  223. data/lib/sequel/extensions/split_array_nil.rb +8 -4
  224. data/lib/sequel/extensions/sql_comments.rb +96 -0
  225. data/lib/sequel/extensions/sql_expr.rb +4 -1
  226. data/lib/sequel/extensions/string_agg.rb +181 -0
  227. data/lib/sequel/extensions/string_date_time.rb +2 -0
  228. data/lib/sequel/extensions/symbol_aref.rb +53 -0
  229. data/lib/sequel/extensions/symbol_aref_refinement.rb +43 -0
  230. data/lib/sequel/extensions/symbol_as.rb +23 -0
  231. data/lib/sequel/extensions/symbol_as_refinement.rb +37 -0
  232. data/lib/sequel/extensions/synchronize_sql.rb +45 -0
  233. data/lib/sequel/extensions/thread_local_timezones.rb +4 -0
  234. data/lib/sequel/extensions/to_dot.rb +15 -5
  235. data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
  236. data/lib/sequel/model.rb +36 -126
  237. data/lib/sequel/model/associations.rb +850 -257
  238. data/lib/sequel/model/base.rb +652 -764
  239. data/lib/sequel/model/dataset_module.rb +13 -10
  240. data/lib/sequel/model/default_inflections.rb +3 -1
  241. data/lib/sequel/model/errors.rb +3 -3
  242. data/lib/sequel/model/exceptions.rb +12 -12
  243. data/lib/sequel/model/inflections.rb +8 -19
  244. data/lib/sequel/model/plugins.rb +111 -0
  245. data/lib/sequel/plugins/accessed_columns.rb +2 -0
  246. data/lib/sequel/plugins/active_model.rb +32 -7
  247. data/lib/sequel/plugins/after_initialize.rb +3 -1
  248. data/lib/sequel/plugins/association_dependencies.rb +27 -18
  249. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  250. data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
  251. data/lib/sequel/plugins/association_pks.rb +181 -83
  252. data/lib/sequel/plugins/association_proxies.rb +33 -9
  253. data/lib/sequel/plugins/auto_validations.rb +58 -23
  254. data/lib/sequel/plugins/before_after_save.rb +8 -0
  255. data/lib/sequel/plugins/blacklist_security.rb +23 -12
  256. data/lib/sequel/plugins/boolean_readers.rb +9 -6
  257. data/lib/sequel/plugins/boolean_subsets.rb +64 -0
  258. data/lib/sequel/plugins/caching.rb +27 -16
  259. data/lib/sequel/plugins/class_table_inheritance.rb +192 -94
  260. data/lib/sequel/plugins/column_conflicts.rb +18 -3
  261. data/lib/sequel/plugins/column_select.rb +9 -5
  262. data/lib/sequel/plugins/columns_updated.rb +42 -0
  263. data/lib/sequel/plugins/composition.rb +36 -24
  264. data/lib/sequel/plugins/constraint_validations.rb +37 -16
  265. data/lib/sequel/plugins/csv_serializer.rb +58 -35
  266. data/lib/sequel/plugins/dataset_associations.rb +60 -18
  267. data/lib/sequel/plugins/def_dataset_method.rb +90 -0
  268. data/lib/sequel/plugins/defaults_setter.rb +74 -13
  269. data/lib/sequel/plugins/delay_add_association.rb +4 -1
  270. data/lib/sequel/plugins/dirty.rb +65 -24
  271. data/lib/sequel/plugins/eager_each.rb +27 -3
  272. data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
  273. data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
  274. data/lib/sequel/plugins/error_splitter.rb +19 -12
  275. data/lib/sequel/plugins/finder.rb +246 -0
  276. data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
  277. data/lib/sequel/plugins/force_encoding.rb +9 -12
  278. data/lib/sequel/plugins/hook_class_methods.rb +39 -54
  279. data/lib/sequel/plugins/input_transformer.rb +20 -10
  280. data/lib/sequel/plugins/insert_conflict.rb +72 -0
  281. data/lib/sequel/plugins/insert_returning_select.rb +4 -2
  282. data/lib/sequel/plugins/instance_filters.rb +12 -8
  283. data/lib/sequel/plugins/instance_hooks.rb +36 -17
  284. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  285. data/lib/sequel/plugins/inverted_subsets.rb +24 -13
  286. data/lib/sequel/plugins/json_serializer.rb +123 -47
  287. data/lib/sequel/plugins/lazy_attributes.rb +20 -14
  288. data/lib/sequel/plugins/list.rb +40 -26
  289. data/lib/sequel/plugins/many_through_many.rb +28 -12
  290. data/lib/sequel/plugins/modification_detection.rb +17 -5
  291. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -5
  292. data/lib/sequel/plugins/nested_attributes.rb +55 -28
  293. data/lib/sequel/plugins/optimistic_locking.rb +5 -3
  294. data/lib/sequel/plugins/pg_array_associations.rb +52 -18
  295. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +348 -0
  296. data/lib/sequel/plugins/pg_row.rb +7 -51
  297. data/lib/sequel/plugins/prepared_statements.rb +53 -72
  298. data/lib/sequel/plugins/prepared_statements_safe.rb +13 -5
  299. data/lib/sequel/plugins/rcte_tree.rb +43 -63
  300. data/lib/sequel/plugins/serialization.rb +37 -44
  301. data/lib/sequel/plugins/serialization_modification_detection.rb +3 -1
  302. data/lib/sequel/plugins/sharding.rb +17 -10
  303. data/lib/sequel/plugins/single_table_inheritance.rb +62 -28
  304. data/lib/sequel/plugins/singular_table_names.rb +2 -0
  305. data/lib/sequel/plugins/skip_create_refresh.rb +5 -3
  306. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  307. data/lib/sequel/plugins/split_values.rb +13 -6
  308. data/lib/sequel/plugins/static_cache.rb +79 -53
  309. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  310. data/lib/sequel/plugins/string_stripper.rb +5 -3
  311. data/lib/sequel/plugins/subclasses.rb +20 -2
  312. data/lib/sequel/plugins/subset_conditions.rb +48 -0
  313. data/lib/sequel/plugins/table_select.rb +4 -2
  314. data/lib/sequel/plugins/tactical_eager_loading.rb +120 -6
  315. data/lib/sequel/plugins/throw_failures.rb +110 -0
  316. data/lib/sequel/plugins/timestamps.rb +22 -8
  317. data/lib/sequel/plugins/touch.rb +21 -8
  318. data/lib/sequel/plugins/tree.rb +57 -30
  319. data/lib/sequel/plugins/typecast_on_load.rb +14 -4
  320. data/lib/sequel/plugins/unlimited_update.rb +3 -7
  321. data/lib/sequel/plugins/update_or_create.rb +6 -4
  322. data/lib/sequel/plugins/update_primary_key.rb +3 -1
  323. data/lib/sequel/plugins/update_refresh.rb +28 -15
  324. data/lib/sequel/plugins/uuid.rb +70 -0
  325. data/lib/sequel/plugins/validate_associated.rb +20 -0
  326. data/lib/sequel/plugins/validation_class_methods.rb +40 -19
  327. data/lib/sequel/plugins/validation_contexts.rb +49 -0
  328. data/lib/sequel/plugins/validation_helpers.rb +49 -31
  329. data/lib/sequel/plugins/whitelist_security.rb +122 -0
  330. data/lib/sequel/plugins/xml_serializer.rb +31 -30
  331. data/lib/sequel/sql.rb +479 -329
  332. data/lib/sequel/timezones.rb +62 -32
  333. data/lib/sequel/version.rb +10 -3
  334. metadata +177 -477
  335. data/Rakefile +0 -165
  336. data/doc/active_record.rdoc +0 -912
  337. data/doc/release_notes/1.0.txt +0 -38
  338. data/doc/release_notes/1.1.txt +0 -143
  339. data/doc/release_notes/1.3.txt +0 -101
  340. data/doc/release_notes/1.4.0.txt +0 -53
  341. data/doc/release_notes/1.5.0.txt +0 -155
  342. data/doc/release_notes/2.0.0.txt +0 -298
  343. data/doc/release_notes/2.1.0.txt +0 -271
  344. data/doc/release_notes/2.10.0.txt +0 -328
  345. data/doc/release_notes/2.11.0.txt +0 -215
  346. data/doc/release_notes/2.12.0.txt +0 -534
  347. data/doc/release_notes/2.2.0.txt +0 -253
  348. data/doc/release_notes/2.3.0.txt +0 -88
  349. data/doc/release_notes/2.4.0.txt +0 -106
  350. data/doc/release_notes/2.5.0.txt +0 -137
  351. data/doc/release_notes/2.6.0.txt +0 -157
  352. data/doc/release_notes/2.7.0.txt +0 -166
  353. data/doc/release_notes/2.8.0.txt +0 -171
  354. data/doc/release_notes/2.9.0.txt +0 -97
  355. data/doc/release_notes/3.0.0.txt +0 -221
  356. data/doc/release_notes/3.1.0.txt +0 -406
  357. data/doc/release_notes/3.10.0.txt +0 -286
  358. data/doc/release_notes/3.11.0.txt +0 -254
  359. data/doc/release_notes/3.12.0.txt +0 -304
  360. data/doc/release_notes/3.13.0.txt +0 -210
  361. data/doc/release_notes/3.14.0.txt +0 -118
  362. data/doc/release_notes/3.15.0.txt +0 -78
  363. data/doc/release_notes/3.16.0.txt +0 -45
  364. data/doc/release_notes/3.17.0.txt +0 -58
  365. data/doc/release_notes/3.18.0.txt +0 -120
  366. data/doc/release_notes/3.19.0.txt +0 -67
  367. data/doc/release_notes/3.2.0.txt +0 -268
  368. data/doc/release_notes/3.20.0.txt +0 -41
  369. data/doc/release_notes/3.21.0.txt +0 -87
  370. data/doc/release_notes/3.22.0.txt +0 -39
  371. data/doc/release_notes/3.23.0.txt +0 -172
  372. data/doc/release_notes/3.24.0.txt +0 -420
  373. data/doc/release_notes/3.25.0.txt +0 -88
  374. data/doc/release_notes/3.26.0.txt +0 -88
  375. data/doc/release_notes/3.27.0.txt +0 -82
  376. data/doc/release_notes/3.28.0.txt +0 -304
  377. data/doc/release_notes/3.29.0.txt +0 -459
  378. data/doc/release_notes/3.3.0.txt +0 -192
  379. data/doc/release_notes/3.30.0.txt +0 -135
  380. data/doc/release_notes/3.31.0.txt +0 -146
  381. data/doc/release_notes/3.32.0.txt +0 -202
  382. data/doc/release_notes/3.33.0.txt +0 -157
  383. data/doc/release_notes/3.34.0.txt +0 -671
  384. data/doc/release_notes/3.35.0.txt +0 -144
  385. data/doc/release_notes/3.36.0.txt +0 -245
  386. data/doc/release_notes/3.37.0.txt +0 -338
  387. data/doc/release_notes/3.38.0.txt +0 -234
  388. data/doc/release_notes/3.39.0.txt +0 -237
  389. data/doc/release_notes/3.4.0.txt +0 -325
  390. data/doc/release_notes/3.40.0.txt +0 -73
  391. data/doc/release_notes/3.41.0.txt +0 -155
  392. data/doc/release_notes/3.42.0.txt +0 -74
  393. data/doc/release_notes/3.43.0.txt +0 -105
  394. data/doc/release_notes/3.44.0.txt +0 -152
  395. data/doc/release_notes/3.45.0.txt +0 -179
  396. data/doc/release_notes/3.46.0.txt +0 -122
  397. data/doc/release_notes/3.47.0.txt +0 -270
  398. data/doc/release_notes/3.48.0.txt +0 -477
  399. data/doc/release_notes/3.5.0.txt +0 -510
  400. data/doc/release_notes/3.6.0.txt +0 -366
  401. data/doc/release_notes/3.7.0.txt +0 -179
  402. data/doc/release_notes/3.8.0.txt +0 -151
  403. data/doc/release_notes/3.9.0.txt +0 -233
  404. data/doc/release_notes/4.0.0.txt +0 -262
  405. data/doc/release_notes/4.1.0.txt +0 -85
  406. data/doc/release_notes/4.10.0.txt +0 -226
  407. data/doc/release_notes/4.11.0.txt +0 -147
  408. data/doc/release_notes/4.12.0.txt +0 -105
  409. data/doc/release_notes/4.13.0.txt +0 -169
  410. data/doc/release_notes/4.14.0.txt +0 -68
  411. data/doc/release_notes/4.15.0.txt +0 -56
  412. data/doc/release_notes/4.16.0.txt +0 -36
  413. data/doc/release_notes/4.17.0.txt +0 -38
  414. data/doc/release_notes/4.18.0.txt +0 -36
  415. data/doc/release_notes/4.19.0.txt +0 -45
  416. data/doc/release_notes/4.2.0.txt +0 -129
  417. data/doc/release_notes/4.20.0.txt +0 -79
  418. data/doc/release_notes/4.21.0.txt +0 -94
  419. data/doc/release_notes/4.22.0.txt +0 -72
  420. data/doc/release_notes/4.23.0.txt +0 -65
  421. data/doc/release_notes/4.24.0.txt +0 -99
  422. data/doc/release_notes/4.25.0.txt +0 -181
  423. data/doc/release_notes/4.26.0.txt +0 -44
  424. data/doc/release_notes/4.3.0.txt +0 -40
  425. data/doc/release_notes/4.4.0.txt +0 -92
  426. data/doc/release_notes/4.5.0.txt +0 -34
  427. data/doc/release_notes/4.6.0.txt +0 -30
  428. data/doc/release_notes/4.7.0.txt +0 -103
  429. data/doc/release_notes/4.8.0.txt +0 -175
  430. data/doc/release_notes/4.9.0.txt +0 -190
  431. data/lib/sequel/adapters/cubrid.rb +0 -142
  432. data/lib/sequel/adapters/do.rb +0 -156
  433. data/lib/sequel/adapters/do/mysql.rb +0 -64
  434. data/lib/sequel/adapters/do/postgres.rb +0 -42
  435. data/lib/sequel/adapters/do/sqlite3.rb +0 -40
  436. data/lib/sequel/adapters/jdbc/as400.rb +0 -82
  437. data/lib/sequel/adapters/jdbc/cubrid.rb +0 -62
  438. data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -34
  439. data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -31
  440. data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -31
  441. data/lib/sequel/adapters/odbc/progress.rb +0 -8
  442. data/lib/sequel/adapters/shared/cubrid.rb +0 -243
  443. data/lib/sequel/adapters/shared/firebird.rb +0 -245
  444. data/lib/sequel/adapters/shared/informix.rb +0 -52
  445. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +0 -150
  446. data/lib/sequel/adapters/shared/progress.rb +0 -38
  447. data/lib/sequel/adapters/swift.rb +0 -158
  448. data/lib/sequel/adapters/swift/mysql.rb +0 -47
  449. data/lib/sequel/adapters/swift/postgres.rb +0 -45
  450. data/lib/sequel/adapters/swift/sqlite.rb +0 -47
  451. data/lib/sequel/adapters/utils/pg_types.rb +0 -68
  452. data/lib/sequel/dataset/mutation.rb +0 -109
  453. data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -3
  454. data/lib/sequel/extensions/filter_having.rb +0 -59
  455. data/lib/sequel/extensions/hash_aliases.rb +0 -45
  456. data/lib/sequel/extensions/meta_def.rb +0 -31
  457. data/lib/sequel/extensions/query_literals.rb +0 -80
  458. data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -22
  459. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -118
  460. data/lib/sequel/extensions/set_overrides.rb +0 -72
  461. data/lib/sequel/no_core_ext.rb +0 -1
  462. data/lib/sequel/plugins/association_autoreloading.rb +0 -7
  463. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -7
  464. data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -78
  465. data/lib/sequel/plugins/prepared_statements_associations.rb +0 -117
  466. data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -59
  467. data/lib/sequel/plugins/schema.rb +0 -80
  468. data/lib/sequel/plugins/scissors.rb +0 -33
  469. data/spec/adapters/db2_spec.rb +0 -160
  470. data/spec/adapters/firebird_spec.rb +0 -411
  471. data/spec/adapters/informix_spec.rb +0 -100
  472. data/spec/adapters/mssql_spec.rb +0 -706
  473. data/spec/adapters/mysql_spec.rb +0 -1287
  474. data/spec/adapters/oracle_spec.rb +0 -313
  475. data/spec/adapters/postgres_spec.rb +0 -3725
  476. data/spec/adapters/spec_helper.rb +0 -43
  477. data/spec/adapters/sqlanywhere_spec.rb +0 -170
  478. data/spec/adapters/sqlite_spec.rb +0 -653
  479. data/spec/bin_spec.rb +0 -254
  480. data/spec/core/connection_pool_spec.rb +0 -1016
  481. data/spec/core/database_spec.rb +0 -2531
  482. data/spec/core/dataset_spec.rb +0 -5098
  483. data/spec/core/deprecated_spec.rb +0 -70
  484. data/spec/core/expression_filters_spec.rb +0 -1243
  485. data/spec/core/mock_adapter_spec.rb +0 -462
  486. data/spec/core/object_graph_spec.rb +0 -303
  487. data/spec/core/placeholder_literalizer_spec.rb +0 -163
  488. data/spec/core/schema_generator_spec.rb +0 -179
  489. data/spec/core/schema_spec.rb +0 -1659
  490. data/spec/core/spec_helper.rb +0 -34
  491. data/spec/core/version_spec.rb +0 -7
  492. data/spec/core_extensions_spec.rb +0 -699
  493. data/spec/extensions/accessed_columns_spec.rb +0 -51
  494. data/spec/extensions/active_model_spec.rb +0 -123
  495. data/spec/extensions/after_initialize_spec.rb +0 -24
  496. data/spec/extensions/arbitrary_servers_spec.rb +0 -109
  497. data/spec/extensions/association_dependencies_spec.rb +0 -117
  498. data/spec/extensions/association_pks_spec.rb +0 -365
  499. data/spec/extensions/association_proxies_spec.rb +0 -86
  500. data/spec/extensions/auto_validations_spec.rb +0 -192
  501. data/spec/extensions/blacklist_security_spec.rb +0 -88
  502. data/spec/extensions/blank_spec.rb +0 -69
  503. data/spec/extensions/boolean_readers_spec.rb +0 -93
  504. data/spec/extensions/caching_spec.rb +0 -270
  505. data/spec/extensions/class_table_inheritance_spec.rb +0 -420
  506. data/spec/extensions/column_conflicts_spec.rb +0 -60
  507. data/spec/extensions/column_select_spec.rb +0 -108
  508. data/spec/extensions/columns_introspection_spec.rb +0 -91
  509. data/spec/extensions/composition_spec.rb +0 -242
  510. data/spec/extensions/connection_validator_spec.rb +0 -120
  511. data/spec/extensions/constraint_validations_plugin_spec.rb +0 -274
  512. data/spec/extensions/constraint_validations_spec.rb +0 -325
  513. data/spec/extensions/core_refinements_spec.rb +0 -519
  514. data/spec/extensions/csv_serializer_spec.rb +0 -173
  515. data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
  516. data/spec/extensions/dataset_associations_spec.rb +0 -311
  517. data/spec/extensions/dataset_source_alias_spec.rb +0 -51
  518. data/spec/extensions/date_arithmetic_spec.rb +0 -150
  519. data/spec/extensions/defaults_setter_spec.rb +0 -101
  520. data/spec/extensions/delay_add_association_spec.rb +0 -52
  521. data/spec/extensions/dirty_spec.rb +0 -180
  522. data/spec/extensions/eager_each_spec.rb +0 -42
  523. data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
  524. data/spec/extensions/error_splitter_spec.rb +0 -18
  525. data/spec/extensions/error_sql_spec.rb +0 -20
  526. data/spec/extensions/eval_inspect_spec.rb +0 -73
  527. data/spec/extensions/filter_having_spec.rb +0 -40
  528. data/spec/extensions/force_encoding_spec.rb +0 -114
  529. data/spec/extensions/from_block_spec.rb +0 -21
  530. data/spec/extensions/graph_each_spec.rb +0 -109
  531. data/spec/extensions/hash_aliases_spec.rb +0 -24
  532. data/spec/extensions/hook_class_methods_spec.rb +0 -429
  533. data/spec/extensions/inflector_spec.rb +0 -183
  534. data/spec/extensions/input_transformer_spec.rb +0 -54
  535. data/spec/extensions/insert_returning_select_spec.rb +0 -46
  536. data/spec/extensions/instance_filters_spec.rb +0 -79
  537. data/spec/extensions/instance_hooks_spec.rb +0 -276
  538. data/spec/extensions/inverted_subsets_spec.rb +0 -33
  539. data/spec/extensions/json_serializer_spec.rb +0 -291
  540. data/spec/extensions/lazy_attributes_spec.rb +0 -170
  541. data/spec/extensions/list_spec.rb +0 -267
  542. data/spec/extensions/looser_typecasting_spec.rb +0 -43
  543. data/spec/extensions/many_through_many_spec.rb +0 -2172
  544. data/spec/extensions/meta_def_spec.rb +0 -21
  545. data/spec/extensions/migration_spec.rb +0 -712
  546. data/spec/extensions/modification_detection_spec.rb +0 -80
  547. data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -91
  548. data/spec/extensions/named_timezones_spec.rb +0 -108
  549. data/spec/extensions/nested_attributes_spec.rb +0 -697
  550. data/spec/extensions/null_dataset_spec.rb +0 -85
  551. data/spec/extensions/optimistic_locking_spec.rb +0 -128
  552. data/spec/extensions/pagination_spec.rb +0 -118
  553. data/spec/extensions/pg_array_associations_spec.rb +0 -736
  554. data/spec/extensions/pg_array_ops_spec.rb +0 -143
  555. data/spec/extensions/pg_array_spec.rb +0 -395
  556. data/spec/extensions/pg_enum_spec.rb +0 -92
  557. data/spec/extensions/pg_hstore_ops_spec.rb +0 -236
  558. data/spec/extensions/pg_hstore_spec.rb +0 -206
  559. data/spec/extensions/pg_inet_ops_spec.rb +0 -101
  560. data/spec/extensions/pg_inet_spec.rb +0 -52
  561. data/spec/extensions/pg_interval_spec.rb +0 -76
  562. data/spec/extensions/pg_json_ops_spec.rb +0 -229
  563. data/spec/extensions/pg_json_spec.rb +0 -218
  564. data/spec/extensions/pg_loose_count_spec.rb +0 -17
  565. data/spec/extensions/pg_range_ops_spec.rb +0 -58
  566. data/spec/extensions/pg_range_spec.rb +0 -404
  567. data/spec/extensions/pg_row_ops_spec.rb +0 -60
  568. data/spec/extensions/pg_row_plugin_spec.rb +0 -62
  569. data/spec/extensions/pg_row_spec.rb +0 -360
  570. data/spec/extensions/pg_static_cache_updater_spec.rb +0 -92
  571. data/spec/extensions/pg_typecast_on_load_spec.rb +0 -63
  572. data/spec/extensions/prepared_statements_associations_spec.rb +0 -159
  573. data/spec/extensions/prepared_statements_safe_spec.rb +0 -61
  574. data/spec/extensions/prepared_statements_spec.rb +0 -103
  575. data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -31
  576. data/spec/extensions/pretty_table_spec.rb +0 -92
  577. data/spec/extensions/query_literals_spec.rb +0 -183
  578. data/spec/extensions/query_spec.rb +0 -102
  579. data/spec/extensions/rcte_tree_spec.rb +0 -392
  580. data/spec/extensions/round_timestamps_spec.rb +0 -43
  581. data/spec/extensions/schema_caching_spec.rb +0 -41
  582. data/spec/extensions/schema_dumper_spec.rb +0 -789
  583. data/spec/extensions/schema_spec.rb +0 -117
  584. data/spec/extensions/scissors_spec.rb +0 -26
  585. data/spec/extensions/select_remove_spec.rb +0 -38
  586. data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -101
  587. data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
  588. data/spec/extensions/serialization_spec.rb +0 -362
  589. data/spec/extensions/server_block_spec.rb +0 -90
  590. data/spec/extensions/set_overrides_spec.rb +0 -61
  591. data/spec/extensions/sharding_spec.rb +0 -198
  592. data/spec/extensions/shared_caching_spec.rb +0 -175
  593. data/spec/extensions/single_table_inheritance_spec.rb +0 -297
  594. data/spec/extensions/singular_table_names_spec.rb +0 -22
  595. data/spec/extensions/skip_create_refresh_spec.rb +0 -17
  596. data/spec/extensions/spec_helper.rb +0 -71
  597. data/spec/extensions/split_array_nil_spec.rb +0 -24
  598. data/spec/extensions/split_values_spec.rb +0 -22
  599. data/spec/extensions/sql_expr_spec.rb +0 -60
  600. data/spec/extensions/static_cache_spec.rb +0 -361
  601. data/spec/extensions/string_date_time_spec.rb +0 -95
  602. data/spec/extensions/string_stripper_spec.rb +0 -68
  603. data/spec/extensions/subclasses_spec.rb +0 -66
  604. data/spec/extensions/table_select_spec.rb +0 -71
  605. data/spec/extensions/tactical_eager_loading_spec.rb +0 -82
  606. data/spec/extensions/thread_local_timezones_spec.rb +0 -67
  607. data/spec/extensions/timestamps_spec.rb +0 -175
  608. data/spec/extensions/to_dot_spec.rb +0 -154
  609. data/spec/extensions/touch_spec.rb +0 -203
  610. data/spec/extensions/tree_spec.rb +0 -274
  611. data/spec/extensions/typecast_on_load_spec.rb +0 -80
  612. data/spec/extensions/unlimited_update_spec.rb +0 -20
  613. data/spec/extensions/update_or_create_spec.rb +0 -87
  614. data/spec/extensions/update_primary_key_spec.rb +0 -100
  615. data/spec/extensions/update_refresh_spec.rb +0 -53
  616. data/spec/extensions/validate_associated_spec.rb +0 -52
  617. data/spec/extensions/validation_class_methods_spec.rb +0 -1027
  618. data/spec/extensions/validation_helpers_spec.rb +0 -541
  619. data/spec/extensions/xml_serializer_spec.rb +0 -207
  620. data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
  621. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
  622. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  623. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  624. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
  625. data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
  626. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
  627. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
  628. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
  629. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
  630. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
  631. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
  632. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
  633. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
  634. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  635. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
  636. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
  637. data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
  638. data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
  639. data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
  640. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  641. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
  642. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  643. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
  644. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  645. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
  646. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
  647. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  648. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  649. data/spec/files/reversible_migrations/001_reversible.rb +0 -5
  650. data/spec/files/reversible_migrations/002_reversible.rb +0 -5
  651. data/spec/files/reversible_migrations/003_reversible.rb +0 -5
  652. data/spec/files/reversible_migrations/004_reversible.rb +0 -5
  653. data/spec/files/reversible_migrations/005_reversible.rb +0 -10
  654. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
  655. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
  656. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
  657. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
  658. data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
  659. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
  660. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
  661. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
  662. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
  663. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
  664. data/spec/guards_helper.rb +0 -55
  665. data/spec/integration/associations_test.rb +0 -2454
  666. data/spec/integration/database_test.rb +0 -113
  667. data/spec/integration/dataset_test.rb +0 -1808
  668. data/spec/integration/eager_loader_test.rb +0 -687
  669. data/spec/integration/migrator_test.rb +0 -240
  670. data/spec/integration/model_test.rb +0 -226
  671. data/spec/integration/plugin_test.rb +0 -2240
  672. data/spec/integration/prepared_statement_test.rb +0 -467
  673. data/spec/integration/schema_test.rb +0 -817
  674. data/spec/integration/spec_helper.rb +0 -48
  675. data/spec/integration/timezone_test.rb +0 -86
  676. data/spec/integration/transaction_test.rb +0 -374
  677. data/spec/integration/type_test.rb +0 -133
  678. data/spec/model/association_reflection_spec.rb +0 -525
  679. data/spec/model/associations_spec.rb +0 -4426
  680. data/spec/model/base_spec.rb +0 -759
  681. data/spec/model/class_dataset_methods_spec.rb +0 -146
  682. data/spec/model/dataset_methods_spec.rb +0 -149
  683. data/spec/model/eager_loading_spec.rb +0 -2137
  684. data/spec/model/hooks_spec.rb +0 -604
  685. data/spec/model/inflector_spec.rb +0 -26
  686. data/spec/model/model_spec.rb +0 -982
  687. data/spec/model/plugins_spec.rb +0 -299
  688. data/spec/model/record_spec.rb +0 -2147
  689. data/spec/model/spec_helper.rb +0 -46
  690. data/spec/model/validations_spec.rb +0 -193
  691. data/spec/sequel_coverage.rb +0 -15
  692. data/spec/spec_config.rb +0 -10
@@ -1,3 +1,5 @@
1
+ # frozen-string-literal: true
2
+
1
3
  module Sequel
2
4
  class Model
3
5
  extend Enumerable
@@ -5,18 +7,33 @@ module Sequel
5
7
 
6
8
  # Class methods for Sequel::Model that implement basic model functionality.
7
9
  #
8
- # * All of the method names in Model::DATASET_METHODS have class methods created that call
9
- # the Model's dataset with the method of the same name with the given arguments.
10
+ # * All of the following methods have class methods created that send the method
11
+ # to the model's dataset: all, as_hash, avg, count, cross_join, distinct, each,
12
+ # each_server, empty?, except, exclude, exclude_having, fetch_rows,
13
+ # filter, first, first!, for_update, from, from_self, full_join, full_outer_join,
14
+ # get, graph, grep, group, group_and_count, group_append, group_by, having, import,
15
+ # inner_join, insert, intersect, invert, join, join_table, last, left_join,
16
+ # left_outer_join, limit, lock_style, map, max, min, multi_insert, naked, natural_full_join,
17
+ # natural_join, natural_left_join, natural_right_join, offset, order, order_append, order_by,
18
+ # order_more, order_prepend, paged_each, qualify, reverse, reverse_order, right_join,
19
+ # right_outer_join, select, select_all, select_append, select_group, select_hash,
20
+ # select_hash_groups, select_map, select_more, select_order_map, server,
21
+ # single_record, single_record!, single_value, single_value!, sum, to_hash, to_hash_groups,
22
+ # truncate, unfiltered, ungraphed, ungrouped, union, unlimited, unordered, where, where_all,
23
+ # where_each, where_single_value, with, with_recursive, with_sql
10
24
  module ClassMethods
11
- # Which columns should be the only columns allowed in a call to a mass assignment method (e.g. set)
12
- # (default: not set, so all columns not otherwise restricted are allowed).
13
- attr_reader :allowed_columns
25
+ # Whether to cache the anonymous models created by Sequel::Model(), true by default. This is
26
+ # required for reloading them correctly (avoiding the superclass mismatch).
27
+ attr_accessor :cache_anonymous_models
14
28
 
15
29
  # Array of modules that extend this model's dataset. Stored
16
30
  # so that if the model's dataset is changed, it will be extended
17
31
  # with all of these modules.
18
32
  attr_reader :dataset_method_modules
19
33
 
34
+ # The Module subclass to use for dataset_module blocks.
35
+ attr_reader :dataset_module_class
36
+
20
37
  # The default options to use for Model#set_fields. These are merged with
21
38
  # the options given to set_fields.
22
39
  attr_accessor :default_set_fields_options
@@ -25,6 +42,10 @@ module Sequel
25
42
  # model instances, or nil if the optimization should not be used. For internal use only.
26
43
  attr_reader :fast_instance_delete_sql
27
44
 
45
+ # SQL string fragment used for faster lookups by primary key, or nil if the optimization
46
+ # should not be used. For internal use only.
47
+ attr_reader :fast_pk_lookup_sql
48
+
28
49
  # The dataset that instance datasets (#this) are based on. Generally a naked version of
29
50
  # the model's dataset limited to one row. For internal use only.
30
51
  attr_reader :instance_dataset
@@ -46,11 +67,8 @@ module Sequel
46
67
  attr_accessor :raise_on_save_failure
47
68
 
48
69
  # Whether to raise an error when unable to typecast data for a column
49
- # (default: true). This should be set to false if you want to use
50
- # validations to display nice error messages to the user (e.g. most
51
- # web applications). You can use the validates_schema_types validation
52
- # (from the validation_helpers plugin) in connection with this setting to
53
- # check for typecast failures during validation.
70
+ # (default: false). This should be set to true if you want to have model
71
+ # setter methods raise errors if the argument cannot be typecast properly.
54
72
  attr_accessor :raise_on_typecast_failure
55
73
 
56
74
  # Whether to raise an error if an UPDATE or DELETE query related to
@@ -58,6 +76,12 @@ module Sequel
58
76
  # Sequel will not check the number of rows modified (default: true).
59
77
  attr_accessor :require_modification
60
78
 
79
+ # If true (the default), requires that all models have valid tables,
80
+ # raising exceptions if creating a model without a valid table backing it.
81
+ # Setting this to false will allow the creation of model classes where the
82
+ # underlying table doesn't exist.
83
+ attr_accessor :require_valid_table
84
+
61
85
  # Should be the literal primary key column name if this Model's table has a simple primary key, or
62
86
  # nil if the model has a compound primary key or no primary key.
63
87
  attr_reader :simple_pk
@@ -66,7 +90,7 @@ module Sequel
66
90
  # or nil otherwise. This and simple_pk are used for an optimization in Model.[].
67
91
  attr_reader :simple_table
68
92
 
69
- # Whether new/set/update and their variants should raise an error
93
+ # Whether mass assigning via .create/.new/#set/#update should raise an error
70
94
  # if an invalid key is used. A key is invalid if no setter method exists
71
95
  # for that key or the access to the setter method is restricted (e.g. due to it
72
96
  # being a primary key field). If set to false, silently skip
@@ -85,15 +109,95 @@ module Sequel
85
109
  # database to typecast the value correctly.
86
110
  attr_accessor :typecast_on_assignment
87
111
 
88
- # Whether to enable the after_commit and after_rollback hooks when saving/destroying
89
- # instances. On by default, can be turned off for performance reasons or when using
90
- # prepared transactions (which aren't compatible with after commit/rollback).
91
- attr_accessor :use_after_commit_rollback
92
-
93
112
  # Whether to use a transaction by default when saving/deleting records (default: true).
94
113
  # If you are sending database queries in before_* or after_* hooks, you shouldn't change
95
114
  # the default setting without a good reason.
96
115
  attr_accessor :use_transactions
116
+
117
+ # Define a Model method on the given module that calls the Model
118
+ # method on the receiver. This is how the Sequel::Model() method is
119
+ # defined, and allows you to define Model() methods on other modules,
120
+ # making it easier to have custom model settings for all models under
121
+ # a namespace. Example:
122
+ #
123
+ # module Foo
124
+ # Model = Class.new(Sequel::Model)
125
+ # Model.def_Model(self)
126
+ # DB = Model.db = Sequel.connect(ENV['FOO_DATABASE_URL'])
127
+ # Model.plugin :prepared_statements
128
+ #
129
+ # class Bar < Model
130
+ # # Uses Foo::DB[:bars]
131
+ # end
132
+ #
133
+ # class Baz < Model(:my_baz)
134
+ # # Uses Foo::DB[:my_baz]
135
+ # end
136
+ # end
137
+ def def_Model(mod)
138
+ model = self
139
+ mod.define_singleton_method(:Model) do |source|
140
+ model.Model(source)
141
+ end
142
+ end
143
+
144
+ # Lets you create a Model subclass with its dataset already set.
145
+ # +source+ should be an instance of one of the following classes:
146
+ #
147
+ # Database :: Sets the database for this model to +source+.
148
+ # Generally only useful when subclassing directly
149
+ # from the returned class, where the name of the
150
+ # subclass sets the table name (which is combined
151
+ # with the +Database+ in +source+ to create the
152
+ # dataset to use)
153
+ # Dataset :: Sets the dataset for this model to +source+.
154
+ # other :: Sets the table name for this model to +source+. The
155
+ # class will use the default database for model
156
+ # classes in order to create the dataset.
157
+ #
158
+ # The purpose of this method is to set the dataset/database automatically
159
+ # for a model class, if the table name doesn't match the default table
160
+ # name that Sequel would use.
161
+ #
162
+ # When creating subclasses of Sequel::Model itself, this method is usually
163
+ # called on Sequel itself, using <tt>Sequel::Model(:something)</tt>.
164
+ #
165
+ # # Using a symbol
166
+ # class Comment < Sequel::Model(:something)
167
+ # table_name # => :something
168
+ # end
169
+ #
170
+ # # Using a dataset
171
+ # class Comment < Sequel::Model(DB1[:something])
172
+ # dataset # => DB1[:something]
173
+ # end
174
+ #
175
+ # # Using a database
176
+ # class Comment < Sequel::Model(DB1)
177
+ # dataset # => DB1[:comments]
178
+ # end
179
+ def Model(source)
180
+ if cache_anonymous_models
181
+ cache = Sequel.synchronize{@Model_cache ||= {}}
182
+ if klass = Sequel.synchronize{cache[source]}
183
+ return klass
184
+ end
185
+ end
186
+
187
+ klass = Class.new(self)
188
+
189
+ if source.is_a?(::Sequel::Database)
190
+ klass.db = source
191
+ else
192
+ klass.set_dataset(source)
193
+ end
194
+
195
+ if cache_anonymous_models
196
+ Sequel.synchronize{cache[source] = klass}
197
+ end
198
+
199
+ klass
200
+ end
97
201
 
98
202
  # Returns the first record from the database matching the conditions.
99
203
  # If a hash is given, it is used as the conditions. If another
@@ -103,11 +207,11 @@ module Sequel
103
207
  # Artist[1] # SELECT * FROM artists WHERE id = 1
104
208
  # # => #<Artist {:id=>1, ...}>
105
209
  #
106
- # Artist[:name=>'Bob'] # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
210
+ # Artist[name: 'Bob'] # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
107
211
  # # => #<Artist {:name=>'Bob', ...}>
108
212
  def [](*args)
109
213
  args = args.first if args.size <= 1
110
- args.is_a?(Hash) ? first_where(args) : (primary_key_lookup(args) unless args.nil?)
214
+ args.is_a?(Hash) ? first(args) : (primary_key_lookup(args) unless args.nil?)
111
215
  end
112
216
 
113
217
  # Initializes a model instance as an existing record. This constructor is
@@ -122,7 +226,7 @@ module Sequel
122
226
 
123
227
  # Clear the setter_methods cache
124
228
  def clear_setter_methods_cache
125
- @setter_methods = nil
229
+ @setter_methods = nil unless frozen?
126
230
  end
127
231
 
128
232
  # Returns the columns in the result set in their original order.
@@ -133,18 +237,20 @@ module Sequel
133
237
  # Artist.columns
134
238
  # # => [:id, :name]
135
239
  def columns
136
- @columns || set_columns(dataset.naked.columns)
240
+ return @columns if @columns
241
+ return nil if frozen?
242
+ set_columns(dataset.naked.columns)
137
243
  end
138
244
 
139
245
  # Creates instance using new with the given values and block, and saves it.
140
246
  #
141
- # Artist.create(:name=>'Bob')
247
+ # Artist.create(name: 'Bob')
142
248
  # # INSERT INTO artists (name) VALUES ('Bob')
143
249
  #
144
250
  # Artist.create do |a|
145
251
  # a.name = 'Jim'
146
252
  # end # INSERT INTO artists (name) VALUES ('Jim')
147
- def create(values = {}, &block)
253
+ def create(values = OPTS, &block)
148
254
  new(values, &block).save
149
255
  end
150
256
 
@@ -167,49 +273,70 @@ module Sequel
167
273
  # a plugin with the methods defined in DatasetMethods.
168
274
  # This is the recommended way to add methods to model datasets.
169
275
  #
170
- # If an argument, it should be a module, and is used to extend
276
+ # If given an argument, it should be a module, and is used to extend
171
277
  # the underlying dataset. Otherwise an anonymous module is created, and
172
278
  # if a block is given, it is module_evaled, allowing you do define
173
279
  # dataset methods directly using the standard ruby def syntax.
174
280
  # Returns the module given or the anonymous module created.
175
281
  #
176
282
  # # Usage with existing module
177
- # Artist.dataset_module Sequel::ColumnsIntrospection
283
+ # Album.dataset_module Sequel::ColumnsIntrospection
178
284
  #
179
285
  # # Usage with anonymous module
180
- # Artist.dataset_module do
286
+ # Album.dataset_module do
181
287
  # def foo
182
288
  # :bar
183
289
  # end
184
290
  # end
185
- # Artist.dataset.foo
291
+ # Album.dataset.foo
186
292
  # # => :bar
187
- # Artist.foo
293
+ # Album.foo
188
294
  # # => :bar
189
295
  #
190
296
  # Any anonymous modules created are actually instances of Sequel::Model::DatasetModule
191
- # (a Module subclass), which allows you to call the subset method on them:
192
- #
193
- # Artist.dataset_module do
194
- # subset :released, Sequel.identifier(release_date) > Sequel::CURRENT_DATE
297
+ # (a Module subclass), which allows you to call the subset method on them, which
298
+ # defines a dataset method that adds a filter. There are also a number of other
299
+ # methods with the same names as the dataset methods, which can use to define
300
+ # named dataset methods:
301
+ #
302
+ # Album.dataset_module do
303
+ # where(:released, Sequel[:release_date] <= Sequel::CURRENT_DATE)
304
+ # order :by_release_date, :release_date
305
+ # select :for_select_options, :id, :name, :release_date
195
306
  # end
307
+ # Album.released.sql
308
+ # # => "SELECT * FROM artists WHERE (release_date <= CURRENT_DATE)"
309
+ # Album.by_release_date.sql
310
+ # # => "SELECT * FROM artists ORDER BY release_date"
311
+ # Album.for_select_options.sql
312
+ # # => "SELECT id, name, release_date FROM artists"
313
+ # Album.released.by_release_date.for_select_options.sql
314
+ # # => "SELECT id, name, release_date FROM artists WHERE (release_date <= CURRENT_DATE) ORDER BY release_date"
315
+ #
316
+ # The following methods are supported: distinct, eager, exclude, exclude_having, grep, group, group_and_count,
317
+ # group_append, having, limit, offset, order, order_append, order_prepend, select, select_all,
318
+ # select_append, select_group, where, and server.
319
+ #
320
+ # The advantage of using these DatasetModule methods to define your dataset
321
+ # methods is that they can take advantage of dataset caching to improve
322
+ # performance.
196
323
  #
197
324
  # Any public methods in the dataset module will have class methods created that
198
325
  # call the method on the dataset, assuming that the class method is not already
199
326
  # defined.
200
- def dataset_module(mod = nil)
327
+ def dataset_module(mod = nil, &block)
201
328
  if mod
202
- raise Error, "can't provide both argument and block to Model.dataset_module" if block_given?
329
+ raise Error, "can't provide both argument and block to Model.dataset_module" if block
203
330
  dataset_extend(mod)
204
331
  mod
205
332
  else
206
- @dataset_module ||= DatasetModule.new(self)
207
- @dataset_module.module_eval(&Proc.new) if block_given?
333
+ @dataset_module ||= dataset_module_class.new(self)
334
+ @dataset_module.module_eval(&block) if block
208
335
  dataset_extend(@dataset_module)
209
336
  @dataset_module
210
337
  end
211
338
  end
212
-
339
+
213
340
  # Returns the database associated with the Model class.
214
341
  # If this model doesn't have a database associated with it,
215
342
  # assumes the superclass's database, or the first object in
@@ -217,7 +344,7 @@ module Sequel
217
344
  # been created, raises an error.
218
345
  #
219
346
  # Artist.db.transaction do # BEGIN
220
- # Artist.create(:name=>'Bob')
347
+ # Artist.create(name: 'Bob')
221
348
  # # INSERT INTO artists (name) VALUES ('Bob')
222
349
  # end # COMMIT
223
350
  def db
@@ -227,23 +354,23 @@ module Sequel
227
354
  @db
228
355
  end
229
356
 
230
- # Sets the database associated with the Model class. If the
231
- # model has an associated dataset, sets the model's dataset
232
- # to a dataset on the new database with the same options
233
- # used by the current dataset. This can be used directly on
234
- # Sequel::Model to set the default database to be used
235
- # by subclasses, or to override the database used for specific
236
- # models:
357
+ # Sets the database associated with the Model class.
358
+ # Should only be used if the Model class currently does not
359
+ # have a dataset defined.
360
+ #
361
+ # This can be used directly on Sequel::Model to set the default database to be used
362
+ # by subclasses, or to override the database used for specific models:
237
363
  #
238
364
  # Sequel::Model.db = DB1
365
+ # Artist = Class.new(Sequel::Model)
239
366
  # Artist.db = DB2
240
367
  #
241
368
  # Note that you should not use this to change the model's database
242
369
  # at runtime. If you have that need, you should look into Sequel's
243
- # sharding support.
370
+ # sharding support, or consider using separate model classes per Database.
244
371
  def db=(db)
372
+ raise Error, "Cannot use Sequel::Model.db= on model with existing dataset. Use Sequel::Model.dataset= instead." if @dataset
245
373
  @db = db
246
- set_dataset(db.dataset.clone(@dataset.opts)) if @dataset
247
374
  end
248
375
 
249
376
  # Returns the cached schema information if available or gets it
@@ -255,7 +382,9 @@ module Sequel
255
382
  # # {:id=>{:type=>:integer, :primary_key=>true, ...},
256
383
  # # :name=>{:type=>:string, :primary_key=>false, ...}}
257
384
  def db_schema
258
- @db_schema ||= get_db_schema
385
+ return @db_schema if @db_schema
386
+ return nil if frozen?
387
+ @db_schema = get_db_schema
259
388
  end
260
389
 
261
390
  # Create a column alias, where the column methods have one name, but the underlying storage uses a
@@ -268,52 +397,16 @@ module Sequel
268
397
  end
269
398
  end
270
399
 
271
- # If a block is given, define a method on the dataset (if the model currently has an dataset) with the given argument name using
272
- # the given block. Also define a class method on the model that calls the
273
- # dataset method. Stores the method name and block so that it can be reapplied if the model's
274
- # dataset changes.
275
- #
276
- # If a block is not given, just define a class method on the model for each argument
277
- # that calls the dataset method of the same argument name.
278
- #
279
- # It is recommended that you define methods inside a block passed to #dataset_module
280
- # instead of using this method, as #dataset_module allows you to use normal
281
- # ruby def syntax.
282
- #
283
- # # Add new dataset method and class method that calls it
284
- # Artist.def_dataset_method(:by_name){order(:name)}
285
- # Artist.filter(:name.like('A%')).by_name
286
- # Artist.by_name.filter(:name.like('A%'))
287
- #
288
- # # Just add a class method that calls an existing dataset method
289
- # Artist.def_dataset_method(:server!)
290
- # Artist.server!(:server1)
291
- def def_dataset_method(*args, &block)
292
- raise(Error, "No arguments given") if args.empty?
293
-
294
- if block
295
- raise(Error, "Defining a dataset method using a block requires only one argument") if args.length > 1
296
- dataset_module{define_method(args.first, &block)}
297
- else
298
- args.each{|arg| def_model_dataset_method(arg)}
299
- end
300
- end
301
-
302
400
  # Finds a single record according to the supplied filter.
303
401
  # You are encouraged to use Model.[] or Model.first instead of this method.
304
402
  #
305
- # Artist.find(:name=>'Bob')
403
+ # Artist.find(name: 'Bob')
306
404
  # # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
307
405
  #
308
406
  # Artist.find{name > 'M'}
309
407
  # # SELECT * FROM artists WHERE (name > 'M') LIMIT 1
310
408
  def find(*args, &block)
311
- if args.length == 1 && !block
312
- # Use optimized finder
313
- first_where(args.first)
314
- else
315
- filter(*args, &block).first
316
- end
409
+ first(*args, &block)
317
410
  end
318
411
 
319
412
  # Like +find+ but invokes create with given conditions when record does not
@@ -321,154 +414,43 @@ module Sequel
321
414
  # to +find+, but instead is passed to +create+ only if +find+ does not
322
415
  # return an object.
323
416
  #
324
- # Artist.find_or_create(:name=>'Bob')
417
+ # Artist.find_or_create(name: 'Bob')
325
418
  # # SELECT * FROM artists WHERE (name = 'Bob') LIMIT 1
326
419
  # # INSERT INTO artists (name) VALUES ('Bob')
327
420
  #
328
- # Artist.find_or_create(:name=>'Jim'){|a| a.hometown = 'Sactown'}
421
+ # Artist.find_or_create(name: 'Jim'){|a| a.hometown = 'Sactown'}
329
422
  # # SELECT * FROM artists WHERE (name = 'Jim') LIMIT 1
330
423
  # # INSERT INTO artists (name, hometown) VALUES ('Jim', 'Sactown')
331
424
  def find_or_create(cond, &block)
332
425
  find(cond) || create(cond, &block)
333
426
  end
334
-
335
427
 
336
- FINDER_TYPES = [:first, :all, :each, :get].freeze
337
-
338
- # Create an optimized finder method using a dataset placeholder literalizer.
339
- # This pre-computes the SQL to use for the query, except for given arguments.
340
- #
341
- # There are two ways to use this. The recommended way is to pass a symbol
342
- # that represents a model class method that returns a dataset:
343
- #
344
- # def Artist.by_name(name)
345
- # where(:name=>name)
346
- # end
347
- #
348
- # Artist.finder :by_name
349
- #
350
- # This creates an optimized first_by_name method, which you can call normally:
351
- #
352
- # Artist.first_by_name("Joe")
353
- #
354
- # The alternative way to use this to pass your own block:
355
- #
356
- # Artist.finder(:name=>:first_by_name){|pl, ds| ds.where(:name=>pl.arg).limit(1)}
357
- #
358
- # Note that if you pass your own block, you are responsible for manually setting
359
- # limits if necessary (as shown above).
360
- #
361
- # Options:
362
- # :arity :: When using a symbol method name, this specifies the arity of the method.
363
- # This should be used if if the method accepts an arbitrary number of arguments,
364
- # or the method has default argument values. Note that if the method is defined
365
- # as a dataset method, the class method Sequel creates accepts an arbitrary number
366
- # of arguments, so you should use this option in that case. If you want to handle
367
- # multiple possible arities, you need to call the finder method multiple times with
368
- # unique :arity and :name methods each time.
369
- # :name :: The name of the method to create. This must be given if you pass a block.
370
- # If you use a symbol, this defaults to the symbol prefixed by the type.
371
- # :mod :: The module in which to create the finder method. Defaults to the singleton
372
- # class of the model.
373
- # :type :: The type of query to run. Can be :first, :each, :all, or :get, defaults to
374
- # :first.
375
- #
376
- # Caveats:
377
- #
378
- # This doesn't handle all possible cases. For example, if you have a method such as:
379
- #
380
- # def Artist.by_name(name)
381
- # name ? where(:name=>name) : exclude(:name=>nil)
382
- # end
383
- #
384
- # Then calling a finder without an argument will not work as you expect.
385
- #
386
- # Artist.finder :by_name
387
- # Artist.by_name(nil).first
388
- # # WHERE (name IS NOT NULL)
389
- # Artist.first_by_name(nil)
390
- # # WHERE (name IS NULL)
391
- #
392
- # See Dataset::PlaceholderLiteralizer for additional caveats.
393
- def finder(meth=OPTS, opts=OPTS, &block)
394
- if block
395
- raise Error, "cannot pass both a method name argument and a block of Model.finder" unless meth.is_a?(Hash)
396
- raise Error, "cannot pass two option hashes to Model.finder" unless opts.equal?(OPTS)
397
- opts = meth
398
- raise Error, "must provide method name via :name option when passing block to Model.finder" unless meth_name = opts[:name]
399
- end
400
-
401
- type = opts.fetch(:type, :first)
402
- unless prepare = opts[:prepare]
403
- raise Error, ":type option to Model.finder must be :first, :all, :each, or :get" unless FINDER_TYPES.include?(type)
404
- end
405
- limit1 = type == :first || type == :get
406
- meth_name ||= opts[:name] || :"#{type}_#{meth}"
407
-
408
- argn = lambda do |model|
409
- if arity = opts[:arity]
410
- arity
411
- else
412
- method = block || model.method(meth)
413
- (method.arity < 0 ? method.arity.abs - 1 : method.arity)
414
- end
415
- end
416
-
417
- loader_proc = if prepare
418
- proc do |model|
419
- args = prepare_method_args('$a', argn.call(model))
420
- ds = if block
421
- model.instance_exec(*args, &block)
422
- else
423
- model.send(meth, *args)
424
- end
425
- ds = ds.limit(1) if limit1
426
- model_name = model.name
427
- if model_name.to_s.empty?
428
- model_name = model.object_id
429
- else
430
- model_name = model_name.gsub(/\W/, '_')
431
- end
432
- ds.prepare(type, :"#{model_name}_#{meth_name}")
433
- end
428
+ # Freeze a model class, disallowing any further changes to it.
429
+ def freeze
430
+ return self if frozen?
431
+ dataset_module.freeze
432
+ overridable_methods_module.freeze
433
+
434
+ if @dataset
435
+ db_schema.freeze.each_value(&:freeze)
436
+ columns.freeze
437
+ setter_methods.freeze
434
438
  else
435
- proc do |model|
436
- n = argn.call(model)
437
- block ||= lambda do |pl, model2|
438
- args = (0...n).map{pl.arg}
439
- ds = model2.send(meth, *args)
440
- ds = ds.limit(1) if limit1
441
- ds
442
- end
443
-
444
- Sequel::Dataset::PlaceholderLiteralizer.loader(model, &block)
445
- end
439
+ @setter_methods = [].freeze
446
440
  end
447
441
 
448
- Sequel.synchronize{@finder_loaders[meth_name] = loader_proc}
449
- mod = opts[:mod] || (class << self; self; end)
450
- if prepare
451
- def_prepare_method(mod, meth_name)
452
- else
453
- def_finder_method(mod, meth_name, type)
454
- end
455
- end
442
+ @dataset_method_modules.freeze
443
+ @default_set_fields_options.freeze
444
+ @plugins.freeze
456
445
 
457
- # An alias for calling first on the model's dataset, but with
458
- # optimized handling of the single argument case.
459
- def first(*args, &block)
460
- if args.length == 1 && !block && !args.first.is_a?(Integer)
461
- # Use optimized finder
462
- first_where(args.first)
463
- else
464
- dataset.first(*args, &block)
465
- end
446
+ super
466
447
  end
467
448
 
468
- # An alias for calling first! on the model's dataset, but with
469
- # optimized handling of the single argument case.
470
- def first!(*args, &block)
471
- first(*args, &block) || raise(Sequel::NoMatchingRow.new(dataset))
449
+ # Whether the model has a dataset. True for most model classes,
450
+ # but can be false if the model class is an abstract model class
451
+ # designed for subclassing, such as Sequel::Model itself.
452
+ def has_dataset?
453
+ !@dataset.nil?
472
454
  end
473
455
 
474
456
  # Clear the setter_methods cache when a module is included, as it
@@ -478,47 +460,6 @@ module Sequel
478
460
  super
479
461
  end
480
462
 
481
- # If possible, set the dataset for the model subclass as soon as it
482
- # is created. Also, make sure the inherited class instance variables
483
- # are copied into the subclass.
484
- #
485
- # Sequel queries the database to get schema information as soon as
486
- # a model class is created:
487
- #
488
- # class Artist < Sequel::Model # Causes schema query
489
- # end
490
- def inherited(subclass)
491
- super
492
- ivs = subclass.instance_variables.collect(&:to_s)
493
- inherited_instance_variables.each do |iv, dup|
494
- next if ivs.include?(iv.to_s)
495
- if (sup_class_value = instance_variable_get(iv)) && dup
496
- sup_class_value = case dup
497
- when :dup
498
- sup_class_value.dup
499
- when :hash_dup
500
- h = {}
501
- sup_class_value.each{|k,v| h[k] = v.dup}
502
- h
503
- when Proc
504
- dup.call(sup_class_value)
505
- else
506
- raise Error, "bad inherited instance variable type: #{dup.inspect}"
507
- end
508
- end
509
- subclass.instance_variable_set(iv, sup_class_value)
510
- end
511
-
512
- unless ivs.include?("@dataset")
513
- if @dataset && self != Model
514
- subclass.set_dataset(@dataset.clone, :inherited=>true) rescue nil
515
- elsif (n = subclass.name) && !n.to_s.empty?
516
- db
517
- subclass.set_dataset(subclass.implicit_table_name) rescue nil
518
- end
519
- end
520
- end
521
-
522
463
  # Returns the implicit table name for the model class, which is the demodulized,
523
464
  # underscored, pluralized name of the class.
524
465
  #
@@ -528,17 +469,11 @@ module Sequel
528
469
  pluralize(underscore(demodulize(name))).to_sym
529
470
  end
530
471
 
531
- # Calls #call with the values hash. Only for backwards compatibility.
472
+ # Calls #call with the values hash.
532
473
  def load(values)
533
474
  call(values)
534
475
  end
535
476
 
536
- # Clear the setter_methods cache when a setter method is added
537
- def method_added(meth)
538
- clear_setter_methods_cache if meth.to_s =~ SETTER_METHOD_REGEXP
539
- super
540
- end
541
-
542
477
  # Mark the model as not having a primary key. Not having a primary key
543
478
  # can cause issues, among which is that you won't be able to update records.
544
479
  #
@@ -552,22 +487,28 @@ module Sequel
552
487
 
553
488
  # Loads a plugin for use with the model class, passing optional arguments
554
489
  # to the plugin. If the plugin is a module, load it directly. Otherwise,
555
- # require the plugin from either sequel/plugins/#{plugin} or
556
- # sequel_#{plugin}, and then attempt to load the module using a
557
- # the camelized plugin name under Sequel::Plugins.
490
+ # require the plugin from sequel/plugins/#{plugin} and then attempt to load
491
+ # the module using a the camelized plugin name under Sequel::Plugins.
558
492
  def plugin(plugin, *args, &block)
559
493
  m = plugin.is_a?(Module) ? plugin : plugin_module(plugin)
494
+
495
+ if !m.respond_to?(:apply) && !m.respond_to?(:configure) && (!args.empty? || block)
496
+ Deprecation.deprecate("Plugin #{plugin} accepts no arguments or block, and passing arguments/block to it", "Remove arguments and block when loading the plugin")
497
+ end
498
+
560
499
  unless @plugins.include?(m)
561
500
  @plugins << m
562
501
  m.apply(self, *args, &block) if m.respond_to?(:apply)
563
- extend(m::ClassMethods) if plugin_module_defined?(m, :ClassMethods)
564
- include(m::InstanceMethods) if plugin_module_defined?(m, :InstanceMethods)
565
- if plugin_module_defined?(m, :DatasetMethods)
502
+ extend(m::ClassMethods) if m.const_defined?(:ClassMethods, false)
503
+ include(m::InstanceMethods) if m.const_defined?(:InstanceMethods, false)
504
+ if m.const_defined?(:DatasetMethods, false)
566
505
  dataset_extend(m::DatasetMethods, :create_class_methods=>false)
567
506
  end
568
507
  end
508
+
569
509
  m.configure(self, *args, &block) if m.respond_to?(:configure)
570
510
  end
511
+ ruby2_keywords(:plugin) if respond_to?(:ruby2_keywords, true)
571
512
 
572
513
  # Returns primary key attribute hash. If using a composite primary key
573
514
  # value such be an array with values for each primary key in the correct
@@ -595,7 +536,7 @@ module Sequel
595
536
  # plan to join other tables to this table and you want the column references
596
537
  # to be qualified.
597
538
  #
598
- # Artist.filter(Artist.qualified_primary_key_hash(1))
539
+ # Artist.where(Artist.qualified_primary_key_hash(1))
599
540
  # # SELECT * FROM artists WHERE (artists.id = 1)
600
541
  def qualified_primary_key_hash(value, qualifier=table_name)
601
542
  case key = @primary_key
@@ -610,27 +551,6 @@ module Sequel
610
551
  end
611
552
  end
612
553
 
613
- # Similar to finder, but uses a prepared statement instead of a placeholder
614
- # literalizer. This makes the SQL used static (cannot vary per call), but
615
- # allows binding argument values instead of literalizing them into the SQL
616
- # query string.
617
- #
618
- # If a block is used with this method, it is instance_execed by the model,
619
- # and should accept the desired number of placeholder arguments.
620
- #
621
- # The options are the same as the options for finder, with the following
622
- # exception:
623
- # :type :: Specifies the type of prepared statement to create
624
- def prepared_finder(meth=OPTS, opts=OPTS, &block)
625
- if block
626
- raise Error, "cannot pass both a method name argument and a block of Model.finder" unless meth.is_a?(Hash)
627
- meth = meth.merge(:prepare=>true)
628
- else
629
- opts = opts.merge(:prepare=>true)
630
- end
631
- finder(meth, opts, &block)
632
- end
633
-
634
554
  # Restrict the setting of the primary key(s) when using mass assignment (e.g. +set+). Because
635
555
  # this is the default, this only make sense to use in a subclass where the
636
556
  # parent class has used +unrestrict_primary_key+.
@@ -645,22 +565,6 @@ module Sequel
645
565
  @restrict_primary_key
646
566
  end
647
567
 
648
- # Set the columns to allow when using mass assignment (e.g. +set+). Using this means that
649
- # any columns not listed here will not be modified. If you have any virtual
650
- # setter methods (methods that end in =) that you want to be used during
651
- # mass assignment, they need to be listed here as well (without the =).
652
- #
653
- # It may be better to use a method such as +set_only+ or +set_fields+ that lets you specify
654
- # the allowed fields per call.
655
- #
656
- # Artist.set_allowed_columns(:name, :hometown)
657
- # Artist.set(:name=>'Bob', :hometown=>'Sactown') # No Error
658
- # Artist.set(:name=>'Bob', :records_sold=>30000) # Error
659
- def set_allowed_columns(*cols)
660
- clear_setter_methods_cache
661
- @allowed_columns = cols
662
- end
663
-
664
568
  # Sets the dataset associated with the Model class. +ds+ can be a +Symbol+,
665
569
  # +LiteralString+, <tt>SQL::Identifier</tt>, <tt>SQL::QualifiedIdentifier</tt>,
666
570
  # <tt>SQL::AliasedExpression</tt>
@@ -670,36 +574,43 @@ module Sequel
670
574
  # database with the table name given. Other arguments raise an +Error+.
671
575
  # Returns self.
672
576
  #
673
- # This changes the row_proc of the dataset to return
674
- # model objects and extends the dataset with the dataset_method_modules.
675
577
  # It also attempts to determine the database schema for the model,
676
578
  # based on the given dataset.
677
579
  #
678
- # Artist.set_dataset(:tbl_artists)
679
- # Artist.set_dataset(DB[:artists])
680
- #
681
580
  # Note that you should not use this to change the model's dataset
682
581
  # at runtime. If you have that need, you should look into Sequel's
683
- # sharding support.
582
+ # sharding support, or creating a separate Model class per dataset
583
+ #
584
+ # You should avoid calling this method directly if possible. Instead you should
585
+ # set the table name or dataset when creating the model class:
586
+ #
587
+ # # table name
588
+ # class Artist < Sequel::Model(:tbl_artists)
589
+ # end
590
+ #
591
+ # # dataset
592
+ # class Artist < Sequel::Model(DB[:tbl_artists])
593
+ # end
684
594
  def set_dataset(ds, opts=OPTS)
685
595
  inherited = opts[:inherited]
686
596
  @dataset = convert_input_dataset(ds)
687
- @require_modification = Sequel::Model.require_modification.nil? ? @dataset.provides_accurate_rows_matched? : Sequel::Model.require_modification
597
+ @require_modification = @dataset.provides_accurate_rows_matched? if require_modification.nil?
688
598
  if inherited
689
599
  self.simple_table = superclass.simple_table
690
- @columns = @dataset.columns rescue nil
600
+ @columns = superclass.instance_variable_get(:@columns)
601
+ @db_schema = superclass.instance_variable_get(:@db_schema)
691
602
  else
692
- @dataset_method_modules.each{|m| @dataset.extend(m)} if @dataset_method_modules
603
+ @dataset = @dataset.with_extend(*@dataset_method_modules.reverse)
604
+ @db_schema = get_db_schema
693
605
  end
694
- @dataset.model = self if @dataset.respond_to?(:model=)
695
- check_non_connection_error{@db_schema = (inherited ? superclass.db_schema : get_db_schema)}
606
+
696
607
  reset_instance_dataset
697
608
  self
698
609
  end
699
610
 
700
611
  # Sets the primary key for this model. You can use either a regular
701
612
  # or a composite primary key. To not use a primary key, set to nil
702
- # or use +no_primary_key+. On most adapters, Sequel can automatically
613
+ # or use +no_primary_key+. On most adapters, Sequel can automatically
703
614
  # determine the primary key to use, so this method is not needed often.
704
615
  #
705
616
  # class Person < Sequel::Model
@@ -721,48 +632,22 @@ module Sequel
721
632
  end
722
633
  end
723
634
  self.simple_pk = if key && !key.is_a?(Array)
724
- (@dataset || db).literal(key)
635
+ (@dataset || db).literal(key).freeze
725
636
  end
726
637
  @primary_key = key
727
638
  end
728
639
 
729
- # Cache of setter methods to allow by default, in order to speed up new/set/update instance methods.
640
+ # Cache of setter methods to allow by default, in order to speed up mass assignment.
730
641
  def setter_methods
731
- @setter_methods ||= get_setter_methods
642
+ @setter_methods || (@setter_methods = get_setter_methods)
732
643
  end
733
644
 
734
- # Sets up a dataset method that returns a filtered dataset.
735
- # Sometimes thought of as a scope, and like most dataset methods,
736
- # they can be chained.
737
- # For example:
738
- #
739
- # Topic.subset(:joes, :username.like('%joe%'))
740
- # Topic.subset(:popular){num_posts > 100}
741
- # Topic.subset(:recent){created_on > Date.today - 7}
742
- #
743
- # Allows you to do:
744
- #
745
- # Topic.joes.recent.popular
746
- #
747
- # to get topics with a username that includes joe that
748
- # have more than 100 posts and were created less than
749
- # 7 days ago.
750
- #
751
- # Both the args given and the block are passed to <tt>Dataset#filter</tt>.
752
- #
753
- # This method creates dataset methods that do not accept arguments. To create
754
- # dataset methods that accept arguments, you should use define a
755
- # method directly inside a #dataset_module block.
756
- def subset(name, *args, &block)
757
- def_dataset_method(name){filter(*args, &block)}
758
- end
759
-
760
645
  # Returns name of primary table for the dataset. If the table for the dataset
761
646
  # is aliased, returns the aliased name.
762
647
  #
763
648
  # Artist.table_name # => :artists
764
649
  # Sequel::Model(:foo).table_name # => :foo
765
- # Sequel::Model(:foo___bar).table_name # => :bar
650
+ # Sequel::Model(Sequel[:foo].as(:bar)).table_name # => :bar
766
651
  def table_name
767
652
  dataset.first_source_alias
768
653
  end
@@ -770,9 +655,9 @@ module Sequel
770
655
  # Allow the setting of the primary key(s) when using the mass assignment methods.
771
656
  # Using this method can open up security issues, be very careful before using it.
772
657
  #
773
- # Artist.set(:id=>1) # Error
658
+ # Artist.set(id: 1) # Error
774
659
  # Artist.unrestrict_primary_key
775
- # Artist.set(:id=>1) # No Error
660
+ # Artist.set(id: 1) # No Error
776
661
  def unrestrict_primary_key
777
662
  clear_setter_methods_cache
778
663
  @restrict_primary_key = false
@@ -789,18 +674,18 @@ module Sequel
789
674
  end
790
675
 
791
676
  # Add model methods that call dataset methods
792
- Plugins.def_dataset_methods(self, DATASET_METHODS)
677
+ Plugins.def_dataset_methods(self, (Dataset::ACTION_METHODS + Dataset::QUERY_METHODS + [:each_server]) - [:<<, :or, :[], :columns, :columns!, :delete, :update, :set_graph_aliases, :add_graph_aliases])
793
678
 
794
679
  private
795
680
 
796
- # Yield to the passed block and swallow all errors other than DatabaseConnectionErrors.
797
- def check_non_connection_error
681
+ # Yield to the passed block and if do_raise is false, swallow all errors other than DatabaseConnectionErrors.
682
+ def check_non_connection_error(do_raise=require_valid_table)
798
683
  begin
799
- yield
684
+ db.transaction(:savepoint=>:only){yield}
800
685
  rescue Sequel::DatabaseConnectionError
801
686
  raise
802
- rescue
803
- nil
687
+ rescue Sequel::Error
688
+ raise if do_raise
804
689
  end
805
690
  end
806
691
 
@@ -809,25 +694,27 @@ module Sequel
809
694
  def convert_input_dataset(ds)
810
695
  case ds
811
696
  when Symbol, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, LiteralString
812
- self.simple_table = db.literal(ds)
697
+ self.simple_table = db.literal(ds).freeze
813
698
  ds = db.from(ds)
814
699
  when Dataset
700
+ ds = ds.from_self(:alias=>ds.first_source) if ds.joined_dataset?
701
+
815
702
  self.simple_table = if ds.send(:simple_select_all?)
816
- ds.literal(ds.first_source_table)
703
+ ds.literal(ds.first_source_table).freeze
817
704
  end
818
705
  @db = ds.db
819
706
  else
820
707
  raise(Error, "Model.set_dataset takes one of the following classes as an argument: Symbol, LiteralString, SQL::Identifier, SQL::QualifiedIdentifier, SQL::AliasedExpression, Dataset")
821
708
  end
822
- set_dataset_row_proc(ds)
823
- ds
709
+
710
+ set_dataset_row_proc(ds.clone(:model=>self))
824
711
  end
825
712
 
826
713
  # Add the module to the class's dataset_method_modules. Extend the dataset with the
827
714
  # module if the model has a dataset. Add dataset methods to the class for all
828
715
  # public dataset methods.
829
716
  def dataset_extend(mod, opts=OPTS)
830
- @dataset.extend(mod) if @dataset
717
+ @dataset = @dataset.with_extend(mod) if @dataset
831
718
  reset_instance_dataset
832
719
  dataset_method_modules << mod
833
720
  unless opts[:create_class_methods] == false
@@ -837,9 +724,11 @@ module Sequel
837
724
 
838
725
  # Create a column accessor for a column with a method name that is hard to use in ruby code.
839
726
  def def_bad_column_accessor(column)
727
+ im = instance_methods
840
728
  overridable_methods_module.module_eval do
841
- define_method(column){self[column]}
842
- define_method("#{column}="){|v| self[column] = v}
729
+ meth = :"#{column}="
730
+ define_method(column){self[column]} unless im.include?(column)
731
+ define_method(meth){|v| self[column] = v} unless im.include?(meth)
843
732
  end
844
733
  end
845
734
 
@@ -847,51 +736,28 @@ module Sequel
847
736
  # use a string to define the method for speed. For other columns names, use a block.
848
737
  def def_column_accessor(*columns)
849
738
  clear_setter_methods_cache
850
- columns, bad_columns = columns.partition{|x| NORMAL_METHOD_NAME_REGEXP.match(x.to_s)}
739
+ columns, bad_columns = columns.partition{|x| /\A[A-Za-z_][A-Za-z0-9_]*\z/.match(x.to_s)}
851
740
  bad_columns.each{|x| def_bad_column_accessor(x)}
852
- im = instance_methods.collect(&:to_s)
741
+ im = instance_methods
853
742
  columns.each do |column|
854
- meth = "#{column}="
855
- overridable_methods_module.module_eval("def #{column}; self[:#{column}] end", __FILE__, __LINE__) unless im.include?(column.to_s)
743
+ meth = :"#{column}="
744
+ overridable_methods_module.module_eval("def #{column}; self[:#{column}] end", __FILE__, __LINE__) unless im.include?(column)
856
745
  overridable_methods_module.module_eval("def #{meth}(v); self[:#{column}] = v end", __FILE__, __LINE__) unless im.include?(meth)
857
746
  end
858
747
  end
859
748
 
860
749
  # Define a model method that calls the dataset method with the same name,
861
- # only used for methods with names that can't be presented directly in
750
+ # only used for methods with names that can't be represented directly in
862
751
  # ruby code.
863
752
  def def_model_dataset_method(meth)
864
753
  return if respond_to?(meth, true)
865
754
 
866
- if meth.to_s =~ NORMAL_METHOD_NAME_REGEXP
755
+ if meth.to_s =~ /\A[A-Za-z_][A-Za-z0-9_]*\z/
867
756
  instance_eval("def #{meth}(*args, &block); dataset.#{meth}(*args, &block) end", __FILE__, __LINE__)
868
757
  else
869
- (class << self; self; end).send(:define_method, meth){|*args, &block| dataset.send(meth, *args, &block)}
758
+ define_singleton_method(meth){|*args, &block| dataset.public_send(meth, *args, &block)}
870
759
  end
871
- end
872
-
873
- # Define a finder method in the given module with the given method name that
874
- # load rows using the finder with the given name.
875
- def def_finder_method(mod, meth, type)
876
- mod.send(:define_method, meth){|*args, &block| finder_for(meth).send(type, *args, &block)}
877
- end
878
-
879
- # Define a prepared_finder method in the given module that will call the associated prepared
880
- # statement.
881
- def def_prepare_method(mod, meth)
882
- mod.send(:define_method, meth){|*args, &block| finder_for(meth).call(prepare_method_arg_hash(args), &block)}
883
- end
884
-
885
- # Find the finder to use for the give method. If a finder has not been loaded
886
- # for the method, load the finder and set correctly in the finders hash, then
887
- # return the finder.
888
- def finder_for(meth)
889
- unless finder = Sequel.synchronize{@finders[meth]}
890
- finder_loader = @finder_loaders.fetch(meth)
891
- finder = finder_loader.call(self)
892
- Sequel.synchronize{@finders[meth] = finder}
893
- end
894
- finder
760
+ singleton_class.send(:ruby2_keywords, meth) if respond_to?(:ruby2_keywords, true)
895
761
  end
896
762
 
897
763
  # Get the schema from the database, fall back on checking the columns
@@ -903,14 +769,14 @@ module Sequel
903
769
  schema_hash = {}
904
770
  ds_opts = dataset.opts
905
771
  get_columns = proc{check_non_connection_error{columns} || []}
906
- schema_array = check_non_connection_error{db.schema(dataset, :reload=>reload)} if db.supports_schema_parsing?
772
+ schema_array = check_non_connection_error(false){db.schema(dataset, :reload=>reload)} if db.supports_schema_parsing?
907
773
  if schema_array
908
774
  schema_array.each{|k,v| schema_hash[k] = v}
909
775
 
910
776
  # Set the primary key(s) based on the schema information,
911
777
  # if the schema information includes primary key information
912
778
  if schema_array.all?{|k,v| v.has_key?(:primary_key)}
913
- pks = schema_array.collect{|k,v| k if v[:primary_key]}.compact
779
+ pks = schema_array.map{|k,v| k if v[:primary_key]}.compact
914
780
  pks.length > 0 ? set_primary_key(pks) : no_primary_key
915
781
  end
916
782
 
@@ -925,11 +791,11 @@ module Sequel
925
791
  # Dataset is for a single table with all columns,
926
792
  # so set the columns based on the order they were
927
793
  # returned by the schema.
928
- cols = schema_array.collect{|k,v| k}
794
+ cols = schema_array.map{|k,v| k}
929
795
  set_columns(cols)
930
796
  # Also set the columns for the dataset, so the dataset
931
797
  # doesn't have to do a query to get them.
932
- dataset.instance_variable_set(:@columns, cols)
798
+ dataset.send(:columns=, cols)
933
799
  end
934
800
  else
935
801
  # If the dataset uses multiple tables or custom sql or getting
@@ -943,21 +809,81 @@ module Sequel
943
809
  # Uncached version of setter_methods, to be overridden by plugins
944
810
  # that want to modify the methods used.
945
811
  def get_setter_methods
946
- if allowed_columns
947
- allowed_columns.map{|x| "#{x}="}
948
- else
949
- meths = instance_methods.collect(&:to_s).grep(SETTER_METHOD_REGEXP) - RESTRICTED_SETTER_METHODS
950
- meths -= Array(primary_key).map{|x| "#{x}="} if primary_key && restrict_primary_key?
951
- meths
952
- end
812
+ meths = instance_methods.map(&:to_s).select{|l| l.end_with?('=')} - RESTRICTED_SETTER_METHODS
813
+ meths -= Array(primary_key).map{|x| "#{x}="} if primary_key && restrict_primary_key?
814
+ meths
953
815
  end
954
816
 
817
+ # If possible, set the dataset for the model subclass as soon as it
818
+ # is created. Also, make sure the inherited class instance variables
819
+ # are copied into the subclass.
820
+ #
821
+ # Sequel queries the database to get schema information as soon as
822
+ # a model class is created:
823
+ #
824
+ # class Artist < Sequel::Model # Causes schema query
825
+ # end
826
+ def inherited(subclass)
827
+ super
828
+ ivs = subclass.instance_variables
829
+ inherited_instance_variables.each do |iv, dup|
830
+ if (sup_class_value = instance_variable_get(iv)) && dup
831
+ sup_class_value = case dup
832
+ when :dup
833
+ sup_class_value.dup
834
+ when :hash_dup
835
+ h = {}
836
+ sup_class_value.each{|k,v| h[k] = v.dup}
837
+ h
838
+ when Proc
839
+ dup.call(sup_class_value)
840
+ else
841
+ raise Error, "bad inherited instance variable type: #{dup.inspect}"
842
+ end
843
+ end
844
+ subclass.instance_variable_set(iv, sup_class_value)
845
+ end
846
+
847
+ unless ivs.include?(:@dataset)
848
+ if @dataset && self != Model
849
+ subclass.set_dataset(@dataset.clone, :inherited=>true)
850
+ elsif (n = subclass.name) && !n.to_s.empty?
851
+ db
852
+ subclass.set_dataset(subclass.implicit_table_name)
853
+ end
854
+ end
855
+ end
856
+
955
857
  # A hash of instance variables to automatically set up in subclasses.
956
- # See Sequel::Model::INHERITED_INSTANCE_VARIABLES. It is safe to modify
957
- # the hash returned by this method, though it may not be safe to modify
958
- # values of the hash.
858
+ # Keys are instance variable symbols, values should be:
859
+ # nil :: Assign directly from superclass to subclass (frozen objects)
860
+ # :dup :: Dup object when assigning from superclass to subclass (mutable objects)
861
+ # :hash_dup :: Assign hash with same keys, but dup all the values
862
+ # Proc :: Call with subclass to do the assignment
959
863
  def inherited_instance_variables
960
- INHERITED_INSTANCE_VARIABLES.dup
864
+ {
865
+ :@cache_anonymous_models=>nil,
866
+ :@dataset_method_modules=>:dup,
867
+ :@dataset_module_class=>nil,
868
+ :@db=>nil,
869
+ :@default_set_fields_options=>:dup,
870
+ :@fast_instance_delete_sql=>nil,
871
+ :@fast_pk_lookup_sql=>nil,
872
+ :@plugins=>:dup,
873
+ :@primary_key=>nil,
874
+ :@raise_on_save_failure=>nil,
875
+ :@raise_on_typecast_failure=>nil,
876
+ :@require_modification=>nil,
877
+ :@require_valid_table=>nil,
878
+ :@restrict_primary_key=>nil,
879
+ :@setter_methods=>nil,
880
+ :@simple_pk=>nil,
881
+ :@simple_table=>nil,
882
+ :@strict_param_setting=>nil,
883
+ :@typecast_empty_string_to_nil=>nil,
884
+ :@typecast_on_assignment=>nil,
885
+ :@use_transactions=>nil
886
+ }
961
887
  end
962
888
 
963
889
  # For the given opts hash and default name or :class option, add a
@@ -968,11 +894,24 @@ module Sequel
968
894
  case opts[:class]
969
895
  when String, Symbol
970
896
  # Delete :class to allow late binding
971
- opts[:class_name] ||= opts.delete(:class).to_s
897
+ class_name = opts.delete(:class).to_s
898
+
899
+ if (namespace = opts[:class_namespace]) && !class_name.start_with?('::')
900
+ class_name = "::#{namespace}::#{class_name}"
901
+ end
902
+
903
+ opts[:class_name] ||= class_name
972
904
  when Class
973
905
  opts[:class_name] ||= opts[:class].name
974
906
  end
975
- opts[:class_name] ||= ((name || '').split("::")[0..-2] + [camelize(default)]).join('::')
907
+
908
+ opts[:class_name] ||= '::' + ((name || '').split("::")[0..-2] + [camelize(default)]).join('::')
909
+ end
910
+
911
+ # Clear the setter_methods cache when a setter method is added.
912
+ def method_added(meth)
913
+ clear_setter_methods_cache if meth.to_s.end_with?('=')
914
+ super
976
915
  end
977
916
 
978
917
  # Module that the class includes that holds methods the class adds for column accessors and
@@ -986,51 +925,12 @@ module Sequel
986
925
  # defined, the corresponding plugin required.
987
926
  def plugin_module(plugin)
988
927
  module_name = plugin.to_s.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
989
- if !Sequel::Plugins.const_defined?(module_name) ||
990
- (Sequel.const_defined?(module_name) &&
991
- Sequel::Plugins.const_get(module_name) == Sequel.const_get(module_name))
992
- begin
993
- require "sequel/plugins/#{plugin}"
994
- rescue LoadError => e
995
- begin
996
- require "sequel_#{plugin}"
997
- rescue LoadError => e2
998
- e.message << "; #{e2.message}"
999
- raise e
1000
- end
1001
- end
928
+ unless Sequel::Plugins.const_defined?(module_name, false)
929
+ require "sequel/plugins/#{plugin}"
1002
930
  end
1003
931
  Sequel::Plugins.const_get(module_name)
1004
932
  end
1005
933
 
1006
- # Check if the plugin module +plugin+ defines the constant named by +submod+.
1007
- def plugin_module_defined?(plugin, submod)
1008
- if RUBY_VERSION >= '1.9'
1009
- plugin.const_defined?(submod, false)
1010
- else
1011
- # :nocov:
1012
- plugin.const_defined?(submod)
1013
- # :nocov:
1014
- end
1015
- end
1016
-
1017
- # An hash of prepared argument values for the given arguments, with keys
1018
- # starting at a. Used by the methods created by prepared_finder.
1019
- def prepare_method_arg_hash(args)
1020
- h = {}
1021
- prepare_method_args('a', args.length).zip(args).each{|k, v| h[k] = v}
1022
- h
1023
- end
1024
-
1025
- # An array of prepared statement argument names, of length n and starting with base.
1026
- def prepare_method_args(base, n)
1027
- (0...n).map do
1028
- s = base.to_sym
1029
- base = base.next
1030
- s
1031
- end
1032
- end
1033
-
1034
934
  # Find the row in the dataset that matches the primary key. Uses
1035
935
  # a static SQL optimization if the table and primary key are simple.
1036
936
  #
@@ -1044,10 +944,8 @@ module Sequel
1044
944
  ds.literal_append(sql, pk)
1045
945
  ds.fetch_rows(sql){|r| return ds.row_proc.call(r)}
1046
946
  nil
1047
- elsif dataset.joined_dataset?
1048
- first_where(qualified_primary_key_hash(pk))
1049
947
  else
1050
- first_where(primary_key_hash(pk))
948
+ dataset.first(primary_key_hash(pk))
1051
949
  end
1052
950
  end
1053
951
 
@@ -1060,18 +958,17 @@ module Sequel
1060
958
  # are used, or set it to nil if not used.
1061
959
  def reset_fast_pk_lookup_sql
1062
960
  @fast_pk_lookup_sql = if @simple_table && @simple_pk
1063
- "SELECT * FROM #@simple_table WHERE #@simple_pk = ".freeze
961
+ "SELECT * FROM #{@simple_table} WHERE #{@simple_pk} = ".freeze
1064
962
  end
1065
963
  @fast_instance_delete_sql = if @simple_table && @simple_pk
1066
- "DELETE FROM #@simple_table WHERE #@simple_pk = ".freeze
964
+ "DELETE FROM #{@simple_table} WHERE #{@simple_pk} = ".freeze
1067
965
  end
1068
966
  end
1069
967
 
1070
968
  # Reset the instance dataset to a modified copy of the current dataset,
1071
969
  # should be used whenever the model's dataset is modified.
1072
970
  def reset_instance_dataset
1073
- @finders.clear if @finders
1074
- @instance_dataset = @dataset.limit(1).naked if @dataset
971
+ @instance_dataset = @dataset.limit(1).naked.skip_limit_check if @dataset
1075
972
  end
1076
973
 
1077
974
  # Set the columns for this model and create accessor methods for each column.
@@ -1083,7 +980,7 @@ module Sequel
1083
980
 
1084
981
  # Set the dataset's row_proc to the current model.
1085
982
  def set_dataset_row_proc(ds)
1086
- ds.row_proc = self
983
+ ds.with_row_proc(self)
1087
984
  end
1088
985
 
1089
986
  # Reset the fast primary key lookup SQL when the simple_pk value changes.
@@ -1107,36 +1004,43 @@ module Sequel
1107
1004
 
1108
1005
  # Sequel::Model instance methods that implement basic model functionality.
1109
1006
  #
1110
- # * All of the methods in +HOOKS+ and +AROUND_HOOKS+ create instance methods that are called
1007
+ # * All of the model before/after/around hooks are implemented as instance methods that are called
1111
1008
  # by Sequel when the appropriate action occurs. For example, when destroying
1112
1009
  # a model object, Sequel will call +around_destroy+, which will call +before_destroy+, do
1113
1010
  # the destroy, and then call +after_destroy+.
1114
1011
  # * The following instance_methods all call the class method of the same
1115
1012
  # name: columns, db, primary_key, db_schema.
1116
- # * All of the methods in +BOOLEAN_SETTINGS+ create attr_writers allowing you
1117
- # to set values for the attribute. It also creates instance getters returning
1118
- # the value of the setting. If the value has not yet been set, it
1119
- # gets the default value from the class by calling the class method of the same name.
1013
+ # * The following accessor methods are defined via metaprogramming:
1014
+ # raise_on_save_failure, raise_on_typecast_failure, require_modification,
1015
+ # strict_param_setting, typecast_empty_string_to_nil, typecast_on_assignment,
1016
+ # and use_transactions. The setter methods will change the setting for the
1017
+ # instance, and the getter methods will check for an instance setting, then
1018
+ # try the class setting if no instance setting has been set.
1120
1019
  module InstanceMethods
1121
1020
  HOOKS.each{|h| class_eval("def #{h}; end", __FILE__, __LINE__)}
1122
- AROUND_HOOKS.each{|h| class_eval("def #{h}; yield end", __FILE__, __LINE__)}
1021
+ [:around_create, :around_update, :around_save, :around_destroy, :around_validation].each{|h| class_eval("def #{h}; yield end", __FILE__, __LINE__)}
1123
1022
 
1124
1023
  # Define instance method(s) that calls class method(s) of the
1125
1024
  # same name. Replaces the construct:
1126
1025
  #
1127
- # define_method(meth){self.class.send(meth)}
1026
+ # define_method(meth){self.class.public_send(meth)}
1128
1027
  [:columns, :db, :primary_key, :db_schema].each{|meth| class_eval("def #{meth}; self.class.#{meth} end", __FILE__, __LINE__)}
1129
1028
 
1130
1029
  # Define instance method(s) that calls class method(s) of the
1131
1030
  # same name, caching the result in an instance variable. Define
1132
1031
  # standard attr_writer method for modifying that instance variable.
1133
- BOOLEAN_SETTINGS.each{|meth| class_eval("def #{meth}; !defined?(@#{meth}) ? (frozen? ? self.class.#{meth} : (@#{meth} = self.class.#{meth})) : @#{meth} end", __FILE__, __LINE__)}
1134
- attr_writer(*BOOLEAN_SETTINGS)
1032
+ [:typecast_empty_string_to_nil, :typecast_on_assignment, :strict_param_setting,
1033
+ :raise_on_save_failure, :raise_on_typecast_failure, :require_modification, :use_transactions].each do |meth|
1034
+ class_eval("def #{meth}; !defined?(@#{meth}) ? (frozen? ? self.class.#{meth} : (@#{meth} = self.class.#{meth})) : @#{meth} end", __FILE__, __LINE__)
1035
+ attr_writer(meth)
1036
+ end
1135
1037
 
1136
1038
  # The hash of attribute values. Keys are symbols with the names of the
1137
- # underlying database columns.
1039
+ # underlying database columns. The returned hash is a reference to the
1040
+ # receiver's values hash, and modifying it will also modify the receiver's
1041
+ # values.
1138
1042
  #
1139
- # Artist.new(:name=>'Bob').values # => {:name=>'Bob'}
1043
+ # Artist.new(name: 'Bob').values # => {:name=>'Bob'}
1140
1044
  # Artist[1].values # => {:id=>1, :name=>'Jim', ...}
1141
1045
  attr_reader :values
1142
1046
  alias to_hash values
@@ -1147,7 +1051,7 @@ module Sequel
1147
1051
  # method names.
1148
1052
  alias get_column_value send
1149
1053
 
1150
- # Set the value of the column. Takes two argument. The first is a
1054
+ # Set the value of the column. Takes two arguments. The first is a
1151
1055
  # symbol or string argument for the column name, suffixed with =. The
1152
1056
  # second is the value to set for the column. By default it calls send
1153
1057
  # with the argument to set the value. This can be overridden if you have
@@ -1161,17 +1065,17 @@ module Sequel
1161
1065
  # Arguments:
1162
1066
  # values :: should be a hash to pass to set.
1163
1067
  #
1164
- # Artist.new(:name=>'Bob')
1068
+ # Artist.new(name: 'Bob')
1165
1069
  #
1166
1070
  # Artist.new do |a|
1167
1071
  # a.name = 'Bob'
1168
1072
  # end
1169
- def initialize(values = {})
1073
+ def initialize(values = OPTS)
1170
1074
  @values = {}
1171
1075
  @new = true
1172
1076
  @modified = true
1173
1077
  initialize_set(values)
1174
- changed_columns.clear
1078
+ _clear_changed_columns(:initialize)
1175
1079
  yield self if block_given?
1176
1080
  end
1177
1081
 
@@ -1207,16 +1111,33 @@ module Sequel
1207
1111
  eql?(obj)
1208
1112
  end
1209
1113
 
1210
- # If pk is not nil, true only if the objects have the same class and pk.
1211
- # If pk is nil, false.
1114
+ # Case equality. By default, checks equality of the primary key value, see
1115
+ # pk_equal?.
1212
1116
  #
1213
- # Artist[1] === Artist[1] # true
1214
- # Artist.new === Artist.new # false
1215
- # Artist[1].set(:name=>'Bob') == Artist[1] # => true
1117
+ # Artist[1] === Artist[1] # => true
1118
+ # Artist.new === Artist.new # => false
1119
+ # Artist[1].set(:name=>'Bob') === Artist[1] # => true
1216
1120
  def ===(obj)
1217
- pk.nil? ? false : (obj.class == model) && (obj.pk == pk)
1121
+ case pkv = pk
1122
+ when nil
1123
+ return false
1124
+ when Array
1125
+ return false if pkv.any?(&:nil?)
1126
+ end
1127
+
1128
+ (obj.class == model) && (obj.pk == pkv)
1218
1129
  end
1219
-
1130
+
1131
+ # If the receiver has a primary key value, returns true if the objects have
1132
+ # the same class and primary key value.
1133
+ # If the receiver's primary key value is nil or is an array containing
1134
+ # nil, returns false.
1135
+ #
1136
+ # Artist[1].pk_equal?(Artist[1]) # => true
1137
+ # Artist.new.pk_equal?(Artist.new) # => false
1138
+ # Artist[1].set(:name=>'Bob').pk_equal?(Artist[1]) # => true
1139
+ alias pk_equal? ===
1140
+
1220
1141
  # class is defined in Object, but it is also a keyword,
1221
1142
  # and since a lot of instance methods call class methods,
1222
1143
  # this alias makes it so you can use model instead of
@@ -1248,9 +1169,9 @@ module Sequel
1248
1169
  # a.name = 'Bob'
1249
1170
  # a.changed_columns # => [:name]
1250
1171
  def changed_columns
1251
- @changed_columns ||= []
1172
+ _changed_columns
1252
1173
  end
1253
-
1174
+
1254
1175
  # Deletes and returns +self+. Does not run destroy hooks.
1255
1176
  # Look into using +destroy+ instead.
1256
1177
  #
@@ -1263,11 +1184,8 @@ module Sequel
1263
1184
  end
1264
1185
 
1265
1186
  # Like delete but runs hooks before and after delete.
1266
- # If before_destroy returns false, returns false without
1267
- # deleting the object from the database. Otherwise, deletes
1268
- # the item from the database and returns self. Uses a transaction
1269
- # if use_transactions is true or if the :transaction option is given and
1270
- # true.
1187
+ # Uses a transaction if use_transactions is true or if the
1188
+ # :transaction option is given and true.
1271
1189
  #
1272
1190
  # Artist[1].destroy # BEGIN; DELETE FROM artists WHERE (id = 1); COMMIT;
1273
1191
  # # => #<Artist {:id=>1, ...}>
@@ -1326,12 +1244,12 @@ module Sequel
1326
1244
  # errors, or dataset.
1327
1245
  def freeze
1328
1246
  values.freeze
1329
- changed_columns.freeze
1247
+ _changed_columns.freeze
1330
1248
  unless errors.frozen?
1331
1249
  validate
1332
1250
  errors.freeze
1333
1251
  end
1334
- this.freeze if !new? && model.primary_key
1252
+ this if !new? && model.primary_key
1335
1253
  super
1336
1254
  end
1337
1255
 
@@ -1339,9 +1257,9 @@ module Sequel
1339
1257
  # the same class and values (if pk is nil).
1340
1258
  #
1341
1259
  # Artist[1].hash == Artist[1].hash # true
1342
- # Artist[1].set(:name=>'Bob').hash == Artist[1].hash # true
1260
+ # Artist[1].set(name: 'Bob').hash == Artist[1].hash # true
1343
1261
  # Artist.new.hash == Artist.new.hash # true
1344
- # Artist.new(:name=>'Bob').hash == Artist.new.hash # false
1262
+ # Artist.new(name: 'Bob').hash == Artist.new.hash # false
1345
1263
  def hash
1346
1264
  case primary_key
1347
1265
  when Array
@@ -1370,23 +1288,37 @@ module Sequel
1370
1288
  # Returns the keys in +values+. May not include all column names.
1371
1289
  #
1372
1290
  # Artist.new.keys # => []
1373
- # Artist.new(:name=>'Bob').keys # => [:name]
1291
+ # Artist.new(name: 'Bob').keys # => [:name]
1374
1292
  # Artist[1].keys # => [:id, :name]
1375
1293
  def keys
1376
1294
  @values.keys
1377
1295
  end
1378
1296
 
1379
- # Refresh this record using +for_update+ unless this is a new record. Returns self.
1380
- # This can be used to make sure no other process is updating the record at the
1381
- # same time.
1297
+ # Refresh this record using +for_update+ (by default, or the specified style when given)
1298
+ # unless this is a new record. Returns self. This can be used to make sure no other
1299
+ # process is updating the record at the same time.
1300
+ #
1301
+ # If style is a string, it will be used directly. You should never pass a string
1302
+ # to this method that is derived from user input, as that can lead to
1303
+ # SQL injection.
1304
+ #
1305
+ # A symbol may be used for database independent locking behavior, but
1306
+ # all supported symbols have separate methods (e.g. for_update).
1307
+ #
1382
1308
  #
1383
1309
  # a = Artist[1]
1384
1310
  # Artist.db.transaction do
1385
1311
  # a.lock!
1386
1312
  # a.update(:name=>'A')
1387
1313
  # end
1388
- def lock!
1389
- _refresh(this.for_update) unless new?
1314
+ #
1315
+ # a = Artist[2]
1316
+ # Artist.db.transaction do
1317
+ # a.lock!('FOR NO KEY UPDATE')
1318
+ # a.update(:name=>'B')
1319
+ # end
1320
+ def lock!(style=:update)
1321
+ _refresh(this.lock_style(style)) unless new?
1390
1322
  self
1391
1323
  end
1392
1324
 
@@ -1416,9 +1348,7 @@ module Sequel
1416
1348
  # a.modified!(:name)
1417
1349
  # a.name.gsub!(/[aeou]/, 'i')
1418
1350
  def modified!(column=nil)
1419
- if column && !changed_columns.include?(column)
1420
- changed_columns << column
1421
- end
1351
+ _add_changed_column(column) if column
1422
1352
  @modified = true
1423
1353
  end
1424
1354
 
@@ -1428,7 +1358,7 @@ module Sequel
1428
1358
  #
1429
1359
  # a = Artist[1]
1430
1360
  # a.modified? # => false
1431
- # a.set(:name=>'Jim')
1361
+ # a.set(name: 'Jim')
1432
1362
  # a.modified? # => true
1433
1363
  #
1434
1364
  # If a column is given, specifically check if the given column has
@@ -1477,12 +1407,12 @@ module Sequel
1477
1407
  model.primary_key_hash(pk)
1478
1408
  end
1479
1409
 
1480
- # Returns a hash mapping the receivers primary key column(s) to their values.
1410
+ # Returns a hash mapping the receivers qualified primary key column(s) to their values.
1481
1411
  #
1482
1412
  # Artist[1].qualified_pk_hash
1483
- # # => {Sequel.qualify(:artists, :id)=>1}
1413
+ # # => {Sequel[:artists][:id]=>1}
1484
1414
  # Artist[[1, 2]].qualified_pk_hash
1485
- # # => {Sequel.qualify(:artists, :id1)=>1, Sequel.qualify(:artists, :id2)=>2}
1415
+ # # => {Sequel[:artists][:id1]=>1, Sequel[:artists][:id2]=>2}
1486
1416
  def qualified_pk_hash(qualifier=model.table_name)
1487
1417
  model.qualified_primary_key_hash(pk, qualifier)
1488
1418
  end
@@ -1510,9 +1440,9 @@ module Sequel
1510
1440
  # is valid and before hooks execute successfully. Fails if:
1511
1441
  #
1512
1442
  # * the record is not valid, or
1513
- # * before_save returns false, or
1514
- # * the record is new and before_create returns false, or
1515
- # * the record is not new and before_update returns false.
1443
+ # * before_save calls cancel_action, or
1444
+ # * the record is new and before_create calls cancel_action, or
1445
+ # * the record is not new and before_update calls cancel_action.
1516
1446
  #
1517
1447
  # If +save+ fails and either raise_on_save_failure or the
1518
1448
  # :raise_on_failure option is true, it raises ValidationFailed
@@ -1520,9 +1450,6 @@ module Sequel
1520
1450
  #
1521
1451
  # If it succeeds, it returns self.
1522
1452
  #
1523
- # You can provide an optional list of columns to update, in which
1524
- # case it only updates those columns, or a options hash.
1525
- #
1526
1453
  # Takes the following options:
1527
1454
  #
1528
1455
  # :changed :: save all changed columns, instead of all columns or the columns given
@@ -1537,12 +1464,9 @@ module Sequel
1537
1464
  def save(opts=OPTS)
1538
1465
  raise Sequel::Error, "can't save frozen object" if frozen?
1539
1466
  set_server(opts[:server]) if opts[:server]
1540
- _before_validation
1541
- if opts[:validate] != false
1542
- unless checked_save_failure(opts){_valid?(true, opts)}
1543
- raise(ValidationFailed.new(self)) if raise_on_failure?(opts)
1544
- return
1545
- end
1467
+ unless _save_valid?(opts)
1468
+ raise(validation_failed_error) if raise_on_failure?(opts)
1469
+ return
1546
1470
  end
1547
1471
  checked_save_failure(opts){checked_transaction(opts){_save(opts)}}
1548
1472
  end
@@ -1565,22 +1489,12 @@ module Sequel
1565
1489
  # a setter method (or ignoring it if <tt>strict_param_setting = false</tt>).
1566
1490
  # Does not save the record.
1567
1491
  #
1568
- # artist.set(:name=>'Jim')
1492
+ # artist.set(name: 'Jim')
1569
1493
  # artist.name # => 'Jim'
1570
1494
  def set(hash)
1571
1495
  set_restricted(hash, :default)
1572
1496
  end
1573
1497
 
1574
- # Set all values using the entries in the hash, ignoring any setting of
1575
- # allowed_columns in the model.
1576
- #
1577
- # Artist.set_allowed_columns(:num_albums)
1578
- # artist.set_all(:name=>'Jim')
1579
- # artist.name # => 'Jim'
1580
- def set_all(hash)
1581
- set_restricted(hash, :all)
1582
- end
1583
-
1584
1498
  # For each of the fields in the given array +fields+, call the setter
1585
1499
  # method with the value of that +hash+ entry for the field. Returns self.
1586
1500
  #
@@ -1593,43 +1507,36 @@ module Sequel
1593
1507
  #
1594
1508
  # Examples:
1595
1509
  #
1596
- # artist.set_fields({:name=>'Jim'}, [:name])
1510
+ # artist.set_fields({name: 'Jim'}, [:name])
1597
1511
  # artist.name # => 'Jim'
1598
1512
  #
1599
- # artist.set_fields({:hometown=>'LA'}, [:name])
1513
+ # artist.set_fields({hometown: 'LA'}, [:name])
1600
1514
  # artist.name # => nil
1601
1515
  # artist.hometown # => 'Sac'
1602
1516
  #
1603
1517
  # artist.name # => 'Jim'
1604
- # artist.set_fields({}, [:name], :missing=>:skip)
1518
+ # artist.set_fields({}, [:name], missing: :skip)
1605
1519
  # artist.name # => 'Jim'
1606
1520
  #
1607
1521
  # artist.name # => 'Jim'
1608
- # artist.set_fields({}, [:name], :missing=>:raise)
1522
+ # artist.set_fields({}, [:name], missing: :raise)
1609
1523
  # # Sequel::Error raised
1610
1524
  def set_fields(hash, fields, opts=nil)
1611
1525
  opts = if opts
1612
- Hash[model.default_set_fields_options].merge!(opts)
1526
+ model.default_set_fields_options.merge(opts)
1613
1527
  else
1614
1528
  model.default_set_fields_options
1615
1529
  end
1616
1530
 
1617
- case opts[:missing]
1618
- when :skip
1531
+ case missing = opts[:missing]
1532
+ when :skip, :raise
1533
+ do_raise = true if missing == :raise
1619
1534
  fields.each do |f|
1620
1535
  if hash.has_key?(f)
1621
1536
  set_column_value("#{f}=", hash[f])
1622
1537
  elsif f.is_a?(Symbol) && hash.has_key?(sf = f.to_s)
1623
1538
  set_column_value("#{sf}=", hash[sf])
1624
- end
1625
- end
1626
- when :raise
1627
- fields.each do |f|
1628
- if hash.has_key?(f)
1629
- set_column_value("#{f}=", hash[f])
1630
- elsif f.is_a?(Symbol) && hash.has_key?(sf = f.to_s)
1631
- set_column_value("#{sf}=", hash[sf])
1632
- else
1539
+ elsif do_raise
1633
1540
  raise(Sequel::Error, "missing field in hash: #{f.inspect} not in #{hash.inspect}")
1634
1541
  end
1635
1542
  end
@@ -1639,31 +1546,28 @@ module Sequel
1639
1546
  self
1640
1547
  end
1641
1548
 
1642
- # Set the values using the entries in the hash, only if the key
1643
- # is included in only. It may be a better idea to use +set_fields+
1644
- # instead of this method.
1645
- #
1646
- # artist.set_only({:name=>'Jim'}, :name)
1647
- # artist.name # => 'Jim'
1648
- #
1649
- # artist.set_only({:hometown=>'LA'}, :name) # Raise Error
1650
- def set_only(hash, *only)
1651
- set_restricted(hash, only.flatten)
1652
- end
1653
-
1654
1549
  # Set the shard that this object is tied to. Returns self.
1655
1550
  def set_server(s)
1656
1551
  @server = s
1657
- @this.opts[:server] = s if @this
1552
+ @this = @this.server(s) if @this
1658
1553
  self
1659
1554
  end
1660
1555
 
1661
1556
  # Clear the setter_methods cache when a method is added
1662
1557
  def singleton_method_added(meth)
1663
- @singleton_setter_added = true if meth.to_s =~ SETTER_METHOD_REGEXP
1558
+ @singleton_setter_added = true if meth.to_s.end_with?('=')
1664
1559
  super
1665
1560
  end
1666
1561
 
1562
+ # Skip all validation of the object on the next call to #save,
1563
+ # including the running of validation hooks. This is designed for
1564
+ # and should only be used in cases where #valid? is called before
1565
+ # saving and the <tt>validate: false</tt> option cannot be passed to
1566
+ # #save.
1567
+ def skip_validation_on_next_save!
1568
+ @skip_validation_on_next_save = true
1569
+ end
1570
+
1667
1571
  # Returns (naked) dataset that should return only this instance.
1668
1572
  #
1669
1573
  # Artist[1].this
@@ -1671,57 +1575,29 @@ module Sequel
1671
1575
  def this
1672
1576
  return @this if @this
1673
1577
  raise Error, "No dataset for model #{model}" unless ds = model.instance_dataset
1674
-
1675
- cond = if ds.joined_dataset?
1676
- qualified_pk_hash
1677
- else
1678
- pk_hash
1679
- end
1680
-
1681
- @this = use_server(ds.where(cond))
1578
+ @this = use_server(ds.where(pk_hash))
1682
1579
  end
1683
1580
 
1684
1581
  # Runs #set with the passed hash and then runs save_changes.
1685
1582
  #
1686
- # artist.update(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
1583
+ # artist.update(name: 'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
1687
1584
  def update(hash)
1688
1585
  update_restricted(hash, :default)
1689
1586
  end
1690
1587
 
1691
- # Update all values using the entries in the hash, ignoring any setting of
1692
- # +allowed_columns+ in the model.
1693
- #
1694
- # Artist.set_allowed_columns(:num_albums)
1695
- # artist.update_all(:name=>'Jim') # UPDATE artists SET name = 'Jim' WHERE (id = 1)
1696
- def update_all(hash)
1697
- update_restricted(hash, :all)
1698
- end
1699
-
1700
- # Update the instances values by calling +set_fields+ with the arguments, then
1701
- # saves any changes to the record. Returns self.
1588
+ # Update the instance's values by calling set_fields with the arguments, then
1589
+ # calls save_changes.
1702
1590
  #
1703
- # artist.update_fields({:name=>'Jim'}, [:name])
1591
+ # artist.update_fields({name: 'Jim'}, [:name])
1704
1592
  # # UPDATE artists SET name = 'Jim' WHERE (id = 1)
1705
1593
  #
1706
- # artist.update_fields({:hometown=>'LA'}, [:name])
1594
+ # artist.update_fields({hometown: 'LA'}, [:name])
1707
1595
  # # UPDATE artists SET name = NULL WHERE (id = 1)
1708
1596
  def update_fields(hash, fields, opts=nil)
1709
1597
  set_fields(hash, fields, opts)
1710
1598
  save_changes
1711
1599
  end
1712
1600
 
1713
- # Update the values using the entries in the hash, only if the key
1714
- # is included in only. It may be a better idea to use +update_fields+
1715
- # instead of this method.
1716
- #
1717
- # artist.update_only({:name=>'Jim'}, :name)
1718
- # # UPDATE artists SET name = 'Jim' WHERE (id = 1)
1719
- #
1720
- # artist.update_only({:hometown=>'LA'}, :name) # Raise Error
1721
- def update_only(hash, *only)
1722
- update_restricted(hash, only.flatten)
1723
- end
1724
-
1725
1601
  # Validates the object. If the object is invalid, errors should be added
1726
1602
  # to the errors attribute. By default, does nothing, as all models
1727
1603
  # are valid by default. See the {"Model Validations" guide}[rdoc-ref:doc/validations.rdoc].
@@ -1733,25 +1609,37 @@ module Sequel
1733
1609
 
1734
1610
  # Validates the object and returns true if no errors are reported.
1735
1611
  #
1736
- # artist(:name=>'Valid').valid? # => true
1737
- # artist(:name=>'Invalid').valid? # => false
1612
+ # artist.set(name: 'Valid').valid? # => true
1613
+ # artist.set(name: 'Invalid').valid? # => false
1738
1614
  # artist.errors.full_messages # => ['name cannot be Invalid']
1739
1615
  def valid?(opts = OPTS)
1740
- _before_validation
1741
- _valid?(false, opts)
1616
+ begin
1617
+ _valid?(opts)
1618
+ rescue HookFailed
1619
+ false
1620
+ end
1742
1621
  end
1743
1622
 
1744
1623
  private
1745
1624
 
1746
- # Run code before any validation is done, but also run it before saving
1747
- # even if validation is skipped. This is a private hook. It exists so that
1748
- # plugins can set values automatically before validation (as the values
1749
- # need to be validated), but should be set even if validation is skipped.
1750
- # Unlike the regular before_validation hook, we do not skip the save/validation
1751
- # if this returns false.
1752
- def _before_validation
1625
+ # Add a column as a changed column.
1626
+ def _add_changed_column(column)
1627
+ cc = _changed_columns
1628
+ cc << column unless cc.include?(column)
1629
+ end
1630
+
1631
+ # Internal changed_columns method that just returns stored array.
1632
+ def _changed_columns
1633
+ @changed_columns ||= []
1753
1634
  end
1754
1635
 
1636
+ # Clear the changed columns. Reason is the reason for clearing
1637
+ # the columns, and should be one of: :initialize, :refresh, :create
1638
+ # or :update.
1639
+ def _clear_changed_columns(_reason)
1640
+ _changed_columns.clear
1641
+ end
1642
+
1755
1643
  # Do the deletion of the object's dataset, and check that the row
1756
1644
  # was actually deleted.
1757
1645
  def _delete
@@ -1760,7 +1648,7 @@ module Sequel
1760
1648
  n
1761
1649
  end
1762
1650
 
1763
- # The dataset to use when deleting the object. The same as the object's
1651
+ # The dataset to use when deleting the object. The same as the object's
1764
1652
  # dataset by default.
1765
1653
  def _delete_dataset
1766
1654
  this
@@ -1782,18 +1670,14 @@ module Sequel
1782
1670
  # Internal destroy method, separted from destroy to
1783
1671
  # allow running inside a transaction
1784
1672
  def _destroy(opts)
1785
- sh = {:server=>this_server}
1786
- db.after_rollback(sh){after_destroy_rollback} if uacr = use_after_commit_rollback
1787
1673
  called = false
1788
1674
  around_destroy do
1789
1675
  called = true
1790
- raise_hook_failure(:before_destroy) if before_destroy == false
1676
+ before_destroy
1791
1677
  _destroy_delete
1792
1678
  after_destroy
1793
- true
1794
1679
  end
1795
1680
  raise_hook_failure(:around_destroy) unless called
1796
- db.after_commit(sh){after_destroy_commit} if uacr
1797
1681
  self
1798
1682
  end
1799
1683
 
@@ -1808,8 +1692,8 @@ module Sequel
1808
1692
  # the record should be refreshed from the database.
1809
1693
  def _insert
1810
1694
  ds = _insert_dataset
1811
- if _use_insert_select?(ds) && (h = _insert_select_raw(ds))
1812
- _save_set_values(h)
1695
+ if _use_insert_select?(ds) && !(h = _insert_select_raw(ds)).nil?
1696
+ _save_set_values(h) if h
1813
1697
  nil
1814
1698
  else
1815
1699
  iid = _insert_raw(ds)
@@ -1830,27 +1714,38 @@ module Sequel
1830
1714
 
1831
1715
  # Insert into the given dataset and return the primary key created (if any).
1832
1716
  def _insert_raw(ds)
1833
- ds.insert(@values)
1717
+ ds.insert(_insert_values)
1834
1718
  end
1835
1719
 
1836
1720
  # Insert into the given dataset and return the hash of column values.
1837
1721
  def _insert_select_raw(ds)
1838
- ds.insert_select(@values)
1722
+ ds.insert_select(_insert_values)
1839
1723
  end
1724
+
1725
+ # The values hash to use when inserting a new record.
1726
+ alias _insert_values values
1727
+ private :_insert_values
1840
1728
 
1841
1729
  # Refresh using a particular dataset, used inside save to make sure the same server
1842
1730
  # is used for reading newly inserted values from the database
1843
1731
  def _refresh(dataset)
1844
- _refresh_set_values(_refresh_get(dataset) || raise(Error, "Record not found"))
1845
- changed_columns.clear
1732
+ _refresh_set_values(_refresh_get(dataset) || raise(NoExistingObject, "Record not found"))
1733
+ _clear_changed_columns(:refresh)
1846
1734
  end
1847
1735
 
1848
1736
  # Get the row of column data from the database.
1849
1737
  def _refresh_get(dataset)
1850
- dataset.first
1738
+ if (sql = model.fast_pk_lookup_sql) && !dataset.opts[:lock]
1739
+ sql = sql.dup
1740
+ ds = use_server(dataset)
1741
+ ds.literal_append(sql, pk)
1742
+ ds.with_sql_first(sql)
1743
+ else
1744
+ dataset.first
1745
+ end
1851
1746
  end
1852
1747
 
1853
- # Set the refreshed values after
1748
+ # Set the values to the given hash after refreshing.
1854
1749
  def _refresh_set_values(h)
1855
1750
  @values = h
1856
1751
  end
@@ -1858,24 +1753,22 @@ module Sequel
1858
1753
  # Internal version of save, split from save to allow running inside
1859
1754
  # it's own transaction.
1860
1755
  def _save(opts)
1861
- sh = {:server=>this_server}
1862
- db.after_rollback(sh){after_rollback} if uacr = use_after_commit_rollback
1863
- was_new = false
1864
1756
  pk = nil
1865
1757
  called_save = false
1866
1758
  called_cu = false
1867
1759
  around_save do
1868
1760
  called_save = true
1869
- raise_hook_failure(:before_save) if before_save == false
1761
+ before_save
1762
+
1870
1763
  if new?
1871
- was_new = true
1872
1764
  around_create do
1873
1765
  called_cu = true
1874
- raise_hook_failure(:before_create) if before_create == false
1766
+ before_create
1875
1767
  pk = _insert
1876
1768
  @this = nil
1877
1769
  @new = false
1878
- @was_new = true
1770
+ @modified = false
1771
+ pk ? _save_refresh : _clear_changed_columns(:create)
1879
1772
  after_create
1880
1773
  true
1881
1774
  end
@@ -1883,22 +1776,23 @@ module Sequel
1883
1776
  else
1884
1777
  around_update do
1885
1778
  called_cu = true
1886
- raise_hook_failure(:before_update) if before_update == false
1779
+ before_update
1887
1780
  columns = opts[:columns]
1888
1781
  if columns.nil?
1889
- @columns_updated = if opts[:changed]
1890
- @values.reject{|k,v| !changed_columns.include?(k)}
1782
+ columns_updated = if opts[:changed]
1783
+ _save_update_changed_colums_hash
1891
1784
  else
1892
1785
  _save_update_all_columns_hash
1893
1786
  end
1894
- changed_columns.clear
1787
+ _clear_changed_columns(:update)
1895
1788
  else # update only the specified columns
1896
1789
  columns = Array(columns)
1897
- @columns_updated = @values.reject{|k, v| !columns.include?(k)}
1898
- changed_columns.reject!{|c| columns.include?(c)}
1790
+ columns_updated = @values.reject{|k, v| !columns.include?(k)}
1791
+ _changed_columns.reject!{|c| columns.include?(c)}
1899
1792
  end
1900
- _update_columns(@columns_updated)
1793
+ _update_columns(columns_updated)
1901
1794
  @this = nil
1795
+ @modified = false
1902
1796
  after_update
1903
1797
  true
1904
1798
  end
@@ -1908,14 +1802,6 @@ module Sequel
1908
1802
  true
1909
1803
  end
1910
1804
  raise_hook_failure(:around_save) unless called_save
1911
- if was_new
1912
- @was_new = nil
1913
- pk ? _save_refresh : changed_columns.clear
1914
- else
1915
- @columns_updated = nil
1916
- end
1917
- @modified = false
1918
- db.after_commit(sh){after_commit} if uacr
1919
1805
  self
1920
1806
  end
1921
1807
 
@@ -1923,8 +1809,8 @@ module Sequel
1923
1809
  # default values of all columns. Separated from _save so it
1924
1810
  # can be overridden to avoid the refresh.
1925
1811
  def _save_refresh
1926
- _save_set_values(_refresh_get(this.server?(:default)) || raise(Error, "Record not found"))
1927
- changed_columns.clear
1812
+ _save_set_values(_refresh_get(this.server?(:default)) || raise(NoExistingObject, "Record not found"))
1813
+ _clear_changed_columns(:create)
1928
1814
  end
1929
1815
 
1930
1816
  # Set values to the provided hash. Called after a create,
@@ -1941,10 +1827,32 @@ module Sequel
1941
1827
  # to their existing values.
1942
1828
  def _save_update_all_columns_hash
1943
1829
  v = Hash[@values]
1944
- Array(primary_key).each{|x| v.delete(x) unless changed_columns.include?(x)}
1830
+ cc = changed_columns
1831
+ Array(primary_key).each{|x| v.delete(x) unless cc.include?(x)}
1945
1832
  v
1946
1833
  end
1947
1834
 
1835
+ # Return a hash of values used when saving changed columns of an
1836
+ # existing object. Defaults to all of the objects current values
1837
+ # that are recorded as modified.
1838
+ def _save_update_changed_colums_hash
1839
+ cc = changed_columns
1840
+ @values.reject{|k,v| !cc.include?(k)}
1841
+ end
1842
+
1843
+ # Validate the object if validating on save. Skips validation
1844
+ # completely (including validation hooks) if
1845
+ # skip_validation_on_save! has been called on the object,
1846
+ # resetting the flag so that future saves will validate.
1847
+ def _save_valid?(opts)
1848
+ if @skip_validation_on_next_save
1849
+ @skip_validation_on_next_save = false
1850
+ return true
1851
+ end
1852
+
1853
+ checked_save_failure(opts){_valid?(opts)}
1854
+ end
1855
+
1948
1856
  # Call _update with the given columns, if any are present.
1949
1857
  # Plugins can override this method in order to update with
1950
1858
  # additional columns, even when the column hash is initially empty.
@@ -1976,38 +1884,25 @@ module Sequel
1976
1884
  (!ds.opts[:select] || ds.opts[:returning]) && ds.supports_insert_select?
1977
1885
  end
1978
1886
 
1979
- # Internal validation method. If +raise_errors+ is +true+, hook
1980
- # failures will be raised as HookFailure exceptions. If it is
1981
- # +false+, +false+ will be returned instead.
1982
- def _valid?(raise_errors, opts)
1887
+ # Internal validation method, running validation hooks.
1888
+ def _valid?(opts)
1983
1889
  return errors.empty? if frozen?
1984
1890
  errors.clear
1985
1891
  called = false
1986
- error = false
1892
+ skip_validate = opts[:validate] == false
1987
1893
  around_validation do
1988
1894
  called = true
1989
- if before_validation == false
1990
- if raise_errors
1991
- raise_hook_failure(:before_validation)
1992
- else
1993
- error = true
1994
- end
1995
- false
1996
- else
1997
- validate
1998
- after_validation
1999
- errors.empty?
2000
- end
1895
+ before_validation
1896
+ validate unless skip_validate
1897
+ after_validation
2001
1898
  end
2002
- error = true unless called
2003
- if error
2004
- if raise_errors
2005
- raise_hook_failure(:around_validation)
2006
- else
2007
- false
2008
- end
2009
- else
1899
+
1900
+ return true if skip_validate
1901
+
1902
+ if called
2010
1903
  errors.empty?
1904
+ else
1905
+ raise_hook_failure(:around_validation)
2011
1906
  end
2012
1907
  end
2013
1908
 
@@ -2032,8 +1927,7 @@ module Sequel
2032
1927
 
2033
1928
  # Change the value of the column to given value, recording the change.
2034
1929
  def change_column_value(column, value)
2035
- cc = changed_columns
2036
- cc << column unless cc.include?(column)
1930
+ _add_changed_column(column)
2037
1931
  @values[column] = value
2038
1932
  end
2039
1933
 
@@ -2042,24 +1936,17 @@ module Sequel
2042
1936
  Errors
2043
1937
  end
2044
1938
 
2045
- if RUBY_VERSION >= '1.9'
2046
- # Clone constructor -- freeze internal data structures if the original's
2047
- # are frozen.
2048
- def initialize_clone(other)
2049
- super
2050
- freeze if other.frozen?
2051
- self
2052
- end
2053
- else
2054
- # :nocov:
2055
- # Ruby 1.8 doesn't support initialize_clone, so override clone to dup and freeze.
2056
- def clone
2057
- o = dup
2058
- o.freeze if frozen?
2059
- o
2060
- end
2061
- public :clone
2062
- # :nocov:
1939
+ # A HookFailed exception for the given message tied to the current instance.
1940
+ def hook_failed_error(msg)
1941
+ HookFailed.new(msg, self)
1942
+ end
1943
+
1944
+ # Clone constructor -- freeze internal data structures if the original's
1945
+ # are frozen.
1946
+ def initialize_clone(other)
1947
+ super
1948
+ freeze if other.frozen?
1949
+ self
2063
1950
  end
2064
1951
 
2065
1952
  # Copy constructor -- Duplicate internal data structures.
@@ -2068,7 +1955,6 @@ module Sequel
2068
1955
  @values = Hash[@values]
2069
1956
  @changed_columns = @changed_columns.dup if @changed_columns
2070
1957
  @errors = @errors.dup if @errors
2071
- @this = @this.dup if @this
2072
1958
  self
2073
1959
  end
2074
1960
 
@@ -2104,9 +1990,9 @@ module Sequel
2104
1990
  "a hook failed"
2105
1991
  end
2106
1992
 
2107
- raise HookFailed.new(msg, self)
1993
+ raise hook_failed_error(msg)
2108
1994
  end
2109
-
1995
+
2110
1996
  # Get the ruby class or classes related to the given column's type.
2111
1997
  def schema_type_class(column)
2112
1998
  if (sch = db_schema[column]) && (type = sch[:type])
@@ -2147,19 +2033,13 @@ module Sequel
2147
2033
  # :all :: Allow setting all setters, except those specifically restricted (such as ==).
2148
2034
  # Array :: Only allow setting of columns in the given array.
2149
2035
  def setter_methods(type)
2150
- if type == :default
2151
- if !@singleton_setter_added || model.allowed_columns
2152
- return model.setter_methods
2153
- end
2036
+ if type == :default && !@singleton_setter_added
2037
+ return model.setter_methods
2154
2038
  end
2155
2039
 
2156
- if type.is_a?(Array)
2157
- type.map{|x| "#{x}="}
2158
- else
2159
- meths = methods.collect(&:to_s).grep(SETTER_METHOD_REGEXP) - RESTRICTED_SETTER_METHODS
2160
- meths -= Array(primary_key).map{|x| "#{x}="} if type != :all && primary_key && model.restrict_primary_key?
2161
- meths
2162
- end
2040
+ meths = methods.map(&:to_s).select{|l| l.end_with?('=')} - RESTRICTED_SETTER_METHODS
2041
+ meths -= Array(primary_key).map{|x| "#{x}="} if primary_key && model.restrict_primary_key?
2042
+ meths
2163
2043
  end
2164
2044
 
2165
2045
  # The server/shard that the model object's dataset uses, or :default if the
@@ -2205,22 +2085,28 @@ module Sequel
2205
2085
  def use_transaction?(opts = OPTS)
2206
2086
  opts.fetch(:transaction, use_transactions)
2207
2087
  end
2088
+
2089
+ # An ValidationFailed exception instance to raise for this instance.
2090
+ def validation_failed_error
2091
+ ValidationFailed.new(self)
2092
+ end
2208
2093
  end
2209
2094
 
2210
- # Dataset methods are methods that the model class extends its dataset with in
2211
- # the call to set_dataset.
2095
+ # DatasetMethods contains methods that all model datasets have.
2212
2096
  module DatasetMethods
2213
2097
  # The model class associated with this dataset
2214
2098
  #
2215
2099
  # Artist.dataset.model # => Artist
2216
- attr_accessor :model
2100
+ def model
2101
+ @opts[:model]
2102
+ end
2217
2103
 
2218
2104
  # Assume if a single integer is given that it is a lookup by primary
2219
2105
  # key, and call with_pk with the argument.
2220
2106
  #
2221
2107
  # Artist.dataset[1] # SELECT * FROM artists WHERE (id = 1) LIMIT 1
2222
2108
  def [](*args)
2223
- if args.length == 1 && (i = args.at(0)) && i.is_a?(Integer)
2109
+ if args.length == 1 && (i = args[0]) && i.is_a?(Integer)
2224
2110
  with_pk(i)
2225
2111
  else
2226
2112
  super
@@ -2241,58 +2127,14 @@ module Sequel
2241
2127
  model.use_transactions ? @db.transaction(:server=>opts[:server], &pr) : pr.call
2242
2128
  end
2243
2129
 
2244
- # Allow Sequel::Model classes to be used as dataset arguments when graphing:
2245
- #
2246
- # Artist.graph(Album, :artist_id=>id)
2247
- # # SELECT artists.id, artists.name, albums.id AS albums_id, albums.artist_id, albums.name AS albums_name
2248
- # # FROM artists LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
2249
- def graph(table, *args, &block)
2250
- if table.is_a?(Class) && table < Sequel::Model
2251
- super(table.dataset, *args, &block)
2252
- else
2253
- super
2254
- end
2255
- end
2256
-
2257
- # Handle Sequel::Model instances when inserting, using the model instance's
2258
- # values for the insert, unless the model instance can be used directly in
2259
- # SQL.
2260
- #
2261
- # Album.insert(Album.load(:name=>'A'))
2262
- # # INSERT INTO albums (name) VALUES ('A')
2263
- def insert_sql(*values)
2264
- if values.size == 1 && (v = values.at(0)).is_a?(Sequel::Model) && !v.respond_to?(:sql_literal_append)
2265
- super(v.to_hash)
2266
- else
2267
- super
2268
- end
2269
- end
2270
-
2271
- # Allow Sequel::Model classes to be used as table name arguments in dataset
2272
- # join methods:
2273
- #
2274
- # Artist.join(Album, :artist_id=>id)
2275
- # # SELECT * FROM artists INNER JOIN albums ON (albums.artist_id = artists.id)
2276
- def join_table(type, table, *args, &block)
2277
- if table.is_a?(Class) && table < Sequel::Model
2278
- if table.dataset.simple_select_all?
2279
- super(type, table.table_name, *args, &block)
2280
- else
2281
- super(type, table.dataset, *args, &block)
2282
- end
2283
- else
2284
- super
2285
- end
2286
- end
2287
-
2288
2130
  # If there is no order already defined on this dataset, order it by
2289
2131
  # the primary key and call last.
2290
2132
  #
2291
2133
  # Album.last
2292
2134
  # # SELECT * FROM albums ORDER BY id DESC LIMIT 1
2293
2135
  def last(*a, &block)
2294
- if opts[:order].nil? && model && (pk = model.primary_key)
2295
- order(*pk).last(*a, &block)
2136
+ if ds = _primary_key_order
2137
+ ds.last(*a, &block)
2296
2138
  else
2297
2139
  super
2298
2140
  end
@@ -2307,30 +2149,35 @@ module Sequel
2307
2149
  # # SELECT * FROM albums ORDER BY id LIMIT 1000 OFFSET 2000
2308
2150
  # # ...
2309
2151
  def paged_each(*a, &block)
2310
- if opts[:order].nil? && model && (pk = model.primary_key)
2311
- order(*pk).paged_each(*a, &block)
2152
+ if ds = _primary_key_order
2153
+ ds.paged_each(*a, &block)
2312
2154
  else
2313
2155
  super
2314
2156
  end
2315
2157
  end
2316
2158
 
2317
- # This allows you to call +to_hash+ without any arguments, which will
2159
+ # This allows you to call +as_hash+ without any arguments, which will
2318
2160
  # result in a hash with the primary key value being the key and the
2319
2161
  # model object being the value.
2320
2162
  #
2321
- # Artist.dataset.to_hash # SELECT * FROM artists
2163
+ # Artist.dataset.as_hash # SELECT * FROM artists
2322
2164
  # # => {1=>#<Artist {:id=>1, ...}>,
2323
2165
  # # 2=>#<Artist {:id=>2, ...}>,
2324
2166
  # # ...}
2325
- def to_hash(key_column=nil, value_column=nil)
2167
+ def as_hash(key_column=nil, value_column=nil, opts=OPTS)
2326
2168
  if key_column
2327
2169
  super
2328
2170
  else
2329
2171
  raise(Sequel::Error, "No primary key for model") unless model && (pk = model.primary_key)
2330
- super(pk, value_column)
2172
+ super(pk, value_column, opts)
2331
2173
  end
2332
2174
  end
2333
2175
 
2176
+ # Alias of as_hash for backwards compatibility.
2177
+ def to_hash(*a)
2178
+ as_hash(*a)
2179
+ end
2180
+
2334
2181
  # Given a primary key value, return the first record in the dataset with that primary key
2335
2182
  # value. If no records matches, returns nil.
2336
2183
  #
@@ -2342,7 +2189,11 @@ module Sequel
2342
2189
  # Artist.dataset.with_pk([1, 2])
2343
2190
  # # SELECT * FROM artists WHERE ((artists.id1 = 1) AND (artists.id2 = 2)) LIMIT 1
2344
2191
  def with_pk(pk)
2345
- first(model.qualified_primary_key_hash(pk))
2192
+ if pk && (loader = _with_pk_loader)
2193
+ loader.first(*pk)
2194
+ else
2195
+ first(model.qualified_primary_key_hash(pk))
2196
+ end
2346
2197
  end
2347
2198
 
2348
2199
  # Same as with_pk, but raises NoMatchingRow instead of returning nil if no
@@ -2350,10 +2201,47 @@ module Sequel
2350
2201
  def with_pk!(pk)
2351
2202
  with_pk(pk) || raise(NoMatchingRow.new(self))
2352
2203
  end
2204
+
2205
+ private
2206
+
2207
+ # If the dataset is not already ordered, and the model has a primary key,
2208
+ # return a clone ordered by the primary key.
2209
+ def _primary_key_order
2210
+ if @opts[:order].nil? && model && (pk = model.primary_key)
2211
+ cached_dataset(:_pk_order_ds){order(*pk)}
2212
+ end
2213
+ end
2214
+
2215
+ # A cached placeholder literalizer, if one exists for the current dataset.
2216
+ def _with_pk_loader
2217
+ cached_placeholder_literalizer(:_with_pk_loader) do |pl|
2218
+ table = model.table_name
2219
+ cond = case primary_key = model.primary_key
2220
+ when Array
2221
+ primary_key.map{|key| [SQL::QualifiedIdentifier.new(table, key), pl.arg]}
2222
+ when Symbol
2223
+ {SQL::QualifiedIdentifier.new(table, primary_key)=>pl.arg}
2224
+ else
2225
+ raise(Error, "#{model} does not have a primary key")
2226
+ end
2227
+
2228
+ where(cond).limit(1)
2229
+ end
2230
+ end
2231
+
2232
+ def non_sql_option?(key)
2233
+ super || key == :model
2234
+ end
2353
2235
  end
2354
2236
 
2355
2237
  extend ClassMethods
2356
2238
  plugin self
2357
- finder(:where, :arity=>1, :mod=>ClassMethods)
2239
+
2240
+ singleton_class.send(:undef_method, :dup, :clone, :initialize_copy)
2241
+ # :nocov:
2242
+ if RUBY_VERSION >= '1.9.3'
2243
+ # :nocov:
2244
+ singleton_class.send(:undef_method, :initialize_clone, :initialize_dup)
2245
+ end
2358
2246
  end
2359
2247
  end