sequel 4.36.0 → 5.61.0

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