sequel 4.26.0 → 5.37.0

Sign up to get free protection for your applications and to get access to all the features.
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