sequel 3.21.0 → 3.36.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 (311) hide show
  1. data/CHANGELOG +855 -3
  2. data/MIT-LICENSE +1 -1
  3. data/README.rdoc +23 -7
  4. data/Rakefile +42 -24
  5. data/bin/sequel +26 -8
  6. data/doc/active_record.rdoc +1 -0
  7. data/doc/advanced_associations.rdoc +171 -11
  8. data/doc/association_basics.rdoc +293 -43
  9. data/doc/dataset_basics.rdoc +5 -5
  10. data/doc/dataset_filtering.rdoc +8 -0
  11. data/doc/mass_assignment.rdoc +56 -0
  12. data/doc/migration.rdoc +85 -552
  13. data/doc/model_hooks.rdoc +78 -27
  14. data/doc/object_model.rdoc +541 -0
  15. data/doc/opening_databases.rdoc +97 -50
  16. data/doc/prepared_statements.rdoc +40 -14
  17. data/doc/querying.rdoc +28 -4
  18. data/doc/reflection.rdoc +30 -4
  19. data/doc/release_notes/3.22.0.txt +39 -0
  20. data/doc/release_notes/3.23.0.txt +172 -0
  21. data/doc/release_notes/3.24.0.txt +420 -0
  22. data/doc/release_notes/3.25.0.txt +88 -0
  23. data/doc/release_notes/3.26.0.txt +88 -0
  24. data/doc/release_notes/3.27.0.txt +82 -0
  25. data/doc/release_notes/3.28.0.txt +304 -0
  26. data/doc/release_notes/3.29.0.txt +459 -0
  27. data/doc/release_notes/3.30.0.txt +135 -0
  28. data/doc/release_notes/3.31.0.txt +146 -0
  29. data/doc/release_notes/3.32.0.txt +202 -0
  30. data/doc/release_notes/3.33.0.txt +157 -0
  31. data/doc/release_notes/3.34.0.txt +671 -0
  32. data/doc/release_notes/3.35.0.txt +144 -0
  33. data/doc/release_notes/3.36.0.txt +245 -0
  34. data/doc/schema_modification.rdoc +572 -0
  35. data/doc/sharding.rdoc +95 -3
  36. data/doc/testing.rdoc +129 -0
  37. data/doc/thread_safety.rdoc +17 -0
  38. data/doc/transactions.rdoc +137 -0
  39. data/doc/virtual_rows.rdoc +51 -2
  40. data/lib/sequel/adapters/ado/mssql.rb +18 -6
  41. data/lib/sequel/adapters/ado.rb +40 -18
  42. data/lib/sequel/adapters/amalgalite.rb +17 -9
  43. data/lib/sequel/adapters/db2.rb +175 -86
  44. data/lib/sequel/adapters/dbi.rb +15 -15
  45. data/lib/sequel/adapters/do/mysql.rb +17 -11
  46. data/lib/sequel/adapters/do/postgres.rb +1 -57
  47. data/lib/sequel/adapters/do/sqlite.rb +0 -10
  48. data/lib/sequel/adapters/do.rb +18 -36
  49. data/lib/sequel/adapters/firebird.rb +27 -208
  50. data/lib/sequel/adapters/ibmdb.rb +453 -0
  51. data/lib/sequel/adapters/informix.rb +6 -23
  52. data/lib/sequel/adapters/jdbc/as400.rb +16 -34
  53. data/lib/sequel/adapters/jdbc/db2.rb +47 -0
  54. data/lib/sequel/adapters/jdbc/derby.rb +287 -0
  55. data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
  56. data/lib/sequel/adapters/jdbc/h2.rb +73 -31
  57. data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
  58. data/lib/sequel/adapters/jdbc/informix.rb +21 -0
  59. data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
  61. data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
  62. data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
  64. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  65. data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
  66. data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
  67. data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
  68. data/lib/sequel/adapters/jdbc.rb +265 -94
  69. data/lib/sequel/adapters/mock.rb +361 -0
  70. data/lib/sequel/adapters/mysql.rb +118 -222
  71. data/lib/sequel/adapters/mysql2.rb +58 -57
  72. data/lib/sequel/adapters/odbc/mssql.rb +15 -7
  73. data/lib/sequel/adapters/odbc.rb +28 -9
  74. data/lib/sequel/adapters/openbase.rb +3 -5
  75. data/lib/sequel/adapters/oracle.rb +349 -53
  76. data/lib/sequel/adapters/postgres.rb +356 -107
  77. data/lib/sequel/adapters/shared/access.rb +32 -9
  78. data/lib/sequel/adapters/shared/db2.rb +342 -0
  79. data/lib/sequel/adapters/shared/firebird.rb +221 -0
  80. data/lib/sequel/adapters/shared/informix.rb +53 -0
  81. data/lib/sequel/adapters/shared/mssql.rb +335 -118
  82. data/lib/sequel/adapters/shared/mysql.rb +340 -76
  83. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
  84. data/lib/sequel/adapters/shared/oracle.rb +243 -45
  85. data/lib/sequel/adapters/shared/postgres.rb +524 -279
  86. data/lib/sequel/adapters/shared/progress.rb +5 -8
  87. data/lib/sequel/adapters/shared/sqlite.rb +265 -89
  88. data/lib/sequel/adapters/sqlite.rb +101 -39
  89. data/lib/sequel/adapters/swift/mysql.rb +9 -12
  90. data/lib/sequel/adapters/swift/postgres.rb +9 -74
  91. data/lib/sequel/adapters/swift/sqlite.rb +8 -6
  92. data/lib/sequel/adapters/swift.rb +8 -7
  93. data/lib/sequel/adapters/tinytds.rb +161 -21
  94. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
  95. data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
  96. data/lib/sequel/ast_transformer.rb +194 -0
  97. data/lib/sequel/connection_pool/sharded_single.rb +11 -1
  98. data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
  99. data/lib/sequel/connection_pool/single.rb +5 -0
  100. data/lib/sequel/connection_pool/threaded.rb +38 -14
  101. data/lib/sequel/connection_pool.rb +10 -2
  102. data/lib/sequel/core.rb +118 -11
  103. data/lib/sequel/database/connecting.rb +38 -28
  104. data/lib/sequel/database/dataset.rb +4 -4
  105. data/lib/sequel/database/dataset_defaults.rb +63 -2
  106. data/lib/sequel/database/logging.rb +7 -2
  107. data/lib/sequel/database/misc.rb +150 -46
  108. data/lib/sequel/database/query.rb +215 -65
  109. data/lib/sequel/database/schema_generator.rb +64 -20
  110. data/lib/sequel/database/schema_methods.rb +212 -54
  111. data/lib/sequel/dataset/actions.rb +322 -85
  112. data/lib/sequel/dataset/features.rb +96 -8
  113. data/lib/sequel/dataset/graph.rb +38 -11
  114. data/lib/sequel/dataset/misc.rb +50 -23
  115. data/lib/sequel/dataset/mutation.rb +12 -7
  116. data/lib/sequel/dataset/prepared_statements.rb +68 -30
  117. data/lib/sequel/dataset/query.rb +237 -44
  118. data/lib/sequel/dataset/sql.rb +698 -278
  119. data/lib/sequel/dataset.rb +8 -0
  120. data/lib/sequel/exceptions.rb +4 -0
  121. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  122. data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
  123. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  124. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
  125. data/lib/sequel/extensions/migration.rb +97 -11
  126. data/lib/sequel/extensions/named_timezones.rb +5 -0
  127. data/lib/sequel/extensions/null_dataset.rb +90 -0
  128. data/lib/sequel/extensions/pg_array.rb +460 -0
  129. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  130. data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
  131. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  132. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  133. data/lib/sequel/extensions/pg_inet.rb +89 -0
  134. data/lib/sequel/extensions/pg_json.rb +178 -0
  135. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  136. data/lib/sequel/extensions/pretty_table.rb +5 -71
  137. data/lib/sequel/extensions/query.rb +1 -1
  138. data/lib/sequel/extensions/query_literals.rb +79 -0
  139. data/lib/sequel/extensions/schema_caching.rb +76 -0
  140. data/lib/sequel/extensions/schema_dumper.rb +251 -37
  141. data/lib/sequel/extensions/select_remove.rb +35 -0
  142. data/lib/sequel/extensions/server_block.rb +139 -0
  143. data/lib/sequel/extensions/sql_expr.rb +4 -110
  144. data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
  145. data/lib/sequel/extensions/to_dot.rb +95 -83
  146. data/lib/sequel/model/associations.rb +1187 -393
  147. data/lib/sequel/model/base.rb +578 -170
  148. data/lib/sequel/model/errors.rb +1 -1
  149. data/lib/sequel/model/exceptions.rb +5 -1
  150. data/lib/sequel/model/inflections.rb +1 -1
  151. data/lib/sequel/model.rb +30 -11
  152. data/lib/sequel/no_core_ext.rb +2 -0
  153. data/lib/sequel/plugins/active_model.rb +13 -1
  154. data/lib/sequel/plugins/association_pks.rb +22 -4
  155. data/lib/sequel/plugins/caching.rb +25 -18
  156. data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
  157. data/lib/sequel/plugins/composition.rb +44 -12
  158. data/lib/sequel/plugins/dataset_associations.rb +100 -0
  159. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  160. data/lib/sequel/plugins/dirty.rb +214 -0
  161. data/lib/sequel/plugins/eager_each.rb +59 -0
  162. data/lib/sequel/plugins/force_encoding.rb +6 -6
  163. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  164. data/lib/sequel/plugins/identity_map.rb +123 -8
  165. data/lib/sequel/plugins/instance_filters.rb +10 -0
  166. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  167. data/lib/sequel/plugins/json_serializer.rb +58 -6
  168. data/lib/sequel/plugins/list.rb +12 -2
  169. data/lib/sequel/plugins/many_through_many.rb +79 -28
  170. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  171. data/lib/sequel/plugins/nested_attributes.rb +139 -63
  172. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  173. data/lib/sequel/plugins/prepared_statements.rb +167 -0
  174. data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
  175. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  176. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  177. data/lib/sequel/plugins/rcte_tree.rb +29 -15
  178. data/lib/sequel/plugins/schema.rb +7 -2
  179. data/lib/sequel/plugins/serialization.rb +98 -49
  180. data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
  181. data/lib/sequel/plugins/sharding.rb +21 -54
  182. data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
  183. data/lib/sequel/plugins/static_cache.rb +99 -0
  184. data/lib/sequel/plugins/subclasses.rb +10 -2
  185. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  186. data/lib/sequel/plugins/timestamps.rb +1 -1
  187. data/lib/sequel/plugins/typecast_on_load.rb +9 -12
  188. data/lib/sequel/plugins/update_primary_key.rb +2 -2
  189. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  190. data/lib/sequel/plugins/xml_serializer.rb +15 -4
  191. data/lib/sequel/sql.rb +526 -59
  192. data/lib/sequel/timezones.rb +57 -38
  193. data/lib/sequel/version.rb +1 -1
  194. data/spec/adapters/db2_spec.rb +146 -0
  195. data/spec/adapters/firebird_spec.rb +1 -1
  196. data/spec/adapters/mssql_spec.rb +142 -40
  197. data/spec/adapters/mysql_spec.rb +380 -234
  198. data/spec/adapters/oracle_spec.rb +67 -77
  199. data/spec/adapters/postgres_spec.rb +1050 -212
  200. data/spec/adapters/spec_helper.rb +8 -5
  201. data/spec/adapters/sqlite_spec.rb +139 -27
  202. data/spec/core/connection_pool_spec.rb +196 -93
  203. data/spec/core/database_spec.rb +879 -510
  204. data/spec/core/dataset_spec.rb +1415 -1086
  205. data/spec/core/expression_filters_spec.rb +554 -109
  206. data/spec/core/mock_adapter_spec.rb +449 -0
  207. data/spec/core/object_graph_spec.rb +86 -114
  208. data/spec/core/schema_generator_spec.rb +4 -4
  209. data/spec/core/schema_spec.rb +482 -38
  210. data/spec/core/spec_helper.rb +6 -48
  211. data/spec/extensions/active_model_spec.rb +13 -0
  212. data/spec/extensions/arbitrary_servers_spec.rb +110 -0
  213. data/spec/extensions/association_autoreloading_spec.rb +18 -10
  214. data/spec/extensions/association_dependencies_spec.rb +15 -25
  215. data/spec/extensions/association_pks_spec.rb +66 -32
  216. data/spec/extensions/association_proxies_spec.rb +4 -4
  217. data/spec/extensions/caching_spec.rb +47 -51
  218. data/spec/extensions/class_table_inheritance_spec.rb +31 -83
  219. data/spec/extensions/columns_introspection_spec.rb +91 -0
  220. data/spec/extensions/composition_spec.rb +18 -13
  221. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
  222. data/spec/extensions/dataset_associations_spec.rb +199 -0
  223. data/spec/extensions/defaults_setter_spec.rb +64 -0
  224. data/spec/extensions/dirty_spec.rb +155 -0
  225. data/spec/extensions/eager_each_spec.rb +34 -0
  226. data/spec/extensions/force_encoding_spec.rb +4 -2
  227. data/spec/extensions/hook_class_methods_spec.rb +69 -92
  228. data/spec/extensions/identity_map_spec.rb +131 -22
  229. data/spec/extensions/inflector_spec.rb +0 -4
  230. data/spec/extensions/instance_filters_spec.rb +11 -21
  231. data/spec/extensions/instance_hooks_spec.rb +72 -0
  232. data/spec/extensions/json_serializer_spec.rb +49 -6
  233. data/spec/extensions/lazy_attributes_spec.rb +16 -20
  234. data/spec/extensions/list_spec.rb +48 -39
  235. data/spec/extensions/many_through_many_spec.rb +283 -250
  236. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  237. data/spec/extensions/migration_spec.rb +140 -35
  238. data/spec/extensions/named_timezones_spec.rb +29 -11
  239. data/spec/extensions/nested_attributes_spec.rb +251 -89
  240. data/spec/extensions/null_dataset_spec.rb +85 -0
  241. data/spec/extensions/optimistic_locking_spec.rb +24 -21
  242. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  243. data/spec/extensions/pg_array_spec.rb +196 -0
  244. data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
  245. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  246. data/spec/extensions/pg_hstore_spec.rb +195 -0
  247. data/spec/extensions/pg_inet_spec.rb +44 -0
  248. data/spec/extensions/pg_json_spec.rb +101 -0
  249. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  250. data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
  251. data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
  252. data/spec/extensions/prepared_statements_spec.rb +87 -0
  253. data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
  254. data/spec/extensions/pretty_table_spec.rb +7 -6
  255. data/spec/extensions/query_literals_spec.rb +168 -0
  256. data/spec/extensions/query_spec.rb +6 -0
  257. data/spec/extensions/rcte_tree_spec.rb +50 -43
  258. data/spec/extensions/schema_caching_spec.rb +41 -0
  259. data/spec/extensions/schema_dumper_spec.rb +429 -22
  260. data/spec/extensions/schema_spec.rb +25 -21
  261. data/spec/extensions/select_remove_spec.rb +38 -0
  262. data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
  263. data/spec/extensions/serialization_spec.rb +85 -43
  264. data/spec/extensions/server_block_spec.rb +90 -0
  265. data/spec/extensions/sharding_spec.rb +69 -143
  266. data/spec/extensions/single_table_inheritance_spec.rb +23 -29
  267. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  268. data/spec/extensions/spec_helper.rb +22 -61
  269. data/spec/extensions/sql_expr_spec.rb +29 -60
  270. data/spec/extensions/static_cache_spec.rb +145 -0
  271. data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
  272. data/spec/extensions/thread_local_timezones_spec.rb +22 -2
  273. data/spec/extensions/timestamps_spec.rb +6 -6
  274. data/spec/extensions/to_dot_spec.rb +4 -7
  275. data/spec/extensions/touch_spec.rb +13 -14
  276. data/spec/extensions/tree_spec.rb +11 -26
  277. data/spec/extensions/typecast_on_load_spec.rb +9 -6
  278. data/spec/extensions/update_primary_key_spec.rb +30 -24
  279. data/spec/extensions/validation_class_methods_spec.rb +30 -51
  280. data/spec/extensions/validation_helpers_spec.rb +16 -35
  281. data/spec/extensions/xml_serializer_spec.rb +29 -4
  282. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
  283. data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
  284. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
  285. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
  286. data/spec/integration/associations_test.rb +659 -21
  287. data/spec/integration/database_test.rb +9 -3
  288. data/spec/integration/dataset_test.rb +605 -91
  289. data/spec/integration/eager_loader_test.rb +19 -21
  290. data/spec/integration/migrator_test.rb +2 -3
  291. data/spec/integration/model_test.rb +81 -2
  292. data/spec/integration/plugin_test.rb +454 -143
  293. data/spec/integration/prepared_statement_test.rb +263 -128
  294. data/spec/integration/schema_test.rb +309 -43
  295. data/spec/integration/spec_helper.rb +40 -30
  296. data/spec/integration/timezone_test.rb +38 -12
  297. data/spec/integration/transaction_test.rb +183 -8
  298. data/spec/integration/type_test.rb +18 -4
  299. data/spec/model/association_reflection_spec.rb +201 -7
  300. data/spec/model/associations_spec.rb +1067 -661
  301. data/spec/model/base_spec.rb +329 -95
  302. data/spec/model/dataset_methods_spec.rb +7 -27
  303. data/spec/model/eager_loading_spec.rb +677 -676
  304. data/spec/model/hooks_spec.rb +349 -67
  305. data/spec/model/model_spec.rb +240 -168
  306. data/spec/model/plugins_spec.rb +24 -13
  307. data/spec/model/record_spec.rb +490 -232
  308. data/spec/model/spec_helper.rb +14 -71
  309. data/spec/model/validations_spec.rb +11 -0
  310. data/spec/rcov.opts +2 -0
  311. metadata +178 -80
data/MIT-LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2007-2008 Sharon Rosner
2
- Copyright (c) 2008-2011 Jeremy Evans
2
+ Copyright (c) 2008-2012 Jeremy Evans
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to
data/README.rdoc CHANGED
@@ -12,15 +12,15 @@ toolkit for Ruby.
12
12
  two-phase commit, transaction isolation, master/slave
13
13
  configurations, and database sharding.
14
14
  * Sequel currently has adapters for ADO, Amalgalite, DataObjects,
15
- DB2, DBI, Firebird, Informix, JDBC, MySQL, Mysql2, ODBC, OpenBase,
16
- Oracle, PostgreSQL, SQLite3, Swift, and TinyTDS.
15
+ DB2, DBI, Firebird, IBM_DB, Informix, JDBC, MySQL, Mysql2, ODBC,
16
+ OpenBase, Oracle, PostgreSQL, SQLite3, Swift, and TinyTDS.
17
17
 
18
18
  == Resources
19
19
 
20
20
  * {Website}[http://sequel.rubyforge.org]
21
21
  * {Blog}[http://sequel.heroku.com]
22
22
  * {Source code}[http://github.com/jeremyevans/sequel]
23
- * {Bug tracking}[http://code.google.com/p/ruby-sequel/issues/list]
23
+ * {Bug tracking}[http://github.com/jeremyevans/sequel/issues]
24
24
  * {Google group}[http://groups.google.com/group/sequel-talk]
25
25
  * {RDoc}[http://sequel.rubyforge.org/rdoc]
26
26
 
@@ -64,12 +64,14 @@ If you have any comments or suggestions please post to the Google group.
64
64
 
65
65
  == The Sequel Console
66
66
 
67
- Sequel includes an IRB console for quick access to databases. You can use it like this:
67
+ Sequel includes an IRB console for quick access to databases (usually referred to as <tt>bin/sequel</tt>). You can use it like this:
68
68
 
69
69
  sequel sqlite://test.db # test.db in current directory
70
70
 
71
71
  You get an IRB session with the database object stored in DB.
72
72
 
73
+ In addition to providing an IRB shell (the default behavior), bin/sequel also has support for migrating databases (-m and -M), dumping schema migrations (-d and -D), and copying databases (-C).
74
+
73
75
  == An Introduction
74
76
 
75
77
  Sequel is designed to take the hassle away from connecting to databases and manipulating them. Sequel deals with all the boring stuff like maintaining connections, formatting SQL correctly and fetching records so you can concentrate on your application.
@@ -573,7 +575,7 @@ You can execute custom code when creating, updating, or deleting records by defi
573
575
 
574
576
  Note the use of +super+ if you define your own hook methods. Almost all <tt>Sequel::Model</tt> class and instance methods (not just hook methods) can be overridden safely, but you have to make sure to call +super+ when doing so, otherwise you risk breaking things.
575
577
 
576
- For the example above, you should probably use a database trigger if you can. Hooks can be used for data integrity, but they will only enforce that integrity when you are modifying the database through model instances. If you plan on allowing any other access to the database, it's best to use database triggers for data integrity.
578
+ For the example above, you should probably use a database trigger if you can. Hooks can be used for data integrity, but they will only enforce that integrity when you are modifying the database through model instances. If you plan on allowing any other access to the database, it's best to use database triggers and constraints for data integrity.
577
579
 
578
580
  === Deleting records
579
581
 
@@ -681,9 +683,23 @@ Associations can be eagerly loaded via +eager+ and the <tt>:eager</tt> associati
681
683
  # Loads all people, their posts, their posts' tags, replies to those posts,
682
684
  # the person for each reply, the tag for each reply, and all posts and
683
685
  # replies that have that tag. Uses a total of 8 queries.
684
- Person.eager(:posts=>{:replies=>[:person, {:tags=>{:posts, :replies}}]}).all
686
+ Person.eager(:posts=>{:replies=>[:person, {:tags=>[:posts, :replies]}]}).all
687
+
688
+ In addition to using +eager+, you can also use +eager_graph+, which will use a single query to get the object and all associated objects. This may be necessary if you want to filter or order the result set based on columns in associated tables. It works with cascading as well, the API is very similar. Note that using +eager_graph+ to eagerly load multiple <tt>*_to_many</tt> associations will cause the result set to be a cartesian product, so you should be very careful with your filters when using it in that case.
689
+
690
+ You can dynamically customize the eagerly loaded dataset by using using a proc. This proc is passed the dataset used for eager loading, and should return a modified copy of that dataset:
691
+
692
+ # Eagerly load only replies containing 'foo'
693
+ Post.eager(:replies=>proc{|ds| ds.filter(text.like('%foo%'))}).all
694
+
695
+ This also works when using +eager_graph+, in which case the proc is called with dataset to graph into the current dataset:
696
+
697
+ Post.eager_graph(:replies=>proc{|ds| ds.filter(text.like('%foo%'))}).all
698
+
699
+ You can dynamically customize eager loads for both +eager+ and +eager_graph+ while also cascading, by making the value a single entry hash with the proc as a key, and the cascaded associations as the value:
685
700
 
686
- In addition to using +eager+, you can also use +eager_graph+, which will use a single query to get the object and all associated objects. This may be necessary if you want to filter or order the result set based on columns in associated tables. It works with cascading as well, the syntax is exactly the same. Note that using eager_graph to eagerly load multiple *_to_many associations will cause the result set to be a cartesian product, so you should be very careful with your filters when using it in that case.
701
+ # Eagerly load only replies containing 'foo', and the person and tags for those replies
702
+ Post.eager(:replies=>{proc{|ds| ds.filter(text.like('%foo%'))}=>[:person, :tags]}).all
687
703
 
688
704
  === Extending the underlying dataset
689
705
 
data/Rakefile CHANGED
@@ -1,11 +1,5 @@
1
1
  require "rake"
2
2
  require "rake/clean"
3
- require "rake/gempackagetask"
4
- begin
5
- require "hanna/rdoctask"
6
- rescue LoadError
7
- require "rake/rdoctask"
8
- end
9
3
 
10
4
  NAME = 'sequel'
11
5
  VERS = lambda do
@@ -13,24 +7,24 @@ VERS = lambda do
13
7
  Sequel.version
14
8
  end
15
9
  CLEAN.include ["**/.*.sw?", "sequel-*.gem", ".config", "rdoc", "coverage", "www/public/*.html", "www/public/rdoc*", '**/*.rbc']
16
- RDOC_DEFAULT_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', 'Sequel: The Database Toolkit for Ruby']
17
- RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc']
10
+ SUDO = ENV['SUDO'] || 'sudo'
18
11
 
19
12
  # Gem Packaging and Release
20
13
 
21
14
  desc "Packages sequel"
22
15
  task :package=>[:clean] do |p|
23
- sh %{gem build sequel.gemspec}
16
+ load './sequel.gemspec'
17
+ Gem::Builder.new(SEQUEL_GEMSPEC).build
24
18
  end
25
19
 
26
20
  desc "Install sequel gem"
27
21
  task :install=>[:package] do
28
- sh %{sudo gem install ./#{NAME}-#{VERS.call} --local}
22
+ sh %{#{SUDO} gem install ./#{NAME}-#{VERS.call} --local}
29
23
  end
30
24
 
31
25
  desc "Uninstall sequel gem"
32
26
  task :uninstall=>[:clean] do
33
- sh %{sudo gem uninstall #{NAME}}
27
+ sh %{#{SUDO} gem uninstall #{NAME}}
34
28
  end
35
29
 
36
30
  desc "Upload sequel gem to gemcutter"
@@ -40,7 +34,20 @@ end
40
34
 
41
35
  ### RDoc
42
36
 
43
- Rake::RDocTask.new do |rdoc|
37
+ RDOC_DEFAULT_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', 'Sequel: The Database Toolkit for Ruby']
38
+
39
+ rdoc_task_class = begin
40
+ require "rdoc/task"
41
+ RDOC_DEFAULT_OPTS.concat(['-f', 'hanna'])
42
+ RDoc::Task
43
+ rescue LoadError
44
+ require "rake/rdoctask"
45
+ Rake::RDocTask
46
+ end
47
+
48
+ RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc']
49
+
50
+ rdoc_task_class.new do |rdoc|
44
51
  rdoc.rdoc_dir = "rdoc"
45
52
  rdoc.options += RDOC_OPTS
46
53
  rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/**/*.rb doc/*.rdoc doc/release_notes/*.txt"
@@ -50,25 +57,25 @@ end
50
57
 
51
58
  desc "Make local version of website"
52
59
  task :website do
53
- sh %{www/make_www.rb}
60
+ sh %{#{FileUtils::RUBY} www/make_www.rb}
54
61
  end
55
62
 
56
63
  desc "Make rdoc for website"
57
64
  task :website_rdoc=>[:website_rdoc_main, :website_rdoc_adapters, :website_rdoc_plugins]
58
65
 
59
- Rake::RDocTask.new(:website_rdoc_main) do |rdoc|
66
+ rdoc_task_class.new(:website_rdoc_main) do |rdoc|
60
67
  rdoc.rdoc_dir = "www/public/rdoc"
61
68
  rdoc.options += RDOC_OPTS
62
69
  rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/*.rb lib/sequel/*.rb lib/sequel/{connection_pool,dataset,database,model}/*.rb doc/*.rdoc doc/release_notes/*.txt lib/sequel/extensions/migration.rb"
63
70
  end
64
71
 
65
- Rake::RDocTask.new(:website_rdoc_adapters) do |rdoc|
72
+ rdoc_task_class.new(:website_rdoc_adapters) do |rdoc|
66
73
  rdoc.rdoc_dir = "www/public/rdoc-adapters"
67
74
  rdoc.options += RDOC_DEFAULT_OPTS + %w'--main Sequel'
68
75
  rdoc.rdoc_files.add %w"lib/sequel/adapters/**/*.rb"
69
76
  end
70
77
 
71
- Rake::RDocTask.new(:website_rdoc_plugins) do |rdoc|
78
+ rdoc_task_class.new(:website_rdoc_plugins) do |rdoc|
72
79
  rdoc.rdoc_dir = "www/public/rdoc-plugins"
73
80
  rdoc.options += RDOC_DEFAULT_OPTS + %w'--main Sequel'
74
81
  rdoc.rdoc_files.add %w"lib/sequel/{extensions,plugins}/**/*.rb"
@@ -92,11 +99,13 @@ begin
92
99
  require "spec/rake/spectask"
93
100
  spec_class = Spec::Rake::SpecTask
94
101
  spec_files_meth = :spec_files=
102
+ spec_opts_meth = :spec_opts=
95
103
  rescue LoadError
96
104
  # RSpec 2
97
105
  require "rspec/core/rake_task"
98
106
  spec_class = RSpec::Core::RakeTask
99
107
  spec_files_meth = :pattern=
108
+ spec_opts_meth = :rspec_opts=
100
109
  end
101
110
 
102
111
  spec = lambda do |name, files, d|
@@ -105,26 +114,35 @@ begin
105
114
  desc d
106
115
  spec_class.new(name) do |t|
107
116
  t.send spec_files_meth, files
108
- t.spec_opts = ENV['SEQUEL_SPEC_OPTS'].split if ENV['SEQUEL_SPEC_OPTS']
117
+ t.send spec_opts_meth, ENV['SEQUEL_SPEC_OPTS'].split if ENV['SEQUEL_SPEC_OPTS']
109
118
  end
110
119
  end
111
120
 
112
- spec_with_cov = lambda do |name, files, d|
121
+ spec_with_cov = lambda do |name, files, d, &b|
113
122
  spec.call(name, files, d)
114
123
  t = spec.call("#{name}_cov", files, "#{d} with coverage")
115
124
  t.rcov = true
116
125
  t.rcov_opts = File.read("spec/rcov.opts").split("\n")
126
+ b.call(t) if b
127
+ t
117
128
  end
118
-
129
+
119
130
  task :default => [:spec]
120
- spec_with_cov.call("spec", Dir["spec/{core,model}/*_spec.rb"], "Run core and model specs")
131
+ spec_with_cov.call("spec", Dir["spec/{core,model}/*_spec.rb"], "Run core and model specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/(adapters/([a-ln-z]|m[a-np-z])|extensions/core_extensions)"')}
121
132
  spec.call("spec_core", Dir["spec/core/*_spec.rb"], "Run core specs")
122
133
  spec.call("spec_model", Dir["spec/model/*_spec.rb"], "Run model specs")
123
- spec_with_cov.call("spec_plugin", Dir["spec/extensions/*_spec.rb"], "Run extension/plugin specs")
134
+ spec.call("_spec_model_no_assoc", Dir["spec/model/*_spec.rb"].delete_if{|f| f =~ /association|eager_loading/}, '')
135
+ spec_with_cov.call("spec_plugin", Dir["spec/extensions/*_spec.rb"], "Run extension/plugin specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/([a-z_]+\.rb|adapters|connection_pool|database|dataset|model)"')}
124
136
  spec_with_cov.call("spec_integration", Dir["spec/integration/*_test.rb"], "Run integration tests")
125
137
 
126
- %w'postgres sqlite mysql informix oracle firebird mssql'.each do |adapter|
127
- spec_with_cov.call("spec_#{adapter}", ["spec/adapters/#{adapter}_spec.rb"] + Dir["spec/integration/*_test.rb"], "Run #{adapter} specs")
138
+ %w'postgres sqlite mysql informix oracle firebird mssql db2'.each do |adapter|
139
+ spec_with_cov.call("spec_#{adapter}", ["spec/adapters/#{adapter}_spec.rb"] + Dir["spec/integration/*_test.rb"], "Run #{adapter} specs"){|t| t.rcov_opts.concat(%w'--exclude "lib/sequel/([a-z_]+\.rb|connection_pool|database|dataset|model|extensions|plugins)"')}
140
+ end
141
+
142
+ desc "Run model specs without the associations code"
143
+ task :spec_model_no_assoc do
144
+ ENV['SEQUEL_NO_ASSOCIATIONS'] = '1'
145
+ Rake::Task['_spec_model_no_assoc'].invoke
128
146
  end
129
147
  rescue LoadError
130
148
  task :default do
@@ -154,5 +172,5 @@ end
154
172
 
155
173
  desc "Check syntax of all .rb files"
156
174
  task :check_syntax do
157
- Dir['**/*.rb'].each{|file| print `#{ENV['RUBY'] || :ruby} -c #{file} | fgrep -v "Syntax OK"`}
175
+ Dir['**/*.rb'].each{|file| print `#{FileUtils::RUBY} -c #{file} | fgrep -v "Syntax OK"`}
158
176
  end
data/bin/sequel CHANGED
@@ -7,6 +7,7 @@ require 'sequel'
7
7
  db_opts = {:test=>true}
8
8
  copy_databases = nil
9
9
  dump_migration = nil
10
+ dump_schema = nil
10
11
  echo = nil
11
12
  env = nil
12
13
  logfile = nil
@@ -14,6 +15,7 @@ migrate_dir = nil
14
15
  migrate_ver = nil
15
16
  backtrace = nil
16
17
  load_dirs = []
18
+ exclusive_options = []
17
19
 
18
20
  opts = OptionParser.new do |opts|
19
21
  opts.banner = "Sequel: The Database Toolkit for Ruby"
@@ -39,10 +41,12 @@ opts = OptionParser.new do |opts|
39
41
 
40
42
  opts.on("-d", "--dump-migration", "print database migration to STDOUT") do
41
43
  dump_migration = true
44
+ exclusive_options << :d
42
45
  end
43
46
 
44
47
  opts.on("-D", "--dump-migration-same-db", "print database migration to STDOUT without type translation") do
45
48
  dump_migration = :same_db
49
+ exclusive_options << :D
46
50
  end
47
51
 
48
52
  opts.on("-e", "--env ENV", "use environment config for database") do |v|
@@ -67,6 +71,7 @@ opts = OptionParser.new do |opts|
67
71
 
68
72
  opts.on("-m", "--migrate-directory DIR", "run the migrations in directory") do |v|
69
73
  migrate_dir = v
74
+ exclusive_options << :m
70
75
  end
71
76
 
72
77
  opts.on("-M", "--migrate-version VER", "migrate the database to version given") do |v|
@@ -81,6 +86,11 @@ opts = OptionParser.new do |opts|
81
86
  load_dirs << [v]
82
87
  end
83
88
 
89
+ opts.on("-S", "--dump-schema filename", "dump the schema for all tables to the file") do |v|
90
+ dump_schema = v
91
+ exclusive_options << :S
92
+ end
93
+
84
94
  opts.on("-t", "--trace", "Output the full backtrace if an exception is raised") do
85
95
  backtrace = true
86
96
  end
@@ -100,8 +110,7 @@ error_proc = lambda do |msg|
100
110
  end
101
111
 
102
112
  error_proc["Error: Must specify -m if using -M"] if migrate_ver && !migrate_dir
103
- error_proc["Error: Cannot specify -D or -d with -m"] if dump_migration && migrate_dir
104
- error_proc["Error: Cannot specify -C with -d, -D, or -m"] if copy_databases && (dump_migration || migrate_dir)
113
+ error_proc["Error: Cannot specify #{exclusive_options.map{|v| "-#{v}"}.join(' and ')} together"] if exclusive_options.length > 1
105
114
 
106
115
  if logfile || echo
107
116
  require 'logger'
@@ -112,11 +121,7 @@ end
112
121
 
113
122
  connect_proc = lambda do |database|
114
123
  db = if database.nil? || database.empty?
115
- db = Sequel::Database.new(:quote_identifiers=>false)
116
- def db.connect(*args); Object.new; end
117
- db.identifier_input_method = nil
118
- db.identifier_output_method = nil
119
- db
124
+ Sequel.connect('mock:///', db_opts)
120
125
  elsif File.exist?(database)
121
126
  require 'yaml'
122
127
  env ||= "development"
@@ -143,6 +148,12 @@ begin
143
148
  puts DB.dump_schema_migration(:same_db=>dump_migration==:same_db)
144
149
  exit
145
150
  end
151
+ if dump_schema
152
+ Sequel.extension :schema_caching
153
+ DB.tables.each{|t| DB.schema(Sequel::SQL::Identifier.new(t))}
154
+ DB.dump_schema_cache(dump_schema)
155
+ exit
156
+ end
146
157
  if copy_databases
147
158
  Sequel.extension :migration, :schema_dumper
148
159
 
@@ -151,10 +162,13 @@ begin
151
162
  start_time = Time.now
152
163
  TO_DB = connect_proc[db2]
153
164
  same_db = DB.database_type==TO_DB.database_type
165
+ index_opts = {:same_db=>same_db}
166
+ index_opts[:index_names] = :namespace if !DB.global_index_namespace? && TO_DB.global_index_namespace?
154
167
 
155
168
  puts "Databases connections successful"
156
169
  schema_migration = eval(DB.dump_schema_migration(:indexes=>false, :same_db=>same_db))
157
- index_migration = eval(DB.dump_indexes_migration(:same_db=>same_db))
170
+ index_migration = eval(DB.dump_indexes_migration(index_opts))
171
+ fk_migration = eval(DB.dump_foreign_key_migration(:same_db=>same_db))
158
172
  puts "Migrations dumped successfully"
159
173
 
160
174
  schema_migration.apply(TO_DB, :up)
@@ -186,6 +200,10 @@ begin
186
200
  index_migration.apply(TO_DB, :up)
187
201
  puts "Finished creating indexes"
188
202
 
203
+ puts "Begin adding foreign key constraints"
204
+ fk_migration.apply(TO_DB, :up)
205
+ puts "Finished adding foreign key constraints"
206
+
189
207
  if TO_DB.database_type == :postgres
190
208
  TO_DB.tables.each{|t| TO_DB.reset_primary_key_sequence(t)}
191
209
  puts "Primary key sequences reset successfully"
@@ -694,6 +694,7 @@ ActiveRecord Method :: Sequel Method
694
694
  +set_primary_key+ :: +set_primary_key+
695
695
  +sum+ :: +sum+
696
696
  +table_name+ :: +table_name+
697
+ +unescoped+ :: +unfiltered+
697
698
 
698
699
  === Class Methods without an Equivalent
699
700
 
@@ -96,25 +96,168 @@ necessary for polymorphic associations). Inside the <tt>:eager_loader</tt>
96
96
  proc, you should get the related objects and populate the
97
97
  associations cache for all objects in the array of records. The hash
98
98
  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
99
+ loading down multiple levels, but it is up to you to use it.
100
+
101
+ The key_hash is a performance enhancement that is used by the default
101
102
  association loaders and is also available to you. It is a hash with keys being
102
103
  foreign/primary key symbols in the current table, and the values
103
104
  being hashes where the key is foreign/primary key values and values
104
105
  being arrays of current model objects having the foreign/primary key
105
106
  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)
107
+ give an example. Let's say you have the following associations
108
+
110
109
  Album.many_to_one :artist
111
110
  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]}}
111
+
112
+ and the following two albums in the database:
113
+
114
+ album1 = Album.create(:artist_id=>3) # id: 1
115
+ album2 = Album.create(:artist_id=>3) # id: 2
116
+
117
+ If you try to eager load this dataset:
118
+
119
+ Album.eager(:artist, :tracks).all
120
+
121
+ Then the key_hash provided to the :eager_loader proc would be:
122
+
123
+ {:id=>{1=>[album1], 2=>[album2]}, :artist_id=>{3=>[album1, album2]}}
124
+
125
+ Let's break down the reason for the makeup of this key_hash. The hash has keys for
126
+ each of foreign/primary keys used in the association. In this case, the artist
127
+ association needs the artist_id foreign key (since it is a many_to_one), and the
128
+ tracks association needs the id primary key (since it is a one_to_many).
129
+
130
+ If you only eagerly loaded the artist association:
131
+
132
+ Album.eager(:artist).all
133
+
134
+ Then the key_hash would only contain artist_id information:
135
+
136
+ {:artist_id=>{3=>[album1, album2]}}
137
+
138
+ Likewise, if you only eagerly loaded the tracks association:
139
+
140
+ Album.eager(:tracks).all
141
+
142
+ Then the key_hash would only contain id information:
143
+
144
+ {:id=>{1=>[album1], 2=>[album2]}}
145
+
146
+ Now, the eager loader for the artist association is only going to care about the
147
+ value of the artist_id key in the hash, so it's going to do the equivalent of:
148
+
149
+ artist_id_map = key_hash[:artist_id] # {3=>[album1, album2]}
150
+
151
+ The artist_id_map contains a mapping of artist_id values to arrays of
152
+ album objects. Since it only has a single entry with a key of 3, when eagerly
153
+ loading the artists, you only need to look for artists that have an id of 3.
154
+
155
+ artists = Artist.where(:id=>artist_id_map.keys).all
156
+
157
+ When the artists are retrieved, you can iterate over them, find entries
158
+ with matching keys, and manually associate them to the albums:
159
+
160
+ artists.each do |artist|
161
+ # Find related albums using the artist_id_map
162
+ if albums = artist_id_map[artist.id]
163
+ # Iterate over the albums
164
+ albums.each do |album|
165
+ # Manually set the artist association for each album
166
+ album.associations[:artist] = artist
167
+ end
168
+ end
169
+ end
170
+
171
+ Similarly, the eager loader for the tracks association is only going to care about the
172
+ value of the id key in the hash:
115
173
 
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.
174
+ id_map = key_hash[:id] # {1=>[album1], 2=>[album2]}
175
+
176
+ Now the id_map contains a mapping of id values to arrays of album objects (in this
177
+ case each array only has a single object, because id is the primary key). So when
178
+ looking for tracks to eagerly load, you only need to look for ones that have an
179
+ album_id of 1 or 2:
180
+
181
+ tracks = Track.where(:album_id=>id_map.keys).all
182
+
183
+ When the tracks are retrieved, you can iterate over them, find entries with matching
184
+ keys, and manually associate them to the albums:
185
+
186
+ tracks.each do |track|
187
+ if albums = id_map[track.album_id]
188
+ albums.each do |album|
189
+ album.associations[:tracks] << track
190
+ end
191
+ end
192
+ end
193
+
194
+ === Two basic example eager loaders
195
+
196
+ Putting the code in the above examples together, you almost have enough for a basic
197
+ working eager loader. The main important thing that is missing is you need to set
198
+ initial values for the eagerly loaded associations. For the artist association, you
199
+ need to initial the values to nil:
200
+
201
+ # rows here is the :rows entry in the hash passed to the eager loader
202
+ rows.each{|album| album.associations[:artist] = nil}
203
+
204
+ For the tracks association, you set the initial value to an empty array:
205
+
206
+ rows.each{|album| album.associations[:track] = []}
207
+
208
+ These are done so that if an album currently being loaded doesn't have an associated
209
+ artist or any associated tracks, the lack of them will be cached, so calling the
210
+ artist or tracks method on the album will not do another database lookup.
211
+
212
+ So putting everything together, the artist eager loader looks like:
213
+
214
+ :eager_loader=>(proc do |eo_opts|
215
+ eo_opts[:rows].each{|album| album.associations[:artist] = nil}
216
+ artist_id_map = eo_opts[:key_hash][:artist_id]
217
+ Artist.where(:id=>artist_id_map.keys).all do |artist|
218
+ if albums = artist_id_map[artist.id]
219
+ albums.each do |album|
220
+ album.associations[:artist] = artist
221
+ end
222
+ end
223
+ end
224
+ end)
225
+
226
+ and the tracks eager loader looks like:
227
+
228
+ :eager_loader=>(proc do |eo_opts|
229
+ eo_opts[:rows].each{|album| album.associations[:tracks] = []}
230
+ id_map = eo_opts[:key_hash][:id]
231
+ Track.where(:id=>id_map.keys).all do |tracks|
232
+ if albums = id_map[track.album_id]
233
+ albums.each do |album|
234
+ album.associations[:tracks] << track
235
+ end
236
+ end
237
+ end
238
+ end)
239
+
240
+ Now, these are both overly simplistic eager loaders that don't respect cascaded
241
+ associations or any of the association options. But hopefully they both
242
+ provide simple examples that you can more easily build and learn from, as
243
+ the custom eager loaders described later in this page are more complex.
244
+
245
+ Basically, the eager loading steps can be broken down into:
246
+
247
+ 1. Set default association values (nil/[]) for each of the current objects
248
+ 2. Build a custom key map mapping foreign/primary key values to arrays of
249
+ current objects (using the key_hash)
250
+ 3. Return just related associated objects by filtering the associated class
251
+ to include only matching values
252
+ 4. Iterating over the returned associated objects, indexing into the custom key
253
+ map using the foreign/primary key value in the associated object to get
254
+ current values associated to that specific object.
255
+ 5. For each of those current values, updating the cached association value to
256
+ include that specific object.
257
+
258
+ Using the :eager_loader proc, you should be able to eagerly load all associations
259
+ that can be eagerly loaded, even if Sequel doesn't natively support such eager
260
+ loading.
118
261
 
119
262
  == ActiveRecord associations
120
263
 
@@ -237,6 +380,23 @@ Sequel::Model:
237
380
 
238
381
  class Invoice < Sequel::Model
239
382
  many_to_one :client
383
+
384
+ # has_one :through equivalent 1
385
+ # eager load with :eager=>:firm option on :client association, and eager loading :client
386
+ def firm
387
+ client.firm if client
388
+ end
389
+
390
+ # has_one :through equivalent 2
391
+ # eager load the usual way
392
+ many_to_many :firms, :join_table=>:clients, :left_key=>:id, :left_primary_key=>:client_id, :right_key=>:firm_id
393
+ def firm
394
+ firms.first
395
+ end
396
+
397
+ # has_one :through equivalent 3
398
+ # eager loading requires custom :eager_loader proc
399
+ many_to_one :firm, :dataset=>proc{Firm.join(:clients, :firm_id=>:id, :id=>client_id).select_all(:firms)}
240
400
  end
241
401
 
242
402
  Firm.first.invoices