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.
- data/CHANGELOG +1061 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +103 -63
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +67 -51
- data/doc/advanced_associations.rdoc +189 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/cheat_sheet.rdoc +21 -21
- data/doc/core_extensions.rdoc +374 -0
- data/doc/dataset_basics.rdoc +10 -10
- data/doc/dataset_filtering.rdoc +54 -42
- data/doc/mass_assignment.rdoc +56 -0
- data/doc/migration.rdoc +89 -557
- data/doc/model_hooks.rdoc +80 -29
- data/doc/object_model.rdoc +547 -0
- data/doc/opening_databases.rdoc +114 -53
- data/doc/prepared_statements.rdoc +41 -15
- data/doc/querying.rdoc +137 -56
- data/doc/reflection.rdoc +36 -10
- 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/release_notes/3.37.0.txt +338 -0
- data/doc/release_notes/3.38.0.txt +234 -0
- data/doc/release_notes/3.39.0.txt +237 -0
- data/doc/schema_modification.rdoc +585 -0
- data/doc/sharding.rdoc +99 -8
- data/doc/sql.rdoc +154 -112
- data/doc/testing.rdoc +169 -0
- data/doc/thread_safety.rdoc +17 -0
- data/doc/transactions.rdoc +137 -0
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +78 -43
- 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 +2 -61
- 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 +56 -0
- data/lib/sequel/adapters/jdbc/derby.rb +325 -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 +40 -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 +77 -52
- 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 +279 -97
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +125 -222
- data/lib/sequel/adapters/mysql2.rb +65 -56
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- 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 +368 -138
- data/lib/sequel/adapters/shared/access.rb +32 -9
- data/lib/sequel/adapters/shared/db2.rb +343 -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 +401 -116
- data/lib/sequel/adapters/shared/mysql.rb +424 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +155 -0
- data/lib/sequel/adapters/shared/oracle.rb +257 -45
- data/lib/sequel/adapters/shared/postgres.rb +675 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +285 -96
- data/lib/sequel/adapters/sqlite.rb +101 -39
- data/lib/sequel/adapters/swift/mysql.rb +10 -12
- data/lib/sequel/adapters/swift/postgres.rb +13 -79
- data/lib/sequel/adapters/swift/sqlite.rb +9 -6
- data/lib/sequel/adapters/swift.rb +28 -19
- data/lib/sequel/adapters/tinytds.rb +174 -22
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +77 -0
- data/lib/sequel/adapters/utils/pg_types.rb +81 -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 +33 -2
- data/lib/sequel/connection_pool/single.rb +5 -0
- data/lib/sequel/connection_pool/threaded.rb +43 -14
- data/lib/sequel/connection_pool.rb +10 -2
- data/lib/sequel/core.rb +143 -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 +190 -46
- data/lib/sequel/database/query.rb +272 -110
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +300 -67
- data/lib/sequel/dataset/actions.rb +344 -90
- data/lib/sequel/dataset/features.rb +101 -8
- data/lib/sequel/dataset/graph.rb +42 -15
- data/lib/sequel/dataset/misc.rb +52 -25
- data/lib/sequel/dataset/mutation.rb +12 -7
- data/lib/sequel/dataset/prepared_statements.rb +68 -30
- data/lib/sequel/dataset/query.rb +305 -54
- data/lib/sequel/dataset/sql.rb +720 -292
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/_pretty_table.rb +83 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +109 -0
- data/lib/sequel/extensions/blank.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +72 -0
- data/lib/sequel/extensions/constraint_validations.rb +451 -0
- data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +30 -46
- data/lib/sequel/extensions/eval_inspect.rb +173 -0
- data/lib/sequel/extensions/inflector.rb +4 -0
- data/lib/sequel/extensions/looser_typecasting.rb +5 -4
- data/lib/sequel/extensions/migration.rb +100 -11
- data/lib/sequel/extensions/named_timezones.rb +9 -0
- data/lib/sequel/extensions/null_dataset.rb +94 -0
- data/lib/sequel/extensions/pagination.rb +4 -0
- data/lib/sequel/extensions/pg_array.rb +537 -0
- data/lib/sequel/extensions/pg_array_ops.rb +263 -0
- data/lib/sequel/extensions/pg_auto_parameterize.rb +175 -0
- data/lib/sequel/extensions/pg_hstore.rb +313 -0
- data/lib/sequel/extensions/pg_hstore_ops.rb +293 -0
- data/lib/sequel/extensions/pg_inet.rb +113 -0
- data/lib/sequel/extensions/pg_interval.rb +191 -0
- data/lib/sequel/extensions/pg_json.rb +208 -0
- data/lib/sequel/extensions/pg_range.rb +512 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +572 -0
- data/lib/sequel/extensions/pg_row_ops.rb +182 -0
- data/lib/sequel/extensions/pg_statement_cache.rb +317 -0
- data/lib/sequel/extensions/pretty_table.rb +17 -72
- data/lib/sequel/extensions/query.rb +8 -4
- 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 +282 -76
- data/lib/sequel/extensions/select_remove.rb +39 -0
- data/lib/sequel/extensions/server_block.rb +140 -0
- data/lib/sequel/extensions/split_array_nil.rb +65 -0
- data/lib/sequel/extensions/sql_expr.rb +8 -110
- data/lib/sequel/extensions/string_date_time.rb +4 -0
- data/lib/sequel/extensions/thread_local_timezones.rb +10 -4
- data/lib/sequel/extensions/to_dot.rb +99 -83
- data/lib/sequel/model/associations.rb +1263 -409
- data/lib/sequel/model/base.rb +624 -171
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +24 -2
- 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/constraint_validations.rb +198 -0
- data/lib/sequel/plugins/dataset_associations.rb +100 -0
- data/lib/sequel/plugins/defaults_setter.rb +72 -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 +134 -15
- 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 +13 -2
- data/lib/sequel/plugins/many_through_many.rb +103 -51
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
- data/lib/sequel/plugins/nested_attributes.rb +150 -66
- data/lib/sequel/plugins/optimistic_locking.rb +8 -0
- data/lib/sequel/plugins/pg_row.rb +121 -0
- data/lib/sequel/plugins/pg_typecast_on_load.rb +65 -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 +31 -17
- data/lib/sequel/plugins/schema.rb +8 -3
- 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 +29 -3
- data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +3 -3
- 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/validation_helpers.rb +55 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +649 -122
- data/lib/sequel/timezones.rb +67 -40
- 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 +194 -66
- data/spec/adapters/mysql_spec.rb +475 -306
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +1982 -325
- data/spec/adapters/spec_helper.rb +9 -6
- data/spec/adapters/sqlite_spec.rb +203 -75
- data/spec/core/connection_pool_spec.rb +218 -93
- data/spec/core/database_spec.rb +956 -522
- data/spec/core/dataset_spec.rb +1631 -1242
- data/spec/core/expression_filters_spec.rb +690 -334
- data/spec/core/mock_adapter_spec.rb +453 -0
- data/spec/core/object_graph_spec.rb +88 -129
- data/spec/core/schema_generator_spec.rb +4 -4
- data/spec/core/schema_spec.rb +556 -53
- data/spec/core/spec_helper.rb +7 -48
- data/spec/core_extensions_spec.rb +626 -0
- 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/boolean_readers_spec.rb +25 -25
- 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/extensions/constraint_validations_plugin_spec.rb +196 -0
- data/spec/extensions/constraint_validations_spec.rb +316 -0
- data/spec/extensions/dataset_associations_spec.rb +199 -0
- data/spec/extensions/defaults_setter_spec.rb +88 -0
- data/spec/extensions/dirty_spec.rb +155 -0
- data/spec/extensions/eager_each_spec.rb +34 -0
- data/spec/extensions/eval_inspect_spec.rb +67 -0
- data/spec/extensions/force_encoding_spec.rb +4 -2
- data/spec/extensions/hook_class_methods_spec.rb +97 -128
- data/spec/extensions/identity_map_spec.rb +142 -24
- 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 -12
- data/spec/extensions/lazy_attributes_spec.rb +16 -20
- data/spec/extensions/list_spec.rb +49 -40
- data/spec/extensions/looser_typecasting_spec.rb +7 -7
- data/spec/extensions/many_through_many_spec.rb +368 -254
- 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 +268 -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 +112 -0
- data/spec/extensions/pg_array_spec.rb +320 -0
- data/spec/extensions/pg_auto_parameterize_spec.rb +65 -0
- data/spec/extensions/pg_hstore_ops_spec.rb +140 -0
- data/spec/extensions/pg_hstore_spec.rb +194 -0
- data/spec/extensions/pg_inet_spec.rb +47 -0
- data/spec/extensions/pg_interval_spec.rb +72 -0
- data/spec/extensions/pg_json_spec.rb +99 -0
- data/spec/extensions/pg_range_ops_spec.rb +56 -0
- data/spec/extensions/pg_range_spec.rb +395 -0
- data/spec/extensions/pg_row_ops_spec.rb +58 -0
- data/spec/extensions/pg_row_plugin_spec.rb +49 -0
- data/spec/extensions/pg_row_spec.rb +323 -0
- data/spec/extensions/pg_statement_cache_spec.rb +208 -0
- data/spec/extensions/pg_typecast_on_load_spec.rb +58 -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 +167 -0
- data/spec/extensions/query_spec.rb +9 -3
- 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 +434 -49
- data/spec/extensions/schema_spec.rb +30 -49
- 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 +86 -48
- 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 +34 -67
- data/spec/extensions/split_array_nil_spec.rb +24 -0
- data/spec/extensions/sql_expr_spec.rb +29 -60
- data/spec/extensions/static_cache_spec.rb +145 -0
- data/spec/extensions/subclasses_spec.rb +14 -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 +8 -11
- 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 +58 -67
- data/spec/extensions/validation_helpers_spec.rb +31 -37
- 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 +11 -5
- data/spec/integration/dataset_test.rb +717 -129
- data/spec/integration/eager_loader_test.rb +38 -54
- data/spec/integration/migrator_test.rb +2 -3
- data/spec/integration/model_test.rb +81 -2
- data/spec/integration/plugin_test.rb +604 -147
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +367 -57
- data/spec/integration/spec_helper.rb +42 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +31 -8
- data/spec/model/association_reflection_spec.rb +207 -8
- data/spec/model/associations_spec.rb +1206 -693
- data/spec/model/base_spec.rb +334 -100
- data/spec/model/dataset_methods_spec.rb +45 -27
- data/spec/model/eager_loading_spec.rb +828 -692
- data/spec/model/hooks_spec.rb +355 -67
- data/spec/model/model_spec.rb +251 -179
- data/spec/model/plugins_spec.rb +24 -13
- data/spec/model/record_spec.rb +506 -232
- data/spec/model/spec_helper.rb +16 -77
- data/spec/model/validations_spec.rb +38 -2
- data/spec/rcov.opts +2 -0
- metadata +209 -80
- 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.
|
|
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|
|
|
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.
|
|
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
|
|
79
|
-
|
|
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
|
-
:
|
|
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.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|
|
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>.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
318
|
-
|
|
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
|
|
323
|
-
|
|
324
|
-
asset.save
|
|
325
|
-
end
|
|
447
|
+
asset.update(:attachable_id=>nil, :attachable_type=>nil)
|
|
448
|
+
end
|
|
326
449
|
def _remove_all_assets
|
|
327
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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{
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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[:
|
|
589
|
+
h = eo[:id_map]
|
|
475
590
|
ids = h.keys
|
|
476
591
|
eo[:rows].each{|r| r.associations[:songs] = []}
|
|
477
|
-
Song.
|
|
478
|
-
|
|
479
|
-
|
|
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.
|
|
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.
|
|
506
|
-
|
|
507
|
-
|
|
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[:
|
|
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.
|