sequel 3.21.0 → 3.41.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 +1131 -3
- data/MIT-LICENSE +1 -1
- data/README.rdoc +106 -65
- data/Rakefile +59 -27
- data/bin/sequel +50 -26
- data/doc/active_record.rdoc +68 -52
- data/doc/advanced_associations.rdoc +201 -75
- data/doc/association_basics.rdoc +327 -79
- data/doc/bin_sequel.rdoc +144 -0
- 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 +576 -0
- data/doc/opening_databases.rdoc +121 -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/release_notes/3.40.0.txt +73 -0
- data/doc/release_notes/3.41.0.txt +155 -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/access.rb +334 -0
- data/lib/sequel/adapters/ado/mssql.rb +18 -12
- data/lib/sequel/adapters/ado.rb +69 -21
- data/lib/sequel/adapters/amalgalite.rb +17 -14
- data/lib/sequel/adapters/cubrid.rb +143 -0
- data/lib/sequel/adapters/db2.rb +178 -85
- data/lib/sequel/adapters/dbi.rb +19 -21
- 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 +20 -39
- data/lib/sequel/adapters/firebird.rb +33 -210
- data/lib/sequel/adapters/ibmdb.rb +452 -0
- data/lib/sequel/adapters/informix.rb +6 -29
- data/lib/sequel/adapters/jdbc/as400.rb +16 -34
- data/lib/sequel/adapters/jdbc/cubrid.rb +52 -0
- data/lib/sequel/adapters/jdbc/db2.rb +78 -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 +68 -31
- data/lib/sequel/adapters/jdbc/hsqldb.rb +189 -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 +16 -28
- 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 +72 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +83 -0
- data/lib/sequel/adapters/jdbc.rb +308 -114
- data/lib/sequel/adapters/mock.rb +372 -0
- data/lib/sequel/adapters/mysql.rb +130 -230
- data/lib/sequel/adapters/mysql2.rb +64 -63
- data/lib/sequel/adapters/odbc/mssql.rb +17 -9
- data/lib/sequel/adapters/odbc.rb +29 -10
- data/lib/sequel/adapters/openbase.rb +7 -11
- data/lib/sequel/adapters/oracle.rb +364 -57
- data/lib/sequel/adapters/postgres.rb +379 -145
- data/lib/sequel/adapters/shared/access.rb +236 -11
- data/lib/sequel/adapters/shared/cubrid.rb +216 -0
- data/lib/sequel/adapters/shared/db2.rb +353 -0
- data/lib/sequel/adapters/shared/firebird.rb +231 -0
- data/lib/sequel/adapters/shared/informix.rb +53 -0
- data/lib/sequel/adapters/shared/mssql.rb +412 -116
- data/lib/sequel/adapters/shared/mysql.rb +396 -89
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +166 -0
- data/lib/sequel/adapters/shared/oracle.rb +262 -45
- data/lib/sequel/adapters/shared/postgres.rb +688 -279
- data/lib/sequel/adapters/shared/progress.rb +5 -8
- data/lib/sequel/adapters/shared/sqlite.rb +317 -96
- data/lib/sequel/adapters/sqlite.rb +109 -46
- 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 +31 -27
- data/lib/sequel/adapters/tinytds.rb +177 -25
- 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/split_alter_table.rb +36 -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 +23 -13
- data/lib/sequel/connection_pool/sharded_threaded.rb +64 -13
- data/lib/sequel/connection_pool/single.rb +11 -3
- data/lib/sequel/connection_pool/threaded.rb +69 -20
- data/lib/sequel/connection_pool.rb +20 -18
- data/lib/sequel/core.rb +143 -11
- data/lib/sequel/database/connecting.rb +66 -29
- 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 +192 -51
- data/lib/sequel/database/query.rb +299 -114
- data/lib/sequel/database/schema_generator.rb +103 -36
- data/lib/sequel/database/schema_methods.rb +310 -69
- 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 +322 -68
- data/lib/sequel/dataset/sql.rb +716 -293
- 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/connection_validator.rb +109 -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 +539 -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 +334 -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 +212 -0
- data/lib/sequel/extensions/pg_range.rb +513 -0
- data/lib/sequel/extensions/pg_range_ops.rb +150 -0
- data/lib/sequel/extensions/pg_row.rb +576 -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 +137 -16
- 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 +77 -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 +168 -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 +57 -12
- 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/touch.rb +18 -6
- 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 +2 -1
- data/lib/sequel/plugins/validation_helpers.rb +57 -4
- data/lib/sequel/plugins/xml_serializer.rb +15 -4
- data/lib/sequel/sql.rb +721 -141
- 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 +53 -39
- data/spec/adapters/mssql_spec.rb +261 -66
- data/spec/adapters/mysql_spec.rb +597 -352
- data/spec/adapters/oracle_spec.rb +92 -117
- data/spec/adapters/postgres_spec.rb +2103 -341
- data/spec/adapters/spec_helper.rb +16 -6
- data/spec/adapters/sqlite_spec.rb +237 -76
- data/spec/core/connection_pool_spec.rb +270 -141
- data/spec/core/database_spec.rb +980 -524
- data/spec/core/dataset_spec.rb +1658 -1243
- data/spec/core/expression_filters_spec.rb +741 -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 +227 -33
- 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/connection_validator_spec.rb +118 -0
- 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 +324 -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 +200 -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 +340 -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 +65 -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 +53 -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 +77 -68
- data/spec/extensions/validation_helpers_spec.rb +48 -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 +25 -5
- data/spec/integration/dataset_test.rb +729 -141
- 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 +871 -160
- data/spec/integration/prepared_statement_test.rb +272 -128
- data/spec/integration/schema_test.rb +377 -57
- data/spec/integration/spec_helper.rb +49 -32
- data/spec/integration/timezone_test.rb +38 -12
- data/spec/integration/transaction_test.rb +183 -8
- data/spec/integration/type_test.rb +40 -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 +223 -81
- 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
|
|
@@ -65,6 +65,18 @@ Remember, using +eager_graph+ is generally only necessary when you need to
|
|
|
65
65
|
filter/order based on columns in an associated table, it is recommended to
|
|
66
66
|
use +eager+ for eager loading if possible.
|
|
67
67
|
|
|
68
|
+
One advantage of using +eager_graph+ is that you can easily filter/order
|
|
69
|
+
on columns in an associated table on a per-query basis, using regular
|
|
70
|
+
Sequel dataset methods. For example, if you only want to retrieve artists
|
|
71
|
+
who have albums that start with A, and eager load just those albums,
|
|
72
|
+
ordered by the albums name, you can do:
|
|
73
|
+
|
|
74
|
+
albums = Artist.
|
|
75
|
+
eager_graph(:albums).
|
|
76
|
+
where(Sequel.like(:albums__name, 'A%')).
|
|
77
|
+
order(:albums__name).
|
|
78
|
+
all
|
|
79
|
+
|
|
68
80
|
For lazy loading (e.g. Model[1].association), the <tt>:dataset</tt> option can be used
|
|
69
81
|
to specify an arbitrary dataset (one that uses different keys, multiple keys,
|
|
70
82
|
joins to other tables, etc.).
|
|
@@ -75,19 +87,15 @@ option. Though it can often be verbose (compared to other things in Sequel),
|
|
|
75
87
|
it allows you complete control over how to eagerly load associations for a
|
|
76
88
|
group of objects.
|
|
77
89
|
|
|
78
|
-
:eager_loader should be a proc that takes
|
|
79
|
-
|
|
90
|
+
:eager_loader should be a proc that takes a single hash argument, which will
|
|
91
|
+
have at least the following keys:
|
|
80
92
|
|
|
81
|
-
:
|
|
93
|
+
:id_map :: A mapping of key values to arrays of current model instances,
|
|
94
|
+
usage described below
|
|
82
95
|
:rows :: An array of model objects
|
|
83
96
|
:associations :: A hash of dependent associations to eagerly load
|
|
84
97
|
: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.
|
|
98
|
+
:eager_block :: A dynamic callback for this eager load.
|
|
91
99
|
|
|
92
100
|
Since you are given all of the records, you can do things like filter on
|
|
93
101
|
associations that are specified by multiple keys, or do multiple
|
|
@@ -96,25 +104,141 @@ necessary for polymorphic associations). Inside the <tt>:eager_loader</tt>
|
|
|
96
104
|
proc, you should get the related objects and populate the
|
|
97
105
|
associations cache for all objects in the array of records. The hash
|
|
98
106
|
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)
|
|
107
|
+
loading down multiple levels, but it is up to you to use it.
|
|
108
|
+
|
|
109
|
+
The id_map is a performance enhancement that is used by the default
|
|
110
|
+
association loaders and is also available to you. It is a hash with keys
|
|
111
|
+
foreign/primary key values, and values being arrays of current model
|
|
112
|
+
objects having the foreign/primary key value associated with the key.
|
|
113
|
+
This may be hard to visualize, so I'll give an example. Let's say you
|
|
114
|
+
have the following associations
|
|
115
|
+
|
|
110
116
|
Album.many_to_one :artist
|
|
111
117
|
Album.one_to_many :tracks
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
|
|
119
|
+
and the following three albums in the database:
|
|
120
|
+
|
|
121
|
+
album1 = Album.create(:artist_id=>3) # id: 1
|
|
122
|
+
album2 = Album.create(:artist_id=>3) # id: 2
|
|
123
|
+
album3 = Album.create(:artist_id=>2) # id: 3
|
|
124
|
+
|
|
125
|
+
If you try to eager load this dataset:
|
|
126
|
+
|
|
127
|
+
Album.eager(:artist, :tracks).all
|
|
128
|
+
|
|
129
|
+
Then the id_map provided to the artist :eager_loader proc would be:
|
|
130
|
+
|
|
131
|
+
{3=>[album1, album2], 2=>[album3]}
|
|
132
|
+
|
|
133
|
+
The artist id_map contains a mapping of artist_id values to arrays of
|
|
134
|
+
album objects. Since both album1 and album2 have the same artist_id,
|
|
135
|
+
the are both in the array related to that key. album3 has a different
|
|
136
|
+
artist_id, so it is in a different array. Eager loading of artists is
|
|
137
|
+
done by looking for any artist having one of the keys in the hash:
|
|
138
|
+
|
|
139
|
+
artists = Artist.where(:id=>id_map.keys).all
|
|
140
|
+
|
|
141
|
+
When the artists are retrieved, you can iterate over them, find entries
|
|
142
|
+
with matching keys, and manually associate them to the albums:
|
|
143
|
+
|
|
144
|
+
artists.each do |artist|
|
|
145
|
+
# Find related albums using the artist_id_map
|
|
146
|
+
if albums = id_map[artist.id]
|
|
147
|
+
# Iterate over the albums
|
|
148
|
+
albums.each do |album|
|
|
149
|
+
# Manually set the artist association for each album
|
|
150
|
+
album.associations[:artist] = artist
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
The id_map provided to the tracks :eager_loader proc would be:
|
|
156
|
+
|
|
157
|
+
{1=>[album1], 2=>[album2], 3=>[album3]}
|
|
158
|
+
|
|
159
|
+
Now the id_map contains a mapping of id values to arrays of album objects (in this
|
|
160
|
+
case each array only has a single object, because id is the primary key). So when
|
|
161
|
+
looking for tracks to eagerly load, you only need to look for ones that have an
|
|
162
|
+
album_id with one of the keys in the hash:
|
|
163
|
+
|
|
164
|
+
tracks = Track.where(:album_id=>id_map.keys).all
|
|
165
|
+
|
|
166
|
+
When the tracks are retrieved, you can iterate over them, find entries with matching
|
|
167
|
+
keys, and manually associate them to the albums:
|
|
168
|
+
|
|
169
|
+
tracks.each do |track|
|
|
170
|
+
if albums = id_map[track.album_id]
|
|
171
|
+
albums.each do |album|
|
|
172
|
+
album.associations[:tracks] << track
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
=== Two basic example eager loaders
|
|
115
178
|
|
|
116
|
-
|
|
117
|
-
|
|
179
|
+
Putting the code in the above examples together, you almost have enough for a basic
|
|
180
|
+
working eager loader. The main important thing that is missing is you need to set
|
|
181
|
+
initial values for the eagerly loaded associations. For the artist association, you
|
|
182
|
+
need to initial the values to nil:
|
|
183
|
+
|
|
184
|
+
# rows here is the :rows entry in the hash passed to the eager loader
|
|
185
|
+
rows.each{|album| album.associations[:artist] = nil}
|
|
186
|
+
|
|
187
|
+
For the tracks association, you set the initial value to an empty array:
|
|
188
|
+
|
|
189
|
+
rows.each{|album| album.associations[:track] = []}
|
|
190
|
+
|
|
191
|
+
These are done so that if an album currently being loaded doesn't have an associated
|
|
192
|
+
artist or any associated tracks, the lack of them will be cached, so calling the
|
|
193
|
+
artist or tracks method on the album will not do another database lookup.
|
|
194
|
+
|
|
195
|
+
So putting everything together, the artist eager loader looks like:
|
|
196
|
+
|
|
197
|
+
:eager_loader=>(proc do |eo_opts|
|
|
198
|
+
eo_opts[:rows].each{|album| album.associations[:artist] = nil}
|
|
199
|
+
id_map = eo_opts[:id_map]
|
|
200
|
+
Artist.where(:id=>id_map.keys).all do |artist|
|
|
201
|
+
if albums = id_map[artist.id]
|
|
202
|
+
albums.each do |album|
|
|
203
|
+
album.associations[:artist] = artist
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end)
|
|
208
|
+
|
|
209
|
+
and the tracks eager loader looks like:
|
|
210
|
+
|
|
211
|
+
:eager_loader=>(proc do |eo_opts|
|
|
212
|
+
eo_opts[:rows].each{|album| album.associations[:tracks] = []}
|
|
213
|
+
id_map = eo_opts[:id_map]
|
|
214
|
+
Track.where(:id=>id_map.keys).all do |tracks|
|
|
215
|
+
if albums = id_map[track.album_id]
|
|
216
|
+
albums.each do |album|
|
|
217
|
+
album.associations[:tracks] << track
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end)
|
|
222
|
+
|
|
223
|
+
Now, these are both overly simplistic eager loaders that don't respect cascaded
|
|
224
|
+
associations or any of the association options. But hopefully they both
|
|
225
|
+
provide simple examples that you can more easily build and learn from, as
|
|
226
|
+
the custom eager loaders described later in this page are more complex.
|
|
227
|
+
|
|
228
|
+
Basically, the eager loading steps can be broken down into:
|
|
229
|
+
|
|
230
|
+
1. Set default association values (nil/[]) for each of the current objects
|
|
231
|
+
2. Return just related associated objects by filtering the associated class
|
|
232
|
+
to include only rows with keys present in the id_map.
|
|
233
|
+
3. Iterating over the returned associated objects, indexing into the id_map
|
|
234
|
+
using the foreign/primary key value in the associated object to get
|
|
235
|
+
current values associated to that specific object.
|
|
236
|
+
4. For each of those current values, updating the cached association value to
|
|
237
|
+
include that specific object.
|
|
238
|
+
|
|
239
|
+
Using the :eager_loader proc, you should be able to eagerly load all associations
|
|
240
|
+
that can be eagerly loaded, even if Sequel doesn't natively support such eager
|
|
241
|
+
loading.
|
|
118
242
|
|
|
119
243
|
== ActiveRecord associations
|
|
120
244
|
|
|
@@ -123,9 +247,9 @@ require different approaches or custom <tt>:eager_loader</tt> options.
|
|
|
123
247
|
|
|
124
248
|
=== Association callbacks
|
|
125
249
|
|
|
126
|
-
Sequel supports the same callbacks that ActiveRecord does
|
|
250
|
+
Sequel supports the same callbacks that ActiveRecord does for +one_to_many+ and
|
|
127
251
|
+many_to_many+ associations: <tt>:before_add</tt>, <tt>:before_remove</tt>, <tt>:after_add</tt>, and
|
|
128
|
-
<tt>:after_remove</tt>.
|
|
252
|
+
<tt>:after_remove</tt>. For +many_to_one+ associations and +one_to_one+ associations, Sequel
|
|
129
253
|
supports the <tt>:before_set</tt> and <tt>:after_set</tt> callbacks. On all associations,
|
|
130
254
|
Sequel supports <tt>:after_load</tt>, which is called after the association has been
|
|
131
255
|
loaded.
|
|
@@ -148,7 +272,7 @@ otherwise modified:
|
|
|
148
272
|
class Author < Sequel::Model
|
|
149
273
|
one_to_many :authorships
|
|
150
274
|
end
|
|
151
|
-
Author.first.authorships_dataset.
|
|
275
|
+
Author.first.authorships_dataset.where{number < 10}.first
|
|
152
276
|
|
|
153
277
|
You can extend a dataset with a module using the <tt>:extend</tt> association option. You can reference
|
|
154
278
|
the model object that created the association dataset via the dataset's
|
|
@@ -237,6 +361,23 @@ Sequel::Model:
|
|
|
237
361
|
|
|
238
362
|
class Invoice < Sequel::Model
|
|
239
363
|
many_to_one :client
|
|
364
|
+
|
|
365
|
+
# has_one :through equivalent 1
|
|
366
|
+
# eager load with :eager=>:firm option on :client association, and eager loading :client
|
|
367
|
+
def firm
|
|
368
|
+
client.firm if client
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
# has_one :through equivalent 2
|
|
372
|
+
# eager load the usual way
|
|
373
|
+
many_to_many :firms, :join_table=>:clients, :left_key=>:id, :left_primary_key=>:client_id, :right_key=>:firm_id
|
|
374
|
+
def firm
|
|
375
|
+
firms.first
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# has_one :through equivalent 3
|
|
379
|
+
# eager loading requires custom :eager_loader proc
|
|
380
|
+
many_to_one :firm, :dataset=>proc{Firm.join(:clients, :firm_id=>:id, :id=>client_id).select_all(:firms)}
|
|
240
381
|
end
|
|
241
382
|
|
|
242
383
|
Firm.first.invoices
|
|
@@ -252,7 +393,7 @@ Polymorphic associations break referential integrity and are significantly more
|
|
|
252
393
|
complex than non-polymorphic associations, so their use is not recommended unless
|
|
253
394
|
you are stuck with an existing design that uses them.
|
|
254
395
|
|
|
255
|
-
If you must use them, look for the sequel_polymorphic plugin, as it makes using
|
|
396
|
+
If you must use them, look for the sequel_polymorphic external plugin, as it makes using
|
|
256
397
|
polymorphic associations in Sequel about as easy as it is in ActiveRecord. However,
|
|
257
398
|
here's how they can be done using Sequel's custom associations (the sequel_polymorphic
|
|
258
399
|
plugin is just a generic version of this code):
|
|
@@ -280,7 +421,7 @@ Sequel::Model:
|
|
|
280
421
|
many_to_one :attachable, :reciprocal=>:assets, \
|
|
281
422
|
:dataset=>(proc do
|
|
282
423
|
klass = attachable_type.constantize
|
|
283
|
-
klass.
|
|
424
|
+
klass.where(klass.primary_key=>attachable_id)
|
|
284
425
|
end), \
|
|
285
426
|
:eager_loader=>(proc do |eo|
|
|
286
427
|
id_map = {}
|
|
@@ -290,7 +431,7 @@ Sequel::Model:
|
|
|
290
431
|
end
|
|
291
432
|
id_map.each do |klass_name, id_map|
|
|
292
433
|
klass = klass_name.constantize
|
|
293
|
-
klass.
|
|
434
|
+
klass.where(klass.primary_key=>id_map.keys).all do |attach|
|
|
294
435
|
id_map[attach.pk].each do |asset|
|
|
295
436
|
asset.associations[:attachable] = attach
|
|
296
437
|
end
|
|
@@ -307,48 +448,34 @@ Sequel::Model:
|
|
|
307
448
|
end
|
|
308
449
|
|
|
309
450
|
class Post < Sequel::Model
|
|
310
|
-
one_to_many :assets, :key=>:attachable_id
|
|
311
|
-
ds.filter(:attachable_type=>'Post')
|
|
312
|
-
end
|
|
451
|
+
one_to_many :assets, :key=>:attachable_id, :reciprocal=>:attachable, :conditions=>{:attachable_type=>'Post'}
|
|
313
452
|
|
|
314
453
|
private
|
|
315
454
|
|
|
316
455
|
def _add_asset(asset)
|
|
317
|
-
asset.attachable_id
|
|
318
|
-
|
|
319
|
-
asset.save
|
|
320
|
-
end
|
|
456
|
+
asset.update(:attachable_id=>pk, :attachable_type=>'Post')
|
|
457
|
+
end
|
|
321
458
|
def _remove_asset(asset)
|
|
322
|
-
asset.attachable_id
|
|
323
|
-
|
|
324
|
-
asset.save
|
|
325
|
-
end
|
|
459
|
+
asset.update(:attachable_id=>nil, :attachable_type=>nil)
|
|
460
|
+
end
|
|
326
461
|
def _remove_all_assets
|
|
327
|
-
|
|
328
|
-
.update(:attachable_id=>nil, :attachable_type=>nil)
|
|
462
|
+
assets_dataset.update(:attachable_id=>nil, :attachable_type=>nil)
|
|
329
463
|
end
|
|
330
464
|
end
|
|
331
465
|
|
|
332
466
|
class Note < Sequel::Model
|
|
333
|
-
one_to_many :assets, :key=>:attachable_id
|
|
334
|
-
ds.filter(:attachable_type=>'Note')
|
|
335
|
-
end
|
|
467
|
+
one_to_many :assets, :key=>:attachable_id, :reciprocal=>:attachable, :conditions=>{:attachable_type=>'Note'}
|
|
336
468
|
|
|
337
469
|
private
|
|
338
470
|
|
|
339
471
|
def _add_asset(asset)
|
|
340
|
-
asset.attachable_id
|
|
341
|
-
asset.attachable_type = 'Note'
|
|
342
|
-
asset.save
|
|
472
|
+
asset.update(:attachable_id=>pk, :attachable_type=>'Note')
|
|
343
473
|
end
|
|
344
474
|
def _remove_asset(asset)
|
|
345
|
-
asset.attachable_id
|
|
346
|
-
asset.attachable_type = nil
|
|
347
|
-
asset.save
|
|
475
|
+
asset.update(:attachable_id=>nil, :attachable_type=>nil)
|
|
348
476
|
end
|
|
349
477
|
def _remove_all_assets
|
|
350
|
-
|
|
351
|
-
.update(:attachable_id=>nil, :attachable_type=>nil)
|
|
478
|
+
assets_dataset.update(:attachable_id=>nil, :attachable_type=>nil)
|
|
352
479
|
end
|
|
353
480
|
end
|
|
354
481
|
|
|
@@ -390,7 +517,7 @@ node.children. You can even eager load the relationship up to a certain depth:
|
|
|
390
517
|
# Eager load three generations of generations of children for a given node
|
|
391
518
|
Node.filter(:id=>1).eager(:children=>{:children=>:children}).all.first
|
|
392
519
|
# Load parents and grandparents for a group of nodes
|
|
393
|
-
Node.filter{
|
|
520
|
+
Node.filter{id < 10}.eager(:parent=>:parent).all
|
|
394
521
|
|
|
395
522
|
What if you want to get all ancestors up to the root node, or all descendents,
|
|
396
523
|
without knowing the depth of the tree?
|
|
@@ -415,7 +542,7 @@ without knowing the depth of the tree?
|
|
|
415
542
|
non_root_nodes.each{|n| (id_map[n.parent_id] ||= []) << n}
|
|
416
543
|
# Doesn't cause an infinte loop, because when only the root node
|
|
417
544
|
# is left, this is not called.
|
|
418
|
-
Node.
|
|
545
|
+
Node.where(Node.primary_key=>id_map.keys).eager(:ancestors).all do |node|
|
|
419
546
|
# Populate the parent association for each node
|
|
420
547
|
id_map[node.pk].each{|n| n.associations[:parent] = node}
|
|
421
548
|
end
|
|
@@ -433,7 +560,7 @@ without knowing the depth of the tree?
|
|
|
433
560
|
# if no records are returned. Exclude id = parent_id to avoid infinite loop
|
|
434
561
|
# if the root note is one of the returned records and it has parent_id = id
|
|
435
562
|
# instead of parent_id = NULL.
|
|
436
|
-
Node.
|
|
563
|
+
Node.where(:parent_id=>id_map.keys).exclude(:id=>:parent_id).eager(:descendants).all do |node|
|
|
437
564
|
# Get the parent from the identity map
|
|
438
565
|
parent = id_map[node.parent_id]
|
|
439
566
|
# Set the child's parent association to the parent
|
|
@@ -456,7 +583,7 @@ this easy:
|
|
|
456
583
|
|
|
457
584
|
=== Joining multiple keys to a single key, through a third table
|
|
458
585
|
|
|
459
|
-
Let's say you have a database
|
|
586
|
+
Let's say you have a database of songs, lyrics, and artists. Each song
|
|
460
587
|
may or may not have a lyric (most songs are instrumental). The lyric can be
|
|
461
588
|
associated to an artist in each of four ways: composer, arranger, vocalist,
|
|
462
589
|
or lyricist. These may all be the same, or they could all be different, and
|
|
@@ -469,14 +596,15 @@ name, with no duplicates?
|
|
|
469
596
|
|
|
470
597
|
class Artist < Sequel::Model
|
|
471
598
|
one_to_many :songs, :order=>:songs__name, \
|
|
472
|
-
:dataset=>proc{Song.
|
|
599
|
+
:dataset=>proc{Song.select_all(:songs).join(Lyric, :id=>:lyric_id, id=>[:composer_id, :arranger_id, :vocalist_id, :lyricist_id])}, \
|
|
473
600
|
:eager_loader=>(proc do |eo|
|
|
474
|
-
h = eo[:
|
|
601
|
+
h = eo[:id_map]
|
|
475
602
|
ids = h.keys
|
|
476
603
|
eo[:rows].each{|r| r.associations[:songs] = []}
|
|
477
|
-
Song.
|
|
478
|
-
|
|
479
|
-
|
|
604
|
+
Song.select_all(:songs).
|
|
605
|
+
select_append(:lyrics__composer_id, :lyrics__arranger_id, :lyrics__vocalist_id, :lyrics__lyricist_id).
|
|
606
|
+
join(Lyric, :id=>:lyric_id){Sequel.or(:composer_id=>ids, :arranger_id=>ids, :vocalist_id=>ids, :lyricist_id=>ids)}.
|
|
607
|
+
order(:songs__name).all do |song|
|
|
480
608
|
[:composer_id, :arranger_id, :vocalist_id, :lyricist_id].each do |x|
|
|
481
609
|
recs = h[song.values.delete(x)]
|
|
482
610
|
recs.each{|r| r.associations[:songs] << song} if recs
|
|
@@ -499,14 +627,14 @@ associated tickets.
|
|
|
499
627
|
class Project < Sequel::Model
|
|
500
628
|
one_to_many :tickets
|
|
501
629
|
many_to_one :ticket_hours, :read_only=>true, :key=>:id,
|
|
502
|
-
:dataset=>proc{Ticket.
|
|
630
|
+
:dataset=>proc{Ticket.where(:project_id=>id).select{sum(hours).as(hours)}},
|
|
503
631
|
:eager_loader=>(proc do |eo|
|
|
504
632
|
eo[:rows].each{|p| p.associations[:ticket_hours] = nil}
|
|
505
|
-
Ticket.
|
|
506
|
-
|
|
507
|
-
|
|
633
|
+
Ticket.where(:project_id=>eo[:id_map].keys).
|
|
634
|
+
select_group(:project_id).
|
|
635
|
+
select_append{sum(hours).as(hours)}.
|
|
508
636
|
all do |t|
|
|
509
|
-
p = eo[:
|
|
637
|
+
p = eo[:id_map][t.values.delete(:project_id)].first
|
|
510
638
|
p.associations[:ticket_hours] = t
|
|
511
639
|
end
|
|
512
640
|
end)
|
|
@@ -525,6 +653,4 @@ associated tickets.
|
|
|
525
653
|
end
|
|
526
654
|
|
|
527
655
|
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).
|
|
656
|
+
a sum cache using +after_create+ and +after_delete+ hooks, or preferrably using a database trigger.
|