sequel 4.49.0 → 5.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG +70 -0
- data/README.rdoc +195 -136
- data/Rakefile +26 -42
- data/bin/sequel +3 -5
- data/doc/advanced_associations.rdoc +86 -163
- data/doc/association_basics.rdoc +197 -274
- data/doc/bin_sequel.rdoc +5 -3
- data/doc/cheat_sheet.rdoc +66 -43
- data/doc/code_order.rdoc +1 -8
- data/doc/core_extensions.rdoc +81 -56
- data/doc/dataset_basics.rdoc +8 -17
- data/doc/dataset_filtering.rdoc +81 -86
- data/doc/extensions.rdoc +3 -10
- data/doc/mass_assignment.rdoc +73 -30
- data/doc/migration.rdoc +19 -36
- data/doc/model_dataset_method_design.rdoc +14 -17
- data/doc/model_hooks.rdoc +15 -25
- data/doc/model_plugins.rdoc +10 -10
- data/doc/mssql_stored_procedures.rdoc +3 -3
- data/doc/object_model.rdoc +52 -70
- data/doc/opening_databases.rdoc +39 -32
- data/doc/postgresql.rdoc +48 -38
- data/doc/prepared_statements.rdoc +27 -22
- data/doc/querying.rdoc +173 -150
- data/doc/reflection.rdoc +5 -6
- data/doc/release_notes/5.0.0.txt +159 -0
- data/doc/schema_modification.rdoc +63 -60
- data/doc/security.rdoc +97 -88
- data/doc/sharding.rdoc +43 -30
- data/doc/sql.rdoc +53 -65
- data/doc/testing.rdoc +3 -5
- data/doc/thread_safety.rdoc +2 -4
- data/doc/transactions.rdoc +18 -17
- data/doc/validations.rdoc +48 -45
- data/doc/virtual_rows.rdoc +87 -115
- data/lib/sequel.rb +1 -1
- data/lib/sequel/adapters/ado.rb +9 -25
- data/lib/sequel/adapters/ado/access.rb +7 -13
- data/lib/sequel/adapters/ado/mssql.rb +2 -9
- data/lib/sequel/adapters/amalgalite.rb +3 -18
- data/lib/sequel/adapters/ibmdb.rb +9 -45
- data/lib/sequel/adapters/jdbc.rb +13 -73
- data/lib/sequel/adapters/jdbc/db2.rb +8 -37
- data/lib/sequel/adapters/jdbc/derby.rb +4 -50
- data/lib/sequel/adapters/jdbc/h2.rb +4 -25
- data/lib/sequel/adapters/jdbc/hsqldb.rb +1 -26
- data/lib/sequel/adapters/jdbc/jtds.rb +2 -9
- data/lib/sequel/adapters/jdbc/mssql.rb +1 -11
- data/lib/sequel/adapters/jdbc/mysql.rb +1 -15
- data/lib/sequel/adapters/jdbc/oracle.rb +4 -26
- data/lib/sequel/adapters/jdbc/postgresql.rb +2 -31
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +4 -17
- data/lib/sequel/adapters/jdbc/sqlite.rb +1 -7
- data/lib/sequel/adapters/jdbc/sqlserver.rb +1 -13
- data/lib/sequel/adapters/jdbc/transactions.rb +1 -14
- data/lib/sequel/adapters/mock.rb +4 -30
- data/lib/sequel/adapters/mysql.rb +7 -44
- data/lib/sequel/adapters/mysql2.rb +5 -23
- data/lib/sequel/adapters/odbc.rb +0 -19
- data/lib/sequel/adapters/odbc/db2.rb +1 -1
- data/lib/sequel/adapters/odbc/mssql.rb +4 -12
- data/lib/sequel/adapters/odbc/oracle.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +7 -13
- data/lib/sequel/adapters/postgres.rb +13 -57
- data/lib/sequel/adapters/postgresql.rb +1 -1
- data/lib/sequel/adapters/shared/access.rb +11 -51
- data/lib/sequel/adapters/shared/db2.rb +3 -61
- data/lib/sequel/adapters/shared/mssql.rb +21 -157
- data/lib/sequel/adapters/shared/mysql.rb +23 -224
- data/lib/sequel/adapters/shared/oracle.rb +13 -41
- data/lib/sequel/adapters/shared/postgres.rb +44 -259
- data/lib/sequel/adapters/shared/sqlanywhere.rb +4 -96
- data/lib/sequel/adapters/shared/sqlite.rb +12 -101
- data/lib/sequel/adapters/sqlanywhere.rb +4 -23
- data/lib/sequel/adapters/sqlite.rb +2 -19
- data/lib/sequel/adapters/tinytds.rb +5 -15
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +1 -1
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -4
- data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +3 -6
- data/lib/sequel/adapters/utils/replace.rb +0 -5
- data/lib/sequel/adapters/utils/stored_procedures.rb +0 -2
- data/lib/sequel/adapters/utils/unmodified_identifiers.rb +2 -0
- data/lib/sequel/ast_transformer.rb +3 -94
- data/lib/sequel/connection_pool.rb +26 -28
- data/lib/sequel/connection_pool/sharded_single.rb +1 -4
- data/lib/sequel/connection_pool/sharded_threaded.rb +97 -95
- data/lib/sequel/connection_pool/single.rb +0 -2
- data/lib/sequel/connection_pool/threaded.rb +94 -110
- data/lib/sequel/core.rb +42 -101
- data/lib/sequel/database.rb +12 -2
- data/lib/sequel/database/connecting.rb +23 -60
- data/lib/sequel/database/dataset.rb +6 -9
- data/lib/sequel/database/dataset_defaults.rb +4 -48
- data/lib/sequel/database/features.rb +5 -4
- data/lib/sequel/database/logging.rb +2 -9
- data/lib/sequel/database/misc.rb +23 -55
- data/lib/sequel/database/query.rb +8 -13
- data/lib/sequel/database/schema_generator.rb +89 -64
- data/lib/sequel/database/schema_methods.rb +61 -79
- data/lib/sequel/database/transactions.rb +4 -24
- data/lib/sequel/dataset.rb +18 -10
- data/lib/sequel/dataset/actions.rb +53 -107
- data/lib/sequel/dataset/dataset_module.rb +3 -15
- data/lib/sequel/dataset/features.rb +30 -30
- data/lib/sequel/dataset/graph.rb +40 -49
- data/lib/sequel/dataset/misc.rb +12 -37
- data/lib/sequel/dataset/placeholder_literalizer.rb +4 -4
- data/lib/sequel/dataset/prepared_statements.rb +23 -51
- data/lib/sequel/dataset/query.rb +71 -155
- data/lib/sequel/dataset/sql.rb +30 -225
- data/lib/sequel/deprecated.rb +18 -27
- data/lib/sequel/exceptions.rb +1 -17
- data/lib/sequel/extensions/_model_pg_row.rb +0 -7
- data/lib/sequel/extensions/_pretty_table.rb +1 -3
- data/lib/sequel/extensions/arbitrary_servers.rb +10 -10
- data/lib/sequel/extensions/connection_expiration.rb +1 -1
- data/lib/sequel/extensions/connection_validator.rb +1 -1
- data/lib/sequel/extensions/constraint_validations.rb +11 -11
- data/lib/sequel/extensions/core_extensions.rb +39 -49
- data/lib/sequel/extensions/core_refinements.rb +39 -45
- data/lib/sequel/extensions/current_datetime_timestamp.rb +0 -4
- data/lib/sequel/extensions/date_arithmetic.rb +7 -7
- data/lib/sequel/extensions/duplicate_columns_handler.rb +12 -9
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +2 -2
- data/lib/sequel/extensions/eval_inspect.rb +4 -11
- data/lib/sequel/extensions/freeze_datasets.rb +1 -69
- data/lib/sequel/extensions/from_block.rb +1 -35
- data/lib/sequel/extensions/graph_each.rb +2 -2
- data/lib/sequel/extensions/identifier_mangling.rb +9 -19
- data/lib/sequel/extensions/implicit_subquery.rb +2 -2
- data/lib/sequel/extensions/inflector.rb +4 -4
- data/lib/sequel/extensions/migration.rb +23 -40
- data/lib/sequel/extensions/no_auto_literal_strings.rb +2 -84
- data/lib/sequel/extensions/null_dataset.rb +2 -8
- data/lib/sequel/extensions/pagination.rb +1 -17
- data/lib/sequel/extensions/pg_array.rb +20 -189
- data/lib/sequel/extensions/pg_hstore.rb +11 -50
- data/lib/sequel/extensions/pg_hstore_ops.rb +2 -2
- data/lib/sequel/extensions/pg_inet.rb +2 -15
- data/lib/sequel/extensions/pg_interval.rb +1 -20
- data/lib/sequel/extensions/pg_json.rb +7 -27
- data/lib/sequel/extensions/pg_loose_count.rb +1 -1
- data/lib/sequel/extensions/pg_range.rb +6 -121
- data/lib/sequel/extensions/pg_range_ops.rb +1 -3
- data/lib/sequel/extensions/pg_row.rb +5 -77
- data/lib/sequel/extensions/pg_row_ops.rb +2 -13
- data/lib/sequel/extensions/query.rb +3 -4
- data/lib/sequel/extensions/round_timestamps.rb +0 -6
- data/lib/sequel/extensions/schema_dumper.rb +13 -13
- data/lib/sequel/extensions/select_remove.rb +3 -3
- data/lib/sequel/extensions/split_array_nil.rb +2 -2
- data/lib/sequel/extensions/sql_comments.rb +2 -2
- data/lib/sequel/extensions/string_agg.rb +11 -8
- data/lib/sequel/extensions/symbol_aref.rb +6 -20
- data/lib/sequel/model.rb +27 -62
- data/lib/sequel/model/associations.rb +128 -131
- data/lib/sequel/model/base.rb +171 -711
- data/lib/sequel/model/default_inflections.rb +1 -1
- data/lib/sequel/model/errors.rb +0 -3
- data/lib/sequel/model/exceptions.rb +2 -6
- data/lib/sequel/model/inflections.rb +1 -26
- data/lib/sequel/model/plugins.rb +1 -0
- data/lib/sequel/plugins/active_model.rb +2 -5
- data/lib/sequel/plugins/association_dependencies.rb +15 -15
- data/lib/sequel/plugins/association_pks.rb +14 -28
- data/lib/sequel/plugins/association_proxies.rb +6 -7
- data/lib/sequel/plugins/auto_validations.rb +4 -4
- data/lib/sequel/plugins/before_after_save.rb +0 -43
- data/lib/sequel/plugins/blacklist_security.rb +9 -8
- data/lib/sequel/plugins/boolean_readers.rb +3 -3
- data/lib/sequel/plugins/boolean_subsets.rb +2 -2
- data/lib/sequel/plugins/caching.rb +5 -5
- data/lib/sequel/plugins/class_table_inheritance.rb +71 -102
- data/lib/sequel/plugins/column_conflicts.rb +2 -2
- data/lib/sequel/plugins/column_select.rb +2 -2
- data/lib/sequel/plugins/composition.rb +15 -24
- data/lib/sequel/plugins/constraint_validations.rb +4 -3
- data/lib/sequel/plugins/csv_serializer.rb +13 -20
- data/lib/sequel/plugins/dataset_associations.rb +2 -2
- data/lib/sequel/plugins/def_dataset_method.rb +5 -5
- data/lib/sequel/plugins/defaults_setter.rb +1 -1
- data/lib/sequel/plugins/delay_add_association.rb +1 -1
- data/lib/sequel/plugins/finder.rb +16 -10
- data/lib/sequel/plugins/force_encoding.rb +1 -7
- data/lib/sequel/plugins/hook_class_methods.rb +4 -106
- data/lib/sequel/plugins/input_transformer.rb +10 -11
- data/lib/sequel/plugins/insert_returning_select.rb +1 -9
- data/lib/sequel/plugins/instance_filters.rb +5 -5
- data/lib/sequel/plugins/instance_hooks.rb +7 -52
- data/lib/sequel/plugins/inverted_subsets.rb +3 -1
- data/lib/sequel/plugins/json_serializer.rb +19 -19
- data/lib/sequel/plugins/lazy_attributes.rb +1 -10
- data/lib/sequel/plugins/list.rb +6 -6
- data/lib/sequel/plugins/many_through_many.rb +11 -8
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +3 -3
- data/lib/sequel/plugins/nested_attributes.rb +18 -31
- data/lib/sequel/plugins/optimistic_locking.rb +3 -3
- data/lib/sequel/plugins/pg_array_associations.rb +8 -2
- data/lib/sequel/plugins/pg_row.rb +2 -11
- data/lib/sequel/plugins/prepared_statements.rb +13 -66
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -1
- data/lib/sequel/plugins/rcte_tree.rb +7 -7
- data/lib/sequel/plugins/serialization.rb +15 -33
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
- data/lib/sequel/plugins/sharding.rb +2 -8
- data/lib/sequel/plugins/single_table_inheritance.rb +10 -13
- data/lib/sequel/plugins/skip_create_refresh.rb +3 -3
- data/lib/sequel/plugins/static_cache.rb +8 -9
- data/lib/sequel/plugins/string_stripper.rb +3 -3
- data/lib/sequel/plugins/subclasses.rb +1 -1
- data/lib/sequel/plugins/subset_conditions.rb +2 -2
- data/lib/sequel/plugins/table_select.rb +2 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +4 -4
- data/lib/sequel/plugins/timestamps.rb +6 -7
- data/lib/sequel/plugins/touch.rb +4 -8
- data/lib/sequel/plugins/tree.rb +3 -3
- data/lib/sequel/plugins/typecast_on_load.rb +2 -2
- data/lib/sequel/plugins/unlimited_update.rb +1 -7
- data/lib/sequel/plugins/update_or_create.rb +3 -3
- data/lib/sequel/plugins/update_refresh.rb +3 -3
- data/lib/sequel/plugins/uuid.rb +7 -11
- data/lib/sequel/plugins/validation_class_methods.rb +10 -9
- data/lib/sequel/plugins/validation_contexts.rb +4 -4
- data/lib/sequel/plugins/validation_helpers.rb +26 -25
- data/lib/sequel/plugins/whitelist_security.rb +13 -9
- data/lib/sequel/plugins/xml_serializer.rb +24 -25
- data/lib/sequel/sql.rb +145 -276
- data/lib/sequel/timezones.rb +8 -22
- data/lib/sequel/version.rb +2 -2
- data/spec/adapter_spec.rb +1 -1
- data/spec/adapters/db2_spec.rb +2 -103
- data/spec/adapters/mssql_spec.rb +89 -68
- data/spec/adapters/mysql_spec.rb +101 -480
- data/spec/adapters/oracle_spec.rb +1 -9
- data/spec/adapters/postgres_spec.rb +312 -565
- data/spec/adapters/spec_helper.rb +12 -31
- data/spec/adapters/sqlanywhere_spec.rb +2 -77
- data/spec/adapters/sqlite_spec.rb +8 -146
- data/spec/bin_spec.rb +11 -16
- data/spec/core/connection_pool_spec.rb +173 -74
- data/spec/core/database_spec.rb +64 -244
- data/spec/core/dataset_spec.rb +81 -415
- data/spec/core/deprecated_spec.rb +3 -3
- data/spec/core/expression_filters_spec.rb +37 -144
- data/spec/core/mock_adapter_spec.rb +176 -4
- data/spec/core/object_graph_spec.rb +11 -60
- data/spec/core/placeholder_literalizer_spec.rb +1 -14
- data/spec/core/schema_generator_spec.rb +51 -40
- data/spec/core/schema_spec.rb +74 -77
- data/spec/core/spec_helper.rb +6 -24
- data/spec/core/version_spec.rb +1 -1
- data/spec/core_extensions_spec.rb +7 -83
- data/spec/core_model_spec.rb +2 -2
- data/spec/deprecation_helper.rb +2 -14
- data/spec/extensions/accessed_columns_spec.rb +1 -1
- data/spec/extensions/active_model_spec.rb +3 -3
- data/spec/extensions/after_initialize_spec.rb +1 -1
- data/spec/extensions/arbitrary_servers_spec.rb +2 -2
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_pks_spec.rb +4 -59
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/auto_literal_strings_spec.rb +1 -12
- data/spec/extensions/auto_validations_spec.rb +1 -1
- data/spec/extensions/blacklist_security_spec.rb +1 -1
- data/spec/extensions/blank_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +1 -1
- data/spec/extensions/boolean_subsets_spec.rb +1 -1
- data/spec/extensions/caching_spec.rb +1 -1
- data/spec/extensions/class_table_inheritance_spec.rb +35 -1086
- data/spec/extensions/column_conflicts_spec.rb +1 -1
- data/spec/extensions/column_select_spec.rb +4 -4
- data/spec/extensions/columns_introspection_spec.rb +1 -1
- data/spec/extensions/columns_updated_spec.rb +1 -1
- data/spec/extensions/composition_spec.rb +1 -7
- data/spec/extensions/connection_expiration_spec.rb +3 -3
- data/spec/extensions/connection_validator_spec.rb +3 -3
- data/spec/extensions/constraint_validations_plugin_spec.rb +1 -1
- data/spec/extensions/constraint_validations_spec.rb +1 -1
- data/spec/extensions/core_refinements_spec.rb +1 -3
- data/spec/extensions/csv_serializer_spec.rb +4 -9
- data/spec/extensions/current_datetime_timestamp_spec.rb +1 -1
- data/spec/extensions/dataset_associations_spec.rb +2 -1
- data/spec/extensions/dataset_source_alias_spec.rb +1 -1
- data/spec/extensions/date_arithmetic_spec.rb +3 -3
- data/spec/extensions/def_dataset_method_spec.rb +1 -1
- data/spec/extensions/defaults_setter_spec.rb +2 -2
- data/spec/extensions/delay_add_association_spec.rb +8 -9
- data/spec/extensions/dirty_spec.rb +1 -1
- data/spec/extensions/duplicate_columns_handler_spec.rb +1 -1
- data/spec/extensions/eager_each_spec.rb +2 -2
- data/spec/extensions/empty_array_consider_nulls_spec.rb +1 -1
- data/spec/extensions/error_splitter_spec.rb +1 -1
- data/spec/extensions/error_sql_spec.rb +1 -1
- data/spec/extensions/eval_inspect_spec.rb +1 -1
- data/spec/extensions/finder_spec.rb +1 -1
- data/spec/extensions/force_encoding_spec.rb +2 -5
- data/spec/extensions/freeze_datasets_spec.rb +1 -1
- data/spec/extensions/graph_each_spec.rb +5 -5
- data/spec/extensions/hook_class_methods_spec.rb +1 -194
- data/spec/extensions/identifier_mangling_spec.rb +17 -170
- data/spec/extensions/implicit_subquery_spec.rb +1 -5
- data/spec/extensions/inflector_spec.rb +1 -1
- data/spec/extensions/input_transformer_spec.rb +7 -2
- data/spec/extensions/insert_returning_select_spec.rb +1 -1
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/instance_hooks_spec.rb +1 -95
- data/spec/extensions/inverted_subsets_spec.rb +1 -1
- data/spec/extensions/json_serializer_spec.rb +1 -1
- data/spec/extensions/lazy_attributes_spec.rb +1 -7
- data/spec/extensions/list_spec.rb +1 -1
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +1 -1
- data/spec/extensions/migration_spec.rb +2 -2
- data/spec/extensions/modification_detection_spec.rb +1 -1
- data/spec/extensions/mssql_optimistic_locking_spec.rb +1 -1
- data/spec/extensions/named_timezones_spec.rb +3 -3
- data/spec/extensions/nested_attributes_spec.rb +1 -29
- data/spec/extensions/null_dataset_spec.rb +1 -11
- data/spec/extensions/optimistic_locking_spec.rb +1 -1
- data/spec/extensions/pagination_spec.rb +1 -1
- data/spec/extensions/pg_array_associations_spec.rb +4 -1
- data/spec/extensions/pg_array_ops_spec.rb +1 -1
- data/spec/extensions/pg_array_spec.rb +3 -48
- data/spec/extensions/pg_enum_spec.rb +1 -1
- data/spec/extensions/pg_hstore_ops_spec.rb +1 -1
- data/spec/extensions/pg_hstore_spec.rb +23 -32
- data/spec/extensions/pg_inet_ops_spec.rb +1 -1
- data/spec/extensions/pg_inet_spec.rb +1 -14
- data/spec/extensions/pg_interval_spec.rb +3 -13
- data/spec/extensions/pg_json_ops_spec.rb +1 -1
- data/spec/extensions/pg_json_spec.rb +1 -13
- data/spec/extensions/pg_loose_count_spec.rb +1 -1
- data/spec/extensions/pg_range_ops_spec.rb +1 -1
- data/spec/extensions/pg_range_spec.rb +3 -88
- data/spec/extensions/pg_row_ops_spec.rb +1 -1
- data/spec/extensions/pg_row_plugin_spec.rb +1 -1
- data/spec/extensions/pg_row_spec.rb +1 -44
- data/spec/extensions/pg_static_cache_updater_spec.rb +1 -1
- data/spec/extensions/prepared_statements_safe_spec.rb +1 -1
- data/spec/extensions/prepared_statements_spec.rb +13 -48
- data/spec/extensions/pretty_table_spec.rb +1 -1
- data/spec/extensions/query_spec.rb +1 -12
- data/spec/extensions/rcte_tree_spec.rb +1 -1
- data/spec/extensions/round_timestamps_spec.rb +1 -5
- data/spec/extensions/s_spec.rb +1 -1
- data/spec/extensions/schema_caching_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +1 -1
- data/spec/extensions/select_remove_spec.rb +1 -1
- data/spec/extensions/sequel_4_dataset_methods_spec.rb +1 -1
- data/spec/extensions/serialization_modification_detection_spec.rb +1 -1
- data/spec/extensions/serialization_spec.rb +2 -14
- data/spec/extensions/server_block_spec.rb +1 -1
- data/spec/extensions/server_logging_spec.rb +2 -2
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/shared_caching_spec.rb +1 -28
- data/spec/extensions/single_table_inheritance_spec.rb +2 -5
- data/spec/extensions/singular_table_names_spec.rb +1 -1
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +5 -27
- data/spec/extensions/split_array_nil_spec.rb +1 -1
- data/spec/extensions/split_values_spec.rb +1 -1
- data/spec/extensions/sql_comments_spec.rb +1 -1
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/static_cache_spec.rb +1 -1
- data/spec/extensions/string_agg_spec.rb +2 -2
- data/spec/extensions/string_date_time_spec.rb +1 -1
- data/spec/extensions/string_stripper_spec.rb +1 -1
- data/spec/extensions/subclasses_spec.rb +1 -1
- data/spec/extensions/subset_conditions_spec.rb +1 -1
- data/spec/extensions/symbol_aref_refinement_spec.rb +1 -1
- data/spec/extensions/symbol_as_refinement_spec.rb +1 -1
- data/spec/extensions/table_select_spec.rb +4 -4
- data/spec/extensions/tactical_eager_loading_spec.rb +1 -6
- data/spec/extensions/thread_local_timezones_spec.rb +1 -1
- data/spec/extensions/timestamps_spec.rb +3 -3
- data/spec/extensions/to_dot_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/tree_spec.rb +1 -1
- data/spec/extensions/typecast_on_load_spec.rb +1 -1
- data/spec/extensions/unlimited_update_spec.rb +1 -1
- data/spec/extensions/update_or_create_spec.rb +1 -1
- data/spec/extensions/update_primary_key_spec.rb +4 -3
- data/spec/extensions/update_refresh_spec.rb +1 -1
- data/spec/extensions/uuid_spec.rb +10 -12
- data/spec/extensions/validate_associated_spec.rb +1 -1
- data/spec/extensions/validation_class_methods_spec.rb +3 -3
- data/spec/extensions/validation_contexts_spec.rb +1 -1
- data/spec/extensions/validation_helpers_spec.rb +10 -44
- data/spec/extensions/whitelist_security_spec.rb +5 -5
- data/spec/extensions/xml_serializer_spec.rb +3 -3
- data/spec/guards_helper.rb +2 -1
- data/spec/integration/associations_test.rb +1 -23
- data/spec/integration/database_test.rb +7 -7
- data/spec/integration/dataset_test.rb +5 -47
- data/spec/integration/eager_loader_test.rb +1 -1
- data/spec/integration/migrator_test.rb +1 -1
- data/spec/integration/model_test.rb +4 -82
- data/spec/integration/plugin_test.rb +6 -22
- data/spec/integration/prepared_statement_test.rb +8 -88
- data/spec/integration/schema_test.rb +6 -6
- data/spec/integration/spec_helper.rb +13 -21
- data/spec/integration/timezone_test.rb +5 -5
- data/spec/integration/transaction_test.rb +3 -55
- data/spec/integration/type_test.rb +9 -9
- data/spec/model/association_reflection_spec.rb +24 -9
- data/spec/model/associations_spec.rb +124 -303
- data/spec/model/base_spec.rb +18 -137
- data/spec/model/class_dataset_methods_spec.rb +2 -20
- data/spec/model/dataset_methods_spec.rb +1 -20
- data/spec/model/eager_loading_spec.rb +17 -11
- data/spec/model/hooks_spec.rb +5 -300
- data/spec/model/inflector_spec.rb +1 -1
- data/spec/model/model_spec.rb +15 -320
- data/spec/model/plugins_spec.rb +2 -16
- data/spec/model/record_spec.rb +29 -121
- data/spec/model/spec_helper.rb +5 -15
- data/spec/model/validations_spec.rb +1 -1
- data/spec/sequel_warning.rb +1 -12
- metadata +8 -64
- data/doc/active_record.rdoc +0 -927
- data/lib/sequel/adapters/cubrid.rb +0 -160
- data/lib/sequel/adapters/do.rb +0 -166
- data/lib/sequel/adapters/do/mysql.rb +0 -69
- data/lib/sequel/adapters/do/postgres.rb +0 -46
- data/lib/sequel/adapters/do/sqlite3.rb +0 -41
- data/lib/sequel/adapters/jdbc/as400.rb +0 -92
- data/lib/sequel/adapters/jdbc/cubrid.rb +0 -65
- data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -37
- data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -34
- data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -34
- data/lib/sequel/adapters/odbc/progress.rb +0 -12
- data/lib/sequel/adapters/shared/cubrid.rb +0 -245
- data/lib/sequel/adapters/shared/firebird.rb +0 -261
- data/lib/sequel/adapters/shared/informix.rb +0 -63
- data/lib/sequel/adapters/shared/progress.rb +0 -40
- data/lib/sequel/adapters/swift.rb +0 -169
- data/lib/sequel/adapters/swift/mysql.rb +0 -50
- data/lib/sequel/adapters/swift/postgres.rb +0 -49
- data/lib/sequel/adapters/swift/sqlite.rb +0 -48
- data/lib/sequel/adapters/utils/pg_types.rb +0 -4
- data/lib/sequel/dataset/mutation.rb +0 -98
- data/lib/sequel/extensions/_deprecated_identifier_mangling.rb +0 -117
- data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -8
- data/lib/sequel/extensions/filter_having.rb +0 -65
- data/lib/sequel/extensions/hash_aliases.rb +0 -51
- data/lib/sequel/extensions/meta_def.rb +0 -37
- data/lib/sequel/extensions/query_literals.rb +0 -86
- data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -26
- data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -133
- data/lib/sequel/extensions/set_overrides.rb +0 -82
- data/lib/sequel/no_core_ext.rb +0 -4
- data/lib/sequel/plugins/association_autoreloading.rb +0 -11
- data/lib/sequel/plugins/identifier_columns.rb +0 -49
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -11
- data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -90
- data/lib/sequel/plugins/prepared_statements_associations.rb +0 -137
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -71
- data/lib/sequel/plugins/schema.rb +0 -84
- data/lib/sequel/plugins/scissors.rb +0 -37
- data/spec/core/dataset_mutation_spec.rb +0 -253
- data/spec/extensions/_deprecated_identifier_mangling_spec.rb +0 -314
- data/spec/extensions/before_after_save_spec.rb +0 -40
- data/spec/extensions/filter_having_spec.rb +0 -42
- data/spec/extensions/from_block_spec.rb +0 -21
- data/spec/extensions/hash_aliases_spec.rb +0 -26
- data/spec/extensions/identifier_columns_spec.rb +0 -19
- data/spec/extensions/meta_def_spec.rb +0 -35
- data/spec/extensions/no_auto_literal_strings_spec.rb +0 -69
- data/spec/extensions/pg_typecast_on_load_spec.rb +0 -70
- data/spec/extensions/prepared_statements_associations_spec.rb +0 -212
- data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -40
- data/spec/extensions/query_literals_spec.rb +0 -185
- data/spec/extensions/schema_spec.rb +0 -123
- data/spec/extensions/scissors_spec.rb +0 -27
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -118
- data/spec/extensions/set_overrides_spec.rb +0 -75
data/Rakefile
CHANGED
@@ -6,20 +6,15 @@ VERS = lambda do
|
|
6
6
|
require File.expand_path("../lib/sequel/version", __FILE__)
|
7
7
|
Sequel.version
|
8
8
|
end
|
9
|
-
CLEAN.include ["
|
9
|
+
CLEAN.include ["sequel-*.gem", "rdoc", "coverage", "www/public/*.html", "www/public/rdoc*", "spec/bin-sequel-*"]
|
10
10
|
|
11
|
-
# Gem Packaging
|
11
|
+
# Gem Packaging
|
12
12
|
|
13
13
|
desc "Build sequel gem"
|
14
14
|
task :package=>[:clean] do |p|
|
15
15
|
sh %{#{FileUtils::RUBY} -S gem build sequel.gemspec}
|
16
16
|
end
|
17
17
|
|
18
|
-
desc "Publish sequel gem to rubygems.org"
|
19
|
-
task :release=>[:package] do
|
20
|
-
sh %{#{FileUtils::RUBY} -S gem push ./#{NAME}-#{VERS.call}.gem}
|
21
|
-
end
|
22
|
-
|
23
18
|
### Website
|
24
19
|
|
25
20
|
desc "Make local version of website"
|
@@ -29,7 +24,7 @@ end
|
|
29
24
|
|
30
25
|
### RDoc
|
31
26
|
|
32
|
-
RDOC_DEFAULT_OPTS = ["--line-numbers",
|
27
|
+
RDOC_DEFAULT_OPTS = ["--line-numbers", '--title', 'Sequel: The Database Toolkit for Ruby']
|
33
28
|
|
34
29
|
begin
|
35
30
|
# Sequel uses hanna-nouveau for the website RDoc.
|
@@ -38,46 +33,35 @@ begin
|
|
38
33
|
rescue Gem::LoadError
|
39
34
|
end
|
40
35
|
|
41
|
-
|
42
|
-
require "rdoc/task"
|
43
|
-
RDoc::Task
|
44
|
-
rescue LoadError
|
45
|
-
begin
|
46
|
-
require "rake/rdoctask"
|
47
|
-
Rake::RDocTask
|
48
|
-
rescue LoadError, StandardError
|
49
|
-
end
|
50
|
-
end
|
36
|
+
require "rdoc/task"
|
51
37
|
|
52
|
-
|
53
|
-
RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc']
|
38
|
+
RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc']
|
54
39
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
40
|
+
RDoc::Task.new do |rdoc|
|
41
|
+
rdoc.rdoc_dir = "rdoc"
|
42
|
+
rdoc.options += RDOC_OPTS
|
43
|
+
rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/**/*.rb doc/*.rdoc doc/release_notes/*.txt"
|
44
|
+
end
|
60
45
|
|
61
|
-
|
62
|
-
|
46
|
+
desc "Make rdoc for website"
|
47
|
+
task :website_rdoc=>[:website_rdoc_main, :website_rdoc_adapters, :website_rdoc_plugins]
|
63
48
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
49
|
+
RDoc::Task.new(:website_rdoc_main) do |rdoc|
|
50
|
+
rdoc.rdoc_dir = "www/public/rdoc"
|
51
|
+
rdoc.options += RDOC_OPTS + %w'--no-ignore-invalid'
|
52
|
+
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"
|
53
|
+
end
|
69
54
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
55
|
+
RDoc::Task.new(:website_rdoc_adapters) do |rdoc|
|
56
|
+
rdoc.rdoc_dir = "www/public/rdoc-adapters"
|
57
|
+
rdoc.options += RDOC_DEFAULT_OPTS + %w'--main Sequel --no-ignore-invalid'
|
58
|
+
rdoc.rdoc_files.add %w"lib/sequel/adapters/**/*.rb"
|
59
|
+
end
|
75
60
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
61
|
+
RDoc::Task.new(:website_rdoc_plugins) do |rdoc|
|
62
|
+
rdoc.rdoc_dir = "www/public/rdoc-plugins"
|
63
|
+
rdoc.options += RDOC_DEFAULT_OPTS + %w'--main Sequel --no-ignore-invalid'
|
64
|
+
rdoc.rdoc_files.add %w"lib/sequel/{extensions,plugins}/**/*.rb doc/core_*"
|
81
65
|
end
|
82
66
|
|
83
67
|
### Specs
|
data/bin/sequel
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen-string-literal: true
|
3
3
|
|
4
|
-
require 'rubygems'
|
5
4
|
require 'optparse'
|
6
5
|
|
7
6
|
code = nil
|
@@ -84,7 +83,7 @@ options = OptionParser.new do |opts|
|
|
84
83
|
end
|
85
84
|
|
86
85
|
opts.on("-M", "--migrate-version VER", "migrate the database to version given") do |v|
|
87
|
-
migrate_ver = Integer(v)
|
86
|
+
migrate_ver = Integer(v, 10)
|
88
87
|
end
|
89
88
|
|
90
89
|
opts.on("-N", "--no-test-connection", "do not test the connection") do
|
@@ -133,12 +132,11 @@ connect_proc = lambda do |database|
|
|
133
132
|
db_config = YAML.load_file(database)
|
134
133
|
db_config = db_config[env] || db_config[env.to_sym] || db_config
|
135
134
|
db_config.keys.each{|k| db_config[k.to_sym] = db_config.delete(k)}
|
136
|
-
Sequel.connect(db_config)
|
135
|
+
Sequel.connect(db_config, :test=>test)
|
137
136
|
else
|
138
|
-
Sequel.connect(database)
|
137
|
+
Sequel.connect(database, :test=>test)
|
139
138
|
end
|
140
139
|
db.loggers = loggers
|
141
|
-
db.test_connection if test
|
142
140
|
db
|
143
141
|
end
|
144
142
|
|
@@ -1,19 +1,22 @@
|
|
1
1
|
= Advanced Associations
|
2
2
|
|
3
|
-
Sequel::Model
|
3
|
+
Sequel::Model's association support is very powerful and flexible, but using that power and flexibility
|
4
|
+
often results in complexity.
|
5
|
+
|
6
|
+
You should probably review the {Model Associations Basics and Options guide}[rdoc-ref:doc/association_basics.rdoc]
|
7
|
+
before reviewing this guide.
|
4
8
|
|
5
9
|
== Background: Sequel::Model association options
|
6
10
|
|
7
11
|
There are a bunch of advanced association options that are available to
|
8
|
-
handle more complex cases. First we'll go over some of
|
9
|
-
the simpler ones:
|
12
|
+
handle more complex cases. First we'll go over some of the simpler ones:
|
10
13
|
|
11
14
|
All associations take a block that can be used to further filter/modify the
|
12
15
|
default dataset. There's also an :eager_block option if you want to use
|
13
16
|
a different block when eager loading via <tt>Dataset#eager</tt>. Association blocks are
|
14
17
|
useful for things like:
|
15
18
|
|
16
|
-
Artist.one_to_many :gold_albums, :
|
19
|
+
Artist.one_to_many :gold_albums, class: :Album do |ds|
|
17
20
|
ds.where{copies_sold > 500000}
|
18
21
|
end
|
19
22
|
|
@@ -42,24 +45,23 @@ These can be used like this:
|
|
42
45
|
|
43
46
|
# Makes Artist.eager_graph(:required_albums).all not return artists that
|
44
47
|
# don't have any albums
|
45
|
-
Artist.one_to_many :required_albums, :
|
48
|
+
Artist.one_to_many :required_albums, class: :Album, graph_join_type: :inner
|
46
49
|
|
47
50
|
# Makes sure all returned albums have the active flag set
|
48
|
-
Artist.one_to_many :active_albums, :
|
49
|
-
:graph_conditions=>{:active=>true}
|
51
|
+
Artist.one_to_many :active_albums, class: :Album, graph_conditions: {active: true}
|
50
52
|
|
51
53
|
# Only returns albums that have sold more than 500,000 copies
|
52
|
-
Artist.one_to_many :gold_albums, :
|
53
|
-
:
|
54
|
+
Artist.one_to_many :gold_albums, class: :Album,
|
55
|
+
graph_block: proc{|j,lj,js| Sequel[j][:copies_sold] > 500000}
|
54
56
|
|
55
57
|
# Handles the case where the tables are associated by a case insensitive name string
|
56
|
-
Artist.one_to_many :albums, :
|
57
|
-
:
|
58
|
-
:
|
58
|
+
Artist.one_to_many :albums, key: :artist_name,
|
59
|
+
graph_only_conditions: nil,
|
60
|
+
graph_block: proc{|j,lj,js| {Sequel.function(:lower, Sequel[j][:artist_name])=>Sequel.function(:lower, Sequel[lj][:name])}}
|
59
61
|
|
60
62
|
# Handles the case where both key columns have the name artist_name, and you want to use
|
61
63
|
# a JOIN USING
|
62
|
-
Artist.one_to_many :albums, :
|
64
|
+
Artist.one_to_many :albums, key: :artist_name, graph_only_conditions: [:artist_name]
|
63
65
|
|
64
66
|
Remember, using +eager_graph+ is generally only necessary when you need to
|
65
67
|
filter/order based on columns in an associated table, it is recommended to
|
@@ -244,7 +246,7 @@ loading.
|
|
244
246
|
|
245
247
|
Sequel supports specifying limits and/or offsets for associations:
|
246
248
|
|
247
|
-
Artist.one_to_many :first_10_albums, :
|
249
|
+
Artist.one_to_many :first_10_albums, class: :Album, order: :release_date, limit: 10
|
248
250
|
|
249
251
|
For retrieving the associated objects for a single object, this just uses
|
250
252
|
a LIMIT:
|
@@ -260,7 +262,7 @@ approach. Sequel has 4 separate strategies for dealing with such cases.
|
|
260
262
|
The default strategy used on all databases is a UNION-based approach, which
|
261
263
|
will submit multiple subqueries in a UNION query:
|
262
264
|
|
263
|
-
Artist.where(:
|
265
|
+
Artist.where(id: [1,2]).eager(:first_10_albums).all
|
264
266
|
# SELECT * FROM (SELECT * FROM albums WHERE (artist_id = 1) LIMIT 10) UNION ALL
|
265
267
|
# SELECT * FROM (SELECT * FROM albums WHERE (artist_id = 2) LIMIT 10)
|
266
268
|
|
@@ -272,9 +274,9 @@ index, you'll want to manually specify the :eager_limit_strategy option as shown
|
|
272
274
|
On PostgreSQL, for *_one associations that don't use an offset, you can
|
273
275
|
choose to use a the distinct on strategy:
|
274
276
|
|
275
|
-
Artist.one_to_one :first_album, :
|
276
|
-
:
|
277
|
-
Artist.where(:
|
277
|
+
Artist.one_to_one :first_album, class: :Album, order: :release_date,
|
278
|
+
eager_limit_strategy: :distinct_on
|
279
|
+
Artist.where(id: [1,2]).eager(:first_album).all
|
278
280
|
# SELECT DISTINCT ON (albums.artist_id) *
|
279
281
|
# FROM albums
|
280
282
|
# WHERE (albums.artist_id IN (1, 2))
|
@@ -283,8 +285,8 @@ choose to use a the distinct on strategy:
|
|
283
285
|
Otherwise, if the database supports window functions, you can choose to use
|
284
286
|
the window function strategy:
|
285
287
|
|
286
|
-
Artist.one_to_many :first_10_albums, :
|
287
|
-
:
|
288
|
+
Artist.one_to_many :first_10_albums, class: :Album, order: :release_date, limit: 10,
|
289
|
+
eager_limit_strategy: :window_function
|
288
290
|
Artist.where(:id=>[1,2]).eager(:first_10_albums).all
|
289
291
|
# SELECT * FROM (
|
290
292
|
# SELECT *, row_number() OVER (PARTITION BY albums.artist_id ORDER BY release_date) AS x_sequel_row_number_x
|
@@ -304,7 +306,7 @@ known at the time of the association definition), Sequel supports an
|
|
304
306
|
:eager_limit dataset option that can be defined in an eager loading callback:
|
305
307
|
|
306
308
|
Artist.one_to_many :albums
|
307
|
-
Artist.where(:
|
309
|
+
Artist.where(id: [1, 2]).eager(albums: lambda{|ds| ds.order(:release_date).clone(eager_limit: 3)}).all
|
308
310
|
# SELECT * FROM (
|
309
311
|
# SELECT *, row_number() OVER (PARTITION BY albums.artist_id ORDER BY release_date) AS x_sequel_row_number_x
|
310
312
|
# FROM albums
|
@@ -314,7 +316,7 @@ known at the time of the association definition), Sequel supports an
|
|
314
316
|
|
315
317
|
You can also customize the :eager_limit_strategy on a case-by-case basis by passing in that option in the same way:
|
316
318
|
|
317
|
-
Artist.where(:
|
319
|
+
Artist.where(id: [1, 2]).eager(albums: lambda{|ds| ds.order(:release_date).clone(eager_limit: 3, eager_limit_strategy: :ruby)}).all
|
318
320
|
# SELECT * FROM albums WHERE (albums.artist_id IN (1, 2)) ORDER BY release_date
|
319
321
|
|
320
322
|
The :eager_limit and :eager_limit_strategy options currently only work when
|
@@ -333,7 +335,7 @@ eager_graph_with_options method with the :limit_strategy option.
|
|
333
335
|
The :distinct_on strategy uses DISTINCT ON in a subquery and JOINs that
|
334
336
|
subquery:
|
335
337
|
|
336
|
-
Artist.eager_graph_with_options(:first_album, :
|
338
|
+
Artist.eager_graph_with_options(:first_album, limit_strategy: :distinct_on).all
|
337
339
|
# SELECT artists.id, artists.name, first_album.id AS first_album_id,
|
338
340
|
# first_album.name AS first_album_name, first_album.artist_id,
|
339
341
|
# first_album.release_date
|
@@ -347,7 +349,7 @@ subquery:
|
|
347
349
|
The :window_function approach JOINs to a nested subquery using a window
|
348
350
|
function:
|
349
351
|
|
350
|
-
Artist.eager_graph_with_options(:first_10_albums, :
|
352
|
+
Artist.eager_graph_with_options(:first_10_albums, limit_strategy: :window_function).all
|
351
353
|
# SELECT artists.id, artists.name, first_10_albums.id AS first_10_albums_id,
|
352
354
|
# first_10_albums.name AS first_10_albums_name, first_10_albums.artist_id,
|
353
355
|
# first_10_albums.release_date
|
@@ -363,7 +365,7 @@ function:
|
|
363
365
|
The :correlated_subquery approach JOINs to a nested subquery using a correlated
|
364
366
|
subquery:
|
365
367
|
|
366
|
-
Artist.eager_graph_with_options(:first_10_albums, :limit_strategy
|
368
|
+
Artist.eager_graph_with_options(:first_10_albums, :limit_strategy=>:correlated_subquery).all
|
367
369
|
# SELECT artists.id, artists.name, first_10_albums.id AS first_10_albums_id,
|
368
370
|
# first_10_albums.name AS first_10_albums_name, first_10_albums.artist_id,
|
369
371
|
# first_10_albums.release_date
|
@@ -380,8 +382,6 @@ subquery:
|
|
380
382
|
# )
|
381
383
|
# ) AS first_10_albums ON (first_10_albums.artist_id = artists.id)
|
382
384
|
|
383
|
-
(SELECT * FROM tracks WHERE (tracks.id IN (SELECT t1.id FROM tracks AS t1 WHERE (t1.album_id = tracks.album_id) LIMIT 1)))
|
384
|
-
|
385
385
|
The reason that Sequel does not automatically use the :distinct_on, :window function
|
386
386
|
or :correlated_subquery strategy for eager_graph is that it can perform much worse than the
|
387
387
|
default of just doing the array slicing in ruby. If you are only using eager_graph to
|
@@ -403,7 +403,7 @@ strategy based on the database you are using, and you can override it using the
|
|
403
403
|
|
404
404
|
The :distinct_on strategy:
|
405
405
|
|
406
|
-
Artist.where(:
|
406
|
+
Artist.where(first_album: Album[1]).all
|
407
407
|
# SELECT *
|
408
408
|
# FROM artists
|
409
409
|
# WHERE (artists.id IN (
|
@@ -417,7 +417,7 @@ The :distinct_on strategy:
|
|
417
417
|
|
418
418
|
The :window_function strategy:
|
419
419
|
|
420
|
-
Artist.where(:
|
420
|
+
Artist.where(first_10_albums: Album[1]).all
|
421
421
|
# SELECT *
|
422
422
|
# FROM artists
|
423
423
|
# WHERE (artists.id IN (
|
@@ -433,7 +433,7 @@ The :window_function strategy:
|
|
433
433
|
|
434
434
|
The :correlated_subquery strategy:
|
435
435
|
|
436
|
-
Artist.where(:
|
436
|
+
Artist.where(first_10_albums: Album[1]).all
|
437
437
|
# SELECT *
|
438
438
|
# FROM artists
|
439
439
|
# WHERE (artists.id IN (
|
@@ -447,11 +447,11 @@ The :correlated_subquery strategy:
|
|
447
447
|
# LIMIT 1
|
448
448
|
# )) AND (albums.id = 1))))
|
449
449
|
|
450
|
-
Note that filtering by limited associations does not work on MySQL, as does not support
|
450
|
+
Note that filtering by limited associations does not work on MySQL, as MySQL does not support
|
451
451
|
any of the strategies. It's also not supported when using composite keys on databases
|
452
452
|
that don't support window functions and don't support multiple columns in IN.
|
453
453
|
|
454
|
-
|
454
|
+
=== Additional Association Types
|
455
455
|
|
456
456
|
While the above examples for limited associations showed one_to_many and one_to_one associations,
|
457
457
|
it's just because those are the simplest examples. Sequel supports all of the same features for
|
@@ -459,29 +459,7 @@ many_to_many and one_through_one associations that are enabled by default, as we
|
|
459
459
|
many_through_many and one_through_many associations that are added by the many_through_many
|
460
460
|
plugin.
|
461
461
|
|
462
|
-
==
|
463
|
-
|
464
|
-
Sequel supports all of associations that ActiveRecord supports, though some
|
465
|
-
require different approaches or custom <tt>:eager_loader</tt> options.
|
466
|
-
|
467
|
-
=== Association callbacks
|
468
|
-
|
469
|
-
Sequel supports the same callbacks that ActiveRecord does for +one_to_many+ and
|
470
|
-
+many_to_many+ associations: <tt>:before_add</tt>, <tt>:before_remove</tt>, <tt>:after_add</tt>, and
|
471
|
-
<tt>:after_remove</tt>. For +many_to_one+ associations and +one_to_one+ associations, Sequel
|
472
|
-
supports the <tt>:before_set</tt> and <tt>:after_set</tt> callbacks. On all associations,
|
473
|
-
Sequel supports <tt>:after_load</tt>, which is called after the association has been
|
474
|
-
loaded.
|
475
|
-
|
476
|
-
Each of these options can be a symbol specifying an instance method
|
477
|
-
that takes one argument (the associated object), or a proc that takes
|
478
|
-
two arguments (the current object and the associated object), or an
|
479
|
-
array of symbols and procs. For <tt>:after_load</tt> with a *_to_many association,
|
480
|
-
the associated object argument is an array of associated objects.
|
481
|
-
|
482
|
-
If any of the before callbacks return +false+, the adding/removing
|
483
|
-
does not happen and it either raises a <tt>Sequel::HookFailed</tt> (the default), or
|
484
|
-
returns false (if +raise_on_save_failure+ is false).
|
462
|
+
== More advanced association examples
|
485
463
|
|
486
464
|
=== Association extensions
|
487
465
|
|
@@ -504,36 +482,21 @@ the model object that created the association dataset via the dataset's
|
|
504
482
|
end
|
505
483
|
end
|
506
484
|
class Author < Sequel::Model
|
507
|
-
one_to_many :authorships, :
|
508
|
-
end
|
509
|
-
Author.first.authorships_dataset.find_or_create(:name=>'Blah', :number=>10)
|
510
|
-
|
511
|
-
=== <tt>has_many :through</tt> associations
|
512
|
-
|
513
|
-
+many_to_many+ handles the usual case of a <tt>has_many :through</tt> with a +belongs_to+ in
|
514
|
-
the associated model. It doesn't break on the case where the join table is a
|
515
|
-
model table, unlike ActiveRecord's +has_and_belongs_to_many+.
|
516
|
-
|
517
|
-
ActiveRecord:
|
518
|
-
|
519
|
-
class Author < ActiveRecord::Base
|
520
|
-
has_many :authorships
|
521
|
-
has_many :books, :through => :authorships
|
485
|
+
one_to_many :authorships, extend: FindOrCreate
|
522
486
|
end
|
487
|
+
Author.first.authorships_dataset.find_or_create(name: 'Blah', number: 10)
|
523
488
|
|
524
|
-
|
525
|
-
belongs_to :author
|
526
|
-
belongs_to :book
|
527
|
-
end
|
489
|
+
=== many_to_many associations through model tables
|
528
490
|
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
491
|
+
The many_to_many association can be used even when the join table is a table used for a
|
492
|
+
model. The only requirement is the join table has foreign keys to both the current
|
493
|
+
model and the associated model. Anytime there is a one_to_many association from model A to
|
494
|
+
model B, and model B has a many_to_one association to model C, you can use a many_to_many
|
495
|
+
association from model A to model C.
|
533
496
|
|
534
497
|
class Author < Sequel::Model
|
535
498
|
one_to_many :authorships
|
536
|
-
many_to_many :books, :
|
499
|
+
many_to_many :books, join_table: :authorships
|
537
500
|
end
|
538
501
|
|
539
502
|
class Authorship < Sequel::Model
|
@@ -544,33 +507,17 @@ Sequel::Model:
|
|
544
507
|
@author = Author.first
|
545
508
|
@author.books
|
546
509
|
|
547
|
-
|
548
|
-
you still use a +many_to_many+ association, but you need to use some options:
|
549
|
-
|
550
|
-
ActiveRecord:
|
551
|
-
|
552
|
-
class Firm < ActiveRecord::Base
|
553
|
-
has_many :clients
|
554
|
-
has_many :invoices, :through => :clients
|
555
|
-
end
|
510
|
+
=== many_to_many for three-level associations
|
556
511
|
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
class Invoice < ActiveRecord::Base
|
563
|
-
belongs_to :client
|
564
|
-
has_one :firm, :through => :client
|
565
|
-
end
|
566
|
-
|
567
|
-
Firm.find(:first).invoices
|
568
|
-
|
569
|
-
Sequel::Model:
|
512
|
+
You can even use a many_to_many association between model A and model C if model A has a
|
513
|
+
one_to_many association to model B, and model B has a one_to_many association to model C.
|
514
|
+
You just need to use the appropriate :right_key and :right_primary_key options. And in
|
515
|
+
the reverse direction from model C to model A, you can use a one_through_one association
|
516
|
+
using the :left_key and :left_primary_key options.
|
570
517
|
|
571
518
|
class Firm < Sequel::Model
|
572
519
|
one_to_many :clients
|
573
|
-
many_to_many :invoices, :
|
520
|
+
many_to_many :invoices, join_table: :clients, right_key: :id, right_primary_key: :client_id
|
574
521
|
end
|
575
522
|
|
576
523
|
class Client < Sequel::Model
|
@@ -580,12 +527,13 @@ Sequel::Model:
|
|
580
527
|
|
581
528
|
class Invoice < Sequel::Model
|
582
529
|
many_to_one :client
|
583
|
-
one_through_one :firm, :
|
530
|
+
one_through_one :firm, join_table: :clients, left_key: :id, left_primary_key: :client_id
|
584
531
|
end
|
585
532
|
|
586
533
|
Firm.first.invoices
|
534
|
+
Invoice.first.firm
|
587
535
|
|
588
|
-
To handle cases where there are multiple join tables, use the many_through_many
|
536
|
+
To handle cases where there are multiple join tables, you can use the many_through_many
|
589
537
|
plugin that ships with Sequel.
|
590
538
|
|
591
539
|
=== Polymorphic Associations
|
@@ -602,38 +550,19 @@ you are stuck with an existing design that uses them.
|
|
602
550
|
If you must use them, look for the sequel_polymorphic external plugin, as it makes using
|
603
551
|
polymorphic associations in Sequel about as easy as it is in ActiveRecord. However,
|
604
552
|
here's how they can be done using Sequel's custom associations (the sequel_polymorphic
|
605
|
-
plugin is just a generic version of this code):
|
606
|
-
|
607
|
-
ActiveRecord:
|
608
|
-
|
609
|
-
class Asset < ActiveRecord::Base
|
610
|
-
belongs_to :attachable, :polymorphic => true
|
611
|
-
end
|
612
|
-
|
613
|
-
class Post < ActiveRecord::Base
|
614
|
-
has_many :assets, :as => :attachable
|
615
|
-
end
|
616
|
-
|
617
|
-
class Note < ActiveRecord::Base
|
618
|
-
has_many :assets, :as => :attachable
|
619
|
-
end
|
620
|
-
|
621
|
-
@asset.attachable = @post
|
622
|
-
@asset.attachable = @note
|
623
|
-
|
624
|
-
Sequel::Model:
|
553
|
+
external plugin is just a generic version of this code):
|
625
554
|
|
626
555
|
class Asset < Sequel::Model
|
627
|
-
many_to_one :attachable, :
|
628
|
-
:
|
556
|
+
many_to_one :attachable, reciprocal: :assets,
|
557
|
+
setter: (lambda do |attachable|
|
629
558
|
self[:attachable_id] = (attachable.pk if attachable)
|
630
559
|
self[:attachable_type] = (attachable.class.name if attachable)
|
631
560
|
end),
|
632
|
-
:
|
561
|
+
dataset: (proc do
|
633
562
|
klass = attachable_type.constantize
|
634
563
|
klass.where(klass.primary_key=>attachable_id)
|
635
564
|
end),
|
636
|
-
:
|
565
|
+
eager_loader: (lambda do |eo|
|
637
566
|
id_map = {}
|
638
567
|
eo[:rows].each do |asset|
|
639
568
|
asset.associations[:attachable] = nil
|
@@ -651,24 +580,22 @@ Sequel::Model:
|
|
651
580
|
end
|
652
581
|
|
653
582
|
class Post < Sequel::Model
|
654
|
-
one_to_many :assets, :
|
655
|
-
:
|
656
|
-
:
|
657
|
-
:
|
583
|
+
one_to_many :assets, key: :attachable_id, reciprocal: :attachable, conditions: {attachable_type: 'Post'},
|
584
|
+
adder: lambda{|asset| asset.update(attachable_id: pk, attachable_type: 'Post')},
|
585
|
+
remover: lambda{|asset| asset.update(attachable_id: nil, attachable_type: nil)},
|
586
|
+
clearer: lambda{assets_dataset.update(attachable_id: nil, attachable_type: nil)}
|
658
587
|
end
|
659
588
|
|
660
589
|
class Note < Sequel::Model
|
661
|
-
one_to_many :assets, :
|
662
|
-
:
|
663
|
-
:
|
664
|
-
:
|
590
|
+
one_to_many :assets, key: :attachable_id, reciprocal: :attachable, conditions: {attachable_type: 'Note'},
|
591
|
+
adder: lambda{|asset| asset.update(attachable_id: pk, attachable_type: 'Note')},
|
592
|
+
remover: lambda{|asset| asset.update(attachable_id: nil, attachable_type: nil)},
|
593
|
+
clearer: lambda{assets_dataset.update(attachable_id: nil, attachable_type: nil)}
|
665
594
|
end
|
666
595
|
|
667
596
|
@asset.attachable = @post
|
668
597
|
@asset.attachable = @note
|
669
598
|
|
670
|
-
== Other advanced associations
|
671
|
-
|
672
599
|
=== Joining on multiple keys
|
673
600
|
|
674
601
|
Let's say you have two tables that are associated with each other with multiple
|
@@ -680,10 +607,10 @@ associations:
|
|
680
607
|
# associated favorite track
|
681
608
|
|
682
609
|
class Track < Sequel::Model
|
683
|
-
many_to_one :favorite_track, :
|
610
|
+
many_to_one :favorite_track, key: [:disc_number, :number, :album_id], primary_key: [:disc_number, :number, :album_id]
|
684
611
|
end
|
685
612
|
class FavoriteTrack < Sequel::Model
|
686
|
-
one_to_one :tracks, :
|
613
|
+
one_to_one :tracks, key: [:disc_number, :number, :album_id], primary_key: [:disc_number, :number, :album_id]
|
687
614
|
end
|
688
615
|
|
689
616
|
=== Tree - All Ancestors and Descendents
|
@@ -692,24 +619,24 @@ Let's say you want to store a tree relationship in your database, it's pretty
|
|
692
619
|
simple:
|
693
620
|
|
694
621
|
class Node < Sequel::Model
|
695
|
-
many_to_one :parent, :
|
696
|
-
one_to_many :children, :
|
622
|
+
many_to_one :parent, class: self
|
623
|
+
one_to_many :children, key: :parent_id, class: self
|
697
624
|
end
|
698
625
|
|
699
626
|
You can easily get a node's parent with node.parent, and a node's children with
|
700
627
|
node.children. You can even eager load the relationship up to a certain depth:
|
701
628
|
|
702
629
|
# Eager load three generations of generations of children for a given node
|
703
|
-
Node.where(:
|
630
|
+
Node.where(id: 1).eager(children: {children: :children}).all.first
|
704
631
|
# Load parents and grandparents for a group of nodes
|
705
|
-
Node.where{id < 10}.eager(:parent
|
632
|
+
Node.where{id < 10}.eager(parent: :parent).all
|
706
633
|
|
707
634
|
What if you want to get all ancestors up to the root node, or all descendents,
|
708
635
|
without knowing the depth of the tree?
|
709
636
|
|
710
637
|
class Node < Sequel::Model
|
711
|
-
many_to_one :ancestors, :
|
712
|
-
:
|
638
|
+
many_to_one :ancestors, class: self,
|
639
|
+
eager_loader: (lambda do |eo|
|
713
640
|
# Handle cases where the root node has the same parent_id as primary_key
|
714
641
|
# and also when it is NULL
|
715
642
|
non_root_nodes = eo[:rows].reject do |n|
|
@@ -727,13 +654,13 @@ without knowing the depth of the tree?
|
|
727
654
|
non_root_nodes.each{|n| (id_map[n.parent_id] ||= []) << n}
|
728
655
|
# Doesn't cause an infinte loop, because when only the root node
|
729
656
|
# is left, this is not called.
|
730
|
-
Node.where(
|
657
|
+
Node.where(id: id_map.keys).eager(:ancestors).all do |node|
|
731
658
|
# Populate the parent association for each node
|
732
659
|
id_map[node.pk].each{|n| n.associations[:parent] = node}
|
733
660
|
end
|
734
661
|
end
|
735
662
|
end)
|
736
|
-
many_to_one :descendants, :
|
663
|
+
many_to_one :descendants, eager_loader: (lambda do |eo|
|
737
664
|
id_map = {}
|
738
665
|
eo[:rows].each do |n|
|
739
666
|
# Initialize an empty array of child associations for each parent node
|
@@ -745,7 +672,7 @@ without knowing the depth of the tree?
|
|
745
672
|
# if no records are returned. Exclude id = parent_id to avoid infinite loop
|
746
673
|
# if the root note is one of the returned records and it has parent_id = id
|
747
674
|
# instead of parent_id = NULL.
|
748
|
-
Node.where(:
|
675
|
+
Node.where(parent_id: id_map.keys).exclude(id: :parent_id).eager(:descendants).all do |node|
|
749
676
|
# Get the parent from the identity map
|
750
677
|
parent = id_map[node.parent_id]
|
751
678
|
# Set the child's parent association to the parent
|
@@ -756,11 +683,7 @@ without knowing the depth of the tree?
|
|
756
683
|
end)
|
757
684
|
end
|
758
685
|
|
759
|
-
Note that
|
760
|
-
The results are not the same as in the above case, as all descendents are stored in a single association,
|
761
|
-
but all descendants can be both lazy loaded or eager loaded in a single query (assuming your database
|
762
|
-
supports recursive common table expressions). Sequel ships with an +rcte_tree+ plugin that makes
|
763
|
-
this easy:
|
686
|
+
Note that Sequel ships with an rcte_tree plugin that does all of the above and more:
|
764
687
|
|
765
688
|
class Node < Sequel::Model
|
766
689
|
plugin :rcte_tree
|
@@ -780,15 +703,15 @@ What you want to do is get all songs for a given artist, ordered by the song's
|
|
780
703
|
name, with no duplicates?
|
781
704
|
|
782
705
|
class Artist < Sequel::Model
|
783
|
-
one_to_many :songs, :
|
784
|
-
:
|
785
|
-
:
|
706
|
+
one_to_many :songs, order: Sequel[:songs][:name],
|
707
|
+
dataset: proc{Song.select_all(:songs).join(:lyrics, id: :lyric_id, id=>[:composer_id, :arranger_id, :vocalist_id, :lyricist_id])},
|
708
|
+
eager_loader: (lambda do |eo|
|
786
709
|
h = eo[:id_map]
|
787
710
|
ids = h.keys
|
788
711
|
eo[:rows].each{|r| r.associations[:songs] = []}
|
789
712
|
Song.select_all(:songs).
|
790
713
|
select_append{[lyrics[:composer_id], lyrics[:arranger_id], lyrics[:vocalist_id], lyrics[:lyricist_id]}.
|
791
|
-
join(
|
714
|
+
join(:lyrics, id: :lyric_id){Sequel.or(composer_id: ids, arranger_id: ids, vocalist_id: ids, lyricist_id: ids)}.
|
792
715
|
order{songs[:name]}.all do |song|
|
793
716
|
[:composer_id, :arranger_id, :vocalist_id, :lyricist_id].each do |x|
|
794
717
|
recs = h[song.values.delete(x)]
|
@@ -811,11 +734,11 @@ associated tickets.
|
|
811
734
|
|
812
735
|
class Project < Sequel::Model
|
813
736
|
one_to_many :tickets
|
814
|
-
many_to_one :ticket_hours, :
|
815
|
-
:
|
816
|
-
:
|
737
|
+
many_to_one :ticket_hours, read_only: true, key: :id,
|
738
|
+
dataset: proc{Ticket.where(:project_id=>id).select{sum(hours).as(hours)}},
|
739
|
+
eager_loader: (lambda do |eo|
|
817
740
|
eo[:rows].each{|p| p.associations[:ticket_hours] = nil}
|
818
|
-
Ticket.where(:
|
741
|
+
Ticket.where(project_id: eo[:id_map].keys).
|
819
742
|
select_group(:project_id).
|
820
743
|
select_append{sum(hours).as(hours)}.
|
821
744
|
all do |t|
|
@@ -838,4 +761,4 @@ associated tickets.
|
|
838
761
|
end
|
839
762
|
|
840
763
|
Note that it is often better to use a sum cache instead of this approach. You can implement
|
841
|
-
a sum cache using +after_create+ and +after_delete+ hooks, or preferrably using a database trigger.
|
764
|
+
a sum cache using +after_create+, +after_update+, and +after_delete+ hooks, or preferrably using a database trigger.
|