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.
- data/CHANGELOG +855 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -7
- data/Rakefile +42 -24
- data/bin/sequel +26 -8
- data/doc/active_record.rdoc +1 -0
- data/doc/advanced_associations.rdoc +171 -11
- data/doc/association_basics.rdoc +293 -43
- data/doc/dataset_basics.rdoc +5 -5
- data/doc/dataset_filtering.rdoc +8 -0
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +85 -552
- data/doc/model_hooks.rdoc +78 -27
- data/doc/object_model.rdoc +541 -0
- data/doc/opening_databases.rdoc +97 -50
- data/doc/prepared_statements.rdoc +40 -14
- data/doc/querying.rdoc +28 -4
- data/doc/reflection.rdoc +30 -4
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/release_notes/3.29.0.txt +459 -0
- data/doc/release_notes/3.30.0.txt +135 -0
- data/doc/release_notes/3.31.0.txt +146 -0
- data/doc/release_notes/3.32.0.txt +202 -0
- data/doc/release_notes/3.33.0.txt +157 -0
- data/doc/release_notes/3.34.0.txt +671 -0
- data/doc/release_notes/3.35.0.txt +144 -0
- data/doc/release_notes/3.36.0.txt +245 -0
- data/doc/schema_modification.rdoc +572 -0
- data/doc/sharding.rdoc +95 -3
- data/doc/testing.rdoc +129 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/virtual_rows.rdoc +51 -2
- data/lib/sequel/adapters/ado/mssql.rb +18 -6
- data/lib/sequel/adapters/ado.rb +40 -18
- data/lib/sequel/adapters/amalgalite.rb +17 -9
- data/lib/sequel/adapters/db2.rb +175 -86
- data/lib/sequel/adapters/dbi.rb +15 -15
- data/lib/sequel/adapters/do/mysql.rb +17 -11
- data/lib/sequel/adapters/do/postgres.rb +1 -57
- data/lib/sequel/adapters/do/sqlite.rb +0 -10
- data/lib/sequel/adapters/do.rb +18 -36
- data/lib/sequel/adapters/firebird.rb +27 -208
- data/lib/sequel/adapters/ibmdb.rb +453 -0
- data/lib/sequel/adapters/informix.rb +6 -23
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/db2.rb +47 -0
- data/lib/sequel/adapters/jdbc/derby.rb +287 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +24 -0
- data/lib/sequel/adapters/jdbc/h2.rb +73 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +184 -0
- data/lib/sequel/adapters/jdbc/informix.rb +21 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +29 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -33
- data/lib/sequel/adapters/jdbc/mysql.rb +7 -24
- data/lib/sequel/adapters/jdbc/oracle.rb +88 -25
- data/lib/sequel/adapters/jdbc/postgresql.rb +58 -48
- data/lib/sequel/adapters/jdbc/progress.rb +21 -0
- data/lib/sequel/adapters/jdbc/sqlite.rb +20 -10
- data/lib/sequel/adapters/jdbc/sqlserver.rb +66 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +265 -94
- data/lib/sequel/adapters/mock.rb +361 -0
- data/lib/sequel/adapters/mysql.rb +118 -222
- data/lib/sequel/adapters/mysql2.rb +58 -57
- data/lib/sequel/adapters/odbc/mssql.rb +15 -7
- data/lib/sequel/adapters/odbc.rb +28 -9
- data/lib/sequel/adapters/openbase.rb +3 -5
- data/lib/sequel/adapters/oracle.rb +349 -53
- data/lib/sequel/adapters/postgres.rb +356 -107
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +342 -0
- data/lib/sequel/adapters/shared/firebird.rb +221 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +335 -118
- data/lib/sequel/adapters/shared/mysql.rb +340 -76
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +243 -45
- data/lib/sequel/adapters/shared/postgres.rb +524 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +265 -89
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +9 -12
- data/lib/sequel/adapters/swift/postgres.rb +9 -74
- data/lib/sequel/adapters/swift/sqlite.rb +8 -6
- data/lib/sequel/adapters/swift.rb +8 -7
- data/lib/sequel/adapters/tinytds.rb +161 -21
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +66 -0
- data/lib/sequel/adapters/utils/stored_procedures.rb +1 -11
- data/lib/sequel/ast_transformer.rb +194 -0
- data/lib/sequel/connection_pool/sharded_single.rb +11 -1
- data/lib/sequel/connection_pool/sharded_threaded.rb +28 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +38 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +118 -11
- data/lib/sequel/database/connecting.rb +38 -28
- data/lib/sequel/database/dataset.rb +4 -4
- data/lib/sequel/database/dataset_defaults.rb +63 -2
- data/lib/sequel/database/logging.rb +7 -2
- data/lib/sequel/database/misc.rb +150 -46
- data/lib/sequel/database/query.rb +215 -65
- data/lib/sequel/database/schema_generator.rb +64 -20
- data/lib/sequel/database/schema_methods.rb +212 -54
- data/lib/sequel/dataset/actions.rb +322 -85
- data/lib/sequel/dataset/features.rb +96 -8
- data/lib/sequel/dataset/graph.rb +38 -11
- data/lib/sequel/dataset/misc.rb +50 -23
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +237 -44
- data/lib/sequel/dataset/sql.rb +698 -278
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +79 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +108 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +16 -34
- data/lib/sequel/extensions/migration.rb +97 -11
- data/lib/sequel/extensions/named_timezones.rb +5 -0
- data/lib/sequel/extensions/null_dataset.rb +90 -0
- data/lib/sequel/extensions/pg_array.rb +460 -0
- data/lib/sequel/extensions/pg_array_ops.rb +220 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +169 -0
- data/lib/sequel/extensions/pg_hstore.rb +296 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
- data/lib/sequel/extensions/pg_inet.rb +89 -0
- data/lib/sequel/extensions/pg_json.rb +178 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
- data/lib/sequel/extensions/pretty_table.rb +5 -71
- data/lib/sequel/extensions/query.rb +1 -1
- data/lib/sequel/extensions/query_literals.rb +79 -0
- data/lib/sequel/extensions/schema_caching.rb +76 -0
- data/lib/sequel/extensions/schema_dumper.rb +251 -37
- data/lib/sequel/extensions/select_remove.rb +35 -0
- data/lib/sequel/extensions/server_block.rb +139 -0
- data/lib/sequel/extensions/sql_expr.rb +4 -110
- data/lib/sequel/extensions/thread_local_timezones.rb +1 -1
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +1187 -393
- data/lib/sequel/model/base.rb +578 -170
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model.rb +30 -11
- data/lib/sequel/no_core_ext.rb +2 -0
- data/lib/sequel/plugins/active_model.rb +13 -1
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/caching.rb +25 -18
- data/lib/sequel/plugins/class_table_inheritance.rb +4 -4
- data/lib/sequel/plugins/composition.rb +44 -12
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/dirty.rb +214 -0
- data/lib/sequel/plugins/eager_each.rb +59 -0
- data/lib/sequel/plugins/force_encoding.rb +6 -6
- data/lib/sequel/plugins/hook_class_methods.rb +1 -1
- data/lib/sequel/plugins/identity_map.rb +123 -8
- data/lib/sequel/plugins/instance_filters.rb +10 -0
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +58 -6
- data/lib/sequel/plugins/list.rb +12 -2
- data/lib/sequel/plugins/many_through_many.rb +79 -28
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +139 -63
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/prepared_statements.rb +167 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +87 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/rcte_tree.rb +29 -15
- data/lib/sequel/plugins/schema.rb +7 -2
- data/lib/sequel/plugins/serialization.rb +98 -49
- data/lib/sequel/plugins/serialization_modification_detection.rb +63 -0
- data/lib/sequel/plugins/sharding.rb +21 -54
- data/lib/sequel/plugins/single_table_inheritance.rb +5 -3
- data/lib/sequel/plugins/static_cache.rb +99 -0
- data/lib/sequel/plugins/subclasses.rb +10 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/typecast_on_load.rb +9 -12
- data/lib/sequel/plugins/update_primary_key.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +526 -59
- data/lib/sequel/timezones.rb +57 -38
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/firebird_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +142 -40
- data/spec/adapters/mysql_spec.rb +380 -234
- data/spec/adapters/oracle_spec.rb +67 -77
- data/spec/adapters/postgres_spec.rb +1050 -212
- data/spec/adapters/spec_helper.rb +8 -5
- data/spec/adapters/sqlite_spec.rb +139 -27
- data/spec/core/connection_pool_spec.rb +196 -93
- data/spec/core/database_spec.rb +879 -510
- data/spec/core/dataset_spec.rb +1415 -1086
- data/spec/core/expression_filters_spec.rb +554 -109
- data/spec/core/mock_adapter_spec.rb +449 -0
- data/spec/core/object_graph_spec.rb +86 -114
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +482 -38
- data/spec/core/spec_helper.rb +6 -48
- data/spec/extensions/active_model_spec.rb +13 -0
- data/spec/extensions/arbitrary_servers_spec.rb +110 -0
- data/spec/extensions/association_autoreloading_spec.rb +18 -10
- data/spec/extensions/association_dependencies_spec.rb +15 -25
- data/spec/extensions/association_pks_spec.rb +66 -32
- data/spec/extensions/association_proxies_spec.rb +4 -4
- data/spec/extensions/caching_spec.rb +47 -51
- data/spec/extensions/class_table_inheritance_spec.rb +31 -83
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/composition_spec.rb +18 -13
- data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +119 -139
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +69 -92
- data/spec/extensions/identity_map_spec.rb +131 -22
- data/spec/extensions/inflector_spec.rb +0 -4
- data/spec/extensions/instance_filters_spec.rb +11 -21
- data/spec/extensions/instance_hooks_spec.rb +72 -0
- data/spec/extensions/json_serializer_spec.rb +49 -6
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +48 -39
- data/spec/extensions/many_through_many_spec.rb +283 -250
- data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
- data/spec/extensions/migration_spec.rb +140 -35
- data/spec/extensions/named_timezones_spec.rb +29 -11
- data/spec/extensions/nested_attributes_spec.rb +251 -89
- data/spec/extensions/null_dataset_spec.rb +85 -0
- data/spec/extensions/optimistic_locking_spec.rb +24 -21
- data/spec/extensions/pg_array_ops_spec.rb +105 -0
- data/spec/extensions/pg_array_spec.rb +196 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
- data/spec/extensions/pg_hstore_spec.rb +195 -0
- data/spec/extensions/pg_inet_spec.rb +44 -0
- data/spec/extensions/pg_json_spec.rb +101 -0
- data/spec/extensions/pg_statement_cache_spec.rb +209 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +111 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +61 -0
- data/spec/extensions/prepared_statements_spec.rb +87 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +31 -0
- data/spec/extensions/pretty_table_spec.rb +7 -6
- data/spec/extensions/query_literals_spec.rb +168 -0
- data/spec/extensions/query_spec.rb +6 -0
- data/spec/extensions/rcte_tree_spec.rb +50 -43
- data/spec/extensions/schema_caching_spec.rb +41 -0
- data/spec/extensions/schema_dumper_spec.rb +429 -22
- data/spec/extensions/schema_spec.rb +25 -21
- data/spec/extensions/select_remove_spec.rb +38 -0
- data/spec/extensions/serialization_modification_detection_spec.rb +72 -0
- data/spec/extensions/serialization_spec.rb +85 -43
- data/spec/extensions/server_block_spec.rb +90 -0
- data/spec/extensions/sharding_spec.rb +69 -143
- data/spec/extensions/single_table_inheritance_spec.rb +23 -29
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +22 -61
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/tactical_eager_loading_spec.rb +17 -19
- data/spec/extensions/thread_local_timezones_spec.rb +22 -2
- data/spec/extensions/timestamps_spec.rb +6 -6
- data/spec/extensions/to_dot_spec.rb +4 -7
- data/spec/extensions/touch_spec.rb +13 -14
- data/spec/extensions/tree_spec.rb +11 -26
- data/spec/extensions/typecast_on_load_spec.rb +9 -6
- data/spec/extensions/update_primary_key_spec.rb +30 -24
- data/spec/extensions/validation_class_methods_spec.rb +30 -51
- data/spec/extensions/validation_helpers_spec.rb +16 -35
- data/spec/extensions/xml_serializer_spec.rb +29 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +4 -0
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +4 -0
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +3 -0
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +3 -0
- data/spec/integration/associations_test.rb +659 -21
- data/spec/integration/database_test.rb +9 -3
- data/spec/integration/dataset_test.rb +605 -91
- data/spec/integration/eager_loader_test.rb +19 -21
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +454 -143
- data/spec/integration/prepared_statement_test.rb +263 -128
- data/spec/integration/schema_test.rb +309 -43
- data/spec/integration/spec_helper.rb +40 -30
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +18 -4
- data/spec/model/association_reflection_spec.rb +201 -7
- data/spec/model/associations_spec.rb +1067 -661
- data/spec/model/base_spec.rb +329 -95
- data/spec/model/dataset_methods_spec.rb +7 -27
- data/spec/model/eager_loading_spec.rb +677 -676
- data/spec/model/hooks_spec.rb +349 -67
- data/spec/model/model_spec.rb +240 -168
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +490 -232
- data/spec/model/spec_helper.rb +14 -71
- data/spec/model/validations_spec.rb +11 -0
- data/spec/rcov.opts +2 -0
- metadata +178 -80
data/MIT-LICENSE
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Copyright (c) 2007-2008 Sharon Rosner
|
|
2
|
-
Copyright (c) 2008-
|
|
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,
|
|
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://
|
|
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=>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 %{
|
|
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 %{
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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 `#{
|
|
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
|
|
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
|
-
|
|
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(
|
|
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"
|
data/doc/active_record.rdoc
CHANGED
|
@@ -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.
|
|
100
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|