sequel 4.49.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (484) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +130 -0
  3. data/README.rdoc +195 -136
  4. data/Rakefile +26 -42
  5. data/bin/sequel +6 -9
  6. data/doc/advanced_associations.rdoc +91 -168
  7. data/doc/association_basics.rdoc +197 -274
  8. data/doc/bin_sequel.rdoc +5 -3
  9. data/doc/cheat_sheet.rdoc +66 -43
  10. data/doc/code_order.rdoc +1 -8
  11. data/doc/core_extensions.rdoc +81 -56
  12. data/doc/dataset_basics.rdoc +8 -17
  13. data/doc/dataset_filtering.rdoc +81 -86
  14. data/doc/extensions.rdoc +3 -10
  15. data/doc/mass_assignment.rdoc +73 -30
  16. data/doc/migration.rdoc +19 -36
  17. data/doc/model_dataset_method_design.rdoc +14 -17
  18. data/doc/model_hooks.rdoc +15 -25
  19. data/doc/model_plugins.rdoc +10 -10
  20. data/doc/mssql_stored_procedures.rdoc +3 -3
  21. data/doc/object_model.rdoc +52 -70
  22. data/doc/opening_databases.rdoc +39 -32
  23. data/doc/postgresql.rdoc +48 -38
  24. data/doc/prepared_statements.rdoc +27 -22
  25. data/doc/querying.rdoc +173 -150
  26. data/doc/reflection.rdoc +5 -6
  27. data/doc/release_notes/5.0.0.txt +159 -0
  28. data/doc/release_notes/5.1.0.txt +31 -0
  29. data/doc/release_notes/5.2.0.txt +33 -0
  30. data/doc/release_notes/5.3.0.txt +121 -0
  31. data/doc/schema_modification.rdoc +78 -64
  32. data/doc/security.rdoc +97 -88
  33. data/doc/sharding.rdoc +43 -30
  34. data/doc/sql.rdoc +53 -65
  35. data/doc/testing.rdoc +4 -5
  36. data/doc/thread_safety.rdoc +2 -4
  37. data/doc/transactions.rdoc +18 -17
  38. data/doc/validations.rdoc +48 -45
  39. data/doc/virtual_rows.rdoc +87 -115
  40. data/lib/sequel/adapters/ado/access.rb +7 -13
  41. data/lib/sequel/adapters/ado/mssql.rb +2 -9
  42. data/lib/sequel/adapters/ado.rb +9 -25
  43. data/lib/sequel/adapters/amalgalite.rb +3 -18
  44. data/lib/sequel/adapters/ibmdb.rb +9 -45
  45. data/lib/sequel/adapters/jdbc/db2.rb +8 -37
  46. data/lib/sequel/adapters/jdbc/derby.rb +4 -50
  47. data/lib/sequel/adapters/jdbc/h2.rb +6 -26
  48. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -27
  49. data/lib/sequel/adapters/jdbc/jtds.rb +2 -9
  50. data/lib/sequel/adapters/jdbc/mssql.rb +1 -11
  51. data/lib/sequel/adapters/jdbc/mysql.rb +11 -15
  52. data/lib/sequel/adapters/jdbc/oracle.rb +4 -26
  53. data/lib/sequel/adapters/jdbc/postgresql.rb +23 -33
  54. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +4 -17
  55. data/lib/sequel/adapters/jdbc/sqlite.rb +1 -7
  56. data/lib/sequel/adapters/jdbc/sqlserver.rb +1 -13
  57. data/lib/sequel/adapters/jdbc/transactions.rb +1 -14
  58. data/lib/sequel/adapters/jdbc.rb +18 -74
  59. data/lib/sequel/adapters/mock.rb +4 -30
  60. data/lib/sequel/adapters/mysql.rb +7 -44
  61. data/lib/sequel/adapters/mysql2.rb +5 -23
  62. data/lib/sequel/adapters/odbc/db2.rb +1 -1
  63. data/lib/sequel/adapters/odbc/mssql.rb +4 -12
  64. data/lib/sequel/adapters/odbc/oracle.rb +1 -1
  65. data/lib/sequel/adapters/odbc.rb +0 -19
  66. data/lib/sequel/adapters/oracle.rb +8 -13
  67. data/lib/sequel/adapters/postgres.rb +28 -150
  68. data/lib/sequel/adapters/postgresql.rb +1 -1
  69. data/lib/sequel/adapters/shared/access.rb +11 -51
  70. data/lib/sequel/adapters/shared/db2.rb +3 -61
  71. data/lib/sequel/adapters/shared/mssql.rb +21 -157
  72. data/lib/sequel/adapters/shared/mysql.rb +61 -227
  73. data/lib/sequel/adapters/shared/oracle.rb +13 -41
  74. data/lib/sequel/adapters/shared/postgres.rb +58 -264
  75. data/lib/sequel/adapters/shared/sqlanywhere.rb +4 -96
  76. data/lib/sequel/adapters/shared/sqlite.rb +22 -101
  77. data/lib/sequel/adapters/sqlanywhere.rb +4 -23
  78. data/lib/sequel/adapters/sqlite.rb +2 -19
  79. data/lib/sequel/adapters/tinytds.rb +5 -15
  80. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +1 -1
  81. data/lib/sequel/adapters/utils/mysql_mysql2.rb +4 -4
  82. data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +3 -6
  83. data/lib/sequel/adapters/utils/replace.rb +0 -5
  84. data/lib/sequel/adapters/utils/stored_procedures.rb +0 -2
  85. data/lib/sequel/adapters/utils/unmodified_identifiers.rb +2 -0
  86. data/lib/sequel/ast_transformer.rb +3 -94
  87. data/lib/sequel/connection_pool/sharded_single.rb +1 -4
  88. data/lib/sequel/connection_pool/sharded_threaded.rb +97 -95
  89. data/lib/sequel/connection_pool/single.rb +0 -2
  90. data/lib/sequel/connection_pool/threaded.rb +94 -110
  91. data/lib/sequel/connection_pool.rb +38 -28
  92. data/lib/sequel/core.rb +42 -101
  93. data/lib/sequel/database/connecting.rb +23 -60
  94. data/lib/sequel/database/dataset.rb +6 -9
  95. data/lib/sequel/database/dataset_defaults.rb +4 -48
  96. data/lib/sequel/database/features.rb +5 -4
  97. data/lib/sequel/database/logging.rb +2 -9
  98. data/lib/sequel/database/misc.rb +36 -55
  99. data/lib/sequel/database/query.rb +8 -13
  100. data/lib/sequel/database/schema_generator.rb +93 -64
  101. data/lib/sequel/database/schema_methods.rb +61 -79
  102. data/lib/sequel/database/transactions.rb +4 -24
  103. data/lib/sequel/database.rb +12 -2
  104. data/lib/sequel/dataset/actions.rb +57 -107
  105. data/lib/sequel/dataset/dataset_module.rb +4 -16
  106. data/lib/sequel/dataset/features.rb +35 -30
  107. data/lib/sequel/dataset/graph.rb +40 -49
  108. data/lib/sequel/dataset/misc.rb +12 -37
  109. data/lib/sequel/dataset/placeholder_literalizer.rb +4 -4
  110. data/lib/sequel/dataset/prepared_statements.rb +23 -51
  111. data/lib/sequel/dataset/query.rb +91 -161
  112. data/lib/sequel/dataset/sql.rb +33 -225
  113. data/lib/sequel/dataset.rb +18 -10
  114. data/lib/sequel/deprecated.rb +18 -27
  115. data/lib/sequel/exceptions.rb +1 -17
  116. data/lib/sequel/extensions/_model_pg_row.rb +0 -7
  117. data/lib/sequel/extensions/_pretty_table.rb +1 -3
  118. data/lib/sequel/extensions/arbitrary_servers.rb +10 -10
  119. data/lib/sequel/extensions/connection_expiration.rb +1 -1
  120. data/lib/sequel/extensions/connection_validator.rb +1 -1
  121. data/lib/sequel/extensions/constraint_validations.rb +11 -11
  122. data/lib/sequel/extensions/core_extensions.rb +39 -49
  123. data/lib/sequel/extensions/core_refinements.rb +39 -45
  124. data/lib/sequel/extensions/current_datetime_timestamp.rb +0 -4
  125. data/lib/sequel/extensions/date_arithmetic.rb +7 -7
  126. data/lib/sequel/extensions/duplicate_columns_handler.rb +12 -9
  127. data/lib/sequel/extensions/empty_array_consider_nulls.rb +2 -2
  128. data/lib/sequel/extensions/eval_inspect.rb +4 -11
  129. data/lib/sequel/extensions/freeze_datasets.rb +1 -69
  130. data/lib/sequel/extensions/from_block.rb +1 -35
  131. data/lib/sequel/extensions/graph_each.rb +2 -2
  132. data/lib/sequel/extensions/identifier_mangling.rb +9 -19
  133. data/lib/sequel/extensions/implicit_subquery.rb +2 -2
  134. data/lib/sequel/extensions/inflector.rb +4 -4
  135. data/lib/sequel/extensions/migration.rb +27 -43
  136. data/lib/sequel/extensions/no_auto_literal_strings.rb +2 -84
  137. data/lib/sequel/extensions/null_dataset.rb +2 -8
  138. data/lib/sequel/extensions/pagination.rb +1 -17
  139. data/lib/sequel/extensions/pg_array.rb +20 -189
  140. data/lib/sequel/extensions/pg_extended_date_support.rb +230 -0
  141. data/lib/sequel/extensions/pg_hstore.rb +11 -50
  142. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -2
  143. data/lib/sequel/extensions/pg_inet.rb +3 -16
  144. data/lib/sequel/extensions/pg_interval.rb +1 -20
  145. data/lib/sequel/extensions/pg_json.rb +7 -27
  146. data/lib/sequel/extensions/pg_loose_count.rb +1 -1
  147. data/lib/sequel/extensions/pg_range.rb +6 -121
  148. data/lib/sequel/extensions/pg_range_ops.rb +1 -3
  149. data/lib/sequel/extensions/pg_row.rb +5 -77
  150. data/lib/sequel/extensions/pg_row_ops.rb +2 -13
  151. data/lib/sequel/extensions/query.rb +3 -4
  152. data/lib/sequel/extensions/round_timestamps.rb +0 -6
  153. data/lib/sequel/extensions/schema_dumper.rb +13 -13
  154. data/lib/sequel/extensions/select_remove.rb +3 -3
  155. data/lib/sequel/extensions/split_array_nil.rb +2 -2
  156. data/lib/sequel/extensions/sql_comments.rb +2 -2
  157. data/lib/sequel/extensions/string_agg.rb +11 -8
  158. data/lib/sequel/extensions/symbol_aref.rb +6 -20
  159. data/lib/sequel/extensions/synchronize_sql.rb +45 -0
  160. data/lib/sequel/model/associations.rb +129 -131
  161. data/lib/sequel/model/base.rb +133 -731
  162. data/lib/sequel/model/default_inflections.rb +1 -1
  163. data/lib/sequel/model/errors.rb +0 -3
  164. data/lib/sequel/model/exceptions.rb +2 -6
  165. data/lib/sequel/model/inflections.rb +1 -26
  166. data/lib/sequel/model/plugins.rb +1 -0
  167. data/lib/sequel/model.rb +27 -62
  168. data/lib/sequel/plugins/active_model.rb +2 -5
  169. data/lib/sequel/plugins/association_dependencies.rb +15 -15
  170. data/lib/sequel/plugins/association_pks.rb +14 -28
  171. data/lib/sequel/plugins/association_proxies.rb +6 -7
  172. data/lib/sequel/plugins/auto_validations.rb +4 -4
  173. data/lib/sequel/plugins/before_after_save.rb +0 -43
  174. data/lib/sequel/plugins/blacklist_security.rb +9 -8
  175. data/lib/sequel/plugins/boolean_readers.rb +3 -3
  176. data/lib/sequel/plugins/boolean_subsets.rb +2 -2
  177. data/lib/sequel/plugins/caching.rb +5 -5
  178. data/lib/sequel/plugins/class_table_inheritance.rb +71 -102
  179. data/lib/sequel/plugins/column_conflicts.rb +2 -2
  180. data/lib/sequel/plugins/column_select.rb +2 -2
  181. data/lib/sequel/plugins/composition.rb +15 -24
  182. data/lib/sequel/plugins/constraint_validations.rb +4 -3
  183. data/lib/sequel/plugins/csv_serializer.rb +13 -20
  184. data/lib/sequel/plugins/dataset_associations.rb +2 -2
  185. data/lib/sequel/plugins/def_dataset_method.rb +5 -5
  186. data/lib/sequel/plugins/defaults_setter.rb +1 -1
  187. data/lib/sequel/plugins/delay_add_association.rb +1 -1
  188. data/lib/sequel/plugins/finder.rb +16 -10
  189. data/lib/sequel/plugins/force_encoding.rb +1 -7
  190. data/lib/sequel/plugins/hook_class_methods.rb +4 -106
  191. data/lib/sequel/plugins/input_transformer.rb +10 -11
  192. data/lib/sequel/plugins/insert_returning_select.rb +1 -9
  193. data/lib/sequel/plugins/instance_filters.rb +5 -5
  194. data/lib/sequel/plugins/instance_hooks.rb +7 -52
  195. data/lib/sequel/plugins/inverted_subsets.rb +3 -1
  196. data/lib/sequel/plugins/json_serializer.rb +19 -19
  197. data/lib/sequel/plugins/lazy_attributes.rb +1 -10
  198. data/lib/sequel/plugins/list.rb +6 -6
  199. data/lib/sequel/plugins/many_through_many.rb +11 -8
  200. data/lib/sequel/plugins/mssql_optimistic_locking.rb +3 -3
  201. data/lib/sequel/plugins/nested_attributes.rb +18 -31
  202. data/lib/sequel/plugins/optimistic_locking.rb +3 -3
  203. data/lib/sequel/plugins/pg_array_associations.rb +8 -2
  204. data/lib/sequel/plugins/pg_row.rb +2 -11
  205. data/lib/sequel/plugins/prepared_statements.rb +13 -66
  206. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -1
  207. data/lib/sequel/plugins/rcte_tree.rb +7 -7
  208. data/lib/sequel/plugins/serialization.rb +15 -33
  209. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
  210. data/lib/sequel/plugins/sharding.rb +2 -8
  211. data/lib/sequel/plugins/single_table_inheritance.rb +10 -13
  212. data/lib/sequel/plugins/skip_create_refresh.rb +3 -3
  213. data/lib/sequel/plugins/static_cache.rb +8 -9
  214. data/lib/sequel/plugins/string_stripper.rb +3 -3
  215. data/lib/sequel/plugins/subclasses.rb +1 -1
  216. data/lib/sequel/plugins/subset_conditions.rb +2 -2
  217. data/lib/sequel/plugins/table_select.rb +2 -2
  218. data/lib/sequel/plugins/tactical_eager_loading.rb +4 -4
  219. data/lib/sequel/plugins/timestamps.rb +6 -7
  220. data/lib/sequel/plugins/touch.rb +4 -8
  221. data/lib/sequel/plugins/tree.rb +3 -3
  222. data/lib/sequel/plugins/typecast_on_load.rb +2 -2
  223. data/lib/sequel/plugins/unlimited_update.rb +1 -7
  224. data/lib/sequel/plugins/update_or_create.rb +3 -3
  225. data/lib/sequel/plugins/update_refresh.rb +3 -3
  226. data/lib/sequel/plugins/uuid.rb +7 -11
  227. data/lib/sequel/plugins/validation_class_methods.rb +10 -9
  228. data/lib/sequel/plugins/validation_contexts.rb +4 -4
  229. data/lib/sequel/plugins/validation_helpers.rb +26 -25
  230. data/lib/sequel/plugins/whitelist_security.rb +13 -9
  231. data/lib/sequel/plugins/xml_serializer.rb +24 -25
  232. data/lib/sequel/sql.rb +145 -276
  233. data/lib/sequel/timezones.rb +8 -23
  234. data/lib/sequel/version.rb +2 -2
  235. data/lib/sequel.rb +1 -1
  236. data/spec/adapter_spec.rb +1 -1
  237. data/spec/adapters/db2_spec.rb +2 -103
  238. data/spec/adapters/mssql_spec.rb +89 -68
  239. data/spec/adapters/mysql_spec.rb +111 -478
  240. data/spec/adapters/oracle_spec.rb +1 -9
  241. data/spec/adapters/postgres_spec.rb +459 -664
  242. data/spec/adapters/spec_helper.rb +12 -31
  243. data/spec/adapters/sqlanywhere_spec.rb +2 -77
  244. data/spec/adapters/sqlite_spec.rb +8 -146
  245. data/spec/bin_spec.rb +11 -16
  246. data/spec/core/connection_pool_spec.rb +173 -74
  247. data/spec/core/database_spec.rb +96 -244
  248. data/spec/core/dataset_spec.rb +99 -414
  249. data/spec/core/deprecated_spec.rb +3 -3
  250. data/spec/core/expression_filters_spec.rb +37 -144
  251. data/spec/core/mock_adapter_spec.rb +241 -4
  252. data/spec/core/object_graph_spec.rb +11 -60
  253. data/spec/core/placeholder_literalizer_spec.rb +1 -14
  254. data/spec/core/schema_generator_spec.rb +51 -40
  255. data/spec/core/schema_spec.rb +88 -77
  256. data/spec/core/spec_helper.rb +6 -24
  257. data/spec/core/version_spec.rb +1 -1
  258. data/spec/core_extensions_spec.rb +7 -83
  259. data/spec/core_model_spec.rb +2 -2
  260. data/spec/deprecation_helper.rb +2 -14
  261. data/spec/extensions/accessed_columns_spec.rb +1 -1
  262. data/spec/extensions/active_model_spec.rb +3 -3
  263. data/spec/extensions/after_initialize_spec.rb +1 -1
  264. data/spec/extensions/arbitrary_servers_spec.rb +2 -2
  265. data/spec/extensions/association_dependencies_spec.rb +1 -1
  266. data/spec/extensions/association_pks_spec.rb +30 -92
  267. data/spec/extensions/association_proxies_spec.rb +1 -1
  268. data/spec/extensions/auto_literal_strings_spec.rb +1 -12
  269. data/spec/extensions/auto_validations_spec.rb +1 -1
  270. data/spec/extensions/blacklist_security_spec.rb +1 -1
  271. data/spec/extensions/blank_spec.rb +1 -1
  272. data/spec/extensions/boolean_readers_spec.rb +1 -1
  273. data/spec/extensions/boolean_subsets_spec.rb +1 -1
  274. data/spec/extensions/caching_spec.rb +1 -1
  275. data/spec/extensions/class_table_inheritance_spec.rb +53 -1118
  276. data/spec/extensions/column_conflicts_spec.rb +1 -1
  277. data/spec/extensions/column_select_spec.rb +4 -4
  278. data/spec/extensions/columns_introspection_spec.rb +1 -1
  279. data/spec/extensions/columns_updated_spec.rb +1 -1
  280. data/spec/extensions/composition_spec.rb +8 -30
  281. data/spec/extensions/connection_expiration_spec.rb +3 -3
  282. data/spec/extensions/connection_validator_spec.rb +3 -3
  283. data/spec/extensions/constraint_validations_plugin_spec.rb +1 -1
  284. data/spec/extensions/constraint_validations_spec.rb +1 -1
  285. data/spec/extensions/core_refinements_spec.rb +1 -3
  286. data/spec/extensions/csv_serializer_spec.rb +4 -9
  287. data/spec/extensions/current_datetime_timestamp_spec.rb +1 -1
  288. data/spec/extensions/dataset_associations_spec.rb +2 -1
  289. data/spec/extensions/dataset_source_alias_spec.rb +1 -1
  290. data/spec/extensions/date_arithmetic_spec.rb +3 -3
  291. data/spec/extensions/def_dataset_method_spec.rb +1 -1
  292. data/spec/extensions/defaults_setter_spec.rb +2 -2
  293. data/spec/extensions/delay_add_association_spec.rb +8 -9
  294. data/spec/extensions/dirty_spec.rb +1 -1
  295. data/spec/extensions/duplicate_columns_handler_spec.rb +1 -1
  296. data/spec/extensions/eager_each_spec.rb +2 -2
  297. data/spec/extensions/empty_array_consider_nulls_spec.rb +1 -1
  298. data/spec/extensions/error_splitter_spec.rb +1 -1
  299. data/spec/extensions/error_sql_spec.rb +1 -1
  300. data/spec/extensions/eval_inspect_spec.rb +1 -1
  301. data/spec/extensions/finder_spec.rb +1 -1
  302. data/spec/extensions/force_encoding_spec.rb +2 -5
  303. data/spec/extensions/freeze_datasets_spec.rb +1 -1
  304. data/spec/extensions/graph_each_spec.rb +5 -5
  305. data/spec/extensions/hook_class_methods_spec.rb +1 -194
  306. data/spec/extensions/identifier_mangling_spec.rb +17 -170
  307. data/spec/extensions/implicit_subquery_spec.rb +1 -5
  308. data/spec/extensions/inflector_spec.rb +1 -1
  309. data/spec/extensions/input_transformer_spec.rb +7 -2
  310. data/spec/extensions/insert_returning_select_spec.rb +1 -1
  311. data/spec/extensions/instance_filters_spec.rb +1 -1
  312. data/spec/extensions/instance_hooks_spec.rb +1 -95
  313. data/spec/extensions/inverted_subsets_spec.rb +1 -1
  314. data/spec/extensions/json_serializer_spec.rb +1 -1
  315. data/spec/extensions/lazy_attributes_spec.rb +1 -7
  316. data/spec/extensions/list_spec.rb +5 -6
  317. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  318. data/spec/extensions/many_through_many_spec.rb +25 -33
  319. data/spec/extensions/migration_spec.rb +12 -2
  320. data/spec/extensions/modification_detection_spec.rb +1 -1
  321. data/spec/extensions/mssql_optimistic_locking_spec.rb +1 -1
  322. data/spec/extensions/named_timezones_spec.rb +3 -3
  323. data/spec/extensions/nested_attributes_spec.rb +1 -29
  324. data/spec/extensions/null_dataset_spec.rb +1 -11
  325. data/spec/extensions/optimistic_locking_spec.rb +2 -2
  326. data/spec/extensions/pagination_spec.rb +1 -1
  327. data/spec/extensions/pg_array_associations_spec.rb +22 -26
  328. data/spec/extensions/pg_array_ops_spec.rb +1 -1
  329. data/spec/extensions/pg_array_spec.rb +3 -48
  330. data/spec/extensions/pg_enum_spec.rb +1 -1
  331. data/spec/extensions/pg_extended_date_support_spec.rb +122 -0
  332. data/spec/extensions/pg_hstore_ops_spec.rb +1 -1
  333. data/spec/extensions/pg_hstore_spec.rb +22 -31
  334. data/spec/extensions/pg_inet_ops_spec.rb +1 -1
  335. data/spec/extensions/pg_inet_spec.rb +1 -14
  336. data/spec/extensions/pg_interval_spec.rb +3 -13
  337. data/spec/extensions/pg_json_ops_spec.rb +1 -1
  338. data/spec/extensions/pg_json_spec.rb +1 -13
  339. data/spec/extensions/pg_loose_count_spec.rb +1 -1
  340. data/spec/extensions/pg_range_ops_spec.rb +1 -1
  341. data/spec/extensions/pg_range_spec.rb +3 -88
  342. data/spec/extensions/pg_row_ops_spec.rb +1 -1
  343. data/spec/extensions/pg_row_plugin_spec.rb +1 -1
  344. data/spec/extensions/pg_row_spec.rb +1 -44
  345. data/spec/extensions/pg_static_cache_updater_spec.rb +1 -1
  346. data/spec/extensions/prepared_statements_safe_spec.rb +7 -7
  347. data/spec/extensions/prepared_statements_spec.rb +13 -48
  348. data/spec/extensions/pretty_table_spec.rb +40 -9
  349. data/spec/extensions/query_spec.rb +1 -12
  350. data/spec/extensions/rcte_tree_spec.rb +23 -34
  351. data/spec/extensions/round_timestamps_spec.rb +1 -5
  352. data/spec/extensions/s_spec.rb +1 -1
  353. data/spec/extensions/schema_caching_spec.rb +1 -1
  354. data/spec/extensions/schema_dumper_spec.rb +43 -32
  355. data/spec/extensions/select_remove_spec.rb +1 -1
  356. data/spec/extensions/sequel_4_dataset_methods_spec.rb +1 -1
  357. data/spec/extensions/serialization_modification_detection_spec.rb +1 -1
  358. data/spec/extensions/serialization_spec.rb +5 -17
  359. data/spec/extensions/server_block_spec.rb +1 -1
  360. data/spec/extensions/server_logging_spec.rb +2 -2
  361. data/spec/extensions/sharding_spec.rb +1 -1
  362. data/spec/extensions/shared_caching_spec.rb +1 -28
  363. data/spec/extensions/single_table_inheritance_spec.rb +2 -5
  364. data/spec/extensions/singular_table_names_spec.rb +1 -1
  365. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  366. data/spec/extensions/spec_helper.rb +5 -27
  367. data/spec/extensions/split_array_nil_spec.rb +1 -1
  368. data/spec/extensions/split_values_spec.rb +1 -1
  369. data/spec/extensions/sql_comments_spec.rb +1 -1
  370. data/spec/extensions/sql_expr_spec.rb +1 -1
  371. data/spec/extensions/static_cache_spec.rb +1 -1
  372. data/spec/extensions/string_agg_spec.rb +2 -2
  373. data/spec/extensions/string_date_time_spec.rb +1 -1
  374. data/spec/extensions/string_stripper_spec.rb +1 -1
  375. data/spec/extensions/subclasses_spec.rb +1 -1
  376. data/spec/extensions/subset_conditions_spec.rb +1 -1
  377. data/spec/extensions/symbol_aref_refinement_spec.rb +1 -1
  378. data/spec/extensions/symbol_as_refinement_spec.rb +1 -1
  379. data/spec/extensions/synchronize_sql_spec.rb +124 -0
  380. data/spec/extensions/table_select_spec.rb +4 -4
  381. data/spec/extensions/tactical_eager_loading_spec.rb +1 -6
  382. data/spec/extensions/thread_local_timezones_spec.rb +1 -1
  383. data/spec/extensions/timestamps_spec.rb +5 -7
  384. data/spec/extensions/to_dot_spec.rb +1 -1
  385. data/spec/extensions/touch_spec.rb +1 -1
  386. data/spec/extensions/tree_spec.rb +1 -1
  387. data/spec/extensions/typecast_on_load_spec.rb +1 -1
  388. data/spec/extensions/unlimited_update_spec.rb +1 -1
  389. data/spec/extensions/update_or_create_spec.rb +12 -16
  390. data/spec/extensions/update_primary_key_spec.rb +4 -3
  391. data/spec/extensions/update_refresh_spec.rb +1 -1
  392. data/spec/extensions/uuid_spec.rb +10 -13
  393. data/spec/extensions/validate_associated_spec.rb +1 -1
  394. data/spec/extensions/validation_class_methods_spec.rb +3 -3
  395. data/spec/extensions/validation_contexts_spec.rb +1 -1
  396. data/spec/extensions/validation_helpers_spec.rb +10 -44
  397. data/spec/extensions/whitelist_security_spec.rb +5 -5
  398. data/spec/extensions/xml_serializer_spec.rb +8 -13
  399. data/spec/guards_helper.rb +2 -1
  400. data/spec/integration/associations_test.rb +1 -23
  401. data/spec/integration/database_test.rb +7 -7
  402. data/spec/integration/dataset_test.rb +12 -47
  403. data/spec/integration/eager_loader_test.rb +1 -1
  404. data/spec/integration/migrator_test.rb +1 -1
  405. data/spec/integration/model_test.rb +4 -82
  406. data/spec/integration/plugin_test.rb +7 -23
  407. data/spec/integration/prepared_statement_test.rb +8 -88
  408. data/spec/integration/schema_test.rb +10 -10
  409. data/spec/integration/spec_helper.rb +17 -21
  410. data/spec/integration/timezone_test.rb +5 -5
  411. data/spec/integration/transaction_test.rb +3 -55
  412. data/spec/integration/type_test.rb +9 -9
  413. data/spec/model/association_reflection_spec.rb +24 -9
  414. data/spec/model/associations_spec.rb +124 -303
  415. data/spec/model/base_spec.rb +43 -137
  416. data/spec/model/class_dataset_methods_spec.rb +2 -20
  417. data/spec/model/dataset_methods_spec.rb +1 -20
  418. data/spec/model/eager_loading_spec.rb +48 -17
  419. data/spec/model/hooks_spec.rb +5 -300
  420. data/spec/model/inflector_spec.rb +1 -1
  421. data/spec/model/model_spec.rb +29 -339
  422. data/spec/model/plugins_spec.rb +2 -16
  423. data/spec/model/record_spec.rb +33 -129
  424. data/spec/model/spec_helper.rb +5 -15
  425. data/spec/model/validations_spec.rb +1 -1
  426. data/spec/sequel_warning.rb +1 -12
  427. metadata +19 -65
  428. data/doc/active_record.rdoc +0 -927
  429. data/lib/sequel/adapters/cubrid.rb +0 -160
  430. data/lib/sequel/adapters/do/mysql.rb +0 -69
  431. data/lib/sequel/adapters/do/postgres.rb +0 -46
  432. data/lib/sequel/adapters/do/sqlite3.rb +0 -41
  433. data/lib/sequel/adapters/do.rb +0 -166
  434. data/lib/sequel/adapters/jdbc/as400.rb +0 -92
  435. data/lib/sequel/adapters/jdbc/cubrid.rb +0 -65
  436. data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -37
  437. data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -34
  438. data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -34
  439. data/lib/sequel/adapters/odbc/progress.rb +0 -12
  440. data/lib/sequel/adapters/shared/cubrid.rb +0 -245
  441. data/lib/sequel/adapters/shared/firebird.rb +0 -261
  442. data/lib/sequel/adapters/shared/informix.rb +0 -63
  443. data/lib/sequel/adapters/shared/progress.rb +0 -40
  444. data/lib/sequel/adapters/swift/mysql.rb +0 -50
  445. data/lib/sequel/adapters/swift/postgres.rb +0 -49
  446. data/lib/sequel/adapters/swift/sqlite.rb +0 -48
  447. data/lib/sequel/adapters/swift.rb +0 -169
  448. data/lib/sequel/adapters/utils/pg_types.rb +0 -4
  449. data/lib/sequel/dataset/mutation.rb +0 -98
  450. data/lib/sequel/extensions/_deprecated_identifier_mangling.rb +0 -117
  451. data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -8
  452. data/lib/sequel/extensions/filter_having.rb +0 -65
  453. data/lib/sequel/extensions/hash_aliases.rb +0 -51
  454. data/lib/sequel/extensions/meta_def.rb +0 -37
  455. data/lib/sequel/extensions/query_literals.rb +0 -86
  456. data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -26
  457. data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -133
  458. data/lib/sequel/extensions/set_overrides.rb +0 -82
  459. data/lib/sequel/no_core_ext.rb +0 -4
  460. data/lib/sequel/plugins/association_autoreloading.rb +0 -11
  461. data/lib/sequel/plugins/identifier_columns.rb +0 -49
  462. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -11
  463. data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -90
  464. data/lib/sequel/plugins/prepared_statements_associations.rb +0 -137
  465. data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -71
  466. data/lib/sequel/plugins/schema.rb +0 -84
  467. data/lib/sequel/plugins/scissors.rb +0 -37
  468. data/spec/core/dataset_mutation_spec.rb +0 -253
  469. data/spec/extensions/_deprecated_identifier_mangling_spec.rb +0 -314
  470. data/spec/extensions/before_after_save_spec.rb +0 -40
  471. data/spec/extensions/filter_having_spec.rb +0 -42
  472. data/spec/extensions/from_block_spec.rb +0 -21
  473. data/spec/extensions/hash_aliases_spec.rb +0 -26
  474. data/spec/extensions/identifier_columns_spec.rb +0 -19
  475. data/spec/extensions/meta_def_spec.rb +0 -35
  476. data/spec/extensions/no_auto_literal_strings_spec.rb +0 -69
  477. data/spec/extensions/pg_typecast_on_load_spec.rb +0 -70
  478. data/spec/extensions/prepared_statements_associations_spec.rb +0 -212
  479. data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -40
  480. data/spec/extensions/query_literals_spec.rb +0 -185
  481. data/spec/extensions/schema_spec.rb +0 -123
  482. data/spec/extensions/scissors_spec.rb +0 -27
  483. data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -118
  484. data/spec/extensions/set_overrides_spec.rb +0 -75
@@ -1,19 +1,22 @@
1
1
  = Advanced Associations
2
2
 
3
- Sequel::Model has the most powerful and flexible associations of any ruby ORM.
3
+ Sequel::Model's association support is very powerful and flexible, but using that power and flexibility
4
+ often results in complexity.
5
+
6
+ You should probably review the {Model Associations Basics and Options guide}[rdoc-ref:doc/association_basics.rdoc]
7
+ before reviewing this guide.
4
8
 
5
9
  == Background: Sequel::Model association options
6
10
 
7
11
  There are a bunch of advanced association options that are available to
8
- handle more complex cases. First we'll go over some of
9
- the simpler ones:
12
+ handle more complex cases. First we'll go over some of the simpler ones:
10
13
 
11
14
  All associations take a block that can be used to further filter/modify the
12
15
  default dataset. There's also an :eager_block option if you want to use
13
16
  a different block when eager loading via <tt>Dataset#eager</tt>. Association blocks are
14
17
  useful for things like:
15
18
 
16
- Artist.one_to_many :gold_albums, :class=>:Album do |ds|
19
+ Artist.one_to_many :gold_albums, class: :Album do |ds|
17
20
  ds.where{copies_sold > 500000}
18
21
  end
19
22
 
@@ -42,24 +45,23 @@ These can be used like this:
42
45
 
43
46
  # Makes Artist.eager_graph(:required_albums).all not return artists that
44
47
  # don't have any albums
45
- Artist.one_to_many :required_albums, :class=>:Album, :graph_join_type=>:inner
48
+ Artist.one_to_many :required_albums, class: :Album, graph_join_type: :inner
46
49
 
47
50
  # Makes sure all returned albums have the active flag set
48
- Artist.one_to_many :active_albums, :class=>:Album, \
49
- :graph_conditions=>{:active=>true}
51
+ Artist.one_to_many :active_albums, class: :Album, graph_conditions: {active: true}
50
52
 
51
53
  # Only returns albums that have sold more than 500,000 copies
52
- Artist.one_to_many :gold_albums, :class=>:Album, \
53
- :graph_block=>proc{|j,lj,js| Sequel[j][:copies_sold] > 500000}
54
+ Artist.one_to_many :gold_albums, class: :Album,
55
+ graph_block: proc{|j,lj,js| Sequel[j][:copies_sold] > 500000}
54
56
 
55
57
  # Handles the case where the tables are associated by a case insensitive name string
56
- Artist.one_to_many :albums, :key=>:artist_name, \
57
- :graph_only_conditions=>nil, \
58
- :graph_block=>proc{|j,lj,js| {Sequel.function(:lower, Sequel[j][:artist_name])=>Sequel.function(:lower, Sequel[lj][:name])}}
58
+ Artist.one_to_many :albums, key: :artist_name,
59
+ graph_only_conditions: nil,
60
+ graph_block: proc{|j,lj,js| {Sequel.function(:lower, Sequel[j][:artist_name])=>Sequel.function(:lower, Sequel[lj][:name])}}
59
61
 
60
62
  # Handles the case where both key columns have the name artist_name, and you want to use
61
63
  # a JOIN USING
62
- Artist.one_to_many :albums, :key=>:artist_name, :graph_only_conditions=>[:artist_name]
64
+ Artist.one_to_many :albums, key: :artist_name, graph_only_conditions: [:artist_name]
63
65
 
64
66
  Remember, using +eager_graph+ is generally only necessary when you need to
65
67
  filter/order based on columns in an associated table, it is recommended to
@@ -244,7 +246,7 @@ loading.
244
246
 
245
247
  Sequel supports specifying limits and/or offsets for associations:
246
248
 
247
- Artist.one_to_many :first_10_albums, :class=>:Album, :order=>:release_date, :limit=>10
249
+ Artist.one_to_many :first_10_albums, class: :Album, order: :release_date, limit: 10
248
250
 
249
251
  For retrieving the associated objects for a single object, this just uses
250
252
  a LIMIT:
@@ -260,7 +262,7 @@ approach. Sequel has 4 separate strategies for dealing with such cases.
260
262
  The default strategy used on all databases is a UNION-based approach, which
261
263
  will submit multiple subqueries in a UNION query:
262
264
 
263
- Artist.where(:id=>[1,2]).eager(:first_10_albums).all
265
+ Artist.where(id: [1,2]).eager(:first_10_albums).all
264
266
  # SELECT * FROM (SELECT * FROM albums WHERE (artist_id = 1) LIMIT 10) UNION ALL
265
267
  # SELECT * FROM (SELECT * FROM albums WHERE (artist_id = 2) LIMIT 10)
266
268
 
@@ -272,9 +274,9 @@ index, you'll want to manually specify the :eager_limit_strategy option as shown
272
274
  On PostgreSQL, for *_one associations that don't use an offset, you can
273
275
  choose to use a the distinct on strategy:
274
276
 
275
- Artist.one_to_one :first_album, :class=>:Album, :order=>:release_date,
276
- :eager_limit_strategy=>:distinct_on
277
- Artist.where(:id=>[1,2]).eager(:first_album).all
277
+ Artist.one_to_one :first_album, class: :Album, order: :release_date,
278
+ eager_limit_strategy: :distinct_on
279
+ Artist.where(id: [1,2]).eager(:first_album).all
278
280
  # SELECT DISTINCT ON (albums.artist_id) *
279
281
  # FROM albums
280
282
  # WHERE (albums.artist_id IN (1, 2))
@@ -283,8 +285,8 @@ choose to use a the distinct on strategy:
283
285
  Otherwise, if the database supports window functions, you can choose to use
284
286
  the window function strategy:
285
287
 
286
- Artist.one_to_many :first_10_albums, :class=>:Album, :order=>:release_date, :limit=>10,
287
- :eager_limit_strategy=>:window_function
288
+ Artist.one_to_many :first_10_albums, class: :Album, order: :release_date, limit: 10,
289
+ eager_limit_strategy: :window_function
288
290
  Artist.where(:id=>[1,2]).eager(:first_10_albums).all
289
291
  # SELECT * FROM (
290
292
  # SELECT *, row_number() OVER (PARTITION BY albums.artist_id ORDER BY release_date) AS x_sequel_row_number_x
@@ -304,7 +306,7 @@ known at the time of the association definition), Sequel supports an
304
306
  :eager_limit dataset option that can be defined in an eager loading callback:
305
307
 
306
308
  Artist.one_to_many :albums
307
- Artist.where(:id => [1, 2]).eager(:albums => proc{|ds| ds.order(:release_date).clone(:eager_limit => 3)}).all
309
+ Artist.where(id: [1, 2]).eager(albums: lambda{|ds| ds.order(:release_date).clone(eager_limit: 3)}).all
308
310
  # SELECT * FROM (
309
311
  # SELECT *, row_number() OVER (PARTITION BY albums.artist_id ORDER BY release_date) AS x_sequel_row_number_x
310
312
  # FROM albums
@@ -314,7 +316,7 @@ known at the time of the association definition), Sequel supports an
314
316
 
315
317
  You can also customize the :eager_limit_strategy on a case-by-case basis by passing in that option in the same way:
316
318
 
317
- Artist.where(:id => [1, 2]).eager(:albums => proc{|ds| ds.order(:release_date).clone(:eager_limit => 3, :eager_limit_strategy => :ruby)}).all
319
+ Artist.where(id: [1, 2]).eager(albums: lambda{|ds| ds.order(:release_date).clone(eager_limit: 3, eager_limit_strategy: :ruby)}).all
318
320
  # SELECT * FROM albums WHERE (albums.artist_id IN (1, 2)) ORDER BY release_date
319
321
 
320
322
  The :eager_limit and :eager_limit_strategy options currently only work when
@@ -333,7 +335,7 @@ eager_graph_with_options method with the :limit_strategy option.
333
335
  The :distinct_on strategy uses DISTINCT ON in a subquery and JOINs that
334
336
  subquery:
335
337
 
336
- Artist.eager_graph_with_options(:first_album, :limit_strategy=>true).all
338
+ Artist.eager_graph_with_options(:first_album, limit_strategy: :distinct_on).all
337
339
  # SELECT artists.id, artists.name, first_album.id AS first_album_id,
338
340
  # first_album.name AS first_album_name, first_album.artist_id,
339
341
  # first_album.release_date
@@ -347,7 +349,7 @@ subquery:
347
349
  The :window_function approach JOINs to a nested subquery using a window
348
350
  function:
349
351
 
350
- Artist.eager_graph_with_options(:first_10_albums, :limit_strategy=>true).all
352
+ Artist.eager_graph_with_options(:first_10_albums, limit_strategy: :window_function).all
351
353
  # SELECT artists.id, artists.name, first_10_albums.id AS first_10_albums_id,
352
354
  # first_10_albums.name AS first_10_albums_name, first_10_albums.artist_id,
353
355
  # first_10_albums.release_date
@@ -363,7 +365,7 @@ function:
363
365
  The :correlated_subquery approach JOINs to a nested subquery using a correlated
364
366
  subquery:
365
367
 
366
- Artist.eager_graph_with_options(:first_10_albums, :limit_strategy=>true).all
368
+ Artist.eager_graph_with_options(:first_10_albums, :limit_strategy=>:correlated_subquery).all
367
369
  # SELECT artists.id, artists.name, first_10_albums.id AS first_10_albums_id,
368
370
  # first_10_albums.name AS first_10_albums_name, first_10_albums.artist_id,
369
371
  # first_10_albums.release_date
@@ -380,8 +382,6 @@ subquery:
380
382
  # )
381
383
  # ) AS first_10_albums ON (first_10_albums.artist_id = artists.id)
382
384
 
383
- (SELECT * FROM tracks WHERE (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) LIMIT 1)))
384
-
385
385
  The reason that Sequel does not automatically use the :distinct_on, :window function
386
386
  or :correlated_subquery strategy for eager_graph is that it can perform much worse than the
387
387
  default of just doing the array slicing in ruby. If you are only using eager_graph to
@@ -403,7 +403,7 @@ strategy based on the database you are using, and you can override it using the
403
403
 
404
404
  The :distinct_on strategy:
405
405
 
406
- Artist.where(:first_album=>Album[1]).all
406
+ Artist.where(first_album: Album[1]).all
407
407
  # SELECT *
408
408
  # FROM artists
409
409
  # WHERE (artists.id IN (
@@ -417,7 +417,7 @@ The :distinct_on strategy:
417
417
 
418
418
  The :window_function strategy:
419
419
 
420
- Artist.where(:first_10_albums=>Album[1]).all
420
+ Artist.where(first_10_albums: Album[1]).all
421
421
  # SELECT *
422
422
  # FROM artists
423
423
  # WHERE (artists.id IN (
@@ -433,7 +433,7 @@ The :window_function strategy:
433
433
 
434
434
  The :correlated_subquery strategy:
435
435
 
436
- Artist.where(:first_10_albums=>Album[1]).all
436
+ Artist.where(first_10_albums: Album[1]).all
437
437
  # SELECT *
438
438
  # FROM artists
439
439
  # WHERE (artists.id IN (
@@ -447,11 +447,11 @@ The :correlated_subquery strategy:
447
447
  # LIMIT 1
448
448
  # )) AND (albums.id = 1))))
449
449
 
450
- Note that filtering by limited associations does not work on MySQL, as does not support
450
+ Note that filtering by limited associations does not work on MySQL, as MySQL does not support
451
451
  any of the strategies. It's also not supported when using composite keys on databases
452
452
  that don't support window functions and don't support multiple columns in IN.
453
453
 
454
- == Additional Association Types
454
+ === Additional Association Types
455
455
 
456
456
  While the above examples for limited associations showed one_to_many and one_to_one associations,
457
457
  it's just because those are the simplest examples. Sequel supports all of the same features for
@@ -459,29 +459,7 @@ many_to_many and one_through_one associations that are enabled by default, as we
459
459
  many_through_many and one_through_many associations that are added by the many_through_many
460
460
  plugin.
461
461
 
462
- == ActiveRecord associations
463
-
464
- Sequel supports all of associations that ActiveRecord supports, though some
465
- require different approaches or custom <tt>:eager_loader</tt> options.
466
-
467
- === Association callbacks
468
-
469
- Sequel supports the same callbacks that ActiveRecord does for +one_to_many+ and
470
- +many_to_many+ associations: <tt>:before_add</tt>, <tt>:before_remove</tt>, <tt>:after_add</tt>, and
471
- <tt>:after_remove</tt>. For +many_to_one+ associations and +one_to_one+ associations, Sequel
472
- supports the <tt>:before_set</tt> and <tt>:after_set</tt> callbacks. On all associations,
473
- Sequel supports <tt>:after_load</tt>, which is called after the association has been
474
- loaded.
475
-
476
- Each of these options can be a symbol specifying an instance method
477
- that takes one argument (the associated object), or a proc that takes
478
- two arguments (the current object and the associated object), or an
479
- array of symbols and procs. For <tt>:after_load</tt> with a *_to_many association,
480
- the associated object argument is an array of associated objects.
481
-
482
- If any of the before callbacks return +false+, the adding/removing
483
- does not happen and it either raises a <tt>Sequel::HookFailed</tt> (the default), or
484
- returns false (if +raise_on_save_failure+ is false).
462
+ == More advanced association examples
485
463
 
486
464
  === Association extensions
487
465
 
@@ -504,36 +482,21 @@ the model object that created the association dataset via the dataset's
504
482
  end
505
483
  end
506
484
  class Author < Sequel::Model
507
- one_to_many :authorships, :extend=>FindOrCreate
485
+ one_to_many :authorships, extend: FindOrCreate
508
486
  end
509
- Author.first.authorships_dataset.find_or_create(:name=>'Blah', :number=>10)
487
+ Author.first.authorships_dataset.find_or_create(name: 'Blah', number: 10)
510
488
 
511
- === <tt>has_many :through</tt> associations
489
+ === many_to_many associations through model tables
512
490
 
513
- +many_to_many+ handles the usual case of a <tt>has_many :through</tt> with a +belongs_to+ in
514
- the associated model. It doesn't break on the case where the join table is a
515
- model table, unlike ActiveRecord's +has_and_belongs_to_many+.
516
-
517
- ActiveRecord:
518
-
519
- class Author < ActiveRecord::Base
520
- has_many :authorships
521
- has_many :books, :through => :authorships
522
- end
523
-
524
- class Authorship < ActiveRecord::Base
525
- belongs_to :author
526
- belongs_to :book
527
- end
528
-
529
- @author = Author.find :first
530
- @author.books
531
-
532
- Sequel::Model:
491
+ The many_to_many association can be used even when the join table is a table used for a
492
+ model. The only requirement is the join table has foreign keys to both the current
493
+ model and the associated model. Anytime there is a one_to_many association from model A to
494
+ model B, and model B has a many_to_one association to model C, you can use a many_to_many
495
+ association from model A to model C.
533
496
 
534
497
  class Author < Sequel::Model
535
498
  one_to_many :authorships
536
- many_to_many :books, :join_table=>:authorships
499
+ many_to_many :books, join_table: :authorships
537
500
  end
538
501
 
539
502
  class Authorship < Sequel::Model
@@ -544,33 +507,17 @@ Sequel::Model:
544
507
  @author = Author.first
545
508
  @author.books
546
509
 
547
- If you use an association other than +belongs_to+ in the associated model, such as a +has_many+,
548
- you still use a +many_to_many+ association, but you need to use some options:
549
-
550
- ActiveRecord:
551
-
552
- class Firm < ActiveRecord::Base
553
- has_many :clients
554
- has_many :invoices, :through => :clients
555
- end
556
-
557
- class Client < ActiveRecord::Base
558
- belongs_to :firm
559
- has_many :invoices
560
- end
510
+ === many_to_many for three-level associations
561
511
 
562
- class Invoice < ActiveRecord::Base
563
- belongs_to :client
564
- has_one :firm, :through => :client
565
- end
566
-
567
- Firm.find(:first).invoices
568
-
569
- Sequel::Model:
512
+ You can even use a many_to_many association between model A and model C if model A has a
513
+ one_to_many association to model B, and model B has a one_to_many association to model C.
514
+ You just need to use the appropriate :right_key and :right_primary_key options. And in
515
+ the reverse direction from model C to model A, you can use a one_through_one association
516
+ using the :left_key and :left_primary_key options.
570
517
 
571
518
  class Firm < Sequel::Model
572
519
  one_to_many :clients
573
- many_to_many :invoices, :join_table=>:clients, :right_key=>:id, :right_primary_key=>:client_id
520
+ many_to_many :invoices, join_table: :clients, right_key: :id, right_primary_key: :client_id
574
521
  end
575
522
 
576
523
  class Client < Sequel::Model
@@ -580,12 +527,13 @@ Sequel::Model:
580
527
 
581
528
  class Invoice < Sequel::Model
582
529
  many_to_one :client
583
- one_through_one :firm, :join_table=>:clients, :left_key=>:id, :left_primary_key=>:client_id, :right_key=>:firm_id
530
+ one_through_one :firm, join_table: :clients, left_key: :id, left_primary_key: :client_id
584
531
  end
585
532
 
586
533
  Firm.first.invoices
534
+ Invoice.first.firm
587
535
 
588
- To handle cases where there are multiple join tables, use the many_through_many
536
+ To handle cases where there are multiple join tables, you can use the many_through_many
589
537
  plugin that ships with Sequel.
590
538
 
591
539
  === Polymorphic Associations
@@ -602,38 +550,19 @@ you are stuck with an existing design that uses them.
602
550
  If you must use them, look for the sequel_polymorphic external plugin, as it makes using
603
551
  polymorphic associations in Sequel about as easy as it is in ActiveRecord. However,
604
552
  here's how they can be done using Sequel's custom associations (the sequel_polymorphic
605
- plugin is just a generic version of this code):
606
-
607
- ActiveRecord:
608
-
609
- class Asset < ActiveRecord::Base
610
- belongs_to :attachable, :polymorphic => true
611
- end
612
-
613
- class Post < ActiveRecord::Base
614
- has_many :assets, :as => :attachable
615
- end
616
-
617
- class Note < ActiveRecord::Base
618
- has_many :assets, :as => :attachable
619
- end
620
-
621
- @asset.attachable = @post
622
- @asset.attachable = @note
623
-
624
- Sequel::Model:
553
+ external plugin is just a generic version of this code):
625
554
 
626
555
  class Asset < Sequel::Model
627
- many_to_one :attachable, :reciprocal=>:assets,
628
- :setter=>(proc do |attachable|
556
+ many_to_one :attachable, reciprocal: :assets,
557
+ setter: (lambda do |attachable|
629
558
  self[:attachable_id] = (attachable.pk if attachable)
630
559
  self[:attachable_type] = (attachable.class.name if attachable)
631
560
  end),
632
- :dataset=>(proc do
561
+ dataset: (proc do
633
562
  klass = attachable_type.constantize
634
563
  klass.where(klass.primary_key=>attachable_id)
635
564
  end),
636
- :eager_loader=>(proc do |eo|
565
+ eager_loader: (lambda do |eo|
637
566
  id_map = {}
638
567
  eo[:rows].each do |asset|
639
568
  asset.associations[:attachable] = nil
@@ -651,24 +580,22 @@ Sequel::Model:
651
580
  end
652
581
 
653
582
  class Post < Sequel::Model
654
- one_to_many :assets, :key=>:attachable_id, :reciprocal=>:attachable, :conditions=>{:attachable_type=>'Post'},
655
- :adder=>proc{|asset| asset.update(:attachable_id=>pk, :attachable_type=>'Post')},
656
- :remover=>proc{|asset| asset.update(:attachable_id=>nil, :attachable_type=>nil)},
657
- :clearer=>proc{assets_dataset.update(:attachable_id=>nil, :attachable_type=>nil)}
583
+ one_to_many :assets, key: :attachable_id, reciprocal: :attachable, conditions: {attachable_type: 'Post'},
584
+ adder: lambda{|asset| asset.update(attachable_id: pk, attachable_type: 'Post')},
585
+ remover: lambda{|asset| asset.update(attachable_id: nil, attachable_type: nil)},
586
+ clearer: lambda{assets_dataset.update(attachable_id: nil, attachable_type: nil)}
658
587
  end
659
588
 
660
589
  class Note < Sequel::Model
661
- one_to_many :assets, :key=>:attachable_id, :reciprocal=>:attachable, :conditions=>{:attachable_type=>'Note'},
662
- :adder=>proc{|asset| asset.update(:attachable_id=>pk, :attachable_type=>'Note')},
663
- :remover=>proc{|asset| asset.update(:attachable_id=>nil, :attachable_type=>nil)},
664
- :clearer=>proc{assets_dataset.update(:attachable_id=>nil, :attachable_type=>nil)}
590
+ one_to_many :assets, key: :attachable_id, reciprocal: :attachable, conditions: {attachable_type: 'Note'},
591
+ adder: lambda{|asset| asset.update(attachable_id: pk, attachable_type: 'Note')},
592
+ remover: lambda{|asset| asset.update(attachable_id: nil, attachable_type: nil)},
593
+ clearer: lambda{assets_dataset.update(attachable_id: nil, attachable_type: nil)}
665
594
  end
666
595
 
667
596
  @asset.attachable = @post
668
597
  @asset.attachable = @note
669
598
 
670
- == Other advanced associations
671
-
672
599
  === Joining on multiple keys
673
600
 
674
601
  Let's say you have two tables that are associated with each other with multiple
@@ -680,10 +607,10 @@ associations:
680
607
  # associated favorite track
681
608
 
682
609
  class Track < Sequel::Model
683
- many_to_one :favorite_track, :key=>[:disc_number, :number, :album_id], :primary_key=>[:disc_number, :number, :album_id]
610
+ many_to_one :favorite_track, key: [:disc_number, :number, :album_id], primary_key: [:disc_number, :number, :album_id]
684
611
  end
685
612
  class FavoriteTrack < Sequel::Model
686
- one_to_one :tracks, :key=>[:disc_number, :number, :album_id], :primary_key=>[:disc_number, :number, :album_id]
613
+ one_to_one :tracks, key: [:disc_number, :number, :album_id], primary_key: [:disc_number, :number, :album_id]
687
614
  end
688
615
 
689
616
  === Tree - All Ancestors and Descendents
@@ -692,24 +619,24 @@ Let's say you want to store a tree relationship in your database, it's pretty
692
619
  simple:
693
620
 
694
621
  class Node < Sequel::Model
695
- many_to_one :parent, :class=>self
696
- one_to_many :children, :key=>:parent_id, :class=>self
622
+ many_to_one :parent, class: self
623
+ one_to_many :children, key: :parent_id, class: self
697
624
  end
698
625
 
699
626
  You can easily get a node's parent with node.parent, and a node's children with
700
627
  node.children. You can even eager load the relationship up to a certain depth:
701
628
 
702
629
  # Eager load three generations of generations of children for a given node
703
- Node.where(:id=>1).eager(:children=>{:children=>:children}).all.first
630
+ Node.where(id: 1).eager(children: {children: :children}).all.first
704
631
  # Load parents and grandparents for a group of nodes
705
- Node.where{id < 10}.eager(:parent=>:parent).all
632
+ Node.where{id < 10}.eager(parent: :parent).all
706
633
 
707
634
  What if you want to get all ancestors up to the root node, or all descendents,
708
635
  without knowing the depth of the tree?
709
636
 
710
637
  class Node < Sequel::Model
711
- many_to_one :ancestors, :class=>self,
712
- :eager_loader=>(proc do |eo|
638
+ many_to_one :ancestors, class: self,
639
+ eager_loader: (lambda do |eo|
713
640
  # Handle cases where the root node has the same parent_id as primary_key
714
641
  # and also when it is NULL
715
642
  non_root_nodes = eo[:rows].reject do |n|
@@ -727,13 +654,13 @@ without knowing the depth of the tree?
727
654
  non_root_nodes.each{|n| (id_map[n.parent_id] ||= []) << n}
728
655
  # Doesn't cause an infinte loop, because when only the root node
729
656
  # is left, this is not called.
730
- Node.where(Node.primary_key=>id_map.keys).eager(:ancestors).all do |node|
657
+ Node.where(id: id_map.keys).eager(:ancestors).all do |node|
731
658
  # Populate the parent association for each node
732
659
  id_map[node.pk].each{|n| n.associations[:parent] = node}
733
660
  end
734
661
  end
735
662
  end)
736
- many_to_one :descendants, :eager_loader=>(proc do |eo|
663
+ many_to_one :descendants, eager_loader: (lambda do |eo|
737
664
  id_map = {}
738
665
  eo[:rows].each do |n|
739
666
  # Initialize an empty array of child associations for each parent node
@@ -745,7 +672,7 @@ without knowing the depth of the tree?
745
672
  # if no records are returned. Exclude id = parent_id to avoid infinite loop
746
673
  # if the root note is one of the returned records and it has parent_id = id
747
674
  # instead of parent_id = NULL.
748
- Node.where(:parent_id=>id_map.keys).exclude(:id=>:parent_id).eager(:descendants).all do |node|
675
+ Node.where(parent_id: id_map.keys).exclude(id: :parent_id).eager(:descendants).all do |node|
749
676
  # Get the parent from the identity map
750
677
  parent = id_map[node.parent_id]
751
678
  # Set the child's parent association to the parent
@@ -756,11 +683,7 @@ without knowing the depth of the tree?
756
683
  end)
757
684
  end
758
685
 
759
- Note that unlike ActiveRecord, Sequel supports common table expressions, which allows you to use recursive queries.
760
- The results are not the same as in the above case, as all descendents are stored in a single association,
761
- but all descendants can be both lazy loaded or eager loaded in a single query (assuming your database
762
- supports recursive common table expressions). Sequel ships with an +rcte_tree+ plugin that makes
763
- this easy:
686
+ Note that Sequel ships with an rcte_tree plugin that does all of the above and more:
764
687
 
765
688
  class Node < Sequel::Model
766
689
  plugin :rcte_tree
@@ -780,21 +703,21 @@ What you want to do is get all songs for a given artist, ordered by the song's
780
703
  name, with no duplicates?
781
704
 
782
705
  class Artist < Sequel::Model
783
- one_to_many :songs, :order=>Sequel[:songs][:name], \
784
- :dataset=>proc{Song.select_all(:songs).join(Lyric, :id=>:lyric_id, id=>[:composer_id, :arranger_id, :vocalist_id, :lyricist_id])}, \
785
- :eager_loader=>(proc do |eo|
706
+ one_to_many :songs, order: Sequel[:songs][:name],
707
+ dataset: proc{Song.select_all(:songs).join(:lyrics, id: :lyric_id, id=>[:composer_id, :arranger_id, :vocalist_id, :lyricist_id])},
708
+ eager_loader: (lambda do |eo|
786
709
  h = eo[:id_map]
787
710
  ids = h.keys
788
711
  eo[:rows].each{|r| r.associations[:songs] = []}
789
712
  Song.select_all(:songs).
790
- select_append{[lyrics[:composer_id], lyrics[:arranger_id], lyrics[:vocalist_id], lyrics[:lyricist_id]}.
791
- join(Lyric, :id=>:lyric_id){Sequel.or(:composer_id=>ids, :arranger_id=>ids, :vocalist_id=>ids, :lyricist_id=>ids)}.
713
+ select_append{[lyrics[:composer_id], lyrics[:arranger_id], lyrics[:vocalist_id], lyrics[:lyricist_id]]}.
714
+ join(:lyrics, id: :lyric_id){Sequel.or(composer_id: ids, arranger_id: ids, vocalist_id: ids, lyricist_id: ids)}.
792
715
  order{songs[:name]}.all do |song|
793
- [:composer_id, :arranger_id, :vocalist_id, :lyricist_id].each do |x|
794
- recs = h[song.values.delete(x)]
795
- recs.each{|r| r.associations[:songs] << song} if recs
716
+ [:composer_id, :arranger_id, :vocalist_id, :lyricist_id].each do |x|
717
+ recs = h[song.values.delete(x)]
718
+ recs.each{|r| r.associations[:songs] << song} if recs
719
+ end
796
720
  end
797
- end
798
721
  eo[:rows].each{|r| r.associations[:songs].uniq!}
799
722
  end)
800
723
  end
@@ -811,11 +734,11 @@ associated tickets.
811
734
 
812
735
  class Project < Sequel::Model
813
736
  one_to_many :tickets
814
- many_to_one :ticket_hours, :read_only=>true, :key=>:id,
815
- :dataset=>proc{Ticket.where(:project_id=>id).select{sum(hours).as(hours)}},
816
- :eager_loader=>(proc do |eo|
737
+ many_to_one :ticket_hours, read_only: true, key: :id,
738
+ dataset: proc{Ticket.where(:project_id=>id).select{sum(hours).as(hours)}},
739
+ eager_loader: (lambda do |eo|
817
740
  eo[:rows].each{|p| p.associations[:ticket_hours] = nil}
818
- Ticket.where(:project_id=>eo[:id_map].keys).
741
+ Ticket.where(project_id: eo[:id_map].keys).
819
742
  select_group(:project_id).
820
743
  select_append{sum(hours).as(hours)}.
821
744
  all do |t|
@@ -838,4 +761,4 @@ associated tickets.
838
761
  end
839
762
 
840
763
  Note that it is often better to use a sum cache instead of this approach. You can implement
841
- a sum cache using +after_create+ and +after_delete+ hooks, or preferrably using a database trigger.
764
+ a sum cache using +after_create+, +after_update+, and +after_delete+ hooks, or preferrably using a database trigger.