sequel 3.21.0 → 3.39.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 (352) hide show
  1. data/CHANGELOG +1061 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +103 -63
  4. data/Rakefile +59 -27
  5. data/bin/sequel +50 -26
  6. data/doc/active_record.rdoc +67 -51
  7. data/doc/advanced_associations.rdoc +189 -75
  8. data/doc/association_basics.rdoc +327 -79
  9. data/doc/cheat_sheet.rdoc +21 -21
  10. data/doc/core_extensions.rdoc +374 -0
  11. data/doc/dataset_basics.rdoc +10 -10
  12. data/doc/dataset_filtering.rdoc +54 -42
  13. data/doc/mass_assignment.rdoc +56 -0
  14. data/doc/migration.rdoc +89 -557
  15. data/doc/model_hooks.rdoc +80 -29
  16. data/doc/object_model.rdoc +547 -0
  17. data/doc/opening_databases.rdoc +114 -53
  18. data/doc/prepared_statements.rdoc +41 -15
  19. data/doc/querying.rdoc +137 -56
  20. data/doc/reflection.rdoc +36 -10
  21. data/doc/release_notes/3.22.0.txt +39 -0
  22. data/doc/release_notes/3.23.0.txt +172 -0
  23. data/doc/release_notes/3.24.0.txt +420 -0
  24. data/doc/release_notes/3.25.0.txt +88 -0
  25. data/doc/release_notes/3.26.0.txt +88 -0
  26. data/doc/release_notes/3.27.0.txt +82 -0
  27. data/doc/release_notes/3.28.0.txt +304 -0
  28. data/doc/release_notes/3.29.0.txt +459 -0
  29. data/doc/release_notes/3.30.0.txt +135 -0
  30. data/doc/release_notes/3.31.0.txt +146 -0
  31. data/doc/release_notes/3.32.0.txt +202 -0
  32. data/doc/release_notes/3.33.0.txt +157 -0
  33. data/doc/release_notes/3.34.0.txt +671 -0
  34. data/doc/release_notes/3.35.0.txt +144 -0
  35. data/doc/release_notes/3.36.0.txt +245 -0
  36. data/doc/release_notes/3.37.0.txt +338 -0
  37. data/doc/release_notes/3.38.0.txt +234 -0
  38. data/doc/release_notes/3.39.0.txt +237 -0
  39. data/doc/schema_modification.rdoc +585 -0
  40. data/doc/sharding.rdoc +99 -8
  41. data/doc/sql.rdoc +154 -112
  42. data/doc/testing.rdoc +169 -0
  43. data/doc/thread_safety.rdoc +17 -0
  44. data/doc/transactions.rdoc +137 -0
  45. data/doc/validations.rdoc +1 -1
  46. data/doc/virtual_rows.rdoc +78 -43
  47. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  48. data/lib/sequel/adapters/ado.rb +40 -18
  49. data/lib/sequel/adapters/amalgalite.rb +17 -9
  50. data/lib/sequel/adapters/db2.rb +175 -86
  51. data/lib/sequel/adapters/dbi.rb +15 -15
  52. data/lib/sequel/adapters/do/mysql.rb +17 -11
  53. data/lib/sequel/adapters/do/postgres.rb +2 -61
  54. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  55. data/lib/sequel/adapters/do.rb +18 -36
  56. data/lib/sequel/adapters/firebird.rb +27 -208
  57. data/lib/sequel/adapters/ibmdb.rb +453 -0
  58. data/lib/sequel/adapters/informix.rb +6 -23
  59. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  60. data/lib/sequel/adapters/jdbc/db2.rb +56 -0
  61. data/lib/sequel/adapters/jdbc/derby.rb +325 -0
  62. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  63. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  64. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  65. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  66. data/lib/sequel/adapters/jdbc/jtds.rb +40 -0
  67. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  68. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  69. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  70. data/lib/sequel/adapters/jdbc/postgresql.rb +77 -52
  71. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  72. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  73. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  74. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  75. data/lib/sequel/adapters/jdbc.rb +279 -97
  76. data/lib/sequel/adapters/mock.rb +372 -0
  77. data/lib/sequel/adapters/mysql.rb +125 -222
  78. data/lib/sequel/adapters/mysql2.rb +65 -56
  79. data/lib/sequel/adapters/odbc/mssql.rb +17 -9
  80. data/lib/sequel/adapters/odbc.rb +28 -9
  81. data/lib/sequel/adapters/openbase.rb +3 -5
  82. data/lib/sequel/adapters/oracle.rb +349 -53
  83. data/lib/sequel/adapters/postgres.rb +368 -138
  84. data/lib/sequel/adapters/shared/access.rb +32 -9
  85. data/lib/sequel/adapters/shared/db2.rb +343 -0
  86. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  87. data/lib/sequel/adapters/shared/informix.rb +53 -0
  88. data/lib/sequel/adapters/shared/mssql.rb +401 -116
  89. data/lib/sequel/adapters/shared/mysql.rb +424 -89
  90. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  91. data/lib/sequel/adapters/shared/oracle.rb +257 -45
  92. data/lib/sequel/adapters/shared/postgres.rb +675 -279
  93. data/lib/sequel/adapters/shared/progress.rb +5 -8
  94. data/lib/sequel/adapters/shared/sqlite.rb +285 -96
  95. data/lib/sequel/adapters/sqlite.rb +101 -39
  96. data/lib/sequel/adapters/swift/mysql.rb +10 -12
  97. data/lib/sequel/adapters/swift/postgres.rb +13 -79
  98. data/lib/sequel/adapters/swift/sqlite.rb +9 -6
  99. data/lib/sequel/adapters/swift.rb +28 -19
  100. data/lib/sequel/adapters/tinytds.rb +174 -22
  101. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
  102. data/lib/sequel/adapters/utils/pg_types.rb +81 -0
  103. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  104. data/lib/sequel/ast_transformer.rb +194 -0
  105. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  106. data/lib/sequel/connection_pool/sharded_threaded.rb +33 -2
  107. data/lib/sequel/connection_pool/single.rb +5 -0
  108. data/lib/sequel/connection_pool/threaded.rb +43 -14
  109. data/lib/sequel/connection_pool.rb +10 -2
  110. data/lib/sequel/core.rb +143 -11
  111. data/lib/sequel/database/connecting.rb +38 -28
  112. data/lib/sequel/database/dataset.rb +4 -4
  113. data/lib/sequel/database/dataset_defaults.rb +63 -2
  114. data/lib/sequel/database/logging.rb +7 -2
  115. data/lib/sequel/database/misc.rb +190 -46
  116. data/lib/sequel/database/query.rb +272 -110
  117. data/lib/sequel/database/schema_generator.rb +103 -36
  118. data/lib/sequel/database/schema_methods.rb +300 -67
  119. data/lib/sequel/dataset/actions.rb +344 -90
  120. data/lib/sequel/dataset/features.rb +101 -8
  121. data/lib/sequel/dataset/graph.rb +42 -15
  122. data/lib/sequel/dataset/misc.rb +52 -25
  123. data/lib/sequel/dataset/mutation.rb +12 -7
  124. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  125. data/lib/sequel/dataset/query.rb +305 -54
  126. data/lib/sequel/dataset/sql.rb +720 -292
  127. data/lib/sequel/dataset.rb +8 -0
  128. data/lib/sequel/exceptions.rb +4 -0
  129. data/lib/sequel/extensions/_pretty_table.rb +83 -0
  130. data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
  131. data/lib/sequel/extensions/blank.rb +4 -0
  132. data/lib/sequel/extensions/columns_introspection.rb +72 -0
  133. data/lib/sequel/extensions/constraint_validations.rb +451 -0
  134. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
  135. data/lib/sequel/extensions/eval_inspect.rb +173 -0
  136. data/lib/sequel/extensions/inflector.rb +4 -0
  137. data/lib/sequel/extensions/looser_typecasting.rb +5 -4
  138. data/lib/sequel/extensions/migration.rb +100 -11
  139. data/lib/sequel/extensions/named_timezones.rb +9 -0
  140. data/lib/sequel/extensions/null_dataset.rb +94 -0
  141. data/lib/sequel/extensions/pagination.rb +4 -0
  142. data/lib/sequel/extensions/pg_array.rb +537 -0
  143. data/lib/sequel/extensions/pg_array_ops.rb +263 -0
  144. data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
  145. data/lib/sequel/extensions/pg_hstore.rb +313 -0
  146. data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
  147. data/lib/sequel/extensions/pg_inet.rb +113 -0
  148. data/lib/sequel/extensions/pg_interval.rb +191 -0
  149. data/lib/sequel/extensions/pg_json.rb +208 -0
  150. data/lib/sequel/extensions/pg_range.rb +512 -0
  151. data/lib/sequel/extensions/pg_range_ops.rb +150 -0
  152. data/lib/sequel/extensions/pg_row.rb +572 -0
  153. data/lib/sequel/extensions/pg_row_ops.rb +182 -0
  154. data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
  155. data/lib/sequel/extensions/pretty_table.rb +17 -72
  156. data/lib/sequel/extensions/query.rb +8 -4
  157. data/lib/sequel/extensions/query_literals.rb +79 -0
  158. data/lib/sequel/extensions/schema_caching.rb +76 -0
  159. data/lib/sequel/extensions/schema_dumper.rb +282 -76
  160. data/lib/sequel/extensions/select_remove.rb +39 -0
  161. data/lib/sequel/extensions/server_block.rb +140 -0
  162. data/lib/sequel/extensions/split_array_nil.rb +65 -0
  163. data/lib/sequel/extensions/sql_expr.rb +8 -110
  164. data/lib/sequel/extensions/string_date_time.rb +4 -0
  165. data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
  166. data/lib/sequel/extensions/to_dot.rb +99 -83
  167. data/lib/sequel/model/associations.rb +1263 -409
  168. data/lib/sequel/model/base.rb +624 -171
  169. data/lib/sequel/model/errors.rb +1 -1
  170. data/lib/sequel/model/exceptions.rb +24 -2
  171. data/lib/sequel/model/inflections.rb +1 -1
  172. data/lib/sequel/model.rb +30 -11
  173. data/lib/sequel/no_core_ext.rb +2 -0
  174. data/lib/sequel/plugins/active_model.rb +13 -1
  175. data/lib/sequel/plugins/association_pks.rb +22 -4
  176. data/lib/sequel/plugins/caching.rb +25 -18
  177. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  178. data/lib/sequel/plugins/composition.rb +44 -12
  179. data/lib/sequel/plugins/constraint_validations.rb +198 -0
  180. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  181. data/lib/sequel/plugins/defaults_setter.rb +72 -0
  182. data/lib/sequel/plugins/dirty.rb +214 -0
  183. data/lib/sequel/plugins/eager_each.rb +59 -0
  184. data/lib/sequel/plugins/force_encoding.rb +6 -6
  185. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  186. data/lib/sequel/plugins/identity_map.rb +134 -15
  187. data/lib/sequel/plugins/instance_filters.rb +10 -0
  188. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  189. data/lib/sequel/plugins/json_serializer.rb +58 -6
  190. data/lib/sequel/plugins/list.rb +13 -2
  191. data/lib/sequel/plugins/many_through_many.rb +103 -51
  192. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  193. data/lib/sequel/plugins/nested_attributes.rb +150 -66
  194. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  195. data/lib/sequel/plugins/pg_row.rb +121 -0
  196. data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -0
  197. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  198. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  199. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  200. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  201. data/lib/sequel/plugins/rcte_tree.rb +31 -17
  202. data/lib/sequel/plugins/schema.rb +8 -3
  203. data/lib/sequel/plugins/serialization.rb +98 -49
  204. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  205. data/lib/sequel/plugins/sharding.rb +21 -54
  206. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  207. data/lib/sequel/plugins/static_cache.rb +99 -0
  208. data/lib/sequel/plugins/subclasses.rb +29 -3
  209. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  210. data/lib/sequel/plugins/timestamps.rb +1 -1
  211. data/lib/sequel/plugins/tree.rb +3 -3
  212. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  213. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  214. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  215. data/lib/sequel/plugins/validation_helpers.rb +55 -4
  216. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  217. data/lib/sequel/sql.rb +649 -122
  218. data/lib/sequel/timezones.rb +67 -40
  219. data/lib/sequel/version.rb +1 -1
  220. data/spec/adapters/db2_spec.rb +146 -0
  221. data/spec/adapters/firebird_spec.rb +1 -1
  222. data/spec/adapters/mssql_spec.rb +194 -66
  223. data/spec/adapters/mysql_spec.rb +475 -306
  224. data/spec/adapters/oracle_spec.rb +92 -117
  225. data/spec/adapters/postgres_spec.rb +1982 -325
  226. data/spec/adapters/spec_helper.rb +9 -6
  227. data/spec/adapters/sqlite_spec.rb +203 -75
  228. data/spec/core/connection_pool_spec.rb +218 -93
  229. data/spec/core/database_spec.rb +956 -522
  230. data/spec/core/dataset_spec.rb +1631 -1242
  231. data/spec/core/expression_filters_spec.rb +690 -334
  232. data/spec/core/mock_adapter_spec.rb +453 -0
  233. data/spec/core/object_graph_spec.rb +88 -129
  234. data/spec/core/schema_generator_spec.rb +4 -4
  235. data/spec/core/schema_spec.rb +556 -53
  236. data/spec/core/spec_helper.rb +7 -48
  237. data/spec/core_extensions_spec.rb +626 -0
  238. data/spec/extensions/active_model_spec.rb +13 -0
  239. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  240. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  241. data/spec/extensions/association_dependencies_spec.rb +15 -25
  242. data/spec/extensions/association_pks_spec.rb +66 -32
  243. data/spec/extensions/association_proxies_spec.rb +4 -4
  244. data/spec/extensions/boolean_readers_spec.rb +25 -25
  245. data/spec/extensions/caching_spec.rb +47 -51
  246. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  247. data/spec/extensions/columns_introspection_spec.rb +91 -0
  248. data/spec/extensions/composition_spec.rb +18 -13
  249. data/spec/extensions/constraint_validations_plugin_spec.rb +196 -0
  250. data/spec/extensions/constraint_validations_spec.rb +316 -0
  251. data/spec/extensions/dataset_associations_spec.rb +199 -0
  252. data/spec/extensions/defaults_setter_spec.rb +88 -0
  253. data/spec/extensions/dirty_spec.rb +155 -0
  254. data/spec/extensions/eager_each_spec.rb +34 -0
  255. data/spec/extensions/eval_inspect_spec.rb +67 -0
  256. data/spec/extensions/force_encoding_spec.rb +4 -2
  257. data/spec/extensions/hook_class_methods_spec.rb +97 -128
  258. data/spec/extensions/identity_map_spec.rb +142 -24
  259. data/spec/extensions/inflector_spec.rb +0 -4
  260. data/spec/extensions/instance_filters_spec.rb +11 -21
  261. data/spec/extensions/instance_hooks_spec.rb +72 -0
  262. data/spec/extensions/json_serializer_spec.rb +49 -12
  263. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  264. data/spec/extensions/list_spec.rb +49 -40
  265. data/spec/extensions/looser_typecasting_spec.rb +7 -7
  266. data/spec/extensions/many_through_many_spec.rb +368 -254
  267. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  268. data/spec/extensions/migration_spec.rb +140 -35
  269. data/spec/extensions/named_timezones_spec.rb +29 -11
  270. data/spec/extensions/nested_attributes_spec.rb +268 -89
  271. data/spec/extensions/null_dataset_spec.rb +85 -0
  272. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  273. data/spec/extensions/pg_array_ops_spec.rb +112 -0
  274. data/spec/extensions/pg_array_spec.rb +320 -0
  275. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  276. data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
  277. data/spec/extensions/pg_hstore_spec.rb +194 -0
  278. data/spec/extensions/pg_inet_spec.rb +47 -0
  279. data/spec/extensions/pg_interval_spec.rb +72 -0
  280. data/spec/extensions/pg_json_spec.rb +99 -0
  281. data/spec/extensions/pg_range_ops_spec.rb +56 -0
  282. data/spec/extensions/pg_range_spec.rb +395 -0
  283. data/spec/extensions/pg_row_ops_spec.rb +58 -0
  284. data/spec/extensions/pg_row_plugin_spec.rb +49 -0
  285. data/spec/extensions/pg_row_spec.rb +323 -0
  286. data/spec/extensions/pg_statement_cache_spec.rb +208 -0
  287. data/spec/extensions/pg_typecast_on_load_spec.rb +58 -0
  288. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  289. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  290. data/spec/extensions/prepared_statements_spec.rb +87 -0
  291. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  292. data/spec/extensions/pretty_table_spec.rb +7 -6
  293. data/spec/extensions/query_literals_spec.rb +167 -0
  294. data/spec/extensions/query_spec.rb +9 -3
  295. data/spec/extensions/rcte_tree_spec.rb +50 -43
  296. data/spec/extensions/schema_caching_spec.rb +41 -0
  297. data/spec/extensions/schema_dumper_spec.rb +434 -49
  298. data/spec/extensions/schema_spec.rb +30 -49
  299. data/spec/extensions/select_remove_spec.rb +38 -0
  300. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  301. data/spec/extensions/serialization_spec.rb +86 -48
  302. data/spec/extensions/server_block_spec.rb +90 -0
  303. data/spec/extensions/sharding_spec.rb +69 -143
  304. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  305. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  306. data/spec/extensions/spec_helper.rb +34 -67
  307. data/spec/extensions/split_array_nil_spec.rb +24 -0
  308. data/spec/extensions/sql_expr_spec.rb +29 -60
  309. data/spec/extensions/static_cache_spec.rb +145 -0
  310. data/spec/extensions/subclasses_spec.rb +14 -0
  311. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  312. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  313. data/spec/extensions/timestamps_spec.rb +6 -6
  314. data/spec/extensions/to_dot_spec.rb +8 -11
  315. data/spec/extensions/touch_spec.rb +13 -14
  316. data/spec/extensions/tree_spec.rb +11 -26
  317. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  318. data/spec/extensions/update_primary_key_spec.rb +30 -24
  319. data/spec/extensions/validation_class_methods_spec.rb +58 -67
  320. data/spec/extensions/validation_helpers_spec.rb +31 -37
  321. data/spec/extensions/xml_serializer_spec.rb +29 -4
  322. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  323. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  324. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  325. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  326. data/spec/integration/associations_test.rb +659 -21
  327. data/spec/integration/database_test.rb +11 -5
  328. data/spec/integration/dataset_test.rb +717 -129
  329. data/spec/integration/eager_loader_test.rb +38 -54
  330. data/spec/integration/migrator_test.rb +2 -3
  331. data/spec/integration/model_test.rb +81 -2
  332. data/spec/integration/plugin_test.rb +604 -147
  333. data/spec/integration/prepared_statement_test.rb +272 -128
  334. data/spec/integration/schema_test.rb +367 -57
  335. data/spec/integration/spec_helper.rb +42 -32
  336. data/spec/integration/timezone_test.rb +38 -12
  337. data/spec/integration/transaction_test.rb +183 -8
  338. data/spec/integration/type_test.rb +31 -8
  339. data/spec/model/association_reflection_spec.rb +207 -8
  340. data/spec/model/associations_spec.rb +1206 -693
  341. data/spec/model/base_spec.rb +334 -100
  342. data/spec/model/dataset_methods_spec.rb +45 -27
  343. data/spec/model/eager_loading_spec.rb +828 -692
  344. data/spec/model/hooks_spec.rb +355 -67
  345. data/spec/model/model_spec.rb +251 -179
  346. data/spec/model/plugins_spec.rb +24 -13
  347. data/spec/model/record_spec.rb +506 -232
  348. data/spec/model/spec_helper.rb +16 -77
  349. data/spec/model/validations_spec.rb +38 -2
  350. data/spec/rcov.opts +2 -0
  351. metadata +209 -80
  352. data/spec/core/core_sql_spec.rb +0 -451
@@ -14,7 +14,7 @@ a different block when eager loading via <tt>Dataset#eager</tt>. Association blo
14
14
  useful for things like:
15
15
 
16
16
  Artist.one_to_many :gold_albums, :class=>:Album do |ds|
17
- ds.filter{copies_sold > 500000}
17
+ ds.where{copies_sold > 500000}
18
18
  end
19
19
 
20
20
  There are a whole bunch of options for changing how the association is eagerly
@@ -50,12 +50,12 @@ These can be used like this:
50
50
 
51
51
  # Only returns albums that have sold more than 500,000 copies
52
52
  Artist.one_to_many :gold_albums, :class=>:Album, \
53
- :graph_block=>proc{|j,lj,js| :copies_sold.qualify(j) > 500000}
53
+ :graph_block=>proc{|j,lj,js| Sequel.qualify(j, :copies_sold) > 500000}
54
54
 
55
55
  # Handles the case where the tables are associated by a case insensitive name string
56
56
  Artist.one_to_many :albums, :key=>:artist_name, \
57
57
  :graph_only_conditions=>nil, \
58
- :graph_block=>proc{|j,lj,js| {:lower.sql_function(artist_name.qualify(j))=>:lower.sql_function(name.qualify(lj))}}
58
+ :graph_block=>proc{|j,lj,js| {Sequel.function(:lower, Sequel.qualify(j, :artist_name))=>Sequel.function(:lower, Sequel.qualify(lj, :name))}}
59
59
 
60
60
  # Handles the case where both key columns have the name artist_name, and you want to use
61
61
  # a JOIN USING
@@ -75,19 +75,15 @@ option. Though it can often be verbose (compared to other things in Sequel),
75
75
  it allows you complete control over how to eagerly load associations for a
76
76
  group of objects.
77
77
 
78
- :eager_loader should be a proc that takes 1 or 3 arguments. If the proc
79
- takes one argument, it will be given a hash with the following keys:
78
+ :eager_loader should be a proc that takes a single hash argument, which will
79
+ have at least the following keys:
80
80
 
81
- :key_hash :: A key_hash, described below
81
+ :id_map :: A mapping of key values to arrays of current model instances,
82
+ usage described below
82
83
  :rows :: An array of model objects
83
84
  :associations :: A hash of dependent associations to eagerly load
84
85
  :self :: The dataset that is doing the eager loading
85
-
86
- If the proc takes three arguments, it gets passed the :key_hash, :rows,
87
- and :associations values. The only way to get the :self value is to
88
- accept one argument. The 3 argument procs are allowed for backwards
89
- compatibility, and it is recommended to use the 1 argument proc format
90
- for new code.
86
+ :eager_block :: A dynamic callback for this eager load.
91
87
 
92
88
  Since you are given all of the records, you can do things like filter on
93
89
  associations that are specified by multiple keys, or do multiple
@@ -96,25 +92,141 @@ necessary for polymorphic associations). Inside the <tt>:eager_loader</tt>
96
92
  proc, you should get the related objects and populate the
97
93
  associations cache for all objects in the array of records. The hash
98
94
  of dependent associations is available for you to cascade the eager
99
- loading down multiple levels, but it is up to you to use it. The
100
- key_hash is a performance enhancement that is used by the default
101
- association loaders and is also available to you. It is a hash with keys being
102
- foreign/primary key symbols in the current table, and the values
103
- being hashes where the key is foreign/primary key values and values
104
- being arrays of current model objects having the foreign/primary key
105
- value associated with the key. This is hard to visualize, so I'll
106
- give an example:
107
-
108
- album1 = Album.load(:id=>1, :artist_id=>2)
109
- album2 = Album.load(:id=>3, :artist_id=>2)
95
+ loading down multiple levels, but it is up to you to use it.
96
+
97
+ The id_map is a performance enhancement that is used by the default
98
+ association loaders and is also available to you. It is a hash with keys
99
+ foreign/primary key values, and values being arrays of current model
100
+ objects having the foreign/primary key value associated with the key.
101
+ This may be hard to visualize, so I'll give an example. Let's say you
102
+ have the following associations
103
+
110
104
  Album.many_to_one :artist
111
105
  Album.one_to_many :tracks
112
- Album.eager(:band, :tracks).all
113
- # The key_hash provided to the :eager_loader proc would be:
114
- {:id=>{1=>[album1], 3=>[album2]}, :artist_id=>{2=>[album1, album2]}}
106
+
107
+ and the following three albums in the database:
108
+
109
+ album1 = Album.create(:artist_id=>3) # id: 1
110
+ album2 = Album.create(:artist_id=>3) # id: 2
111
+ album3 = Album.create(:artist_id=>2) # id: 3
112
+
113
+ If you try to eager load this dataset:
114
+
115
+ Album.eager(:artist, :tracks).all
116
+
117
+ Then the id_map provided to the artist :eager_loader proc would be:
118
+
119
+ {3=>[album1, album2], 2=>[album3]}
120
+
121
+ The artist id_map contains a mapping of artist_id values to arrays of
122
+ album objects. Since both album1 and album2 have the same artist_id,
123
+ the are both in the array related to that key. album3 has a different
124
+ artist_id, so it is in a different array. Eager loading of artists is
125
+ done by looking for any artist having one of the keys in the hash:
126
+
127
+ artists = Artist.where(:id=>id_map.keys).all
128
+
129
+ When the artists are retrieved, you can iterate over them, find entries
130
+ with matching keys, and manually associate them to the albums:
131
+
132
+ artists.each do |artist|
133
+ # Find related albums using the artist_id_map
134
+ if albums = id_map[artist.id]
135
+ # Iterate over the albums
136
+ albums.each do |album|
137
+ # Manually set the artist association for each album
138
+ album.associations[:artist] = artist
139
+ end
140
+ end
141
+ end
142
+
143
+ The id_map provided to the tracks :eager_loader proc would be:
144
+
145
+ {1=>[album1], 2=>[album2], 3=>[album3]}
146
+
147
+ Now the id_map contains a mapping of id values to arrays of album objects (in this
148
+ case each array only has a single object, because id is the primary key). So when
149
+ looking for tracks to eagerly load, you only need to look for ones that have an
150
+ album_id with one of the keys in the hash:
151
+
152
+ tracks = Track.where(:album_id=>id_map.keys).all
153
+
154
+ When the tracks are retrieved, you can iterate over them, find entries with matching
155
+ keys, and manually associate them to the albums:
156
+
157
+ tracks.each do |track|
158
+ if albums = id_map[track.album_id]
159
+ albums.each do |album|
160
+ album.associations[:tracks] << track
161
+ end
162
+ end
163
+ end
164
+
165
+ === Two basic example eager loaders
166
+
167
+ Putting the code in the above examples together, you almost have enough for a basic
168
+ working eager loader. The main important thing that is missing is you need to set
169
+ initial values for the eagerly loaded associations. For the artist association, you
170
+ need to initial the values to nil:
115
171
 
116
- Using these options, you can build associations that Sequel doesn't natively support,
117
- and still be able to use the same eager loading features that you are used to.
172
+ # rows here is the :rows entry in the hash passed to the eager loader
173
+ rows.each{|album| album.associations[:artist] = nil}
174
+
175
+ For the tracks association, you set the initial value to an empty array:
176
+
177
+ rows.each{|album| album.associations[:track] = []}
178
+
179
+ These are done so that if an album currently being loaded doesn't have an associated
180
+ artist or any associated tracks, the lack of them will be cached, so calling the
181
+ artist or tracks method on the album will not do another database lookup.
182
+
183
+ So putting everything together, the artist eager loader looks like:
184
+
185
+ :eager_loader=>(proc do |eo_opts|
186
+ eo_opts[:rows].each{|album| album.associations[:artist] = nil}
187
+ id_map = eo_opts[:id_map]
188
+ Artist.where(:id=>id_map.keys).all do |artist|
189
+ if albums = id_map[artist.id]
190
+ albums.each do |album|
191
+ album.associations[:artist] = artist
192
+ end
193
+ end
194
+ end
195
+ end)
196
+
197
+ and the tracks eager loader looks like:
198
+
199
+ :eager_loader=>(proc do |eo_opts|
200
+ eo_opts[:rows].each{|album| album.associations[:tracks] = []}
201
+ id_map = eo_opts[:id_map]
202
+ Track.where(:id=>id_map.keys).all do |tracks|
203
+ if albums = id_map[track.album_id]
204
+ albums.each do |album|
205
+ album.associations[:tracks] << track
206
+ end
207
+ end
208
+ end
209
+ end)
210
+
211
+ Now, these are both overly simplistic eager loaders that don't respect cascaded
212
+ associations or any of the association options. But hopefully they both
213
+ provide simple examples that you can more easily build and learn from, as
214
+ the custom eager loaders described later in this page are more complex.
215
+
216
+ Basically, the eager loading steps can be broken down into:
217
+
218
+ 1. Set default association values (nil/[]) for each of the current objects
219
+ 2. Return just related associated objects by filtering the associated class
220
+ to include only rows with keys present in the id_map.
221
+ 3. Iterating over the returned associated objects, indexing into the id_map
222
+ using the foreign/primary key value in the associated object to get
223
+ current values associated to that specific object.
224
+ 4. For each of those current values, updating the cached association value to
225
+ include that specific object.
226
+
227
+ Using the :eager_loader proc, you should be able to eagerly load all associations
228
+ that can be eagerly loaded, even if Sequel doesn't natively support such eager
229
+ loading.
118
230
 
119
231
  == ActiveRecord associations
120
232
 
@@ -123,9 +235,9 @@ require different approaches or custom <tt>:eager_loader</tt> options.
123
235
 
124
236
  === Association callbacks
125
237
 
126
- Sequel supports the same callbacks that ActiveRecord does on +one_to_many+ and
238
+ Sequel supports the same callbacks that ActiveRecord does for +one_to_many+ and
127
239
  +many_to_many+ associations: <tt>:before_add</tt>, <tt>:before_remove</tt>, <tt>:after_add</tt>, and
128
- <tt>:after_remove</tt>. One +many_to_one+ associations and +one_to_one+ associations, Sequel
240
+ <tt>:after_remove</tt>. For +many_to_one+ associations and +one_to_one+ associations, Sequel
129
241
  supports the <tt>:before_set</tt> and <tt>:after_set</tt> callbacks. On all associations,
130
242
  Sequel supports <tt>:after_load</tt>, which is called after the association has been
131
243
  loaded.
@@ -148,7 +260,7 @@ otherwise modified:
148
260
  class Author < Sequel::Model
149
261
  one_to_many :authorships
150
262
  end
151
- Author.first.authorships_dataset.filter{|o| o.number < 10}.first
263
+ Author.first.authorships_dataset.where{number < 10}.first
152
264
 
153
265
  You can extend a dataset with a module using the <tt>:extend</tt> association option. You can reference
154
266
  the model object that created the association dataset via the dataset's
@@ -237,6 +349,23 @@ Sequel::Model:
237
349
 
238
350
  class Invoice < Sequel::Model
239
351
  many_to_one :client
352
+
353
+ # has_one :through equivalent 1
354
+ # eager load with :eager=>:firm option on :client association, and eager loading :client
355
+ def firm
356
+ client.firm if client
357
+ end
358
+
359
+ # has_one :through equivalent 2
360
+ # eager load the usual way
361
+ many_to_many :firms, :join_table=>:clients, :left_key=>:id, :left_primary_key=>:client_id, :right_key=>:firm_id
362
+ def firm
363
+ firms.first
364
+ end
365
+
366
+ # has_one :through equivalent 3
367
+ # eager loading requires custom :eager_loader proc
368
+ many_to_one :firm, :dataset=>proc{Firm.join(:clients, :firm_id=>:id, :id=>client_id).select_all(:firms)}
240
369
  end
241
370
 
242
371
  Firm.first.invoices
@@ -252,7 +381,7 @@ Polymorphic associations break referential integrity and are significantly more
252
381
  complex than non-polymorphic associations, so their use is not recommended unless
253
382
  you are stuck with an existing design that uses them.
254
383
 
255
- If you must use them, look for the sequel_polymorphic plugin, as it makes using
384
+ If you must use them, look for the sequel_polymorphic external plugin, as it makes using
256
385
  polymorphic associations in Sequel about as easy as it is in ActiveRecord. However,
257
386
  here's how they can be done using Sequel's custom associations (the sequel_polymorphic
258
387
  plugin is just a generic version of this code):
@@ -280,7 +409,7 @@ Sequel::Model:
280
409
  many_to_one :attachable, :reciprocal=>:assets, \
281
410
  :dataset=>(proc do
282
411
  klass = attachable_type.constantize
283
- klass.filter(klass.primary_key=>attachable_id)
412
+ klass.where(klass.primary_key=>attachable_id)
284
413
  end), \
285
414
  :eager_loader=>(proc do |eo|
286
415
  id_map = {}
@@ -290,7 +419,7 @@ Sequel::Model:
290
419
  end
291
420
  id_map.each do |klass_name, id_map|
292
421
  klass = klass_name.constantize
293
- klass.filter(klass.primary_key=>id_map.keys).all do |attach|
422
+ klass.where(klass.primary_key=>id_map.keys).all do |attach|
294
423
  id_map[attach.pk].each do |asset|
295
424
  asset.associations[:attachable] = attach
296
425
  end
@@ -307,48 +436,34 @@ Sequel::Model:
307
436
  end
308
437
 
309
438
  class Post < Sequel::Model
310
- one_to_many :assets, :key=>:attachable_id do |ds|
311
- ds.filter(:attachable_type=>'Post')
312
- end
439
+ one_to_many :assets, :key=>:attachable_id, :reciprocal=>:attachable, :conditions=>{:attachable_type=>'Post'}
313
440
 
314
441
  private
315
442
 
316
443
  def _add_asset(asset)
317
- asset.attachable_id = pk
318
- asset.attachable_type = 'Post'
319
- asset.save
320
- end
444
+ asset.update(:attachable_id=>pk, :attachable_type=>'Post')
445
+ end
321
446
  def _remove_asset(asset)
322
- asset.attachable_id = nil
323
- asset.attachable_type = nil
324
- asset.save
325
- end
447
+ asset.update(:attachable_id=>nil, :attachable_type=>nil)
448
+ end
326
449
  def _remove_all_assets
327
- Asset.filter(:attachable_id=>pk, :attachable_type=>'Post')\
328
- .update(:attachable_id=>nil, :attachable_type=>nil)
450
+ assets_dataset.update(:attachable_id=>nil, :attachable_type=>nil)
329
451
  end
330
452
  end
331
453
 
332
454
  class Note < Sequel::Model
333
- one_to_many :assets, :key=>:attachable_id do |ds|
334
- ds.filter(:attachable_type=>'Note')
335
- end
455
+ one_to_many :assets, :key=>:attachable_id, :reciprocal=>:attachable, :conditions=>{:attachable_type=>'Note'}
336
456
 
337
457
  private
338
458
 
339
459
  def _add_asset(asset)
340
- asset.attachable_id = pk
341
- asset.attachable_type = 'Note'
342
- asset.save
460
+ asset.update(:attachable_id=>pk, :attachable_type=>'Note')
343
461
  end
344
462
  def _remove_asset(asset)
345
- asset.attachable_id = nil
346
- asset.attachable_type = nil
347
- asset.save
463
+ asset.update(:attachable_id=>nil, :attachable_type=>nil)
348
464
  end
349
465
  def _remove_all_assets
350
- Asset.filter(:attachable_id=>pk, :attachable_type=>'Note')\
351
- .update(:attachable_id=>nil, :attachable_type=>nil)
466
+ assets_dataset.update(:attachable_id=>nil, :attachable_type=>nil)
352
467
  end
353
468
  end
354
469
 
@@ -390,7 +505,7 @@ node.children. You can even eager load the relationship up to a certain depth:
390
505
  # Eager load three generations of generations of children for a given node
391
506
  Node.filter(:id=>1).eager(:children=>{:children=>:children}).all.first
392
507
  # Load parents and grandparents for a group of nodes
393
- Node.filter{|o| o.id < 10}.eager(:parent=>:parent).all
508
+ Node.filter{id < 10}.eager(:parent=>:parent).all
394
509
 
395
510
  What if you want to get all ancestors up to the root node, or all descendents,
396
511
  without knowing the depth of the tree?
@@ -415,7 +530,7 @@ without knowing the depth of the tree?
415
530
  non_root_nodes.each{|n| (id_map[n.parent_id] ||= []) << n}
416
531
  # Doesn't cause an infinte loop, because when only the root node
417
532
  # is left, this is not called.
418
- Node.filter(Node.primary_key=>id_map.keys).eager(:ancestors).all do |node|
533
+ Node.where(Node.primary_key=>id_map.keys).eager(:ancestors).all do |node|
419
534
  # Populate the parent association for each node
420
535
  id_map[node.pk].each{|n| n.associations[:parent] = node}
421
536
  end
@@ -433,7 +548,7 @@ without knowing the depth of the tree?
433
548
  # if no records are returned. Exclude id = parent_id to avoid infinite loop
434
549
  # if the root note is one of the returned records and it has parent_id = id
435
550
  # instead of parent_id = NULL.
436
- Node.filter(:parent_id=>id_map.keys).exclude(:id=>:parent_id).eager(:descendants).all do |node|
551
+ Node.where(:parent_id=>id_map.keys).exclude(:id=>:parent_id).eager(:descendants).all do |node|
437
552
  # Get the parent from the identity map
438
553
  parent = id_map[node.parent_id]
439
554
  # Set the child's parent association to the parent
@@ -456,7 +571,7 @@ this easy:
456
571
 
457
572
  === Joining multiple keys to a single key, through a third table
458
573
 
459
- Let's say you have a database, of songs, lyrics, and artists. Each song
574
+ Let's say you have a database of songs, lyrics, and artists. Each song
460
575
  may or may not have a lyric (most songs are instrumental). The lyric can be
461
576
  associated to an artist in each of four ways: composer, arranger, vocalist,
462
577
  or lyricist. These may all be the same, or they could all be different, and
@@ -469,14 +584,15 @@ name, with no duplicates?
469
584
 
470
585
  class Artist < Sequel::Model
471
586
  one_to_many :songs, :order=>:songs__name, \
472
- :dataset=>proc{Song.select(:songs.*).join(Lyric, :id=>:lyric_id, id=>[:composer_id, :arranger_id, :vocalist_id, :lyricist_id])}, \
587
+ :dataset=>proc{Song.select_all(:songs).join(Lyric, :id=>:lyric_id, id=>[:composer_id, :arranger_id, :vocalist_id, :lyricist_id])}, \
473
588
  :eager_loader=>(proc do |eo|
474
- h = eo[:key_hash][:id]
589
+ h = eo[:id_map]
475
590
  ids = h.keys
476
591
  eo[:rows].each{|r| r.associations[:songs] = []}
477
- Song.select(:songs.*, :lyrics__composer_id, :lyrics__arranger_id, :lyrics__vocalist_id, :lyrics__lyricist_id)\
478
- .join(Lyric, :id=>:lyric_id){{:composer_id=>ids, :arranger_id=>ids, :vocalist_id=>ids, :lyricist_id=>ids}.sql_or}\
479
- .order(:songs__name).all do |song|
592
+ Song.select_all(:songs).
593
+ select_append(:lyrics__composer_id, :lyrics__arranger_id, :lyrics__vocalist_id, :lyrics__lyricist_id).
594
+ join(Lyric, :id=>:lyric_id){Sequel.or(:composer_id=>ids, :arranger_id=>ids, :vocalist_id=>ids, :lyricist_id=>ids)}.
595
+ order(:songs__name).all do |song|
480
596
  [:composer_id, :arranger_id, :vocalist_id, :lyricist_id].each do |x|
481
597
  recs = h[song.values.delete(x)]
482
598
  recs.each{|r| r.associations[:songs] << song} if recs
@@ -499,14 +615,14 @@ associated tickets.
499
615
  class Project < Sequel::Model
500
616
  one_to_many :tickets
501
617
  many_to_one :ticket_hours, :read_only=>true, :key=>:id,
502
- :dataset=>proc{Ticket.filter(:project_id=>id).select{sum(hours).as(hours)}},
618
+ :dataset=>proc{Ticket.where(:project_id=>id).select{sum(hours).as(hours)}},
503
619
  :eager_loader=>(proc do |eo|
504
620
  eo[:rows].each{|p| p.associations[:ticket_hours] = nil}
505
- Ticket.filter(:project_id=>eo[:key_hash][:id].keys).
506
- group(:project_id).
507
- select{[project_id, sum(hours).as(hours)]}.
621
+ Ticket.where(:project_id=>eo[:id_map].keys).
622
+ select_group(:project_id).
623
+ select_append{sum(hours).as(hours)}.
508
624
  all do |t|
509
- p = eo[:key_hash][:id][t.values.delete(:project_id)].first
625
+ p = eo[:id_map][t.values.delete(:project_id)].first
510
626
  p.associations[:ticket_hours] = t
511
627
  end
512
628
  end)
@@ -525,6 +641,4 @@ associated tickets.
525
641
  end
526
642
 
527
643
  Note that it is often better to use a sum cache instead of this approach. You can implement
528
- a sum cache using +after_create+ and +after_delete+ hooks, or using a database trigger
529
- (the preferred method if you only have to support one database and that database supports
530
- triggers).
644
+ a sum cache using +after_create+ and +after_delete+ hooks, or preferrably using a database trigger.