sequel 4.36.0 → 5.61.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 +5 -5
- data/CHANGELOG +548 -5749
- data/MIT-LICENSE +1 -1
- data/README.rdoc +265 -159
- data/bin/sequel +34 -12
- data/doc/advanced_associations.rdoc +228 -187
- data/doc/association_basics.rdoc +281 -291
- data/doc/bin_sequel.rdoc +5 -3
- data/doc/cheat_sheet.rdoc +86 -51
- data/doc/code_order.rdoc +25 -19
- data/doc/core_extensions.rdoc +104 -63
- data/doc/dataset_basics.rdoc +12 -21
- data/doc/dataset_filtering.rdoc +99 -86
- data/doc/extensions.rdoc +3 -10
- data/doc/fork_safety.rdoc +84 -0
- data/doc/mass_assignment.rdoc +74 -31
- data/doc/migration.rdoc +59 -51
- data/doc/model_dataset_method_design.rdoc +129 -0
- data/doc/model_hooks.rdoc +15 -25
- data/doc/model_plugins.rdoc +12 -12
- data/doc/mssql_stored_procedures.rdoc +3 -3
- data/doc/object_model.rdoc +58 -68
- data/doc/opening_databases.rdoc +85 -95
- data/doc/postgresql.rdoc +263 -38
- data/doc/prepared_statements.rdoc +29 -24
- data/doc/querying.rdoc +189 -167
- data/doc/reflection.rdoc +5 -6
- data/doc/release_notes/5.0.0.txt +159 -0
- data/doc/release_notes/5.1.0.txt +31 -0
- data/doc/release_notes/5.10.0.txt +84 -0
- data/doc/release_notes/5.11.0.txt +83 -0
- data/doc/release_notes/5.12.0.txt +141 -0
- data/doc/release_notes/5.13.0.txt +27 -0
- data/doc/release_notes/5.14.0.txt +63 -0
- data/doc/release_notes/5.15.0.txt +39 -0
- data/doc/release_notes/5.16.0.txt +110 -0
- data/doc/release_notes/5.17.0.txt +31 -0
- data/doc/release_notes/5.18.0.txt +69 -0
- data/doc/release_notes/5.19.0.txt +28 -0
- data/doc/release_notes/5.2.0.txt +33 -0
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/release_notes/5.21.0.txt +87 -0
- data/doc/release_notes/5.22.0.txt +48 -0
- data/doc/release_notes/5.23.0.txt +56 -0
- data/doc/release_notes/5.24.0.txt +56 -0
- data/doc/release_notes/5.25.0.txt +32 -0
- data/doc/release_notes/5.26.0.txt +35 -0
- data/doc/release_notes/5.27.0.txt +21 -0
- data/doc/release_notes/5.28.0.txt +16 -0
- data/doc/release_notes/5.29.0.txt +22 -0
- data/doc/release_notes/5.3.0.txt +121 -0
- data/doc/release_notes/5.30.0.txt +20 -0
- data/doc/release_notes/5.31.0.txt +148 -0
- data/doc/release_notes/5.32.0.txt +46 -0
- data/doc/release_notes/5.33.0.txt +24 -0
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/release_notes/5.38.0.txt +28 -0
- data/doc/release_notes/5.39.0.txt +19 -0
- data/doc/release_notes/5.4.0.txt +80 -0
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.5.0.txt +61 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.6.0.txt +31 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.7.0.txt +108 -0
- data/doc/release_notes/5.8.0.txt +170 -0
- data/doc/release_notes/5.9.0.txt +99 -0
- data/doc/schema_modification.rdoc +95 -75
- data/doc/security.rdoc +109 -80
- data/doc/sharding.rdoc +74 -47
- data/doc/sql.rdoc +147 -122
- data/doc/testing.rdoc +43 -20
- data/doc/thread_safety.rdoc +2 -4
- data/doc/transactions.rdoc +97 -18
- data/doc/validations.rdoc +52 -50
- data/doc/virtual_rows.rdoc +90 -109
- data/lib/sequel/adapters/ado/access.rb +15 -17
- data/lib/sequel/adapters/ado/mssql.rb +6 -15
- data/lib/sequel/adapters/ado.rb +150 -20
- data/lib/sequel/adapters/amalgalite.rb +11 -23
- data/lib/sequel/adapters/ibmdb.rb +47 -55
- data/lib/sequel/adapters/jdbc/db2.rb +29 -39
- data/lib/sequel/adapters/jdbc/derby.rb +58 -54
- data/lib/sequel/adapters/jdbc/h2.rb +93 -35
- data/lib/sequel/adapters/jdbc/hsqldb.rb +24 -31
- data/lib/sequel/adapters/jdbc/jtds.rb +2 -10
- data/lib/sequel/adapters/jdbc/mssql.rb +3 -11
- data/lib/sequel/adapters/jdbc/mysql.rb +17 -20
- data/lib/sequel/adapters/jdbc/oracle.rb +22 -18
- data/lib/sequel/adapters/jdbc/postgresql.rb +69 -71
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +11 -23
- data/lib/sequel/adapters/jdbc/sqlite.rb +47 -11
- data/lib/sequel/adapters/jdbc/sqlserver.rb +34 -9
- data/lib/sequel/adapters/jdbc/transactions.rb +22 -38
- data/lib/sequel/adapters/jdbc.rb +145 -130
- data/lib/sequel/adapters/mock.rb +100 -111
- data/lib/sequel/adapters/mysql.rb +114 -122
- data/lib/sequel/adapters/mysql2.rb +147 -63
- data/lib/sequel/adapters/odbc/db2.rb +1 -1
- data/lib/sequel/adapters/odbc/mssql.rb +8 -14
- data/lib/sequel/adapters/odbc/oracle.rb +11 -0
- data/lib/sequel/adapters/odbc.rb +20 -25
- data/lib/sequel/adapters/oracle.rb +50 -56
- data/lib/sequel/adapters/postgres.rb +305 -327
- data/lib/sequel/adapters/postgresql.rb +1 -1
- data/lib/sequel/adapters/shared/access.rb +74 -78
- data/lib/sequel/adapters/shared/db2.rb +118 -71
- data/lib/sequel/adapters/shared/mssql.rb +301 -220
- data/lib/sequel/adapters/shared/mysql.rb +299 -217
- data/lib/sequel/adapters/shared/oracle.rb +226 -65
- data/lib/sequel/adapters/shared/postgres.rb +935 -395
- data/lib/sequel/adapters/shared/sqlanywhere.rb +105 -126
- data/lib/sequel/adapters/shared/sqlite.rb +447 -173
- data/lib/sequel/adapters/sqlanywhere.rb +48 -35
- data/lib/sequel/adapters/sqlite.rb +156 -111
- data/lib/sequel/adapters/tinytds.rb +30 -38
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +3 -6
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +2 -2
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +87 -0
- data/lib/sequel/adapters/utils/mysql_prepared_statements.rb +56 -0
- data/lib/sequel/adapters/utils/replace.rb +1 -4
- data/lib/sequel/adapters/utils/stored_procedures.rb +7 -22
- data/lib/sequel/adapters/utils/unmodified_identifiers.rb +28 -0
- data/lib/sequel/ast_transformer.rb +17 -89
- data/lib/sequel/connection_pool/sharded_single.rb +18 -15
- data/lib/sequel/connection_pool/sharded_threaded.rb +130 -111
- data/lib/sequel/connection_pool/single.rb +18 -13
- data/lib/sequel/connection_pool/threaded.rb +121 -120
- data/lib/sequel/connection_pool.rb +48 -29
- data/lib/sequel/core.rb +351 -301
- data/lib/sequel/database/connecting.rb +69 -57
- data/lib/sequel/database/dataset.rb +13 -5
- data/lib/sequel/database/dataset_defaults.rb +18 -102
- data/lib/sequel/database/features.rb +18 -4
- data/lib/sequel/database/logging.rb +12 -11
- data/lib/sequel/database/misc.rb +180 -122
- data/lib/sequel/database/query.rb +47 -27
- data/lib/sequel/database/schema_generator.rb +178 -84
- data/lib/sequel/database/schema_methods.rb +172 -97
- data/lib/sequel/database/transactions.rb +205 -44
- data/lib/sequel/database.rb +17 -2
- data/lib/sequel/dataset/actions.rb +339 -155
- data/lib/sequel/dataset/dataset_module.rb +46 -0
- data/lib/sequel/dataset/features.rb +90 -35
- data/lib/sequel/dataset/graph.rb +80 -58
- data/lib/sequel/dataset/misc.rb +137 -47
- data/lib/sequel/dataset/placeholder_literalizer.rb +63 -25
- data/lib/sequel/dataset/prepared_statements.rb +188 -85
- data/lib/sequel/dataset/query.rb +530 -222
- data/lib/sequel/dataset/sql.rb +590 -368
- data/lib/sequel/dataset.rb +26 -16
- data/lib/sequel/deprecated.rb +12 -2
- data/lib/sequel/exceptions.rb +46 -16
- data/lib/sequel/extensions/_model_constraint_validations.rb +16 -0
- data/lib/sequel/extensions/_model_pg_row.rb +43 -0
- data/lib/sequel/extensions/_pretty_table.rb +2 -5
- data/lib/sequel/extensions/any_not_empty.rb +45 -0
- data/lib/sequel/extensions/arbitrary_servers.rb +10 -10
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +74 -0
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/caller_logging.rb +79 -0
- data/lib/sequel/extensions/columns_introspection.rb +4 -3
- data/lib/sequel/extensions/connection_expiration.rb +20 -10
- data/lib/sequel/extensions/connection_validator.rb +11 -10
- data/lib/sequel/extensions/constant_sql_override.rb +65 -0
- data/lib/sequel/extensions/constraint_validations.rb +62 -39
- data/lib/sequel/extensions/core_extensions.rb +42 -48
- data/lib/sequel/extensions/core_refinements.rb +80 -59
- data/lib/sequel/extensions/current_datetime_timestamp.rb +1 -4
- data/lib/sequel/extensions/date_arithmetic.rb +98 -39
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +41 -0
- data/lib/sequel/extensions/duplicate_columns_handler.rb +21 -14
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +2 -2
- data/lib/sequel/extensions/escaped_like.rb +100 -0
- data/lib/sequel/extensions/eval_inspect.rb +12 -15
- data/lib/sequel/extensions/exclude_or_null.rb +68 -0
- data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
- data/lib/sequel/extensions/freeze_datasets.rb +3 -0
- data/lib/sequel/extensions/from_block.rb +1 -34
- data/lib/sequel/extensions/graph_each.rb +4 -4
- data/lib/sequel/extensions/identifier_mangling.rb +180 -0
- data/lib/sequel/extensions/implicit_subquery.rb +48 -0
- data/lib/sequel/extensions/index_caching.rb +109 -0
- data/lib/sequel/extensions/inflector.rb +13 -5
- data/lib/sequel/extensions/integer64.rb +32 -0
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +17 -8
- data/lib/sequel/extensions/migration.rb +119 -78
- data/lib/sequel/extensions/named_timezones.rb +88 -23
- data/lib/sequel/extensions/no_auto_literal_strings.rb +2 -82
- data/lib/sequel/extensions/null_dataset.rb +8 -8
- data/lib/sequel/extensions/pagination.rb +32 -29
- data/lib/sequel/extensions/pg_array.rb +221 -287
- data/lib/sequel/extensions/pg_array_ops.rb +17 -9
- data/lib/sequel/extensions/pg_enum.rb +63 -23
- data/lib/sequel/extensions/pg_extended_date_support.rb +241 -0
- data/lib/sequel/extensions/pg_hstore.rb +45 -54
- data/lib/sequel/extensions/pg_hstore_ops.rb +58 -6
- data/lib/sequel/extensions/pg_inet.rb +31 -12
- data/lib/sequel/extensions/pg_inet_ops.rb +2 -2
- data/lib/sequel/extensions/pg_interval.rb +56 -29
- data/lib/sequel/extensions/pg_json.rb +417 -140
- data/lib/sequel/extensions/pg_json_ops.rb +270 -18
- data/lib/sequel/extensions/pg_loose_count.rb +4 -2
- data/lib/sequel/extensions/pg_multirange.rb +372 -0
- data/lib/sequel/extensions/pg_range.rb +131 -191
- data/lib/sequel/extensions/pg_range_ops.rb +42 -13
- data/lib/sequel/extensions/pg_row.rb +48 -81
- data/lib/sequel/extensions/pg_row_ops.rb +33 -14
- data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
- data/lib/sequel/extensions/pg_timestamptz.rb +28 -0
- data/lib/sequel/extensions/query.rb +9 -7
- data/lib/sequel/extensions/round_timestamps.rb +0 -6
- data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
- data/lib/sequel/extensions/s.rb +60 -0
- data/lib/sequel/extensions/schema_caching.rb +10 -1
- data/lib/sequel/extensions/schema_dumper.rb +71 -48
- data/lib/sequel/extensions/select_remove.rb +4 -4
- data/lib/sequel/extensions/sequel_4_dataset_methods.rb +85 -0
- data/lib/sequel/extensions/server_block.rb +51 -27
- data/lib/sequel/extensions/split_array_nil.rb +4 -4
- data/lib/sequel/extensions/sql_comments.rb +119 -7
- data/lib/sequel/extensions/sql_expr.rb +2 -1
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +11 -8
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +55 -0
- data/lib/sequel/extensions/symbol_aref_refinement.rb +43 -0
- data/lib/sequel/extensions/symbol_as.rb +23 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +37 -0
- data/lib/sequel/extensions/synchronize_sql.rb +45 -0
- data/lib/sequel/extensions/to_dot.rb +10 -4
- data/lib/sequel/extensions/virtual_row_method_block.rb +44 -0
- data/lib/sequel/model/associations.rb +1006 -284
- data/lib/sequel/model/base.rb +560 -805
- data/lib/sequel/model/dataset_module.rb +11 -10
- data/lib/sequel/model/default_inflections.rb +1 -1
- data/lib/sequel/model/errors.rb +10 -3
- data/lib/sequel/model/exceptions.rb +8 -10
- data/lib/sequel/model/inflections.rb +7 -20
- data/lib/sequel/model/plugins.rb +114 -0
- data/lib/sequel/model.rb +32 -82
- data/lib/sequel/plugins/active_model.rb +30 -14
- data/lib/sequel/plugins/after_initialize.rb +1 -1
- data/lib/sequel/plugins/association_dependencies.rb +25 -18
- data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
- data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
- data/lib/sequel/plugins/association_pks.rb +147 -70
- data/lib/sequel/plugins/association_proxies.rb +33 -9
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +95 -28
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/before_after_save.rb +0 -42
- data/lib/sequel/plugins/blacklist_security.rb +21 -12
- data/lib/sequel/plugins/boolean_readers.rb +5 -5
- data/lib/sequel/plugins/boolean_subsets.rb +13 -8
- data/lib/sequel/plugins/caching.rb +25 -16
- data/lib/sequel/plugins/class_table_inheritance.rb +179 -100
- data/lib/sequel/plugins/column_conflicts.rb +16 -3
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/column_select.rb +7 -5
- data/lib/sequel/plugins/columns_updated.rb +42 -0
- data/lib/sequel/plugins/composition.rb +42 -26
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +20 -14
- data/lib/sequel/plugins/csv_serializer.rb +56 -35
- data/lib/sequel/plugins/dataset_associations.rb +40 -17
- data/lib/sequel/plugins/def_dataset_method.rb +90 -0
- data/lib/sequel/plugins/defaults_setter.rb +65 -10
- data/lib/sequel/plugins/delay_add_association.rb +1 -1
- data/lib/sequel/plugins/dirty.rb +62 -24
- data/lib/sequel/plugins/eager_each.rb +3 -3
- data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
- data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/error_splitter.rb +17 -12
- data/lib/sequel/plugins/finder.rb +246 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
- data/lib/sequel/plugins/force_encoding.rb +7 -12
- data/lib/sequel/plugins/hook_class_methods.rb +37 -54
- data/lib/sequel/plugins/input_transformer.rb +18 -10
- data/lib/sequel/plugins/insert_conflict.rb +76 -0
- data/lib/sequel/plugins/insert_returning_select.rb +2 -2
- data/lib/sequel/plugins/instance_filters.rb +10 -8
- data/lib/sequel/plugins/instance_hooks.rb +34 -17
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/inverted_subsets.rb +22 -13
- data/lib/sequel/plugins/json_serializer.rb +124 -64
- data/lib/sequel/plugins/lazy_attributes.rb +21 -14
- data/lib/sequel/plugins/list.rb +35 -21
- data/lib/sequel/plugins/many_through_many.rb +134 -21
- data/lib/sequel/plugins/modification_detection.rb +15 -5
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +6 -5
- data/lib/sequel/plugins/nested_attributes.rb +61 -31
- data/lib/sequel/plugins/optimistic_locking.rb +3 -3
- data/lib/sequel/plugins/pg_array_associations.rb +103 -53
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +350 -0
- data/lib/sequel/plugins/pg_row.rb +5 -51
- data/lib/sequel/plugins/prepared_statements.rb +60 -72
- data/lib/sequel/plugins/prepared_statements_safe.rb +9 -4
- data/lib/sequel/plugins/rcte_tree.rb +68 -82
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +43 -46
- data/lib/sequel/plugins/serialization_modification_detection.rb +3 -2
- data/lib/sequel/plugins/sharding.rb +15 -10
- data/lib/sequel/plugins/single_table_inheritance.rb +67 -28
- data/lib/sequel/plugins/skip_create_refresh.rb +3 -3
- data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
- data/lib/sequel/plugins/split_values.rb +11 -6
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +77 -53
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/string_stripper.rb +3 -3
- data/lib/sequel/plugins/subclasses.rb +43 -10
- data/lib/sequel/plugins/subset_conditions.rb +15 -5
- data/lib/sequel/plugins/table_select.rb +2 -2
- data/lib/sequel/plugins/tactical_eager_loading.rb +96 -12
- data/lib/sequel/plugins/throw_failures.rb +110 -0
- data/lib/sequel/plugins/timestamps.rb +20 -8
- data/lib/sequel/plugins/touch.rb +19 -8
- data/lib/sequel/plugins/tree.rb +62 -32
- data/lib/sequel/plugins/typecast_on_load.rb +12 -4
- data/lib/sequel/plugins/unlimited_update.rb +1 -7
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +4 -4
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/plugins/update_refresh.rb +26 -15
- data/lib/sequel/plugins/uuid.rb +7 -11
- data/lib/sequel/plugins/validate_associated.rb +18 -0
- data/lib/sequel/plugins/validation_class_methods.rb +38 -19
- data/lib/sequel/plugins/validation_contexts.rb +49 -0
- data/lib/sequel/plugins/validation_helpers.rb +57 -41
- data/lib/sequel/plugins/whitelist_security.rb +122 -0
- data/lib/sequel/plugins/xml_serializer.rb +30 -31
- data/lib/sequel/sql.rb +471 -331
- data/lib/sequel/timezones.rb +78 -47
- data/lib/sequel/version.rb +7 -2
- data/lib/sequel.rb +1 -1
- metadata +217 -521
- data/Rakefile +0 -164
- data/doc/active_record.rdoc +0 -928
- data/doc/release_notes/1.0.txt +0 -38
- data/doc/release_notes/1.1.txt +0 -143
- data/doc/release_notes/1.3.txt +0 -101
- data/doc/release_notes/1.4.0.txt +0 -53
- data/doc/release_notes/1.5.0.txt +0 -155
- data/doc/release_notes/2.0.0.txt +0 -298
- data/doc/release_notes/2.1.0.txt +0 -271
- data/doc/release_notes/2.10.0.txt +0 -328
- data/doc/release_notes/2.11.0.txt +0 -215
- data/doc/release_notes/2.12.0.txt +0 -534
- data/doc/release_notes/2.2.0.txt +0 -253
- data/doc/release_notes/2.3.0.txt +0 -88
- data/doc/release_notes/2.4.0.txt +0 -106
- data/doc/release_notes/2.5.0.txt +0 -137
- data/doc/release_notes/2.6.0.txt +0 -157
- data/doc/release_notes/2.7.0.txt +0 -166
- data/doc/release_notes/2.8.0.txt +0 -171
- data/doc/release_notes/2.9.0.txt +0 -97
- data/doc/release_notes/3.0.0.txt +0 -221
- data/doc/release_notes/3.1.0.txt +0 -406
- data/doc/release_notes/3.10.0.txt +0 -286
- data/doc/release_notes/3.11.0.txt +0 -254
- data/doc/release_notes/3.12.0.txt +0 -304
- data/doc/release_notes/3.13.0.txt +0 -210
- data/doc/release_notes/3.14.0.txt +0 -118
- data/doc/release_notes/3.15.0.txt +0 -78
- data/doc/release_notes/3.16.0.txt +0 -45
- data/doc/release_notes/3.17.0.txt +0 -58
- data/doc/release_notes/3.18.0.txt +0 -120
- data/doc/release_notes/3.19.0.txt +0 -67
- data/doc/release_notes/3.2.0.txt +0 -268
- data/doc/release_notes/3.20.0.txt +0 -41
- data/doc/release_notes/3.21.0.txt +0 -87
- data/doc/release_notes/3.22.0.txt +0 -39
- data/doc/release_notes/3.23.0.txt +0 -172
- data/doc/release_notes/3.24.0.txt +0 -420
- data/doc/release_notes/3.25.0.txt +0 -88
- data/doc/release_notes/3.26.0.txt +0 -88
- data/doc/release_notes/3.27.0.txt +0 -82
- data/doc/release_notes/3.28.0.txt +0 -304
- data/doc/release_notes/3.29.0.txt +0 -459
- data/doc/release_notes/3.3.0.txt +0 -192
- data/doc/release_notes/3.30.0.txt +0 -135
- data/doc/release_notes/3.31.0.txt +0 -146
- data/doc/release_notes/3.32.0.txt +0 -202
- data/doc/release_notes/3.33.0.txt +0 -157
- data/doc/release_notes/3.34.0.txt +0 -671
- data/doc/release_notes/3.35.0.txt +0 -144
- data/doc/release_notes/3.36.0.txt +0 -245
- data/doc/release_notes/3.37.0.txt +0 -338
- data/doc/release_notes/3.38.0.txt +0 -234
- data/doc/release_notes/3.39.0.txt +0 -237
- data/doc/release_notes/3.4.0.txt +0 -325
- data/doc/release_notes/3.40.0.txt +0 -73
- data/doc/release_notes/3.41.0.txt +0 -155
- data/doc/release_notes/3.42.0.txt +0 -74
- data/doc/release_notes/3.43.0.txt +0 -105
- data/doc/release_notes/3.44.0.txt +0 -152
- data/doc/release_notes/3.45.0.txt +0 -179
- data/doc/release_notes/3.46.0.txt +0 -122
- data/doc/release_notes/3.47.0.txt +0 -270
- data/doc/release_notes/3.48.0.txt +0 -477
- data/doc/release_notes/3.5.0.txt +0 -510
- data/doc/release_notes/3.6.0.txt +0 -366
- data/doc/release_notes/3.7.0.txt +0 -179
- data/doc/release_notes/3.8.0.txt +0 -151
- data/doc/release_notes/3.9.0.txt +0 -233
- data/doc/release_notes/4.0.0.txt +0 -262
- data/doc/release_notes/4.1.0.txt +0 -85
- data/doc/release_notes/4.10.0.txt +0 -226
- data/doc/release_notes/4.11.0.txt +0 -147
- data/doc/release_notes/4.12.0.txt +0 -105
- data/doc/release_notes/4.13.0.txt +0 -169
- data/doc/release_notes/4.14.0.txt +0 -68
- data/doc/release_notes/4.15.0.txt +0 -56
- data/doc/release_notes/4.16.0.txt +0 -36
- data/doc/release_notes/4.17.0.txt +0 -38
- data/doc/release_notes/4.18.0.txt +0 -36
- data/doc/release_notes/4.19.0.txt +0 -45
- data/doc/release_notes/4.2.0.txt +0 -129
- data/doc/release_notes/4.20.0.txt +0 -79
- data/doc/release_notes/4.21.0.txt +0 -94
- data/doc/release_notes/4.22.0.txt +0 -72
- data/doc/release_notes/4.23.0.txt +0 -65
- data/doc/release_notes/4.24.0.txt +0 -99
- data/doc/release_notes/4.25.0.txt +0 -181
- data/doc/release_notes/4.26.0.txt +0 -44
- data/doc/release_notes/4.27.0.txt +0 -78
- data/doc/release_notes/4.28.0.txt +0 -57
- data/doc/release_notes/4.29.0.txt +0 -41
- data/doc/release_notes/4.3.0.txt +0 -40
- data/doc/release_notes/4.30.0.txt +0 -37
- data/doc/release_notes/4.31.0.txt +0 -57
- data/doc/release_notes/4.32.0.txt +0 -132
- data/doc/release_notes/4.33.0.txt +0 -88
- data/doc/release_notes/4.34.0.txt +0 -86
- data/doc/release_notes/4.35.0.txt +0 -130
- data/doc/release_notes/4.36.0.txt +0 -116
- data/doc/release_notes/4.4.0.txt +0 -92
- data/doc/release_notes/4.5.0.txt +0 -34
- data/doc/release_notes/4.6.0.txt +0 -30
- data/doc/release_notes/4.7.0.txt +0 -103
- data/doc/release_notes/4.8.0.txt +0 -175
- data/doc/release_notes/4.9.0.txt +0 -190
- data/lib/sequel/adapters/cubrid.rb +0 -144
- data/lib/sequel/adapters/do/mysql.rb +0 -66
- data/lib/sequel/adapters/do/postgres.rb +0 -44
- data/lib/sequel/adapters/do/sqlite3.rb +0 -42
- data/lib/sequel/adapters/do.rb +0 -158
- data/lib/sequel/adapters/jdbc/as400.rb +0 -84
- data/lib/sequel/adapters/jdbc/cubrid.rb +0 -64
- data/lib/sequel/adapters/jdbc/firebirdsql.rb +0 -36
- data/lib/sequel/adapters/jdbc/informix-sqli.rb +0 -33
- data/lib/sequel/adapters/jdbc/jdbcprogress.rb +0 -33
- data/lib/sequel/adapters/odbc/progress.rb +0 -10
- data/lib/sequel/adapters/shared/cubrid.rb +0 -245
- data/lib/sequel/adapters/shared/firebird.rb +0 -247
- data/lib/sequel/adapters/shared/informix.rb +0 -54
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +0 -152
- data/lib/sequel/adapters/shared/progress.rb +0 -40
- data/lib/sequel/adapters/swift/mysql.rb +0 -49
- data/lib/sequel/adapters/swift/postgres.rb +0 -47
- data/lib/sequel/adapters/swift/sqlite.rb +0 -49
- data/lib/sequel/adapters/swift.rb +0 -160
- data/lib/sequel/adapters/utils/pg_types.rb +0 -70
- data/lib/sequel/dataset/mutation.rb +0 -111
- data/lib/sequel/extensions/empty_array_ignore_nulls.rb +0 -5
- data/lib/sequel/extensions/filter_having.rb +0 -63
- data/lib/sequel/extensions/hash_aliases.rb +0 -49
- data/lib/sequel/extensions/meta_def.rb +0 -35
- data/lib/sequel/extensions/query_literals.rb +0 -84
- data/lib/sequel/extensions/ruby18_symbol_extensions.rb +0 -24
- data/lib/sequel/extensions/sequel_3_dataset_methods.rb +0 -122
- data/lib/sequel/extensions/set_overrides.rb +0 -76
- data/lib/sequel/no_core_ext.rb +0 -3
- data/lib/sequel/plugins/association_autoreloading.rb +0 -9
- data/lib/sequel/plugins/identifier_columns.rb +0 -47
- data/lib/sequel/plugins/many_to_one_pk_lookup.rb +0 -9
- data/lib/sequel/plugins/pg_typecast_on_load.rb +0 -81
- data/lib/sequel/plugins/prepared_statements_associations.rb +0 -119
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +0 -61
- data/lib/sequel/plugins/schema.rb +0 -82
- data/lib/sequel/plugins/scissors.rb +0 -35
- data/spec/adapter_spec.rb +0 -4
- data/spec/adapters/db2_spec.rb +0 -160
- data/spec/adapters/firebird_spec.rb +0 -411
- data/spec/adapters/informix_spec.rb +0 -100
- data/spec/adapters/mssql_spec.rb +0 -733
- data/spec/adapters/mysql_spec.rb +0 -1319
- data/spec/adapters/oracle_spec.rb +0 -313
- data/spec/adapters/postgres_spec.rb +0 -3790
- data/spec/adapters/spec_helper.rb +0 -49
- data/spec/adapters/sqlanywhere_spec.rb +0 -170
- data/spec/adapters/sqlite_spec.rb +0 -688
- data/spec/bin_spec.rb +0 -258
- data/spec/core/connection_pool_spec.rb +0 -1045
- data/spec/core/database_spec.rb +0 -2636
- data/spec/core/dataset_spec.rb +0 -5175
- data/spec/core/deprecated_spec.rb +0 -70
- data/spec/core/expression_filters_spec.rb +0 -1247
- data/spec/core/mock_adapter_spec.rb +0 -464
- data/spec/core/object_graph_spec.rb +0 -303
- data/spec/core/placeholder_literalizer_spec.rb +0 -163
- data/spec/core/schema_generator_spec.rb +0 -203
- data/spec/core/schema_spec.rb +0 -1676
- data/spec/core/spec_helper.rb +0 -34
- data/spec/core/version_spec.rb +0 -7
- data/spec/core_extensions_spec.rb +0 -699
- data/spec/core_model_spec.rb +0 -2
- data/spec/core_spec.rb +0 -1
- data/spec/extensions/accessed_columns_spec.rb +0 -51
- data/spec/extensions/active_model_spec.rb +0 -85
- data/spec/extensions/after_initialize_spec.rb +0 -24
- data/spec/extensions/arbitrary_servers_spec.rb +0 -109
- data/spec/extensions/association_dependencies_spec.rb +0 -117
- data/spec/extensions/association_pks_spec.rb +0 -405
- data/spec/extensions/association_proxies_spec.rb +0 -86
- data/spec/extensions/auto_validations_spec.rb +0 -192
- data/spec/extensions/before_after_save_spec.rb +0 -40
- data/spec/extensions/blacklist_security_spec.rb +0 -88
- data/spec/extensions/blank_spec.rb +0 -69
- data/spec/extensions/boolean_readers_spec.rb +0 -93
- data/spec/extensions/boolean_subsets_spec.rb +0 -47
- data/spec/extensions/caching_spec.rb +0 -270
- data/spec/extensions/class_table_inheritance_spec.rb +0 -444
- data/spec/extensions/column_conflicts_spec.rb +0 -60
- data/spec/extensions/column_select_spec.rb +0 -108
- data/spec/extensions/columns_introspection_spec.rb +0 -91
- data/spec/extensions/composition_spec.rb +0 -242
- data/spec/extensions/connection_expiration_spec.rb +0 -121
- data/spec/extensions/connection_validator_spec.rb +0 -127
- data/spec/extensions/constraint_validations_plugin_spec.rb +0 -288
- data/spec/extensions/constraint_validations_spec.rb +0 -389
- data/spec/extensions/core_refinements_spec.rb +0 -519
- data/spec/extensions/csv_serializer_spec.rb +0 -180
- data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
- data/spec/extensions/dataset_associations_spec.rb +0 -343
- data/spec/extensions/dataset_source_alias_spec.rb +0 -51
- data/spec/extensions/date_arithmetic_spec.rb +0 -167
- data/spec/extensions/defaults_setter_spec.rb +0 -102
- data/spec/extensions/delay_add_association_spec.rb +0 -74
- data/spec/extensions/dirty_spec.rb +0 -180
- data/spec/extensions/duplicate_columns_handler_spec.rb +0 -110
- data/spec/extensions/eager_each_spec.rb +0 -66
- data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
- data/spec/extensions/error_splitter_spec.rb +0 -18
- data/spec/extensions/error_sql_spec.rb +0 -20
- data/spec/extensions/eval_inspect_spec.rb +0 -73
- data/spec/extensions/filter_having_spec.rb +0 -40
- data/spec/extensions/force_encoding_spec.rb +0 -114
- data/spec/extensions/from_block_spec.rb +0 -21
- data/spec/extensions/graph_each_spec.rb +0 -119
- data/spec/extensions/hash_aliases_spec.rb +0 -24
- data/spec/extensions/hook_class_methods_spec.rb +0 -429
- data/spec/extensions/identifier_columns_spec.rb +0 -17
- data/spec/extensions/inflector_spec.rb +0 -183
- data/spec/extensions/input_transformer_spec.rb +0 -54
- data/spec/extensions/insert_returning_select_spec.rb +0 -46
- data/spec/extensions/instance_filters_spec.rb +0 -79
- data/spec/extensions/instance_hooks_spec.rb +0 -276
- data/spec/extensions/inverted_subsets_spec.rb +0 -33
- data/spec/extensions/json_serializer_spec.rb +0 -304
- data/spec/extensions/lazy_attributes_spec.rb +0 -170
- data/spec/extensions/list_spec.rb +0 -278
- data/spec/extensions/looser_typecasting_spec.rb +0 -43
- data/spec/extensions/many_through_many_spec.rb +0 -2172
- data/spec/extensions/meta_def_spec.rb +0 -21
- data/spec/extensions/migration_spec.rb +0 -728
- data/spec/extensions/modification_detection_spec.rb +0 -80
- data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -91
- data/spec/extensions/named_timezones_spec.rb +0 -108
- data/spec/extensions/nested_attributes_spec.rb +0 -697
- data/spec/extensions/no_auto_literal_strings_spec.rb +0 -65
- data/spec/extensions/null_dataset_spec.rb +0 -85
- data/spec/extensions/optimistic_locking_spec.rb +0 -128
- data/spec/extensions/pagination_spec.rb +0 -118
- data/spec/extensions/pg_array_associations_spec.rb +0 -736
- data/spec/extensions/pg_array_ops_spec.rb +0 -143
- data/spec/extensions/pg_array_spec.rb +0 -390
- data/spec/extensions/pg_enum_spec.rb +0 -92
- data/spec/extensions/pg_hstore_ops_spec.rb +0 -236
- data/spec/extensions/pg_hstore_spec.rb +0 -206
- data/spec/extensions/pg_inet_ops_spec.rb +0 -101
- data/spec/extensions/pg_inet_spec.rb +0 -52
- data/spec/extensions/pg_interval_spec.rb +0 -76
- data/spec/extensions/pg_json_ops_spec.rb +0 -275
- data/spec/extensions/pg_json_spec.rb +0 -218
- data/spec/extensions/pg_loose_count_spec.rb +0 -17
- data/spec/extensions/pg_range_ops_spec.rb +0 -58
- data/spec/extensions/pg_range_spec.rb +0 -473
- data/spec/extensions/pg_row_ops_spec.rb +0 -60
- data/spec/extensions/pg_row_plugin_spec.rb +0 -62
- data/spec/extensions/pg_row_spec.rb +0 -360
- data/spec/extensions/pg_static_cache_updater_spec.rb +0 -92
- data/spec/extensions/pg_typecast_on_load_spec.rb +0 -63
- data/spec/extensions/prepared_statements_associations_spec.rb +0 -159
- data/spec/extensions/prepared_statements_safe_spec.rb +0 -61
- data/spec/extensions/prepared_statements_spec.rb +0 -103
- data/spec/extensions/prepared_statements_with_pk_spec.rb +0 -31
- data/spec/extensions/pretty_table_spec.rb +0 -92
- data/spec/extensions/query_literals_spec.rb +0 -183
- data/spec/extensions/query_spec.rb +0 -102
- data/spec/extensions/rcte_tree_spec.rb +0 -392
- data/spec/extensions/round_timestamps_spec.rb +0 -43
- data/spec/extensions/schema_caching_spec.rb +0 -41
- data/spec/extensions/schema_dumper_spec.rb +0 -814
- data/spec/extensions/schema_spec.rb +0 -117
- data/spec/extensions/scissors_spec.rb +0 -26
- data/spec/extensions/select_remove_spec.rb +0 -38
- data/spec/extensions/sequel_3_dataset_methods_spec.rb +0 -101
- data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
- data/spec/extensions/serialization_spec.rb +0 -362
- data/spec/extensions/server_block_spec.rb +0 -90
- data/spec/extensions/server_logging_spec.rb +0 -45
- data/spec/extensions/set_overrides_spec.rb +0 -61
- data/spec/extensions/sharding_spec.rb +0 -198
- data/spec/extensions/shared_caching_spec.rb +0 -175
- data/spec/extensions/single_table_inheritance_spec.rb +0 -297
- data/spec/extensions/singular_table_names_spec.rb +0 -22
- data/spec/extensions/skip_create_refresh_spec.rb +0 -17
- data/spec/extensions/spec_helper.rb +0 -71
- data/spec/extensions/split_array_nil_spec.rb +0 -24
- data/spec/extensions/split_values_spec.rb +0 -22
- data/spec/extensions/sql_comments_spec.rb +0 -27
- data/spec/extensions/sql_expr_spec.rb +0 -60
- data/spec/extensions/static_cache_spec.rb +0 -361
- data/spec/extensions/string_agg_spec.rb +0 -85
- data/spec/extensions/string_date_time_spec.rb +0 -95
- data/spec/extensions/string_stripper_spec.rb +0 -68
- data/spec/extensions/subclasses_spec.rb +0 -66
- data/spec/extensions/subset_conditions_spec.rb +0 -38
- data/spec/extensions/table_select_spec.rb +0 -71
- data/spec/extensions/tactical_eager_loading_spec.rb +0 -136
- data/spec/extensions/thread_local_timezones_spec.rb +0 -67
- data/spec/extensions/timestamps_spec.rb +0 -175
- data/spec/extensions/to_dot_spec.rb +0 -154
- data/spec/extensions/touch_spec.rb +0 -203
- data/spec/extensions/tree_spec.rb +0 -274
- data/spec/extensions/typecast_on_load_spec.rb +0 -80
- data/spec/extensions/unlimited_update_spec.rb +0 -20
- data/spec/extensions/update_or_create_spec.rb +0 -87
- data/spec/extensions/update_primary_key_spec.rb +0 -100
- data/spec/extensions/update_refresh_spec.rb +0 -53
- data/spec/extensions/uuid_spec.rb +0 -106
- data/spec/extensions/validate_associated_spec.rb +0 -52
- data/spec/extensions/validation_class_methods_spec.rb +0 -1027
- data/spec/extensions/validation_helpers_spec.rb +0 -554
- data/spec/extensions/xml_serializer_spec.rb +0 -207
- data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
- data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
- data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
- data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
- data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
- data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
- data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
- data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
- data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
- data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
- data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
- data/spec/files/double_migration/001_create_sessions.rb +0 -9
- data/spec/files/double_migration/002_create_nodes.rb +0 -19
- data/spec/files/double_migration/003_3_create_users.rb +0 -4
- data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
- data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
- data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
- data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
- data/spec/files/empty_migration/001_create_sessions.rb +0 -9
- data/spec/files/empty_migration/002_create_nodes.rb +0 -0
- data/spec/files/empty_migration/003_3_create_users.rb +0 -4
- data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
- data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
- data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
- data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
- data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
- data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
- data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
- data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
- data/spec/files/reversible_migrations/001_reversible.rb +0 -5
- data/spec/files/reversible_migrations/002_reversible.rb +0 -5
- data/spec/files/reversible_migrations/003_reversible.rb +0 -5
- data/spec/files/reversible_migrations/004_reversible.rb +0 -5
- data/spec/files/reversible_migrations/005_reversible.rb +0 -10
- data/spec/files/reversible_migrations/006_reversible.rb +0 -10
- data/spec/files/reversible_migrations/007_reversible.rb +0 -10
- data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
- data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
- data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
- data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
- data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
- data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
- data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
- data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
- data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
- data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
- data/spec/guards_helper.rb +0 -55
- data/spec/integration/associations_test.rb +0 -2506
- data/spec/integration/database_test.rb +0 -113
- data/spec/integration/dataset_test.rb +0 -1858
- data/spec/integration/eager_loader_test.rb +0 -687
- data/spec/integration/migrator_test.rb +0 -262
- data/spec/integration/model_test.rb +0 -230
- data/spec/integration/plugin_test.rb +0 -2297
- data/spec/integration/prepared_statement_test.rb +0 -467
- data/spec/integration/schema_test.rb +0 -815
- data/spec/integration/spec_helper.rb +0 -56
- data/spec/integration/timezone_test.rb +0 -86
- data/spec/integration/transaction_test.rb +0 -406
- data/spec/integration/type_test.rb +0 -133
- data/spec/model/association_reflection_spec.rb +0 -565
- data/spec/model/associations_spec.rb +0 -4589
- data/spec/model/base_spec.rb +0 -759
- data/spec/model/class_dataset_methods_spec.rb +0 -150
- data/spec/model/dataset_methods_spec.rb +0 -149
- data/spec/model/eager_loading_spec.rb +0 -2197
- data/spec/model/hooks_spec.rb +0 -604
- data/spec/model/inflector_spec.rb +0 -26
- data/spec/model/model_spec.rb +0 -1097
- data/spec/model/plugins_spec.rb +0 -299
- data/spec/model/record_spec.rb +0 -2162
- data/spec/model/spec_helper.rb +0 -46
- data/spec/model/validations_spec.rb +0 -193
- data/spec/model_no_assoc_spec.rb +0 -1
- data/spec/model_spec.rb +0 -1
- data/spec/plugin_spec.rb +0 -1
- data/spec/sequel_coverage.rb +0 -15
- data/spec/spec_config.rb +0 -10
@@ -7,46 +7,54 @@ module Sequel
|
|
7
7
|
module Associations
|
8
8
|
# Map of association type symbols to association reflection classes.
|
9
9
|
ASSOCIATION_TYPES = {}
|
10
|
-
|
10
|
+
|
11
11
|
# Set an empty association reflection hash in the model
|
12
12
|
def self.apply(model)
|
13
|
-
model.
|
13
|
+
model.instance_exec do
|
14
14
|
@association_reflections = {}
|
15
15
|
@autoreloading_associations = {}
|
16
16
|
@cache_associations = true
|
17
|
+
@default_eager_limit_strategy = true
|
17
18
|
@default_association_options = {}
|
19
|
+
@default_association_type_options = {}
|
20
|
+
@dataset_module_class = DatasetModule
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
24
|
+
# The dataset module to use for classes using the associations plugin.
|
25
|
+
class DatasetModule < Model::DatasetModule
|
26
|
+
def_dataset_caching_method(self, :eager)
|
27
|
+
end
|
28
|
+
|
21
29
|
# AssociationReflection is a Hash subclass that keeps information on Sequel::Model associations. It
|
22
30
|
# provides methods to reduce internal code duplication. It should not
|
23
31
|
# be instantiated by the user.
|
24
32
|
class AssociationReflection < Hash
|
25
33
|
include Sequel::Inflections
|
26
|
-
|
34
|
+
|
27
35
|
# Name symbol for the _add internal association method
|
28
36
|
def _add_method
|
29
|
-
|
37
|
+
self[:_add_method]
|
30
38
|
end
|
31
39
|
|
32
40
|
# Name symbol for the _remove_all internal association method
|
33
41
|
def _remove_all_method
|
34
|
-
|
42
|
+
self[:_remove_all_method]
|
35
43
|
end
|
36
44
|
|
37
45
|
# Name symbol for the _remove internal association method
|
38
46
|
def _remove_method
|
39
|
-
|
47
|
+
self[:_remove_method]
|
40
48
|
end
|
41
49
|
|
42
50
|
# Name symbol for the _setter association method
|
43
51
|
def _setter_method
|
44
|
-
|
52
|
+
self[:_setter_method]
|
45
53
|
end
|
46
54
|
|
47
55
|
# Name symbol for the add association method
|
48
56
|
def add_method
|
49
|
-
|
57
|
+
self[:add_method]
|
50
58
|
end
|
51
59
|
|
52
60
|
# Name symbol for association method, the same as the name of the association.
|
@@ -56,7 +64,13 @@ module Sequel
|
|
56
64
|
|
57
65
|
# The class associated to the current model class via this association
|
58
66
|
def associated_class
|
59
|
-
cached_fetch(:class)
|
67
|
+
cached_fetch(:class) do
|
68
|
+
begin
|
69
|
+
constantize(self[:class_name])
|
70
|
+
rescue NameError => e
|
71
|
+
raise NameError, "#{e.message} (this happened when attempting to find the associated class for #{inspect})", e.backtrace
|
72
|
+
end
|
73
|
+
end
|
60
74
|
end
|
61
75
|
|
62
76
|
# The dataset associated via this association, with the non-instance specific
|
@@ -68,16 +82,17 @@ module Sequel
|
|
68
82
|
|
69
83
|
# Apply all non-instance specific changes to the given dataset and return it.
|
70
84
|
def apply_dataset_changes(ds)
|
71
|
-
ds.
|
72
|
-
|
73
|
-
|
85
|
+
ds = ds.with_extend(AssociationDatasetMethods).clone(:association_reflection => self)
|
86
|
+
if exts = self[:reverse_extend]
|
87
|
+
ds = ds.with_extend(*exts)
|
88
|
+
end
|
74
89
|
ds = ds.select(*select) if select
|
75
90
|
if c = self[:conditions]
|
76
91
|
ds = (c.is_a?(Array) && !Sequel.condition_specifier?(c)) ? ds.where(*c) : ds.where(c)
|
77
92
|
end
|
78
93
|
ds = ds.order(*self[:order]) if self[:order]
|
79
94
|
ds = ds.limit(*self[:limit]) if self[:limit]
|
80
|
-
ds = ds.limit(1) if limit_to_single_row?
|
95
|
+
ds = ds.limit(1).skip_limit_check if limit_to_single_row?
|
81
96
|
ds = ds.eager(self[:eager]) if self[:eager]
|
82
97
|
ds = ds.distinct if self[:distinct]
|
83
98
|
ds
|
@@ -129,7 +144,8 @@ module Sequel
|
|
129
144
|
def apply_window_function_eager_limit_strategy(ds, limit_and_offset=limit_and_offset())
|
130
145
|
rn = ds.row_number_column
|
131
146
|
limit, offset = limit_and_offset
|
132
|
-
ds = ds.unordered.select_append{|o| o.row_number
|
147
|
+
ds = ds.unordered.select_append{|o| o.row_number.function.over(:partition=>predicate_key, :order=>ds.opts[:order]).as(rn)}.from_self
|
148
|
+
ds = ds.order(rn) if ds.db.database_type == :mysql
|
133
149
|
ds = if !returns_array?
|
134
150
|
ds.where(rn => offset ? offset+1 : 1)
|
135
151
|
elsif offset
|
@@ -148,11 +164,11 @@ module Sequel
|
|
148
164
|
# range to return the object(s) at the correct offset/limit.
|
149
165
|
def apply_ruby_eager_limit_strategy(rows, limit_and_offset = limit_and_offset())
|
150
166
|
name = self[:name]
|
167
|
+
return unless range = slice_range(limit_and_offset)
|
151
168
|
if returns_array?
|
152
|
-
range = slice_range(limit_and_offset)
|
153
169
|
rows.each{|o| o.associations[name] = o.associations[name][range] || []}
|
154
|
-
|
155
|
-
offset =
|
170
|
+
else
|
171
|
+
offset = range.begin
|
156
172
|
rows.each{|o| o.associations[name] = o.associations[name][offset]}
|
157
173
|
end
|
158
174
|
end
|
@@ -178,7 +194,7 @@ module Sequel
|
|
178
194
|
|
179
195
|
# Name symbol for the dataset association method
|
180
196
|
def dataset_method
|
181
|
-
|
197
|
+
self[:dataset_method]
|
182
198
|
end
|
183
199
|
|
184
200
|
# Whether the dataset needs a primary key to function, true by default.
|
@@ -197,7 +213,13 @@ module Sequel
|
|
197
213
|
# Return an dataset that will load the appropriate associated objects for
|
198
214
|
# the given object using this association.
|
199
215
|
def association_dataset_for(object)
|
200
|
-
|
216
|
+
condition = if can_have_associated_objects?(object)
|
217
|
+
predicate_keys.zip(predicate_key_values(object))
|
218
|
+
else
|
219
|
+
false
|
220
|
+
end
|
221
|
+
|
222
|
+
associated_dataset.where(condition)
|
201
223
|
end
|
202
224
|
|
203
225
|
ASSOCIATION_DATASET_PROC = proc{|r| r.association_dataset_for(self)}
|
@@ -241,7 +263,9 @@ module Sequel
|
|
241
263
|
# yielding each row to the block.
|
242
264
|
def eager_load_results(eo, &block)
|
243
265
|
rows = eo[:rows]
|
244
|
-
|
266
|
+
unless eo[:initialize_rows] == false
|
267
|
+
Sequel.synchronize_with(eo[:mutex]){initialize_association_cache(rows)}
|
268
|
+
end
|
245
269
|
if eo[:id_map]
|
246
270
|
ids = eo[:id_map].keys
|
247
271
|
return ids if ids.empty?
|
@@ -250,13 +274,16 @@ module Sequel
|
|
250
274
|
cascade = eo[:associations]
|
251
275
|
eager_limit = nil
|
252
276
|
|
253
|
-
if eo[:
|
277
|
+
if eo[:no_results]
|
278
|
+
no_results = true
|
279
|
+
elsif eo[:eager_block] || eo[:loader] == false
|
254
280
|
ds = eager_loading_dataset(eo)
|
255
281
|
|
256
282
|
strategy = ds.opts[:eager_limit_strategy] || strategy
|
257
283
|
|
258
284
|
eager_limit =
|
259
285
|
if el = ds.opts[:eager_limit]
|
286
|
+
raise Error, "The :eager_limit dataset option is not supported for associations returning a single record" unless returns_array?
|
260
287
|
strategy ||= true_eager_graph_limit_strategy
|
261
288
|
if el.is_a?(Array)
|
262
289
|
el
|
@@ -270,6 +297,7 @@ module Sequel
|
|
270
297
|
strategy = true_eager_graph_limit_strategy if strategy == :union
|
271
298
|
# Correlated subqueries are not supported for regular eager loading
|
272
299
|
strategy = :ruby if strategy == :correlated_subquery
|
300
|
+
strategy = nil if strategy == :ruby && assign_singular?
|
273
301
|
objects = apply_eager_limit_strategy(ds, strategy, eager_limit).all
|
274
302
|
elsif strategy == :union
|
275
303
|
objects = []
|
@@ -287,7 +315,8 @@ module Sequel
|
|
287
315
|
objects = loader.all(ids)
|
288
316
|
end
|
289
317
|
|
290
|
-
objects.each(&block)
|
318
|
+
Sequel.synchronize_with(eo[:mutex]){objects.each(&block)} unless no_results
|
319
|
+
|
291
320
|
if strategy == :ruby
|
292
321
|
apply_ruby_eager_limit_strategy(rows, eager_limit || limit_and_offset)
|
293
322
|
end
|
@@ -304,11 +333,6 @@ module Sequel
|
|
304
333
|
false
|
305
334
|
end
|
306
335
|
|
307
|
-
# Alias of predicate_key, only for backwards compatibility.
|
308
|
-
def eager_loading_predicate_key
|
309
|
-
predicate_key
|
310
|
-
end
|
311
|
-
|
312
336
|
# Whether to eagerly graph a lazy dataset, true by default. If this
|
313
337
|
# is false, the association won't respect the :eager_graph option
|
314
338
|
# when loading the association for a single record.
|
@@ -331,6 +355,44 @@ module Sequel
|
|
331
355
|
{filter_by_associations_conditions_key=>ds}
|
332
356
|
end
|
333
357
|
|
358
|
+
# Finalize the association by first attempting to populate the thread-safe cache,
|
359
|
+
# and then transfering the thread-safe cache value to the association itself,
|
360
|
+
# so that a mutex is not needed to get the value.
|
361
|
+
def finalize
|
362
|
+
return unless cache = self[:cache]
|
363
|
+
|
364
|
+
finalizer = proc do |meth, key|
|
365
|
+
next if has_key?(key)
|
366
|
+
|
367
|
+
# Allow calling private methods to make sure caching is done appropriately
|
368
|
+
send(meth)
|
369
|
+
self[key] = cache.delete(key) if cache.has_key?(key)
|
370
|
+
end
|
371
|
+
|
372
|
+
finalize_settings.each(&finalizer)
|
373
|
+
|
374
|
+
unless self[:instance_specific]
|
375
|
+
finalizer.call(:associated_eager_dataset, :associated_eager_dataset)
|
376
|
+
finalizer.call(:filter_by_associations_conditions_dataset, :filter_by_associations_conditions_dataset)
|
377
|
+
end
|
378
|
+
|
379
|
+
nil
|
380
|
+
end
|
381
|
+
|
382
|
+
# Map of methods to cache keys used for finalizing associations.
|
383
|
+
FINALIZE_SETTINGS = {
|
384
|
+
:associated_class=>:class,
|
385
|
+
:associated_dataset=>:_dataset,
|
386
|
+
:eager_limit_strategy=>:_eager_limit_strategy,
|
387
|
+
:placeholder_loader=>:placeholder_loader,
|
388
|
+
:predicate_key=>:predicate_key,
|
389
|
+
:predicate_keys=>:predicate_keys,
|
390
|
+
:reciprocal=>:reciprocal,
|
391
|
+
}.freeze
|
392
|
+
def finalize_settings
|
393
|
+
FINALIZE_SETTINGS
|
394
|
+
end
|
395
|
+
|
334
396
|
# Whether to handle silent modification failure when adding/removing
|
335
397
|
# associated records, false by default.
|
336
398
|
def handle_silent_modification_failure?
|
@@ -347,6 +409,18 @@ module Sequel
|
|
347
409
|
end
|
348
410
|
end
|
349
411
|
|
412
|
+
# Show which type of reflection this is, and a guess at what code was used to create the
|
413
|
+
# association.
|
414
|
+
def inspect
|
415
|
+
o = self[:orig_opts].dup
|
416
|
+
o.delete(:class)
|
417
|
+
o.delete(:class_name)
|
418
|
+
o.delete(:block) unless o[:block]
|
419
|
+
o[:class] = self[:orig_class] if self[:orig_class]
|
420
|
+
|
421
|
+
"#<#{self.class} #{self[:model]}.#{self[:type]} #{self[:name].inspect}#{", #{o.inspect[1...-1]}" unless o.empty?}>"
|
422
|
+
end
|
423
|
+
|
350
424
|
# The limit and offset for this association (returned as a two element array).
|
351
425
|
def limit_and_offset
|
352
426
|
if (v = self[:limit]).is_a?(Array)
|
@@ -368,7 +442,11 @@ module Sequel
|
|
368
442
|
if use_placeholder_loader?
|
369
443
|
cached_fetch(:placeholder_loader) do
|
370
444
|
Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
|
371
|
-
ds.where(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)})
|
445
|
+
ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
|
446
|
+
if self[:block]
|
447
|
+
ds = self[:block].call(ds)
|
448
|
+
end
|
449
|
+
ds
|
372
450
|
end
|
373
451
|
end
|
374
452
|
end
|
@@ -384,16 +462,14 @@ module Sequel
|
|
384
462
|
predicate_key_methods.map{|k| object.get_column_value(k)}
|
385
463
|
end
|
386
464
|
|
387
|
-
# Qualify +col+ with the given table name.
|
388
|
-
# return an array of qualified columns. Only qualifies Symbols and SQL::Identifier
|
389
|
-
# values, other values are not modified.
|
465
|
+
# Qualify +col+ with the given table name.
|
390
466
|
def qualify(table, col)
|
391
467
|
transform(col) do |k|
|
392
468
|
case k
|
393
469
|
when Symbol, SQL::Identifier
|
394
470
|
SQL::QualifiedIdentifier.new(table, k)
|
395
471
|
else
|
396
|
-
Sequel::Qualifier.new(
|
472
|
+
Sequel::Qualifier.new(table).transform(k)
|
397
473
|
end
|
398
474
|
end
|
399
475
|
end
|
@@ -439,7 +515,7 @@ module Sequel
|
|
439
515
|
|
440
516
|
# Name symbol for the remove_all_ association method
|
441
517
|
def remove_all_method
|
442
|
-
|
518
|
+
self[:remove_all_method]
|
443
519
|
end
|
444
520
|
|
445
521
|
# Whether associated objects need to be removed from the association before
|
@@ -450,7 +526,7 @@ module Sequel
|
|
450
526
|
|
451
527
|
# Name symbol for the remove_ association method
|
452
528
|
def remove_method
|
453
|
-
|
529
|
+
self[:remove_method]
|
454
530
|
end
|
455
531
|
|
456
532
|
# Whether to check that an object to be disassociated is already associated to this object, false by default.
|
@@ -477,7 +553,7 @@ module Sequel
|
|
477
553
|
|
478
554
|
# Name symbol for the setter association method
|
479
555
|
def setter_method
|
480
|
-
|
556
|
+
self[:setter_method]
|
481
557
|
end
|
482
558
|
|
483
559
|
# The range used for slicing when using the :ruby eager limit strategy.
|
@@ -490,8 +566,10 @@ module Sequel
|
|
490
566
|
|
491
567
|
private
|
492
568
|
|
493
|
-
#
|
494
|
-
#
|
569
|
+
# If the key exists in the reflection hash, return it.
|
570
|
+
# If the key doesn't exist and association reflections are uncached, then yield to get the value.
|
571
|
+
# If the key doesn't exist and association reflection are cached, check the cache and return
|
572
|
+
# the value if present, or yield to get the value, cache the value, and return it.
|
495
573
|
def cached_fetch(key)
|
496
574
|
fetch(key) do
|
497
575
|
return yield unless h = self[:cache]
|
@@ -501,7 +579,7 @@ module Sequel
|
|
501
579
|
end
|
502
580
|
end
|
503
581
|
|
504
|
-
# Cache the value at the given key
|
582
|
+
# Cache the value at the given key if caching.
|
505
583
|
def cached_set(key, value)
|
506
584
|
return unless h = self[:cache]
|
507
585
|
Sequel.synchronize{h[key] = value}
|
@@ -510,10 +588,10 @@ module Sequel
|
|
510
588
|
# The base dataset used for the association, before any order/conditions
|
511
589
|
# options have been applied.
|
512
590
|
def _associated_dataset
|
513
|
-
associated_class.dataset
|
591
|
+
associated_class.dataset
|
514
592
|
end
|
515
593
|
|
516
|
-
# Whether for the reciprocal type for the given association
|
594
|
+
# Whether for the reciprocal type for the given association cannot be
|
517
595
|
# known in advantage, false by default.
|
518
596
|
def ambiguous_reciprocal_type?
|
519
597
|
false
|
@@ -562,17 +640,20 @@ module Sequel
|
|
562
640
|
# given the hash passed to the eager loader.
|
563
641
|
def eager_loading_dataset(eo=OPTS)
|
564
642
|
ds = eo[:dataset] || associated_eager_dataset
|
565
|
-
|
566
|
-
ds = ds.where(eager_loading_predicate_condition(id_map.keys))
|
567
|
-
end
|
643
|
+
ds = eager_loading_set_predicate_condition(ds, eo)
|
568
644
|
if associations = eo[:associations]
|
569
645
|
ds = ds.eager(associations)
|
570
646
|
end
|
571
647
|
if block = eo[:eager_block]
|
648
|
+
orig_ds = ds
|
572
649
|
ds = block.call(ds)
|
573
650
|
end
|
574
651
|
if eager_loading_use_associated_key?
|
575
|
-
ds = ds.
|
652
|
+
ds = if ds.opts[:eager_graph] && !orig_ds.opts[:eager_graph]
|
653
|
+
block.call(orig_ds.select_append(*associated_key_array))
|
654
|
+
else
|
655
|
+
ds.select_append(*associated_key_array)
|
656
|
+
end
|
576
657
|
end
|
577
658
|
if self[:eager_graph]
|
578
659
|
raise(Error, "cannot eagerly load a #{self[:type]} association that uses :eager_graph") if eager_loading_use_associated_key?
|
@@ -586,6 +667,15 @@ module Sequel
|
|
586
667
|
self[:model].default_eager_limit_strategy || :ruby
|
587
668
|
end
|
588
669
|
|
670
|
+
# Set the predicate condition for the eager loading dataset based on the id map
|
671
|
+
# in the eager loading options.
|
672
|
+
def eager_loading_set_predicate_condition(ds, eo)
|
673
|
+
if id_map = eo[:id_map]
|
674
|
+
ds = ds.where(eager_loading_predicate_condition(id_map.keys))
|
675
|
+
end
|
676
|
+
ds
|
677
|
+
end
|
678
|
+
|
589
679
|
# The predicate condition to use for the eager_loader.
|
590
680
|
def eager_loading_predicate_condition(keys)
|
591
681
|
{predicate_key=>keys}
|
@@ -630,14 +720,12 @@ module Sequel
|
|
630
720
|
v = fetch(:filter_limit_strategy, self[:eager_limit_strategy])
|
631
721
|
if v || self[:limit] || !returns_array?
|
632
722
|
case v ||= self[:model].default_eager_limit_strategy
|
633
|
-
when :union, :ruby
|
723
|
+
when true, :union, :ruby
|
634
724
|
# Can't use a union or ruby-based strategy for filtering by associations, switch to default eager graph limit
|
635
725
|
# strategy.
|
636
726
|
true_eager_graph_limit_strategy
|
637
727
|
when Symbol
|
638
728
|
v
|
639
|
-
when true
|
640
|
-
true_eager_graph_limit_strategy
|
641
729
|
end
|
642
730
|
end
|
643
731
|
end
|
@@ -685,8 +773,8 @@ module Sequel
|
|
685
773
|
|
686
774
|
# If +s+ is an array, map +s+ over the block. Otherwise, just call the
|
687
775
|
# block with +s+.
|
688
|
-
def transform(s)
|
689
|
-
s.is_a?(Array) ? s.map(&
|
776
|
+
def transform(s, &block)
|
777
|
+
s.is_a?(Array) ? s.map(&block) : (yield s)
|
690
778
|
end
|
691
779
|
|
692
780
|
# What eager limit strategy should be used when true is given as the value,
|
@@ -729,7 +817,7 @@ module Sequel
|
|
729
817
|
|
730
818
|
# Whether the placeholder loader can be used to load the association.
|
731
819
|
def use_placeholder_loader?
|
732
|
-
|
820
|
+
self[:use_placeholder_loader]
|
733
821
|
end
|
734
822
|
end
|
735
823
|
|
@@ -774,6 +862,18 @@ module Sequel
|
|
774
862
|
nil
|
775
863
|
end
|
776
864
|
|
865
|
+
FINALIZE_SETTINGS = superclass::FINALIZE_SETTINGS.merge(
|
866
|
+
:primary_key=>:primary_key,
|
867
|
+
:primary_keys=>:primary_keys,
|
868
|
+
:primary_key_method=>:primary_key_method,
|
869
|
+
:primary_key_methods=>:primary_key_methods,
|
870
|
+
:qualified_primary_key=>:qualified_primary_key,
|
871
|
+
:reciprocal_type=>:reciprocal_type
|
872
|
+
).freeze
|
873
|
+
def finalize_settings
|
874
|
+
FINALIZE_SETTINGS
|
875
|
+
end
|
876
|
+
|
777
877
|
# The expression to use on the left hand side of the IN lookup when eager loading
|
778
878
|
def predicate_key
|
779
879
|
cached_fetch(:predicate_key){qualified_primary_key}
|
@@ -918,6 +1018,13 @@ module Sequel
|
|
918
1018
|
:"#{underscore(demodulize(self[:model].name))}_id"
|
919
1019
|
end
|
920
1020
|
|
1021
|
+
FINALIZE_SETTINGS = superclass::FINALIZE_SETTINGS.merge(
|
1022
|
+
:qualified_primary_key=>:qualified_primary_key
|
1023
|
+
).freeze
|
1024
|
+
def finalize_settings
|
1025
|
+
FINALIZE_SETTINGS
|
1026
|
+
end
|
1027
|
+
|
921
1028
|
# Handle silent failure of add/remove methods if raise_on_save_failure is false.
|
922
1029
|
def handle_silent_modification_failure?
|
923
1030
|
self[:raise_on_save_failure] == false
|
@@ -1023,7 +1130,7 @@ module Sequel
|
|
1023
1130
|
end
|
1024
1131
|
|
1025
1132
|
# Support automatic use of correlated subqueries if :ruby option is best available option,
|
1026
|
-
#
|
1133
|
+
# the database supports them, and either the associated class has a non-composite primary key
|
1027
1134
|
# or the database supports multiple columns in IN.
|
1028
1135
|
def true_eager_graph_limit_strategy
|
1029
1136
|
r = super
|
@@ -1158,7 +1265,9 @@ module Sequel
|
|
1158
1265
|
else
|
1159
1266
|
assoc_record.values.delete(left_key_alias)
|
1160
1267
|
end
|
1161
|
-
|
1268
|
+
|
1269
|
+
objects = h[hash_key]
|
1270
|
+
|
1162
1271
|
if assign_singular
|
1163
1272
|
objects.each do |object|
|
1164
1273
|
object.associations[name] ||= assoc_record
|
@@ -1188,6 +1297,22 @@ module Sequel
|
|
1188
1297
|
:"#{singularize(self[:name])}_id"
|
1189
1298
|
end
|
1190
1299
|
|
1300
|
+
FINALIZE_SETTINGS = superclass::FINALIZE_SETTINGS.merge(
|
1301
|
+
:associated_key_array=>:associated_key_array,
|
1302
|
+
:qualified_right_key=>:qualified_right_key,
|
1303
|
+
:join_table_source=>:join_table_source,
|
1304
|
+
:join_table_alias=>:join_table_alias,
|
1305
|
+
:qualified_right_primary_key=>:qualified_right_primary_key,
|
1306
|
+
:right_primary_key=>:right_primary_key,
|
1307
|
+
:right_primary_keys=>:right_primary_keys,
|
1308
|
+
:right_primary_key_method=>:right_primary_key_method,
|
1309
|
+
:right_primary_key_methods=>:right_primary_key_methods,
|
1310
|
+
:select=>:select
|
1311
|
+
).freeze
|
1312
|
+
def finalize_settings
|
1313
|
+
FINALIZE_SETTINGS
|
1314
|
+
end
|
1315
|
+
|
1191
1316
|
# The hash key to use for the eager loading predicate (left side of IN (1, 2, 3)).
|
1192
1317
|
# The left key qualified by the join table.
|
1193
1318
|
def predicate_key
|
@@ -1202,7 +1327,7 @@ module Sequel
|
|
1202
1327
|
|
1203
1328
|
# many_to_many associations need to select a key in an associated table to eagerly load
|
1204
1329
|
def eager_loading_use_associated_key?
|
1205
|
-
|
1330
|
+
!separate_query_per_table?
|
1206
1331
|
end
|
1207
1332
|
|
1208
1333
|
# The source of the join table. This is the join table itself, unless it
|
@@ -1259,10 +1384,30 @@ module Sequel
|
|
1259
1384
|
cached_fetch(:select){default_select}
|
1260
1385
|
end
|
1261
1386
|
|
1387
|
+
# Whether a separate query should be used for the join table.
|
1388
|
+
def separate_query_per_table?
|
1389
|
+
self[:join_table_db]
|
1390
|
+
end
|
1391
|
+
|
1262
1392
|
private
|
1263
1393
|
|
1394
|
+
# Join to the the join table, unless using a separate query per table.
|
1264
1395
|
def _associated_dataset
|
1265
|
-
|
1396
|
+
if separate_query_per_table?
|
1397
|
+
super
|
1398
|
+
else
|
1399
|
+
super.inner_join(self[:join_table], self[:right_keys].zip(right_primary_keys), :qualify=>:deep)
|
1400
|
+
end
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
# Use the right_keys from the eager loading options if
|
1404
|
+
# using a separate query per table.
|
1405
|
+
def eager_loading_set_predicate_condition(ds, eo)
|
1406
|
+
if separate_query_per_table?
|
1407
|
+
ds.where(right_primary_key=>eo[:right_keys])
|
1408
|
+
else
|
1409
|
+
super
|
1410
|
+
end
|
1266
1411
|
end
|
1267
1412
|
|
1268
1413
|
# The default selection for associations that require joins. These do not use the default
|
@@ -1353,10 +1498,20 @@ module Sequel
|
|
1353
1498
|
# This module contains methods added to all association datasets
|
1354
1499
|
module AssociationDatasetMethods
|
1355
1500
|
# The model object that created the association dataset
|
1356
|
-
|
1501
|
+
def model_object
|
1502
|
+
@opts[:model_object]
|
1503
|
+
end
|
1357
1504
|
|
1358
1505
|
# The association reflection related to the association dataset
|
1359
|
-
|
1506
|
+
def association_reflection
|
1507
|
+
@opts[:association_reflection]
|
1508
|
+
end
|
1509
|
+
|
1510
|
+
private
|
1511
|
+
|
1512
|
+
def non_sql_option?(key)
|
1513
|
+
super || key == :model_object || key == :association_reflection
|
1514
|
+
end
|
1360
1515
|
end
|
1361
1516
|
|
1362
1517
|
# Each kind of association adds a number of instance methods to the model class which
|
@@ -1397,7 +1552,7 @@ module Sequel
|
|
1397
1552
|
# Project.associations
|
1398
1553
|
# => [:portfolio, :milestones]
|
1399
1554
|
# Project.association_reflection(:portfolio)
|
1400
|
-
# =>
|
1555
|
+
# => #<Sequel::Model::Associations::ManyToOneAssociationReflection Project.many_to_one :portfolio>
|
1401
1556
|
#
|
1402
1557
|
# Associations should not have the same names as any of the columns in the
|
1403
1558
|
# model's current table they reference. If you are dealing with an existing schema that
|
@@ -1419,13 +1574,19 @@ module Sequel
|
|
1419
1574
|
|
1420
1575
|
# Whether association metadata should be cached in the association reflection. If not cached, it will be computed
|
1421
1576
|
# on demand. In general you only want to set this to false when using code reloading. When using code reloading,
|
1422
|
-
# setting this will make sure that if an associated class is removed or modified, this class will not
|
1577
|
+
# setting this will make sure that if an associated class is removed or modified, this class will not have a reference to
|
1423
1578
|
# the previous class.
|
1424
1579
|
attr_accessor :cache_associations
|
1425
1580
|
|
1426
|
-
# The default options to use for all associations.
|
1581
|
+
# The default options to use for all associations. This hash is merged into the association reflection hash for
|
1582
|
+
# all association reflections.
|
1427
1583
|
attr_accessor :default_association_options
|
1428
1584
|
|
1585
|
+
# The default options to use for all associations of a given type. This is a hash keyed by association type
|
1586
|
+
# symbol. If there is a value for the association type symbol key, the resulting hash will be merged into the
|
1587
|
+
# association reflection hash for all association reflections of that type.
|
1588
|
+
attr_accessor :default_association_type_options
|
1589
|
+
|
1429
1590
|
# The default :eager_limit_strategy option to use for limited or offset associations (default: true, causing Sequel
|
1430
1591
|
# to use what it considers the most appropriate strategy).
|
1431
1592
|
attr_accessor :default_eager_limit_strategy
|
@@ -1463,6 +1624,7 @@ module Sequel
|
|
1463
1624
|
# === Multiple Types
|
1464
1625
|
# :adder :: Proc used to define the private _add_* method for doing the database work
|
1465
1626
|
# to associate the given object to the current object (*_to_many assocations).
|
1627
|
+
# Set to nil to not define a add_* method for the association.
|
1466
1628
|
# :after_add :: Symbol, Proc, or array of both/either specifying a callback to call
|
1467
1629
|
# after a new item is added to the association.
|
1468
1630
|
# :after_load :: Symbol, Proc, or array of both/either specifying a callback to call
|
@@ -1473,6 +1635,8 @@ module Sequel
|
|
1473
1635
|
# after an item is set using the association setter method.
|
1474
1636
|
# :allow_eager :: If set to false, you cannot load the association eagerly
|
1475
1637
|
# via eager or eager_graph
|
1638
|
+
# :allow_eager_graph :: If set to false, you cannot load the association eagerly via eager_graph.
|
1639
|
+
# :allow_filtering_by :: If set to false, you cannot use the association when filtering
|
1476
1640
|
# :before_add :: Symbol, Proc, or array of both/either specifying a callback to call
|
1477
1641
|
# before a new item is added to the association.
|
1478
1642
|
# :before_remove :: Symbol, Proc, or array of both/either specifying a callback to call
|
@@ -1488,16 +1652,22 @@ module Sequel
|
|
1488
1652
|
# singularized unless the type is :many_to_one, :one_to_one, or one_through_one). If this is specified
|
1489
1653
|
# as a string or symbol, you must specify the full class name (e.g. "::SomeModule::MyModel").
|
1490
1654
|
# :class_namespace :: If :class is given as a string or symbol, sets the default namespace in which to look for
|
1491
|
-
# the class. <tt
|
1655
|
+
# the class. <tt>class: 'Foo', class_namespace: 'Bar'</tt> looks for <tt>::Bar::Foo</tt>.)
|
1492
1656
|
# :clearer :: Proc used to define the private _remove_all_* method for doing the database work
|
1493
1657
|
# to remove all objects associated to the current object (*_to_many assocations).
|
1658
|
+
# Set to nil to not define a remove_all_* method for the association.
|
1494
1659
|
# :clone :: Merge the current options and block into the options and block used in defining
|
1495
1660
|
# the given association. Can be used to DRY up a bunch of similar associations that
|
1496
1661
|
# all share the same options such as :class and :key, while changing the order and block used.
|
1497
1662
|
# :conditions :: The conditions to use to filter the association, can be any argument passed to where.
|
1498
|
-
#
|
1663
|
+
# This option is not respected when using eager_graph or association_join, unless it
|
1664
|
+
# is hash or array of two element arrays. Consider also specifying the :graph_block
|
1665
|
+
# option if the value for this option is not a hash or array of two element arrays
|
1666
|
+
# and you plan to use this association in eager_graph or association_join.
|
1667
|
+
# :dataset :: A proc that is used to define the method to get the base dataset to use (before the other
|
1499
1668
|
# options are applied). If the proc accepts an argument, it is passed the related
|
1500
|
-
# association reflection.
|
1669
|
+
# association reflection. It is a best practice to always have the dataset accept an argument
|
1670
|
+
# and use the argument to return the appropriate dataset.
|
1501
1671
|
# :distinct :: Use the DISTINCT clause when selecting associating object, both when
|
1502
1672
|
# lazy loading and eager loading via .eager (but not when using .eager_graph).
|
1503
1673
|
# :eager :: The associations to eagerly load via +eager+ when loading the associated object(s).
|
@@ -1540,31 +1710,37 @@ module Sequel
|
|
1540
1710
|
# :graph_only_conditions :: The conditions to use on the SQL join when eagerly loading
|
1541
1711
|
# the association via +eager_graph+, instead of the default conditions specified by the
|
1542
1712
|
# foreign/primary keys. This option causes the :graph_conditions option to be ignored.
|
1543
|
-
# :graph_order ::
|
1713
|
+
# :graph_order :: the order to use when using eager_graph, instead of the default order. This should be used
|
1544
1714
|
# in the case where :order contains an identifier qualified by the table's name, which may not match
|
1545
1715
|
# the alias used when eager graphing. By setting this to the unqualified identifier, it will be
|
1546
1716
|
# automatically qualified when using eager_graph.
|
1547
1717
|
# :graph_select :: A column or array of columns to select from the associated table
|
1548
1718
|
# when eagerly loading the association via +eager_graph+. Defaults to all
|
1549
1719
|
# columns in the associated table.
|
1720
|
+
# :instance_specific :: Marks the association as instance specific. Should be used if the association block
|
1721
|
+
# uses instance specific state, or transient state (accessing current date/time, etc.).
|
1550
1722
|
# :limit :: Limit the number of records to the provided value. Use
|
1551
1723
|
# an array with two elements for the value to specify a
|
1552
1724
|
# limit (first element) and an offset (second element).
|
1553
1725
|
# :methods_module :: The module that methods the association creates will be placed into. Defaults
|
1554
1726
|
# to the module containing the model's columns.
|
1727
|
+
# :no_association_method :: Do not add a method for the association. This can save memory if the association
|
1728
|
+
# method is never used.
|
1729
|
+
# :no_dataset_method :: Do not add a method for the association dataset. This can save memory if the dataset
|
1730
|
+
# method is never used.
|
1555
1731
|
# :order :: the column(s) by which to order the association dataset. Can be a
|
1556
1732
|
# singular column symbol or an array of column symbols.
|
1557
1733
|
# :order_eager_graph :: Whether to add the association's order to the graphed dataset's order when graphing
|
1558
1734
|
# via +eager_graph+. Defaults to true, so set to false to disable.
|
1559
1735
|
# :read_only :: Do not add a setter method (for many_to_one or one_to_one associations),
|
1560
|
-
# or add_/remove_/remove_all_ methods (for one_to_many and many_to_many associations).
|
1561
|
-
# true for one_through_one associations.
|
1736
|
+
# or add_/remove_/remove_all_ methods (for one_to_many and many_to_many associations).
|
1562
1737
|
# :reciprocal :: the symbol name of the reciprocal association,
|
1563
1738
|
# if it exists. By default, Sequel will try to determine it by looking at the
|
1564
1739
|
# associated model's assocations for a association that matches
|
1565
1740
|
# the current association's key(s). Set to nil to not use a reciprocal.
|
1566
1741
|
# :remover :: Proc used to define the private _remove_* method for doing the database work
|
1567
1742
|
# to remove the association between the given object and the current object (*_to_many assocations).
|
1743
|
+
# Set to nil to not define a remove_* method for the association.
|
1568
1744
|
# :select :: the columns to select. Defaults to the associated class's table_name.* in an association
|
1569
1745
|
# that uses joins, which means it doesn't include the attributes from the
|
1570
1746
|
# join table. If you want to include the join table attributes, you can
|
@@ -1573,6 +1749,7 @@ module Sequel
|
|
1573
1749
|
# the same name in both the join table and the associated table.
|
1574
1750
|
# :setter :: Proc used to define the private _*= method for doing the work to setup the assocation
|
1575
1751
|
# between the given object and the current object (*_to_one associations).
|
1752
|
+
# Set to nil to not define a setter method for the association.
|
1576
1753
|
# :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
|
1577
1754
|
# loading limited associations using the default :union strategy.
|
1578
1755
|
# :validate :: Set to false to not validate when implicitly saving any associated object.
|
@@ -1589,7 +1766,7 @@ module Sequel
|
|
1589
1766
|
# array of symbols for a composite key association.
|
1590
1767
|
# :primary_key_method :: the method symbol or array of method symbols to call on the associated
|
1591
1768
|
# object to get the foreign key values. Defaults to :primary_key option.
|
1592
|
-
# :qualify :: Whether to use
|
1769
|
+
# :qualify :: Whether to use qualified primary keys when loading the association. The default
|
1593
1770
|
# is true, so you must set to false to not qualify. Qualification rarely causes
|
1594
1771
|
# problems, but it's necessary to disable in some cases, such as when you are doing
|
1595
1772
|
# a JOIN USING operation on the column on Oracle.
|
@@ -1629,6 +1806,9 @@ module Sequel
|
|
1629
1806
|
# underscored, sorted, and joined with '_'.
|
1630
1807
|
# :join_table_block :: proc that can be used to modify the dataset used in the add/remove/remove_all
|
1631
1808
|
# methods. Should accept a dataset argument and return a modified dataset if present.
|
1809
|
+
# :join_table_db :: When retrieving records when using lazy loading or eager loading via +eager+, instead of
|
1810
|
+
# a join between to the join table and the associated table, use a separate query for the
|
1811
|
+
# join table using the given Database object.
|
1632
1812
|
# :left_key :: foreign key in join table that points to current model's
|
1633
1813
|
# primary key, as a symbol. Defaults to :"#{self.name.underscore}_id".
|
1634
1814
|
# Can use an array of symbols for a composite key association.
|
@@ -1650,7 +1830,7 @@ module Sequel
|
|
1650
1830
|
# Defaults to :right_primary_key option.
|
1651
1831
|
# :uniq :: Adds a after_load callback that makes the array of objects unique.
|
1652
1832
|
def associate(type, name, opts = OPTS, &block)
|
1653
|
-
raise(Error, 'invalid association type') unless assoc_class = ASSOCIATION_TYPES[type]
|
1833
|
+
raise(Error, 'invalid association type') unless assoc_class = Sequel.synchronize{ASSOCIATION_TYPES[type]}
|
1654
1834
|
raise(Error, 'Model.associate name argument must be a symbol') unless name.is_a?(Symbol)
|
1655
1835
|
|
1656
1836
|
# dup early so we don't modify opts
|
@@ -1658,16 +1838,35 @@ module Sequel
|
|
1658
1838
|
|
1659
1839
|
if opts[:clone]
|
1660
1840
|
cloned_assoc = association_reflection(opts[:clone])
|
1841
|
+
remove_class_name = orig_opts[:class] && !orig_opts[:class_name]
|
1661
1842
|
orig_opts = cloned_assoc[:orig_opts].merge(orig_opts)
|
1843
|
+
orig_opts.delete(:class_name) if remove_class_name
|
1662
1844
|
end
|
1663
1845
|
|
1664
|
-
opts = default_association_options
|
1846
|
+
opts = Hash[default_association_options]
|
1847
|
+
if type_options = default_association_type_options[type]
|
1848
|
+
opts.merge!(type_options)
|
1849
|
+
end
|
1850
|
+
opts.merge!(orig_opts)
|
1851
|
+
opts.merge!(:type => type, :name => name, :cache=>({} if cache_associations), :model => self)
|
1852
|
+
|
1665
1853
|
opts[:block] = block if block
|
1666
|
-
|
1854
|
+
opts[:instance_specific] = true if orig_opts[:dataset]
|
1855
|
+
if !opts.has_key?(:instance_specific) && (block || orig_opts[:block])
|
1667
1856
|
# It's possible the association is instance specific, in that it depends on
|
1668
1857
|
# values other than the foreign key value. This needs to be checked for
|
1669
1858
|
# in certain places to disable optimizations.
|
1670
|
-
opts[:instance_specific] =
|
1859
|
+
opts[:instance_specific] = _association_instance_specific_default(name)
|
1860
|
+
end
|
1861
|
+
if (orig_opts[:instance_specific] || orig_opts[:dataset]) && !opts.has_key?(:allow_eager) && !opts[:eager_loader]
|
1862
|
+
# For associations explicitly marked as instance specific, or that use the
|
1863
|
+
# :dataset option, where :allow_eager is not set, and no :eager_loader is
|
1864
|
+
# provided, disallow eager loading. In these cases, eager loading is
|
1865
|
+
# unlikely to work. This is not done for implicit setting of :instance_specific,
|
1866
|
+
# because implicit use is done by default for all associations with blocks,
|
1867
|
+
# and the vast majority of associations with blocks use the block for filtering
|
1868
|
+
# in a manner compatible with eager loading.
|
1869
|
+
opts[:allow_eager] = false
|
1671
1870
|
end
|
1672
1871
|
opts = assoc_class.new.merge!(opts)
|
1673
1872
|
|
@@ -1675,10 +1874,8 @@ module Sequel
|
|
1675
1874
|
raise(Error, "cannot clone an association to an association of different type (association #{name} with type #{type} cloning #{opts[:clone]} with type #{cloned_assoc[:type]})")
|
1676
1875
|
end
|
1677
1876
|
|
1877
|
+
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
|
1678
1878
|
opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
|
1679
|
-
if !opts.has_key?(:predicate_key) && opts.has_key?(:eager_loading_predicate_key)
|
1680
|
-
opts[:predicate_key] = opts[:eager_loading_predicate_key]
|
1681
|
-
end
|
1682
1879
|
opts[:graph_join_type] ||= :left_outer
|
1683
1880
|
opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
|
1684
1881
|
conds = opts[:conditions]
|
@@ -1686,18 +1883,24 @@ module Sequel
|
|
1686
1883
|
opts[:graph_conditions] = conds if !opts.include?(:graph_conditions) and Sequel.condition_specifier?(conds)
|
1687
1884
|
opts[:graph_conditions] = opts.fetch(:graph_conditions, []).to_a
|
1688
1885
|
opts[:graph_select] = Array(opts[:graph_select]) if opts[:graph_select]
|
1689
|
-
[:before_add, :before_remove, :after_add, :after_remove, :after_load, :before_set, :after_set
|
1690
|
-
opts[cb_type] = Array(opts[cb_type])
|
1886
|
+
[:before_add, :before_remove, :after_add, :after_remove, :after_load, :before_set, :after_set].each do |cb_type|
|
1887
|
+
opts[cb_type] = Array(opts[cb_type]) if opts[cb_type]
|
1888
|
+
end
|
1889
|
+
|
1890
|
+
if opts[:extend]
|
1891
|
+
opts[:extend] = Array(opts[:extend])
|
1892
|
+
opts[:reverse_extend] = opts[:extend].reverse
|
1691
1893
|
end
|
1894
|
+
|
1692
1895
|
late_binding_class_option(opts, opts.returns_array? ? singularize(name) : name)
|
1693
1896
|
|
1694
1897
|
# Remove :class entry if it exists and is nil, to work with cached_fetch
|
1695
1898
|
opts.delete(:class) unless opts[:class]
|
1696
1899
|
|
1697
|
-
|
1698
|
-
def_association_instance_methods(opts)
|
1900
|
+
def_association(opts)
|
1699
1901
|
|
1700
1902
|
orig_opts.delete(:clone)
|
1903
|
+
opts[:orig_class] = orig_opts[:class] || orig_opts[:class_name]
|
1701
1904
|
orig_opts.merge!(:class_name=>opts[:class_name], :class=>opts[:class], :block=>opts[:block])
|
1702
1905
|
opts[:orig_opts] = orig_opts
|
1703
1906
|
# don't add to association_reflections until we are sure there are no errors
|
@@ -1719,6 +1922,25 @@ module Sequel
|
|
1719
1922
|
opts.eager_load_results(eo, &block)
|
1720
1923
|
end
|
1721
1924
|
|
1925
|
+
# Freeze association related metadata when freezing model class.
|
1926
|
+
def freeze
|
1927
|
+
@association_reflections.freeze.each_value(&:freeze)
|
1928
|
+
@autoreloading_associations.freeze.each_value(&:freeze)
|
1929
|
+
@default_association_options.freeze
|
1930
|
+
@default_association_type_options.freeze
|
1931
|
+
@default_association_type_options.each_value(&:freeze)
|
1932
|
+
|
1933
|
+
super
|
1934
|
+
end
|
1935
|
+
|
1936
|
+
# Finalize all associations such that values that are looked up
|
1937
|
+
# dynamically in associated classes are set statically.
|
1938
|
+
# As this modifies the associations, it must be done before
|
1939
|
+
# calling freeze.
|
1940
|
+
def finalize_associations
|
1941
|
+
@association_reflections.each_value(&:finalize)
|
1942
|
+
end
|
1943
|
+
|
1722
1944
|
# Shortcut for adding a many_to_many association, see #associate
|
1723
1945
|
def many_to_many(name, opts=OPTS, &block)
|
1724
1946
|
associate(:many_to_many, name, opts, &block)
|
@@ -1729,7 +1951,7 @@ module Sequel
|
|
1729
1951
|
associate(:many_to_one, name, opts, &block)
|
1730
1952
|
end
|
1731
1953
|
|
1732
|
-
# Shortcut for adding a one_through_one association, see #associate
|
1954
|
+
# Shortcut for adding a one_through_one association, see #associate
|
1733
1955
|
def one_through_one(name, opts=OPTS, &block)
|
1734
1956
|
associate(:one_through_one, name, opts, &block)
|
1735
1957
|
end
|
@@ -1739,15 +1961,21 @@ module Sequel
|
|
1739
1961
|
associate(:one_to_many, name, opts, &block)
|
1740
1962
|
end
|
1741
1963
|
|
1742
|
-
# Shortcut for adding a one_to_one association, see #associate
|
1964
|
+
# Shortcut for adding a one_to_one association, see #associate
|
1743
1965
|
def one_to_one(name, opts=OPTS, &block)
|
1744
1966
|
associate(:one_to_one, name, opts, &block)
|
1745
1967
|
end
|
1746
1968
|
|
1747
|
-
Plugins.inherited_instance_variables(self, :@association_reflections=>:dup, :@autoreloading_associations=>:hash_dup, :@default_association_options=>:dup, :@cache_associations=>nil, :@default_eager_limit_strategy=>nil)
|
1969
|
+
Plugins.inherited_instance_variables(self, :@association_reflections=>:dup, :@autoreloading_associations=>:hash_dup, :@default_association_options=>:dup, :@default_association_type_options=>:hash_dup, :@cache_associations=>nil, :@default_eager_limit_strategy=>nil)
|
1748
1970
|
Plugins.def_dataset_methods(self, [:eager, :eager_graph, :eager_graph_with_options, :association_join, :association_full_join, :association_inner_join, :association_left_join, :association_right_join])
|
1749
1971
|
|
1750
1972
|
private
|
1973
|
+
|
1974
|
+
# The default value for the instance_specific option, if the association
|
1975
|
+
# could be instance specific and the :instance_specific option is not specified.
|
1976
|
+
def _association_instance_specific_default(_)
|
1977
|
+
true
|
1978
|
+
end
|
1751
1979
|
|
1752
1980
|
# The module to use for the association's methods. Defaults to
|
1753
1981
|
# the overridable_methods_module.
|
@@ -1759,7 +1987,22 @@ module Sequel
|
|
1759
1987
|
# can be easily overridden in the class itself while allowing for
|
1760
1988
|
# super to be called.
|
1761
1989
|
def association_module_def(name, opts=OPTS, &block)
|
1762
|
-
association_module(opts)
|
1990
|
+
mod = association_module(opts)
|
1991
|
+
mod.send(:define_method, name, &block)
|
1992
|
+
mod.send(:alias_method, name, name)
|
1993
|
+
end
|
1994
|
+
|
1995
|
+
# Add a method to the module included in the class, so the method
|
1996
|
+
# can be easily overridden in the class itself while allowing for
|
1997
|
+
# super to be called. This method allows passing keywords through
|
1998
|
+
# the defined methods.
|
1999
|
+
def association_module_delegate_def(name, opts, &block)
|
2000
|
+
mod = association_module(opts)
|
2001
|
+
mod.send(:define_method, name, &block)
|
2002
|
+
# :nocov:
|
2003
|
+
mod.send(:ruby2_keywords, name) if mod.respond_to?(:ruby2_keywords, true)
|
2004
|
+
# :nocov:
|
2005
|
+
mod.send(:alias_method, name, name)
|
1763
2006
|
end
|
1764
2007
|
|
1765
2008
|
# Add a private method to the module included in the class.
|
@@ -1768,37 +2011,67 @@ module Sequel
|
|
1768
2011
|
association_module(opts).send(:private, name)
|
1769
2012
|
end
|
1770
2013
|
|
2014
|
+
# Delegate to the type-specific association method to setup the
|
2015
|
+
# association, and define the association instance methods.
|
2016
|
+
def def_association(opts)
|
2017
|
+
send(:"def_#{opts[:type]}", opts)
|
2018
|
+
def_association_instance_methods(opts)
|
2019
|
+
end
|
2020
|
+
|
1771
2021
|
# Adds the association method to the association methods module.
|
1772
2022
|
def def_association_method(opts)
|
1773
|
-
association_module_def(opts.association_method, opts)
|
2023
|
+
association_module_def(opts.association_method, opts) do |dynamic_opts=OPTS, &block|
|
2024
|
+
load_associated_objects(opts, dynamic_opts, &block)
|
2025
|
+
end
|
1774
2026
|
end
|
1775
2027
|
|
1776
2028
|
# Define all of the association instance methods for this association.
|
1777
2029
|
def def_association_instance_methods(opts)
|
1778
|
-
|
1779
|
-
|
2030
|
+
# Always set the method names in the association reflection, even if they
|
2031
|
+
# are not used, for backwards compatibility.
|
2032
|
+
opts[:dataset_method] = :"#{opts[:name]}_dataset"
|
2033
|
+
if opts.returns_array?
|
2034
|
+
sname = singularize(opts[:name])
|
2035
|
+
opts[:_add_method] = :"_add_#{sname}"
|
2036
|
+
opts[:add_method] = :"add_#{sname}"
|
2037
|
+
opts[:_remove_method] = :"_remove_#{sname}"
|
2038
|
+
opts[:remove_method] = :"remove_#{sname}"
|
2039
|
+
opts[:_remove_all_method] = :"_remove_all_#{opts[:name]}"
|
2040
|
+
opts[:remove_all_method] = :"remove_all_#{opts[:name]}"
|
2041
|
+
else
|
2042
|
+
opts[:_setter_method] = :"_#{opts[:name]}="
|
2043
|
+
opts[:setter_method] = :"#{opts[:name]}="
|
2044
|
+
end
|
2045
|
+
|
2046
|
+
association_module_def(opts.dataset_method, opts){_dataset(opts)} unless opts[:no_dataset_method]
|
2047
|
+
if opts[:block]
|
2048
|
+
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
2049
|
+
end
|
2050
|
+
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
2051
|
+
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
2052
|
+
def_association_method(opts) unless opts[:no_association_method]
|
1780
2053
|
|
1781
2054
|
return if opts[:read_only]
|
1782
2055
|
|
1783
2056
|
if opts[:setter] && opts[:_setter]
|
1784
2057
|
# This is backwards due to backwards compatibility
|
1785
|
-
association_module_private_def(opts
|
1786
|
-
association_module_def(opts
|
2058
|
+
association_module_private_def(opts[:_setter_method], opts, &opts[:setter])
|
2059
|
+
association_module_def(opts[:setter_method], opts, &opts[:_setter])
|
1787
2060
|
end
|
1788
2061
|
|
1789
2062
|
if adder = opts[:adder]
|
1790
|
-
association_module_private_def(opts
|
1791
|
-
|
2063
|
+
association_module_private_def(opts[:_add_method], opts, &adder)
|
2064
|
+
association_module_delegate_def(opts[:add_method], opts){|o,*args| add_associated_object(opts, o, *args)}
|
1792
2065
|
end
|
1793
2066
|
|
1794
2067
|
if remover = opts[:remover]
|
1795
|
-
association_module_private_def(opts
|
1796
|
-
|
2068
|
+
association_module_private_def(opts[:_remove_method], opts, &remover)
|
2069
|
+
association_module_delegate_def(opts[:remove_method], opts){|o,*args| remove_associated_object(opts, o, *args)}
|
1797
2070
|
end
|
1798
2071
|
|
1799
2072
|
if clearer = opts[:clearer]
|
1800
|
-
association_module_private_def(opts
|
1801
|
-
|
2073
|
+
association_module_private_def(opts[:_remove_all_method], opts, &clearer)
|
2074
|
+
association_module_delegate_def(opts[:remove_all_method], opts){|*args| remove_all_associated_objects(opts, *args)}
|
1802
2075
|
end
|
1803
2076
|
end
|
1804
2077
|
|
@@ -1820,14 +2093,84 @@ module Sequel
|
|
1820
2093
|
raise(Error, "mismatched number of right keys: #{rcks.inspect} vs #{rcpks.inspect}") unless rcks.length == rcpks.length
|
1821
2094
|
end
|
1822
2095
|
opts[:uses_left_composite_keys] = lcks.length > 1
|
1823
|
-
opts[:uses_right_composite_keys] = rcks.length > 1
|
2096
|
+
uses_rcks = opts[:uses_right_composite_keys] = rcks.length > 1
|
1824
2097
|
opts[:cartesian_product_number] ||= one_through_one ? 0 : 1
|
1825
2098
|
join_table = (opts[:join_table] ||= opts.default_join_table)
|
1826
2099
|
opts[:left_key_alias] ||= opts.default_associated_key_alias
|
1827
2100
|
opts[:graph_join_table_join_type] ||= opts[:graph_join_type]
|
1828
|
-
|
1829
|
-
|
1830
|
-
|
2101
|
+
if opts[:uniq]
|
2102
|
+
opts[:after_load] ||= []
|
2103
|
+
opts[:after_load].unshift(:array_uniq!)
|
2104
|
+
end
|
2105
|
+
if join_table_db = opts[:join_table_db]
|
2106
|
+
opts[:use_placeholder_loader] = false
|
2107
|
+
opts[:allow_eager_graph] = false
|
2108
|
+
opts[:allow_filtering_by] = false
|
2109
|
+
opts[:eager_limit_strategy] = nil
|
2110
|
+
join_table_ds = join_table_db.from(join_table)
|
2111
|
+
opts[:dataset] ||= proc do |r|
|
2112
|
+
vals = join_table_ds.where(lcks.zip(lcpks.map{|k| get_column_value(k)})).select_map(right)
|
2113
|
+
ds = r.associated_dataset.where(opts.right_primary_key => vals)
|
2114
|
+
if uses_rcks
|
2115
|
+
vals.delete_if{|v| v.any?(&:nil?)}
|
2116
|
+
else
|
2117
|
+
vals.delete(nil)
|
2118
|
+
end
|
2119
|
+
ds = ds.clone(:no_results=>true) if vals.empty?
|
2120
|
+
ds
|
2121
|
+
end
|
2122
|
+
opts[:eager_loader] ||= proc do |eo|
|
2123
|
+
h = eo[:id_map]
|
2124
|
+
assign_singular = opts.assign_singular?
|
2125
|
+
rpk = opts.right_primary_key
|
2126
|
+
name = opts[:name]
|
2127
|
+
|
2128
|
+
join_map = join_table_ds.where(left=>h.keys).select_hash_groups(right, left)
|
2129
|
+
|
2130
|
+
if uses_rcks
|
2131
|
+
join_map.delete_if{|v,| v.any?(&:nil?)}
|
2132
|
+
else
|
2133
|
+
join_map.delete(nil)
|
2134
|
+
end
|
2135
|
+
|
2136
|
+
eo = Hash[eo]
|
2137
|
+
|
2138
|
+
if join_map.empty?
|
2139
|
+
eo[:no_results] = true
|
2140
|
+
else
|
2141
|
+
join_map.each_value do |vs|
|
2142
|
+
vs.replace(vs.flat_map{|v| h[v]})
|
2143
|
+
vs.uniq!
|
2144
|
+
end
|
2145
|
+
|
2146
|
+
eo[:loader] = false
|
2147
|
+
eo[:right_keys] = join_map.keys
|
2148
|
+
end
|
2149
|
+
|
2150
|
+
opts[:model].eager_load_results(opts, eo) do |assoc_record|
|
2151
|
+
rpkv = if uses_rcks
|
2152
|
+
assoc_record.values.values_at(*rpk)
|
2153
|
+
else
|
2154
|
+
assoc_record.values[rpk]
|
2155
|
+
end
|
2156
|
+
|
2157
|
+
objects = join_map[rpkv]
|
2158
|
+
|
2159
|
+
if assign_singular
|
2160
|
+
objects.each do |object|
|
2161
|
+
object.associations[name] ||= assoc_record
|
2162
|
+
end
|
2163
|
+
else
|
2164
|
+
objects.each do |object|
|
2165
|
+
object.associations[name].push(assoc_record)
|
2166
|
+
end
|
2167
|
+
end
|
2168
|
+
end
|
2169
|
+
end
|
2170
|
+
else
|
2171
|
+
opts[:dataset] ||= opts.association_dataset_proc
|
2172
|
+
opts[:eager_loader] ||= opts.method(:default_eager_loader)
|
2173
|
+
end
|
1831
2174
|
|
1832
2175
|
join_type = opts[:graph_join_type]
|
1833
2176
|
select = opts[:graph_select]
|
@@ -1861,50 +2204,60 @@ module Sequel
|
|
1861
2204
|
return if opts[:read_only]
|
1862
2205
|
|
1863
2206
|
if one_through_one
|
1864
|
-
opts
|
1865
|
-
|
1866
|
-
|
1867
|
-
|
1868
|
-
|
1869
|
-
checked_transaction do
|
1870
|
-
current = jtds.first
|
2207
|
+
unless opts.has_key?(:setter)
|
2208
|
+
opts[:setter] = proc do |o|
|
2209
|
+
h = {}
|
2210
|
+
lh = lcks.zip(lcpks.map{|k| get_column_value(k)})
|
2211
|
+
jtds = _join_table_dataset(opts).where(lh)
|
1871
2212
|
|
1872
|
-
|
1873
|
-
|
1874
|
-
rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
|
1875
|
-
end
|
2213
|
+
checked_transaction do
|
2214
|
+
current = jtds.first
|
1876
2215
|
|
1877
|
-
if current
|
1878
|
-
current_values = rcks.map{|k| current[k]}
|
1879
|
-
jtds = jtds.where(rcks.zip(current_values))
|
1880
2216
|
if o
|
1881
|
-
|
1882
|
-
|
2217
|
+
new_values = []
|
2218
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
|
2219
|
+
end
|
2220
|
+
|
2221
|
+
if current
|
2222
|
+
current_values = rcks.map{|k| current[k]}
|
2223
|
+
jtds = jtds.where(rcks.zip(current_values))
|
2224
|
+
if o
|
2225
|
+
if current_values != new_values
|
2226
|
+
jtds.update(h)
|
2227
|
+
end
|
2228
|
+
else
|
2229
|
+
jtds.delete
|
1883
2230
|
end
|
1884
|
-
|
1885
|
-
|
2231
|
+
elsif o
|
2232
|
+
lh.each{|k,v| h[k] = v}
|
2233
|
+
jtds.insert(h)
|
1886
2234
|
end
|
1887
|
-
elsif o
|
1888
|
-
lh.each{|k,v| h[k] = v}
|
1889
|
-
jtds.insert(h)
|
1890
2235
|
end
|
1891
2236
|
end
|
1892
2237
|
end
|
1893
|
-
opts
|
2238
|
+
if opts.fetch(:setter, true)
|
2239
|
+
opts[:_setter] = proc{|o| set_one_through_one_associated_object(opts, o)}
|
2240
|
+
end
|
1894
2241
|
else
|
1895
|
-
opts
|
1896
|
-
|
1897
|
-
|
1898
|
-
|
1899
|
-
|
2242
|
+
unless opts.has_key?(:adder)
|
2243
|
+
opts[:adder] = proc do |o|
|
2244
|
+
h = {}
|
2245
|
+
lcks.zip(lcpks).each{|k, pk| h[k] = get_column_value(pk)}
|
2246
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.get_column_value(pk)}
|
2247
|
+
_join_table_dataset(opts).insert(h)
|
2248
|
+
end
|
1900
2249
|
end
|
1901
2250
|
|
1902
|
-
opts
|
1903
|
-
|
2251
|
+
unless opts.has_key?(:remover)
|
2252
|
+
opts[:remover] = proc do |o|
|
2253
|
+
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)}) + rcks.zip(opts.right_primary_key_methods.map{|k| o.get_column_value(k)})).delete
|
2254
|
+
end
|
1904
2255
|
end
|
1905
2256
|
|
1906
|
-
opts
|
1907
|
-
|
2257
|
+
unless opts.has_key?(:clearer)
|
2258
|
+
opts[:clearer] = proc do
|
2259
|
+
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)})).delete
|
2260
|
+
end
|
1908
2261
|
end
|
1909
2262
|
end
|
1910
2263
|
end
|
@@ -1943,9 +2296,7 @@ module Sequel
|
|
1943
2296
|
|
1944
2297
|
eager_load_results(opts, eo) do |assoc_record|
|
1945
2298
|
hash_key = uses_cks ? pk_meths.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(opts.primary_key_method)
|
1946
|
-
|
1947
|
-
objects.each{|object| object.associations[name] = assoc_record}
|
1948
|
-
end
|
2299
|
+
h[hash_key].each{|object| object.associations[name] = assoc_record}
|
1949
2300
|
end
|
1950
2301
|
end
|
1951
2302
|
|
@@ -1958,13 +2309,17 @@ module Sequel
|
|
1958
2309
|
graph_cks = opts[:graph_keys]
|
1959
2310
|
opts[:eager_grapher] ||= proc do |eo|
|
1960
2311
|
ds = eo[:self]
|
1961
|
-
ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : opts.primary_keys.zip(graph_cks) + conditions,
|
2312
|
+
ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : opts.primary_keys.zip(graph_cks) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
|
1962
2313
|
end
|
1963
2314
|
|
1964
2315
|
return if opts[:read_only]
|
1965
2316
|
|
1966
|
-
|
1967
|
-
|
2317
|
+
unless opts.has_key?(:setter)
|
2318
|
+
opts[:setter] = proc{|o| cks.zip(opts.primary_key_methods).each{|k, pk| set_column_value(:"#{k}=", (o.get_column_value(pk) if o))}}
|
2319
|
+
end
|
2320
|
+
if opts.fetch(:setter, true)
|
2321
|
+
opts[:_setter] = proc{|o| set_associated_object(opts, o)}
|
2322
|
+
end
|
1968
2323
|
end
|
1969
2324
|
|
1970
2325
|
# Configures one_to_many and one_to_one association reflections and adds the related association methods
|
@@ -1992,7 +2347,7 @@ module Sequel
|
|
1992
2347
|
eager_load_results(opts, eo) do |assoc_record|
|
1993
2348
|
assoc_record.values.delete(delete_rn) if delete_rn
|
1994
2349
|
hash_key = uses_cks ? km.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(km)
|
1995
|
-
|
2350
|
+
objects = h[hash_key]
|
1996
2351
|
if assign_singular
|
1997
2352
|
objects.each do |object|
|
1998
2353
|
unless object.associations[name]
|
@@ -2018,7 +2373,7 @@ module Sequel
|
|
2018
2373
|
graph_block = opts[:graph_block]
|
2019
2374
|
opts[:eager_grapher] ||= proc do |eo|
|
2020
2375
|
ds = eo[:self]
|
2021
|
-
ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions,
|
2376
|
+
ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
|
2022
2377
|
# We only load reciprocals for one_to_many associations, as other reciprocals don't make sense
|
2023
2378
|
ds.opts[:eager_graph][:reciprocals][eo[:table_alias]] = opts.reciprocal
|
2024
2379
|
ds
|
@@ -2031,33 +2386,59 @@ module Sequel
|
|
2031
2386
|
cks.each{|k| ck_nil_hash[k] = nil}
|
2032
2387
|
|
2033
2388
|
if one_to_one
|
2034
|
-
opts
|
2035
|
-
|
2036
|
-
|
2037
|
-
|
2038
|
-
|
2039
|
-
|
2040
|
-
|
2041
|
-
|
2042
|
-
|
2389
|
+
unless opts.has_key?(:setter)
|
2390
|
+
opts[:setter] = proc do |o|
|
2391
|
+
up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
|
2392
|
+
|
2393
|
+
if (froms = up_ds.opts[:from]) && (from = froms[0]) && (from.is_a?(Sequel::Dataset) || (from.is_a?(Sequel::SQL::AliasedExpression) && from.expression.is_a?(Sequel::Dataset)))
|
2394
|
+
if old = up_ds.first
|
2395
|
+
cks.each{|k| old.set_column_value(:"#{k}=", nil)}
|
2396
|
+
end
|
2397
|
+
save_old = true
|
2398
|
+
end
|
2399
|
+
|
2400
|
+
if o
|
2401
|
+
if !o.new? && !save_old
|
2402
|
+
up_ds = up_ds.exclude(o.pk_hash)
|
2403
|
+
end
|
2404
|
+
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2405
|
+
end
|
2406
|
+
|
2407
|
+
checked_transaction do
|
2408
|
+
if save_old
|
2409
|
+
old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
|
2410
|
+
else
|
2411
|
+
up_ds.skip_limit_check.update(ck_nil_hash)
|
2412
|
+
end
|
2413
|
+
|
2414
|
+
o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
2415
|
+
end
|
2043
2416
|
end
|
2044
2417
|
end
|
2045
|
-
opts
|
2418
|
+
if opts.fetch(:setter, true)
|
2419
|
+
opts[:_setter] = proc{|o| set_one_to_one_associated_object(opts, o)}
|
2420
|
+
end
|
2046
2421
|
else
|
2047
2422
|
save_opts[:raise_on_failure] = opts[:raise_on_save_failure] != false
|
2048
2423
|
|
2049
|
-
opts
|
2050
|
-
|
2051
|
-
|
2424
|
+
unless opts.has_key?(:adder)
|
2425
|
+
opts[:adder] = proc do |o|
|
2426
|
+
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2427
|
+
o.save(save_opts)
|
2428
|
+
end
|
2052
2429
|
end
|
2053
2430
|
|
2054
|
-
opts
|
2055
|
-
|
2056
|
-
|
2431
|
+
unless opts.has_key?(:remover)
|
2432
|
+
opts[:remover] = proc do |o|
|
2433
|
+
cks.each{|k| o.set_column_value(:"#{k}=", nil)}
|
2434
|
+
o.save(save_opts)
|
2435
|
+
end
|
2057
2436
|
end
|
2058
2437
|
|
2059
|
-
opts
|
2060
|
-
|
2438
|
+
unless opts.has_key?(:clearer)
|
2439
|
+
opts[:clearer] = proc do
|
2440
|
+
_apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)}))).update(ck_nil_hash)
|
2441
|
+
end
|
2061
2442
|
end
|
2062
2443
|
end
|
2063
2444
|
end
|
@@ -2101,8 +2482,10 @@ module Sequel
|
|
2101
2482
|
# retrieving associations after freezing will still work in most cases,
|
2102
2483
|
# but the associations will not be cached in the association cache.
|
2103
2484
|
def freeze
|
2104
|
-
associations
|
2485
|
+
associations
|
2105
2486
|
super
|
2487
|
+
associations.freeze
|
2488
|
+
self
|
2106
2489
|
end
|
2107
2490
|
|
2108
2491
|
private
|
@@ -2112,15 +2495,16 @@ module Sequel
|
|
2112
2495
|
unless ds.kind_of?(AssociationDatasetMethods)
|
2113
2496
|
ds = opts.apply_dataset_changes(ds)
|
2114
2497
|
end
|
2115
|
-
ds.model_object
|
2498
|
+
ds = ds.clone(:model_object => self)
|
2116
2499
|
ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
|
2117
|
-
|
2500
|
+
# block method is private
|
2501
|
+
ds = send(opts[:block_method], ds) if opts[:block_method]
|
2118
2502
|
ds
|
2119
2503
|
end
|
2120
2504
|
|
2121
2505
|
# Return a dataset for the association after applying any dynamic callback.
|
2122
2506
|
def _associated_dataset(opts, dynamic_opts)
|
2123
|
-
ds =
|
2507
|
+
ds = public_send(opts.dataset_method)
|
2124
2508
|
if callback = dynamic_opts[:callback]
|
2125
2509
|
ds = callback.call(ds)
|
2126
2510
|
end
|
@@ -2137,17 +2521,18 @@ module Sequel
|
|
2137
2521
|
# Return an association dataset for the given association reflection
|
2138
2522
|
def _dataset(opts)
|
2139
2523
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
2140
|
-
ds = if opts[:
|
2141
|
-
|
2524
|
+
ds = if opts[:dataset_opt_arity] == 1
|
2525
|
+
# dataset_opt_method is private
|
2526
|
+
send(opts[:dataset_opt_method], opts)
|
2142
2527
|
else
|
2143
|
-
|
2528
|
+
send(opts[:dataset_opt_method])
|
2144
2529
|
end
|
2145
2530
|
_apply_association_options(opts, ds)
|
2146
2531
|
end
|
2147
2532
|
|
2148
2533
|
# Dataset for the join table of the given many to many association reflection
|
2149
2534
|
def _join_table_dataset(opts)
|
2150
|
-
ds = model.db.from(opts.join_table_source)
|
2535
|
+
ds = (opts[:join_table_db] || model.db).from(opts.join_table_source)
|
2151
2536
|
opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
|
2152
2537
|
end
|
2153
2538
|
|
@@ -2168,7 +2553,12 @@ module Sequel
|
|
2168
2553
|
if loader = _associated_object_loader(opts, dynamic_opts)
|
2169
2554
|
loader.all(*opts.predicate_key_values(self))
|
2170
2555
|
else
|
2171
|
-
_associated_dataset(opts, dynamic_opts)
|
2556
|
+
ds = _associated_dataset(opts, dynamic_opts)
|
2557
|
+
if ds.opts[:no_results]
|
2558
|
+
[]
|
2559
|
+
else
|
2560
|
+
ds.all
|
2561
|
+
end
|
2172
2562
|
end
|
2173
2563
|
end
|
2174
2564
|
|
@@ -2200,7 +2590,8 @@ module Sequel
|
|
2200
2590
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
2201
2591
|
ensure_associated_primary_key(opts, o, *args)
|
2202
2592
|
return if run_association_callbacks(opts, :before_add, o) == false
|
2203
|
-
|
2593
|
+
# Allow calling private _add method
|
2594
|
+
return if !send(opts[:_add_method], o, *args) && opts.handle_silent_modification_failure?
|
2204
2595
|
if array = associations[opts[:name]] and !array.include?(o)
|
2205
2596
|
array.push(o)
|
2206
2597
|
end
|
@@ -2208,6 +2599,9 @@ module Sequel
|
|
2208
2599
|
run_association_callbacks(opts, :after_add, o)
|
2209
2600
|
o
|
2210
2601
|
end
|
2602
|
+
# :nocov:
|
2603
|
+
ruby2_keywords(:add_associated_object) if respond_to?(:ruby2_keywords, true)
|
2604
|
+
# :nocov:
|
2211
2605
|
|
2212
2606
|
# Add/Set the current object to/as the given object's reciprocal association.
|
2213
2607
|
def add_reciprocal_object(opts, o)
|
@@ -2232,7 +2626,32 @@ module Sequel
|
|
2232
2626
|
# cached associations.
|
2233
2627
|
def change_column_value(column, value)
|
2234
2628
|
if assocs = model.autoreloading_associations[column]
|
2235
|
-
|
2629
|
+
vals = @values
|
2630
|
+
if new?
|
2631
|
+
# Do deeper checking for new objects, so that associations are
|
2632
|
+
# not deleted when values do not change. This code is run at
|
2633
|
+
# a higher level for existing objects.
|
2634
|
+
if value == (c = vals[column]) && value.class == c.class
|
2635
|
+
# If the value is the same, there is no reason to delete
|
2636
|
+
# the related associations, so exit early in that case.
|
2637
|
+
return super
|
2638
|
+
end
|
2639
|
+
|
2640
|
+
only_delete_nil = c.nil?
|
2641
|
+
elsif vals[column].nil?
|
2642
|
+
only_delete_nil = true
|
2643
|
+
end
|
2644
|
+
|
2645
|
+
if only_delete_nil
|
2646
|
+
# If the current foreign key value is nil, but the association
|
2647
|
+
# is already present in the cache, it was probably added to the
|
2648
|
+
# cache for a reason, and we do not want to delete it in that case.
|
2649
|
+
# However, we still want to delete associations with nil values
|
2650
|
+
# to remove the cached false negative.
|
2651
|
+
assocs.each{|a| associations.delete(a) if associations[a].nil?}
|
2652
|
+
else
|
2653
|
+
assocs.each{|a| associations.delete(a)}
|
2654
|
+
end
|
2236
2655
|
end
|
2237
2656
|
super
|
2238
2657
|
end
|
@@ -2254,25 +2673,10 @@ module Sequel
|
|
2254
2673
|
self
|
2255
2674
|
end
|
2256
2675
|
|
2257
|
-
#
|
2258
|
-
# reasons, you can pass true/false/nil or a callable argument to
|
2259
|
-
# associations. That will be going away in Sequel 5, but we'll still
|
2260
|
-
# support it until then.
|
2676
|
+
# If a block is given, assign it as the :callback option in the hash, and return the hash.
|
2261
2677
|
def load_association_objects_options(dynamic_opts, &block)
|
2262
|
-
|
2263
|
-
|
2264
|
-
{:reload=>dynamic_opts}
|
2265
|
-
when Hash
|
2266
|
-
Hash[dynamic_opts]
|
2267
|
-
else
|
2268
|
-
if dynamic_opts.respond_to?(:call)
|
2269
|
-
{:callback=>dynamic_opts}
|
2270
|
-
else
|
2271
|
-
{:reload=>true}
|
2272
|
-
end
|
2273
|
-
end
|
2274
|
-
|
2275
|
-
if block_given?
|
2678
|
+
if block
|
2679
|
+
dynamic_opts = Hash[dynamic_opts]
|
2276
2680
|
dynamic_opts[:callback] = block
|
2277
2681
|
end
|
2278
2682
|
|
@@ -2280,7 +2684,7 @@ module Sequel
|
|
2280
2684
|
end
|
2281
2685
|
|
2282
2686
|
# Load the associated objects using the dataset, handling callbacks, reciprocals, and caching.
|
2283
|
-
def load_associated_objects(opts, dynamic_opts
|
2687
|
+
def load_associated_objects(opts, dynamic_opts, &block)
|
2284
2688
|
dynamic_opts = load_association_objects_options(dynamic_opts, &block)
|
2285
2689
|
name = opts[:name]
|
2286
2690
|
if associations.include?(name) && !dynamic_opts[:callback] && !dynamic_opts[:reload]
|
@@ -2334,11 +2738,15 @@ module Sequel
|
|
2334
2738
|
# Remove all associated objects from the given association
|
2335
2739
|
def remove_all_associated_objects(opts, *args)
|
2336
2740
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
2337
|
-
|
2741
|
+
# Allow calling private _remove_all method
|
2742
|
+
send(opts[:_remove_all_method], *args)
|
2338
2743
|
ret = associations[opts[:name]].each{|o| remove_reciprocal_object(opts, o)} if associations.include?(opts[:name])
|
2339
2744
|
associations[opts[:name]] = []
|
2340
2745
|
ret
|
2341
2746
|
end
|
2747
|
+
# :nocov:
|
2748
|
+
ruby2_keywords(:remove_all_associated_objects) if respond_to?(:ruby2_keywords, true)
|
2749
|
+
# :nocov:
|
2342
2750
|
|
2343
2751
|
# Remove the given associated object from the given association
|
2344
2752
|
def remove_associated_object(opts, o, *args)
|
@@ -2347,18 +2755,22 @@ module Sequel
|
|
2347
2755
|
o = remove_check_existing_object_from_pk(opts, o, *args)
|
2348
2756
|
elsif !o.is_a?(klass)
|
2349
2757
|
raise(Sequel::Error, "associated object #{o.inspect} not of correct type #{klass}")
|
2350
|
-
elsif opts.remove_should_check_existing? &&
|
2758
|
+
elsif opts.remove_should_check_existing? && public_send(opts.dataset_method).where(o.pk_hash).empty?
|
2351
2759
|
raise(Sequel::Error, "associated object #{o.inspect} is not currently associated to #{inspect}")
|
2352
2760
|
end
|
2353
2761
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
2354
2762
|
raise(Sequel::Error, "associated object #{o.inspect} does not have a primary key") if opts.need_associated_primary_key? && !o.pk
|
2355
2763
|
return if run_association_callbacks(opts, :before_remove, o) == false
|
2356
|
-
|
2764
|
+
# Allow calling private _remove method
|
2765
|
+
return if !send(opts[:_remove_method], o, *args) && opts.handle_silent_modification_failure?
|
2357
2766
|
associations[opts[:name]].delete_if{|x| o === x} if associations.include?(opts[:name])
|
2358
2767
|
remove_reciprocal_object(opts, o)
|
2359
2768
|
run_association_callbacks(opts, :after_remove, o)
|
2360
2769
|
o
|
2361
2770
|
end
|
2771
|
+
# :nocov:
|
2772
|
+
ruby2_keywords(:remove_associated_object) if respond_to?(:ruby2_keywords, true)
|
2773
|
+
# :nocov:
|
2362
2774
|
|
2363
2775
|
# Check that the object from the associated table specified by the primary key
|
2364
2776
|
# is currently associated to the receiver. If it is associated, return the object, otherwise
|
@@ -2366,7 +2778,7 @@ module Sequel
|
|
2366
2778
|
def remove_check_existing_object_from_pk(opts, o, *args)
|
2367
2779
|
key = o
|
2368
2780
|
pkh = opts.associated_class.qualified_primary_key_hash(key)
|
2369
|
-
raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o =
|
2781
|
+
raise(Sequel::Error, "no object with key(s) #{key.inspect} is currently associated to #{inspect}") unless o = public_send(opts.dataset_method).first(pkh)
|
2370
2782
|
o
|
2371
2783
|
end
|
2372
2784
|
|
@@ -2384,40 +2796,52 @@ module Sequel
|
|
2384
2796
|
|
2385
2797
|
# Run the callback for the association with the object.
|
2386
2798
|
def run_association_callbacks(reflection, callback_type, object)
|
2387
|
-
|
2388
|
-
|
2389
|
-
|
2390
|
-
|
2391
|
-
|
2392
|
-
|
2393
|
-
|
2394
|
-
|
2395
|
-
|
2396
|
-
|
2397
|
-
|
2398
|
-
|
2399
|
-
|
2400
|
-
raise Error, "callbacks should either be Procs or Symbols"
|
2401
|
-
end
|
2402
|
-
|
2403
|
-
if res == false and stop_on_false
|
2404
|
-
raise(HookFailed, "Unable to modify association for #{inspect}: one of the #{callback_type} hooks returned false")
|
2799
|
+
return unless cbs = reflection[callback_type]
|
2800
|
+
|
2801
|
+
begin
|
2802
|
+
cbs.each do |cb|
|
2803
|
+
case cb
|
2804
|
+
when Symbol
|
2805
|
+
# Allow calling private methods in association callbacks
|
2806
|
+
send(cb, object)
|
2807
|
+
when Proc
|
2808
|
+
cb.call(self, object)
|
2809
|
+
else
|
2810
|
+
raise Error, "callbacks should either be Procs or Symbols"
|
2811
|
+
end
|
2405
2812
|
end
|
2813
|
+
rescue HookFailed
|
2814
|
+
# The reason we automatically set raise_error for singular associations is that
|
2815
|
+
# assignment in ruby always returns the argument instead of the result of the
|
2816
|
+
# method, so we can't return nil to signal that the association callback prevented
|
2817
|
+
# the modification
|
2818
|
+
return false unless raise_on_save_failure || !reflection.returns_array?
|
2819
|
+
raise
|
2406
2820
|
end
|
2407
|
-
rescue HookFailed
|
2408
|
-
return false unless raise_error
|
2409
|
-
raise
|
2410
2821
|
end
|
2411
2822
|
|
2412
2823
|
# Set the given object as the associated object for the given *_to_one association reflection
|
2413
2824
|
def _set_associated_object(opts, o)
|
2414
2825
|
a = associations[opts[:name]]
|
2415
|
-
|
2826
|
+
reciprocal = opts.reciprocal
|
2827
|
+
if set_associated_object_if_same?
|
2828
|
+
if reciprocal
|
2829
|
+
remove_reciprocal = a && (a != o || a.associations[reciprocal] != self)
|
2830
|
+
add_reciprocal = o && o.associations[reciprocal] != self
|
2831
|
+
end
|
2832
|
+
else
|
2833
|
+
return if a && a == o
|
2834
|
+
if reciprocal
|
2835
|
+
remove_reciprocal = a
|
2836
|
+
add_reciprocal = o
|
2837
|
+
end
|
2838
|
+
end
|
2416
2839
|
run_association_callbacks(opts, :before_set, o)
|
2417
|
-
remove_reciprocal_object(opts, a) if
|
2418
|
-
|
2840
|
+
remove_reciprocal_object(opts, a) if remove_reciprocal
|
2841
|
+
# Allow calling private _setter method
|
2842
|
+
send(opts[:_setter_method], o)
|
2419
2843
|
associations[opts[:name]] = o
|
2420
|
-
add_reciprocal_object(opts, o) if
|
2844
|
+
add_reciprocal_object(opts, o) if add_reciprocal
|
2421
2845
|
run_association_callbacks(opts, :after_set, o)
|
2422
2846
|
o
|
2423
2847
|
end
|
@@ -2472,48 +2896,112 @@ module Sequel
|
|
2472
2896
|
# Album.eager(:artist, :genre).all
|
2473
2897
|
# Album.eager_graph(:artist, :genre).all
|
2474
2898
|
# Album.eager(:artist).eager(:genre).all
|
2475
|
-
# Album.eager_graph(:artist).
|
2476
|
-
# Artist.eager(:
|
2477
|
-
# Artist.eager_graph(:
|
2478
|
-
# Artist.eager(:
|
2479
|
-
# Artist.eager_graph(:
|
2899
|
+
# Album.eager_graph(:artist).eager_graph(:genre).all
|
2900
|
+
# Artist.eager(albums: :tracks).all
|
2901
|
+
# Artist.eager_graph(albums: :tracks).all
|
2902
|
+
# Artist.eager(albums: {tracks: :genre}).all
|
2903
|
+
# Artist.eager_graph(albums: {tracks: :genre}).all
|
2480
2904
|
#
|
2481
2905
|
# You can also pass a callback as a hash value in order to customize the dataset being
|
2482
2906
|
# eager loaded at query time, analogous to the way the :eager_block association option
|
2483
2907
|
# allows you to customize it at association definition time. For example,
|
2484
2908
|
# if you wanted artists with their albums since 1990:
|
2485
2909
|
#
|
2486
|
-
# Artist.eager(:
|
2910
|
+
# Artist.eager(albums: proc{|ds| ds.where{year > 1990}})
|
2487
2911
|
#
|
2488
2912
|
# Or if you needed albums and their artist's name only, using a single query:
|
2489
2913
|
#
|
2490
|
-
# Albums.eager_graph(:
|
2914
|
+
# Albums.eager_graph(artist: proc{|ds| ds.select(:name)})
|
2491
2915
|
#
|
2492
2916
|
# To cascade eager loading while using a callback, you substitute the cascaded
|
2493
2917
|
# associations with a single entry hash that has the proc callback as the key and
|
2494
2918
|
# the cascaded associations as the value. This will load artists with their albums
|
2495
2919
|
# since 1990, and also the tracks on those albums and the genre for those tracks:
|
2496
2920
|
#
|
2497
|
-
# Artist.eager(:
|
2921
|
+
# Artist.eager(albums: {proc{|ds| ds.where{year > 1990}}=>{tracks: :genre}})
|
2498
2922
|
module DatasetMethods
|
2499
|
-
Sequel::Dataset.def_mutation_method(:eager, :eager_graph, :module=>self)
|
2500
|
-
|
2501
2923
|
%w'inner left right full'.each do |type|
|
2502
|
-
class_eval
|
2924
|
+
class_eval(<<-END, __FILE__, __LINE__+1)
|
2503
2925
|
def association_#{type}_join(*associations)
|
2504
2926
|
_association_join(:#{type}, associations)
|
2505
2927
|
end
|
2506
|
-
END
|
2928
|
+
END
|
2507
2929
|
end
|
2508
2930
|
|
2509
2931
|
# Adds one or more INNER JOINs to the existing dataset using the keys and conditions
|
2510
|
-
# specified by the given association.
|
2511
|
-
#
|
2932
|
+
# specified by the given association(s). Take the same arguments as eager_graph, and
|
2933
|
+
# operates similarly, but only adds the joins as opposed to making the other changes
|
2934
|
+
# (such as adding selected columns and setting up eager loading).
|
2935
|
+
#
|
2936
|
+
# The following methods also exist for specifying a different type of JOIN:
|
2512
2937
|
#
|
2513
2938
|
# association_full_join :: FULL JOIN
|
2514
2939
|
# association_inner_join :: INNER JOIN
|
2515
2940
|
# association_left_join :: LEFT JOIN
|
2516
2941
|
# association_right_join :: RIGHT JOIN
|
2942
|
+
#
|
2943
|
+
# Examples:
|
2944
|
+
#
|
2945
|
+
# # For each album, association_join load the artist
|
2946
|
+
# Album.association_join(:artist).all
|
2947
|
+
# # SELECT *
|
2948
|
+
# # FROM albums
|
2949
|
+
# # INNER JOIN artists AS artist ON (artists.id = albums.artist_id)
|
2950
|
+
#
|
2951
|
+
# # For each album, association_join load the artist, using a specified alias
|
2952
|
+
# Album.association_join(Sequel[:artist].as(:a)).all
|
2953
|
+
# # SELECT *
|
2954
|
+
# # FROM albums
|
2955
|
+
# # INNER JOIN artists AS a ON (a.id = albums.artist_id)
|
2956
|
+
#
|
2957
|
+
# # For each album, association_join load the artist and genre
|
2958
|
+
# Album.association_join(:artist, :genre).all
|
2959
|
+
# Album.association_join(:artist).association_join(:genre).all
|
2960
|
+
# # SELECT *
|
2961
|
+
# # FROM albums
|
2962
|
+
# # INNER JOIN artists AS artist ON (artist.id = albums.artist_id)
|
2963
|
+
# # INNER JOIN genres AS genre ON (genre.id = albums.genre_id)
|
2964
|
+
#
|
2965
|
+
# # For each artist, association_join load albums and tracks for each album
|
2966
|
+
# Artist.association_join(albums: :tracks).all
|
2967
|
+
# # SELECT *
|
2968
|
+
# # FROM artists
|
2969
|
+
# # INNER JOIN albums ON (albums.artist_id = artists.id)
|
2970
|
+
# # INNER JOIN tracks ON (tracks.album_id = albums.id)
|
2971
|
+
#
|
2972
|
+
# # For each artist, association_join load albums, tracks for each album, and genre for each track
|
2973
|
+
# Artist.association_join(albums: {tracks: :genre}).all
|
2974
|
+
# # SELECT *
|
2975
|
+
# # FROM artists
|
2976
|
+
# # INNER JOIN albums ON (albums.artist_id = artists.id)
|
2977
|
+
# # INNER JOIN tracks ON (tracks.album_id = albums.id)
|
2978
|
+
# # INNER JOIN genres AS genre ON (genre.id = tracks.genre_id)
|
2979
|
+
#
|
2980
|
+
# # For each artist, association_join load albums with year > 1990
|
2981
|
+
# Artist.association_join(albums: proc{|ds| ds.where{year > 1990}}).all
|
2982
|
+
# # SELECT *
|
2983
|
+
# # FROM artists
|
2984
|
+
# # INNER JOIN (
|
2985
|
+
# # SELECT * FROM albums WHERE (year > 1990)
|
2986
|
+
# # ) AS albums ON (albums.artist_id = artists.id)
|
2987
|
+
#
|
2988
|
+
# # For each artist, association_join load albums and tracks 1-10 for each album
|
2989
|
+
# Artist.association_join(albums: {tracks: proc{|ds| ds.where(number: 1..10)}}).all
|
2990
|
+
# # SELECT *
|
2991
|
+
# # FROM artists
|
2992
|
+
# # INNER JOIN albums ON (albums.artist_id = artists.id)
|
2993
|
+
# # INNER JOIN (
|
2994
|
+
# # SELECT * FROM tracks WHERE ((number >= 1) AND (number <= 10))
|
2995
|
+
# # ) AS tracks ON (tracks.albums_id = albums.id)
|
2996
|
+
#
|
2997
|
+
# # For each artist, association_join load albums with year > 1990, and tracks for those albums
|
2998
|
+
# Artist.association_join(albums: {proc{|ds| ds.where{year > 1990}}=>:tracks}).all
|
2999
|
+
# # SELECT *
|
3000
|
+
# # FROM artists
|
3001
|
+
# # INNER JOIN (
|
3002
|
+
# # SELECT * FROM albums WHERE (year > 1990)
|
3003
|
+
# # ) AS albums ON (albums.artist_id = artists.id)
|
3004
|
+
# # INNER JOIN tracks ON (tracks.album_id = albums.id)
|
2517
3005
|
def association_join(*associations)
|
2518
3006
|
association_inner_join(*associations)
|
2519
3007
|
end
|
@@ -2526,11 +3014,13 @@ END
|
|
2526
3014
|
# types, this is a simple transformation, but for +many_to_many+ associations this
|
2527
3015
|
# creates a subquery to the join table.
|
2528
3016
|
def complex_expression_sql_append(sql, op, args)
|
2529
|
-
r = args
|
2530
|
-
if (((op == :'=' || op == :'!=')
|
2531
|
-
(multiple = ((op == :IN || op == :'NOT IN')
|
2532
|
-
l = args
|
3017
|
+
r = args[1]
|
3018
|
+
if (((op == :'=' || op == :'!=') && r.is_a?(Sequel::Model)) ||
|
3019
|
+
(multiple = ((op == :IN || op == :'NOT IN') && ((is_ds = r.is_a?(Sequel::Dataset)) || (r.respond_to?(:all?) && r.all?{|x| x.is_a?(Sequel::Model)})))))
|
3020
|
+
l = args[0]
|
2533
3021
|
if ar = model.association_reflections[l]
|
3022
|
+
raise Error, "filtering by associations is not allowed for #{ar.inspect}" if ar[:allow_filtering_by] == false
|
3023
|
+
|
2534
3024
|
if multiple
|
2535
3025
|
klass = ar.associated_class
|
2536
3026
|
if is_ds
|
@@ -2584,7 +3074,7 @@ END
|
|
2584
3074
|
# it avoids problems such as aliasing conflicts and creating cartesian product
|
2585
3075
|
# result sets if multiple one_to_many or many_to_many eager associations are requested.
|
2586
3076
|
#
|
2587
|
-
# One limitation of using this method is that you cannot filter the dataset
|
3077
|
+
# One limitation of using this method is that you cannot filter the current dataset
|
2588
3078
|
# based on values of columns in an associated table, since the associations are loaded
|
2589
3079
|
# in separate queries. To do that you need to load all associations in the
|
2590
3080
|
# same query, and extract an object graph from the results of that query. If you
|
@@ -2593,15 +3083,66 @@ END
|
|
2593
3083
|
#
|
2594
3084
|
# Each association's order, if defined, is respected.
|
2595
3085
|
# If the association uses a block or has an :eager_block argument, it is used.
|
3086
|
+
#
|
3087
|
+
# To modify the associated dataset that will be used for the eager load, you should use a
|
3088
|
+
# hash for the association, with the key being the association name symbol, and the value being
|
3089
|
+
# a callable object that is called with the associated dataset and should return a modified
|
3090
|
+
# dataset. If that association also has dependent associations, instead of a callable object,
|
3091
|
+
# use a hash with the callable object being the key, and the dependent association(s) as the value.
|
3092
|
+
#
|
3093
|
+
# Examples:
|
3094
|
+
#
|
3095
|
+
# # For each album, eager load the artist
|
3096
|
+
# Album.eager(:artist).all
|
3097
|
+
# # SELECT * FROM albums
|
3098
|
+
# # SELECT * FROM artists WHERE (id IN (...))
|
3099
|
+
#
|
3100
|
+
# # For each album, eager load the artist and genre
|
3101
|
+
# Album.eager(:artist, :genre).all
|
3102
|
+
# Album.eager(:artist).eager(:genre).all
|
3103
|
+
# # SELECT * FROM albums
|
3104
|
+
# # SELECT * FROM artists WHERE (id IN (...))
|
3105
|
+
# # SELECT * FROM genres WHERE (id IN (...))
|
3106
|
+
#
|
3107
|
+
# # For each artist, eager load albums and tracks for each album
|
3108
|
+
# Artist.eager(albums: :tracks).all
|
3109
|
+
# # SELECT * FROM artists
|
3110
|
+
# # SELECT * FROM albums WHERE (artist_id IN (...))
|
3111
|
+
# # SELECT * FROM tracks WHERE (album_id IN (...))
|
3112
|
+
#
|
3113
|
+
# # For each artist, eager load albums, tracks for each album, and genre for each track
|
3114
|
+
# Artist.eager(albums: {tracks: :genre}).all
|
3115
|
+
# # SELECT * FROM artists
|
3116
|
+
# # SELECT * FROM albums WHERE (artist_id IN (...))
|
3117
|
+
# # SELECT * FROM tracks WHERE (album_id IN (...))
|
3118
|
+
# # SELECT * FROM genre WHERE (id IN (...))
|
3119
|
+
#
|
3120
|
+
# # For each artist, eager load albums with year > 1990
|
3121
|
+
# Artist.eager(albums: proc{|ds| ds.where{year > 1990}}).all
|
3122
|
+
# # SELECT * FROM artists
|
3123
|
+
# # SELECT * FROM albums WHERE ((year > 1990) AND (artist_id IN (...)))
|
3124
|
+
#
|
3125
|
+
# # For each artist, eager load albums and tracks 1-10 for each album
|
3126
|
+
# Artist.eager(albums: {tracks: proc{|ds| ds.where(number: 1..10)}}).all
|
3127
|
+
# # SELECT * FROM artists
|
3128
|
+
# # SELECT * FROM albums WHERE (artist_id IN (...))
|
3129
|
+
# # SELECT * FROM tracks WHERE ((number >= 1) AND (number <= 10) AND (album_id IN (...)))
|
3130
|
+
#
|
3131
|
+
# # For each artist, eager load albums with year > 1990, and tracks for those albums
|
3132
|
+
# Artist.eager(albums: {proc{|ds| ds.where{year > 1990}}=>:tracks}).all
|
3133
|
+
# # SELECT * FROM artists
|
3134
|
+
# # SELECT * FROM albums WHERE ((year > 1990) AND (artist_id IN (...)))
|
3135
|
+
# # SELECT * FROM albums WHERE (artist_id IN (...))
|
2596
3136
|
def eager(*associations)
|
2597
3137
|
opts = @opts[:eager]
|
2598
3138
|
association_opts = eager_options_for_associations(associations)
|
2599
|
-
opts = opts ?
|
2600
|
-
clone(:eager=>opts)
|
3139
|
+
opts = opts ? opts.merge(association_opts) : association_opts
|
3140
|
+
clone(:eager=>opts.freeze)
|
2601
3141
|
end
|
2602
3142
|
|
2603
3143
|
# The secondary eager loading method. Loads all associations in a single query. This
|
2604
|
-
# method should only be used if you need to filter or order based on columns in associated tables
|
3144
|
+
# method should only be used if you need to filter or order based on columns in associated tables,
|
3145
|
+
# or if you have done comparative benchmarking and determined it is faster.
|
2605
3146
|
#
|
2606
3147
|
# This method uses <tt>Dataset#graph</tt> to create appropriate aliases for columns in all the
|
2607
3148
|
# tables. Then it uses the graph's metadata to build the associations from the single hash, and
|
@@ -2609,7 +3150,7 @@ END
|
|
2609
3150
|
#
|
2610
3151
|
# Be very careful when using this with multiple one_to_many or many_to_many associations, as you can
|
2611
3152
|
# create large cartesian products. If you must graph multiple one_to_many and many_to_many associations,
|
2612
|
-
# make sure your filters are narrow if
|
3153
|
+
# make sure your filters are narrow if the datasets are large.
|
2613
3154
|
#
|
2614
3155
|
# Each association's order, if defined, is respected. +eager_graph+ probably
|
2615
3156
|
# won't work correctly on a limited dataset, unless you are
|
@@ -2620,6 +3161,88 @@ END
|
|
2620
3161
|
#
|
2621
3162
|
# Like +eager+, you need to call +all+ on the dataset for the eager loading to work. If you just
|
2622
3163
|
# call +each+, it will yield plain hashes, each containing all columns from all the tables.
|
3164
|
+
#
|
3165
|
+
# To modify the associated dataset that will be joined to the current dataset, you should use a
|
3166
|
+
# hash for the association, with the key being the association name symbol, and the value being
|
3167
|
+
# a callable object that is called with the associated dataset and should return a modified
|
3168
|
+
# dataset. If that association also has dependent associations, instead of a callable object,
|
3169
|
+
# use a hash with the callable object being the key, and the dependent association(s) as the value.
|
3170
|
+
#
|
3171
|
+
# You can specify an custom alias and/or join type on a per-association basis by providing an
|
3172
|
+
# Sequel::SQL::AliasedExpression object instead of an a Symbol for the association name.
|
3173
|
+
#
|
3174
|
+
# You cannot mix calls to +eager_graph+ and +graph+ on the same dataset.
|
3175
|
+
#
|
3176
|
+
# Examples:
|
3177
|
+
#
|
3178
|
+
# # For each album, eager_graph load the artist
|
3179
|
+
# Album.eager_graph(:artist).all
|
3180
|
+
# # SELECT ...
|
3181
|
+
# # FROM albums
|
3182
|
+
# # LEFT OUTER JOIN artists AS artist ON (artists.id = albums.artist_id)
|
3183
|
+
#
|
3184
|
+
# # For each album, eager_graph load the artist, using a specified alias
|
3185
|
+
# Album.eager_graph(Sequel[:artist].as(:a)).all
|
3186
|
+
# # SELECT ...
|
3187
|
+
# # FROM albums
|
3188
|
+
# # LEFT OUTER JOIN artists AS a ON (a.id = albums.artist_id)
|
3189
|
+
#
|
3190
|
+
# # For each album, eager_graph load the artist, using a specified alias
|
3191
|
+
# # and custom join type
|
3192
|
+
#
|
3193
|
+
# Album.eager_graph(Sequel[:artist].as(:a, join_type: :inner)).all
|
3194
|
+
# # SELECT ...
|
3195
|
+
# # FROM albums
|
3196
|
+
# # INNER JOIN artists AS a ON (a.id = albums.artist_id)
|
3197
|
+
#
|
3198
|
+
# # For each album, eager_graph load the artist and genre
|
3199
|
+
# Album.eager_graph(:artist, :genre).all
|
3200
|
+
# Album.eager_graph(:artist).eager_graph(:genre).all
|
3201
|
+
# # SELECT ...
|
3202
|
+
# # FROM albums
|
3203
|
+
# # LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id)
|
3204
|
+
# # LEFT OUTER JOIN genres AS genre ON (genre.id = albums.genre_id)
|
3205
|
+
#
|
3206
|
+
# # For each artist, eager_graph load albums and tracks for each album
|
3207
|
+
# Artist.eager_graph(albums: :tracks).all
|
3208
|
+
# # SELECT ...
|
3209
|
+
# # FROM artists
|
3210
|
+
# # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
|
3211
|
+
# # LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
|
3212
|
+
#
|
3213
|
+
# # For each artist, eager_graph load albums, tracks for each album, and genre for each track
|
3214
|
+
# Artist.eager_graph(albums: {tracks: :genre}).all
|
3215
|
+
# # SELECT ...
|
3216
|
+
# # FROM artists
|
3217
|
+
# # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
|
3218
|
+
# # LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
|
3219
|
+
# # LEFT OUTER JOIN genres AS genre ON (genre.id = tracks.genre_id)
|
3220
|
+
#
|
3221
|
+
# # For each artist, eager_graph load albums with year > 1990
|
3222
|
+
# Artist.eager_graph(albums: proc{|ds| ds.where{year > 1990}}).all
|
3223
|
+
# # SELECT ...
|
3224
|
+
# # FROM artists
|
3225
|
+
# # LEFT OUTER JOIN (
|
3226
|
+
# # SELECT * FROM albums WHERE (year > 1990)
|
3227
|
+
# # ) AS albums ON (albums.artist_id = artists.id)
|
3228
|
+
#
|
3229
|
+
# # For each artist, eager_graph load albums and tracks 1-10 for each album
|
3230
|
+
# Artist.eager_graph(albums: {tracks: proc{|ds| ds.where(number: 1..10)}}).all
|
3231
|
+
# # SELECT ...
|
3232
|
+
# # FROM artists
|
3233
|
+
# # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
|
3234
|
+
# # LEFT OUTER JOIN (
|
3235
|
+
# # SELECT * FROM tracks WHERE ((number >= 1) AND (number <= 10))
|
3236
|
+
# # ) AS tracks ON (tracks.albums_id = albums.id)
|
3237
|
+
#
|
3238
|
+
# # For each artist, eager_graph load albums with year > 1990, and tracks for those albums
|
3239
|
+
# Artist.eager_graph(albums: {proc{|ds| ds.where{year > 1990}}=>:tracks}).all
|
3240
|
+
# # SELECT ...
|
3241
|
+
# # FROM artists
|
3242
|
+
# # LEFT OUTER JOIN (
|
3243
|
+
# # SELECT * FROM albums WHERE (year > 1990)
|
3244
|
+
# # ) AS albums ON (albums.artist_id = artists.id)
|
3245
|
+
# # LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
|
2623
3246
|
def eager_graph(*associations)
|
2624
3247
|
eager_graph_with_options(associations)
|
2625
3248
|
end
|
@@ -2645,8 +3268,11 @@ END
|
|
2645
3268
|
# significantly slower in some cases (perhaps even the majority of cases), so you should
|
2646
3269
|
# only use this if you have benchmarked that it is faster for your use cases.
|
2647
3270
|
def eager_graph_with_options(associations, opts=OPTS)
|
3271
|
+
return self if associations.empty?
|
3272
|
+
|
3273
|
+
opts = opts.dup unless opts.frozen?
|
2648
3274
|
associations = [associations] unless associations.is_a?(Array)
|
2649
|
-
if eg = @opts[:eager_graph]
|
3275
|
+
ds = if eg = @opts[:eager_graph]
|
2650
3276
|
eg = eg.dup
|
2651
3277
|
[:requirements, :reflections, :reciprocals, :limits].each{|k| eg[k] = eg[k].dup}
|
2652
3278
|
eg[:local] = opts
|
@@ -2660,13 +3286,17 @@ END
|
|
2660
3286
|
# :limits :: Any limit/offset array slicing that need to be handled in ruby land after loading
|
2661
3287
|
opts = {:requirements=>{}, :master=>alias_symbol(first_source), :reflections=>{}, :reciprocals=>{}, :limits=>{}, :local=>opts, :cartesian_product_number=>0, :row_proc=>row_proc}
|
2662
3288
|
ds = clone(:eager_graph=>opts)
|
2663
|
-
ds.eager_graph_associations(ds, model, ds.opts[:eager_graph][:master], [], *associations).naked
|
3289
|
+
ds = ds.eager_graph_associations(ds, model, ds.opts[:eager_graph][:master], [], *associations).naked
|
2664
3290
|
end
|
3291
|
+
|
3292
|
+
ds.opts[:eager_graph].freeze
|
3293
|
+
ds.opts[:eager_graph].each_value{|v| v.freeze if v.is_a?(Hash)}
|
3294
|
+
ds
|
2665
3295
|
end
|
2666
3296
|
|
2667
3297
|
# If the dataset is being eagerly loaded, default to calling all
|
2668
3298
|
# instead of each.
|
2669
|
-
def
|
3299
|
+
def as_hash(key_column=nil, value_column=nil, opts=OPTS)
|
2670
3300
|
if (@opts[:eager_graph] || @opts[:eager]) && !opts.has_key?(:all)
|
2671
3301
|
opts = Hash[opts]
|
2672
3302
|
opts[:all] = true
|
@@ -2691,7 +3321,7 @@ END
|
|
2691
3321
|
def ungraphed
|
2692
3322
|
ds = super.clone(:eager_graph=>nil)
|
2693
3323
|
if (eg = @opts[:eager_graph]) && (rp = eg[:row_proc])
|
2694
|
-
ds
|
3324
|
+
ds = ds.with_row_proc(rp)
|
2695
3325
|
end
|
2696
3326
|
ds
|
2697
3327
|
end
|
@@ -2709,11 +3339,16 @@ END
|
|
2709
3339
|
# ta :: table_alias used for the parent association
|
2710
3340
|
# requirements :: an array, used as a stack for requirements
|
2711
3341
|
# r :: association reflection for the current association, or an SQL::AliasedExpression
|
2712
|
-
# with the reflection as the expression
|
3342
|
+
# with the reflection as the expression, the alias base as the alias (or nil to
|
3343
|
+
# use the default alias), and an optional hash with a :join_type entry as the columns
|
3344
|
+
# to use a custom join type.
|
2713
3345
|
# *associations :: any associations dependent on this one
|
2714
3346
|
def eager_graph_association(ds, model, ta, requirements, r, *associations)
|
2715
3347
|
if r.is_a?(SQL::AliasedExpression)
|
2716
3348
|
alias_base = r.alias
|
3349
|
+
if r.columns.is_a?(Hash)
|
3350
|
+
join_type = r.columns[:join_type]
|
3351
|
+
end
|
2717
3352
|
r = r.expression
|
2718
3353
|
else
|
2719
3354
|
alias_base = r[:graph_alias_base]
|
@@ -2731,9 +3366,14 @@ END
|
|
2731
3366
|
end
|
2732
3367
|
local_opts = ds.opts[:eager_graph][:local]
|
2733
3368
|
limit_strategy = r.eager_graph_limit_strategy(local_opts[:limit_strategy])
|
2734
|
-
|
3369
|
+
|
3370
|
+
if r[:conditions] && !Sequel.condition_specifier?(r[:conditions]) && !r[:orig_opts].has_key?(:graph_conditions) && !r[:orig_opts].has_key?(:graph_only_conditions) && !r.has_key?(:graph_block)
|
3371
|
+
raise Error, "Cannot eager_graph association when :conditions specified and not a hash or an array of pairs. Specify :graph_conditions, :graph_only_conditions, or :graph_block for the association. Model: #{r[:model]}, association: #{r[:name]}"
|
3372
|
+
end
|
3373
|
+
|
3374
|
+
ds = loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>(ta == ds.opts[:eager_graph][:master]) ? first_source : qualifier_from_alias_symbol(ta, first_source), :callback=>callback, :join_type=>join_type || local_opts[:join_type], :join_only=>local_opts[:join_only], :limit_strategy=>limit_strategy, :from_self_alias=>ds.opts[:eager_graph][:master])
|
2735
3375
|
if r[:order_eager_graph] && (order = r.fetch(:graph_order, r[:order]))
|
2736
|
-
ds = ds.
|
3376
|
+
ds = ds.order_append(*qualified_expression(order, assoc_table_alias))
|
2737
3377
|
end
|
2738
3378
|
eager_graph = ds.opts[:eager_graph]
|
2739
3379
|
eager_graph[:requirements][assoc_table_alias] = requirements.dup
|
@@ -2756,7 +3396,6 @@ END
|
|
2756
3396
|
# requirements :: an array, used as a stack for requirements
|
2757
3397
|
# *associations :: the associations to add to the graph
|
2758
3398
|
def eager_graph_associations(ds, model, ta, requirements, *associations)
|
2759
|
-
return ds if associations.empty?
|
2760
3399
|
associations.flatten.each do |association|
|
2761
3400
|
ds = case association
|
2762
3401
|
when Symbol, SQL::AliasedExpression
|
@@ -2776,7 +3415,7 @@ END
|
|
2776
3415
|
# Replace the array of plain hashes with an array of model objects will all eager_graphed
|
2777
3416
|
# associations set in the associations cache for each object.
|
2778
3417
|
def eager_graph_build_associations(hashes)
|
2779
|
-
hashes.replace(
|
3418
|
+
hashes.replace(_eager_graph_build_associations(hashes, eager_graph_loader))
|
2780
3419
|
end
|
2781
3420
|
|
2782
3421
|
private
|
@@ -2787,12 +3426,18 @@ END
|
|
2787
3426
|
clone(:join=>clone(:graph_from_self=>false).eager_graph_with_options(associations, :join_type=>type, :join_only=>true).opts[:join])
|
2788
3427
|
end
|
2789
3428
|
|
3429
|
+
# Process the array of hashes using the eager graph loader to return an array
|
3430
|
+
# of model objects with the associations set.
|
3431
|
+
def _eager_graph_build_associations(hashes, egl)
|
3432
|
+
egl.load(hashes)
|
3433
|
+
end
|
3434
|
+
|
2790
3435
|
# If the association has conditions itself, then it requires additional filters be
|
2791
3436
|
# added to the current dataset to ensure that the passed in object would also be
|
2792
3437
|
# included by the association's conditions.
|
2793
3438
|
def add_association_filter_conditions(ref, obj, expr)
|
2794
3439
|
if expr != SQL::Constants::FALSE && ref.filter_by_associations_add_conditions?
|
2795
|
-
Sequel
|
3440
|
+
Sequel[ref.filter_by_associations_conditions_expression(obj)]
|
2796
3441
|
else
|
2797
3442
|
expr
|
2798
3443
|
end
|
@@ -2820,6 +3465,7 @@ END
|
|
2820
3465
|
# Return an expression for filtering by the given association reflection and associated object.
|
2821
3466
|
def association_filter_expression(op, ref, obj)
|
2822
3467
|
meth = :"#{ref[:type]}_association_filter_expression"
|
3468
|
+
# Allow calling private association specific method to get filter expression
|
2823
3469
|
send(meth, op, ref, obj) if respond_to?(meth, true)
|
2824
3470
|
end
|
2825
3471
|
|
@@ -2870,23 +3516,60 @@ END
|
|
2870
3516
|
# Allow associations that are eagerly graphed to be specified as an SQL::AliasedExpression, for
|
2871
3517
|
# per-call determining of the alias base.
|
2872
3518
|
def eager_graph_check_association(model, association)
|
2873
|
-
if association.is_a?(SQL::AliasedExpression)
|
2874
|
-
|
3519
|
+
reflection = if association.is_a?(SQL::AliasedExpression)
|
3520
|
+
expr = association.expression
|
3521
|
+
if expr.is_a?(SQL::Identifier)
|
3522
|
+
expr = expr.value
|
3523
|
+
if expr.is_a?(String)
|
3524
|
+
expr = expr.to_sym
|
3525
|
+
end
|
3526
|
+
end
|
3527
|
+
|
3528
|
+
check_reflection = check_association(model, expr)
|
3529
|
+
SQL::AliasedExpression.new(check_reflection, association.alias || expr, association.columns)
|
2875
3530
|
else
|
2876
|
-
check_association(model, association)
|
3531
|
+
check_reflection = check_association(model, association)
|
3532
|
+
end
|
3533
|
+
|
3534
|
+
if check_reflection && check_reflection[:allow_eager_graph] == false
|
3535
|
+
raise Error, "eager_graph not allowed for #{reflection.inspect}"
|
2877
3536
|
end
|
3537
|
+
|
3538
|
+
reflection
|
2878
3539
|
end
|
2879
3540
|
|
2880
|
-
#
|
3541
|
+
# The EagerGraphLoader instance used for converting eager_graph results.
|
3542
|
+
def eager_graph_loader
|
3543
|
+
unless egl = cache_get(:_model_eager_graph_loader)
|
3544
|
+
egl = cache_set(:_model_eager_graph_loader, EagerGraphLoader.new(self))
|
3545
|
+
end
|
3546
|
+
egl.dup
|
3547
|
+
end
|
3548
|
+
|
3549
|
+
# Eagerly load all specified associations.
|
2881
3550
|
def eager_load(a, eager_assoc=@opts[:eager])
|
2882
3551
|
return if a.empty?
|
2883
|
-
|
3552
|
+
|
3553
|
+
# Reflections for all associations to eager load
|
3554
|
+
reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
3555
|
+
|
3556
|
+
perform_eager_loads(prepare_eager_load(a, reflections, eager_assoc))
|
3557
|
+
|
3558
|
+
reflections.each do |r|
|
3559
|
+
a.each{|object| object.send(:run_association_callbacks, r, :after_load, object.associations[r[:name]])} if r[:after_load]
|
3560
|
+
end
|
3561
|
+
|
3562
|
+
nil
|
3563
|
+
end
|
3564
|
+
|
3565
|
+
# Prepare a hash loaders and eager options which will be used to implement the eager loading.
|
3566
|
+
def prepare_eager_load(a, reflections, eager_assoc)
|
3567
|
+
eager_load_data = {}
|
3568
|
+
|
3569
|
+
# Key is foreign/primary key name symbol.
|
2884
3570
|
# Value is hash with keys being foreign/primary key values (generally integers)
|
2885
|
-
#
|
2886
|
-
# specific foreign/primary key
|
3571
|
+
# and values being an array of current model objects with that specific foreign/primary key
|
2887
3572
|
key_hash = {}
|
2888
|
-
# Reflections for all associations to eager load
|
2889
|
-
reflections = eager_assoc.keys.collect{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
2890
3573
|
|
2891
3574
|
# Populate the key_hash entry for each association being eagerly loaded
|
2892
3575
|
reflections.each do |r|
|
@@ -2917,17 +3600,30 @@ END
|
|
2917
3600
|
id_map = nil
|
2918
3601
|
end
|
2919
3602
|
|
2920
|
-
loader = r[:eager_loader]
|
2921
3603
|
associations = eager_assoc[r[:name]]
|
2922
3604
|
if associations.respond_to?(:call)
|
2923
3605
|
eager_block = associations
|
2924
|
-
associations =
|
3606
|
+
associations = OPTS
|
2925
3607
|
elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && pr_assoc.first.respond_to?(:call)
|
2926
3608
|
eager_block, associations = pr_assoc
|
2927
3609
|
end
|
2928
|
-
|
2929
|
-
|
2930
|
-
end
|
3610
|
+
|
3611
|
+
eager_load_data[r[:eager_loader]] = {:key_hash=>key_hash, :rows=>a, :associations=>associations, :self=>self, :eager_block=>eager_block, :id_map=>id_map}
|
3612
|
+
end
|
3613
|
+
|
3614
|
+
eager_load_data
|
3615
|
+
end
|
3616
|
+
|
3617
|
+
# Using the hash of loaders and eager options, perform the eager loading.
|
3618
|
+
def perform_eager_loads(eager_load_data)
|
3619
|
+
eager_load_data.map do |loader, eo|
|
3620
|
+
perform_eager_load(loader, eo)
|
3621
|
+
end
|
3622
|
+
end
|
3623
|
+
|
3624
|
+
# Perform eager loading for a single association using the loader and eager options.
|
3625
|
+
def perform_eager_load(loader, eo)
|
3626
|
+
loader.call(eo)
|
2931
3627
|
end
|
2932
3628
|
|
2933
3629
|
# Return a subquery expression for filering by a many_to_many association
|
@@ -2982,6 +3678,10 @@ END
|
|
2982
3678
|
end
|
2983
3679
|
alias one_to_one_association_filter_expression one_to_many_association_filter_expression
|
2984
3680
|
|
3681
|
+
def non_sql_option?(key)
|
3682
|
+
super || key == :eager || key == :eager_graph
|
3683
|
+
end
|
3684
|
+
|
2985
3685
|
# Build associations from the graph if #eager_graph was used,
|
2986
3686
|
# and/or load other associations if #eager was used.
|
2987
3687
|
def post_load(all_records)
|
@@ -3011,7 +3711,6 @@ END
|
|
3011
3711
|
# Hash with table alias symbol keys and [limit, offset] values
|
3012
3712
|
attr_reader :limit_map
|
3013
3713
|
|
3014
|
-
# Hash with table alias symbol keys and callable values used to create model instances
|
3015
3714
|
# The table alias symbol for the primary model
|
3016
3715
|
attr_reader :master
|
3017
3716
|
|
@@ -3054,19 +3753,22 @@ END
|
|
3054
3753
|
after_load_map = @after_load_map = {}
|
3055
3754
|
reflection_map.each do |k, v|
|
3056
3755
|
alias_map[k] = v[:name]
|
3057
|
-
after_load_map[k] = v[:after_load]
|
3756
|
+
after_load_map[k] = v[:after_load] if v[:after_load]
|
3058
3757
|
type_map[k] = if v.returns_array?
|
3059
3758
|
true
|
3060
3759
|
elsif (limit_and_offset = limit_map[k]) && !limit_and_offset.last.nil?
|
3061
3760
|
:offset
|
3062
3761
|
end
|
3063
3762
|
end
|
3763
|
+
after_load_map.freeze
|
3764
|
+
alias_map.freeze
|
3765
|
+
type_map.freeze
|
3064
3766
|
|
3065
3767
|
# Make dependency map hash out of requirements array for each association.
|
3066
3768
|
# This builds a tree of dependencies that will be used for recursion
|
3067
3769
|
# to ensure that all parts of the object graph are loaded into the
|
3068
3770
|
# appropriate subordinate association.
|
3069
|
-
@dependency_map = {}
|
3771
|
+
dependency_map = @dependency_map = {}
|
3070
3772
|
# Sort the associations by requirements length, so that
|
3071
3773
|
# requirements are added to the dependency hash before their
|
3072
3774
|
# dependencies.
|
@@ -3082,20 +3784,14 @@ END
|
|
3082
3784
|
hash[ta] = {}
|
3083
3785
|
end
|
3084
3786
|
end
|
3787
|
+
freezer = lambda do |h|
|
3788
|
+
h.freeze
|
3789
|
+
h.each_value(&freezer)
|
3790
|
+
end
|
3791
|
+
freezer.call(dependency_map)
|
3085
3792
|
|
3086
|
-
# This mapping is used to make sure that duplicate entries in the
|
3087
|
-
# result set are mapped to a single record. For example, using a
|
3088
|
-
# single one_to_many association with 10 associated records,
|
3089
|
-
# the main object column values appear in the object graph 10 times.
|
3090
|
-
# We map by primary key, if available, or by the object's entire values,
|
3091
|
-
# if not. The mapping must be per table, so create sub maps for each table
|
3092
|
-
# alias.
|
3093
|
-
records_map = {@master=>{}}
|
3094
|
-
alias_map.keys.each{|ta| records_map[ta] = {}}
|
3095
|
-
@records_map = records_map
|
3096
|
-
|
3097
3793
|
datasets = opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
|
3098
|
-
column_aliases = opts[:
|
3794
|
+
column_aliases = opts[:graph][:column_aliases]
|
3099
3795
|
primary_keys = {}
|
3100
3796
|
column_maps = {}
|
3101
3797
|
models = {}
|
@@ -3119,9 +3815,9 @@ END
|
|
3119
3815
|
h.select{|ca, c| primary_keys[ta] = ca if pk == c}
|
3120
3816
|
end
|
3121
3817
|
end
|
3122
|
-
@column_maps = column_maps
|
3123
|
-
@primary_keys = primary_keys
|
3124
|
-
@row_procs = row_procs
|
3818
|
+
@column_maps = column_maps.freeze
|
3819
|
+
@primary_keys = primary_keys.freeze
|
3820
|
+
@row_procs = row_procs.freeze
|
3125
3821
|
|
3126
3822
|
# For performance, create two special maps for the master table,
|
3127
3823
|
# so you can skip a hash lookup.
|
@@ -3133,22 +3829,35 @@ END
|
|
3133
3829
|
# used for performance, to get all values in one hash lookup instead of
|
3134
3830
|
# separate hash lookups for each data structure.
|
3135
3831
|
ta_map = {}
|
3136
|
-
alias_map.
|
3137
|
-
ta_map[ta] = [
|
3832
|
+
alias_map.each_key do |ta|
|
3833
|
+
ta_map[ta] = [row_procs[ta], alias_map[ta], type_map[ta], reciprocal_map[ta]].freeze
|
3138
3834
|
end
|
3139
|
-
@ta_map = ta_map
|
3835
|
+
@ta_map = ta_map.freeze
|
3836
|
+
freeze
|
3140
3837
|
end
|
3141
3838
|
|
3142
3839
|
# Return an array of primary model instances with the associations cache prepopulated
|
3143
3840
|
# for all model objects (both primary and associated).
|
3144
3841
|
def load(hashes)
|
3842
|
+
# This mapping is used to make sure that duplicate entries in the
|
3843
|
+
# result set are mapped to a single record. For example, using a
|
3844
|
+
# single one_to_many association with 10 associated records,
|
3845
|
+
# the main object column values appear in the object graph 10 times.
|
3846
|
+
# We map by primary key, if available, or by the object's entire values,
|
3847
|
+
# if not. The mapping must be per table, so create sub maps for each table
|
3848
|
+
# alias.
|
3849
|
+
@records_map = records_map = {}
|
3850
|
+
alias_map.keys.each{|ta| records_map[ta] = {}}
|
3851
|
+
|
3145
3852
|
master = master()
|
3146
3853
|
|
3147
3854
|
# Assign to local variables for speed increase
|
3148
3855
|
rp = row_procs[master]
|
3149
|
-
rm = records_map[master]
|
3856
|
+
rm = records_map[master] = {}
|
3150
3857
|
dm = dependency_map
|
3151
3858
|
|
3859
|
+
records_map.freeze
|
3860
|
+
|
3152
3861
|
# This will hold the final record set that we will be replacing the object graph with.
|
3153
3862
|
records = []
|
3154
3863
|
|
@@ -3169,6 +3878,9 @@ END
|
|
3169
3878
|
# Run after_load procs if there are any
|
3170
3879
|
post_process(records, dm) if @unique || !after_load_map.empty? || !limit_map.empty?
|
3171
3880
|
|
3881
|
+
records_map.each_value(&:freeze)
|
3882
|
+
freeze
|
3883
|
+
|
3172
3884
|
records
|
3173
3885
|
end
|
3174
3886
|
|
@@ -3188,7 +3900,17 @@ END
|
|
3188
3900
|
end
|
3189
3901
|
key = hkey(ta_h)
|
3190
3902
|
end
|
3191
|
-
|
3903
|
+
rp, assoc_name, tm, rcm = @ta_map[ta]
|
3904
|
+
rm = records_map[ta]
|
3905
|
+
|
3906
|
+
# Check type map for all dependencies, and use a unique
|
3907
|
+
# object if any are dependencies for multiple objects,
|
3908
|
+
# to prevent duplicate objects from showing up in the case
|
3909
|
+
# the normal duplicate removal code is not being used.
|
3910
|
+
if !@unique && !deps.empty? && deps.any?{|dep_key,_| @ta_map[dep_key][2]}
|
3911
|
+
key = [current.object_id, key]
|
3912
|
+
end
|
3913
|
+
|
3192
3914
|
unless rec = rm[key]
|
3193
3915
|
rec = rm[key] = rp.call(hfor(ta, h))
|
3194
3916
|
end
|
@@ -3217,7 +3939,7 @@ END
|
|
3217
3939
|
# Return a suitable hash key for any subhash +h+, which is an array of values by column order.
|
3218
3940
|
# This is only used if the primary key cannot be used.
|
3219
3941
|
def hkey(h)
|
3220
|
-
h.sort_by{|x| x[0]
|
3942
|
+
h.sort_by{|x| x[0]}
|
3221
3943
|
end
|
3222
3944
|
|
3223
3945
|
# Return the subhash for the master table by parsing the values out of the main hash +h+
|
@@ -3262,7 +3984,7 @@ END
|
|
3262
3984
|
records.each do |record|
|
3263
3985
|
dependency_map.each do |ta, deps|
|
3264
3986
|
assoc_name = alias_map[ta]
|
3265
|
-
list = record.
|
3987
|
+
list = record.public_send(assoc_name)
|
3266
3988
|
rec_list = if type_map[ta]
|
3267
3989
|
list.uniq!
|
3268
3990
|
if lo = limit_map[ta]
|