activerecord 6.1.7 → 7.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2030 -1020
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +51 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +39 -35
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/preloader/association.rb +210 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +446 -306
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +27 -12
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +161 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +65 -31
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +108 -137
- data/lib/active_record/core.rb +242 -233
- data/lib/active_record/counter_cache.rb +52 -27
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +88 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +66 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +92 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +154 -63
- data/lib/active_record/errors.rb +172 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +147 -86
- data/lib/active_record/future_result.rb +174 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +135 -22
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +119 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +37 -22
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +112 -14
- data/lib/active_record/migration/compatibility.rb +233 -46
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +361 -173
- data/lib/active_record/model_schema.rb +125 -101
- data/lib/active_record/nested_attributes.rb +50 -20
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +409 -88
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +4 -22
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +220 -44
- data/lib/active_record/railties/controller_runtime.rb +15 -10
- data/lib/active_record/railties/databases.rake +188 -252
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +248 -81
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +246 -90
- data/lib/active_record/relation/delegation.rb +28 -14
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +670 -129
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +287 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +32 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +10 -8
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +251 -140
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +117 -96
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +5 -13
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +141 -20
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +96 -16
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -6,20 +6,23 @@ require "zlib"
|
|
6
6
|
require "set"
|
7
7
|
require "active_support/dependencies"
|
8
8
|
require "active_support/core_ext/digest/uuid"
|
9
|
-
require "active_record/fixture_set/file"
|
10
|
-
require "active_record/fixture_set/render_context"
|
11
|
-
require "active_record/fixture_set/table_rows"
|
12
9
|
require "active_record/test_fixtures"
|
13
10
|
|
14
11
|
module ActiveRecord
|
15
|
-
class FixtureClassNotFound < ActiveRecord::ActiveRecordError
|
12
|
+
class FixtureClassNotFound < ActiveRecord::ActiveRecordError # :nodoc:
|
16
13
|
end
|
17
14
|
|
15
|
+
# = Active Record \Fixtures
|
16
|
+
#
|
18
17
|
# \Fixtures are a way of organizing data that you want to test against; in short, sample data.
|
19
18
|
#
|
20
|
-
# They are stored in YAML files, one file per model, which are placed in
|
21
|
-
#
|
22
|
-
#
|
19
|
+
# They are stored in YAML files, one file per model, which are by default placed in either
|
20
|
+
# <tt><your-rails-app>/test/fixtures/</tt> or in the <tt>test/fixtures</tt>
|
21
|
+
# folder under any of your application's engines.
|
22
|
+
#
|
23
|
+
# The location can also be changed with ActiveSupport::TestCase.fixture_paths=,
|
24
|
+
# once you have <tt>require "rails/test_help"</tt> in your +test_helper.rb+.
|
25
|
+
#
|
23
26
|
# The fixture file ends with the +.yml+ file extension, for example:
|
24
27
|
# <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
|
25
28
|
#
|
@@ -35,14 +38,21 @@ module ActiveRecord
|
|
35
38
|
# name: Google
|
36
39
|
# url: http://www.google.com
|
37
40
|
#
|
38
|
-
# This fixture file includes two fixtures. Each YAML fixture (
|
41
|
+
# This fixture file includes two fixtures. Each YAML fixture (i.e. record) is given a name and
|
39
42
|
# is followed by an indented list of key/value pairs in the "key: value" format. Records are
|
40
43
|
# separated by a blank line for your viewing pleasure.
|
41
44
|
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
45
|
+
# == Ordering
|
46
|
+
#
|
47
|
+
# Fixtures by default are unordered. This is because the maps in YAML are unordered.
|
48
|
+
#
|
49
|
+
# If you want ordered fixtures, use the omap YAML type.
|
50
|
+
# See https://yaml.org/type/omap.html for the specification.
|
51
|
+
#
|
52
|
+
# You will need ordered fixtures when you have foreign key constraints
|
53
|
+
# on keys in the same table. This is commonly needed for tree structures.
|
54
|
+
#
|
55
|
+
# For example:
|
46
56
|
#
|
47
57
|
# --- !omap
|
48
58
|
# - parent:
|
@@ -54,7 +64,7 @@ module ActiveRecord
|
|
54
64
|
# parent_id: 1
|
55
65
|
# title: Child
|
56
66
|
#
|
57
|
-
#
|
67
|
+
# == Using Fixtures in Test Cases
|
58
68
|
#
|
59
69
|
# Since fixtures are a testing construct, we use them in our unit and functional tests. There
|
60
70
|
# are two ways to use the fixtures, but first let's take a look at a sample unit test:
|
@@ -124,7 +134,7 @@ module ActiveRecord
|
|
124
134
|
# traversed in the database to create the fixture hash and/or instance variables. This is expensive for
|
125
135
|
# large sets of fixtured data.
|
126
136
|
#
|
127
|
-
#
|
137
|
+
# == Dynamic fixtures with \ERB
|
128
138
|
#
|
129
139
|
# Sometimes you don't care about the content of the fixtures as much as you care about the volume.
|
130
140
|
# In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
|
@@ -152,7 +162,7 @@ module ActiveRecord
|
|
152
162
|
# - define a helper method in <tt>test_helper.rb</tt>
|
153
163
|
# module FixtureFileHelpers
|
154
164
|
# def file_sha(path)
|
155
|
-
# Digest::
|
165
|
+
# OpenSSL::Digest::SHA256.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
|
156
166
|
# end
|
157
167
|
# end
|
158
168
|
# ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers
|
@@ -162,7 +172,7 @@ module ActiveRecord
|
|
162
172
|
# name: kitten.png
|
163
173
|
# sha: <%= file_sha 'files/kitten.png' %>
|
164
174
|
#
|
165
|
-
#
|
175
|
+
# == Transactional Tests
|
166
176
|
#
|
167
177
|
# Test cases can use begin+rollback to isolate their changes to the database instead of having to
|
168
178
|
# delete+insert for every test case.
|
@@ -198,7 +208,7 @@ module ActiveRecord
|
|
198
208
|
# 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
|
199
209
|
# Use InnoDB, MaxDB, or NDB instead.
|
200
210
|
#
|
201
|
-
#
|
211
|
+
# == Advanced Fixtures
|
202
212
|
#
|
203
213
|
# Fixtures that don't specify an ID get some extra features:
|
204
214
|
#
|
@@ -212,7 +222,7 @@ module ActiveRecord
|
|
212
222
|
# * Fixture label interpolation
|
213
223
|
# * Support for YAML defaults
|
214
224
|
#
|
215
|
-
#
|
225
|
+
# === Stable, Autogenerated IDs
|
216
226
|
#
|
217
227
|
# Here, have a monkey fixture:
|
218
228
|
#
|
@@ -241,13 +251,13 @@ module ActiveRecord
|
|
241
251
|
# The generated ID for a given label is constant, so we can discover
|
242
252
|
# any fixture's ID without loading anything, as long as we know the label.
|
243
253
|
#
|
244
|
-
#
|
254
|
+
# === Label references for associations (+belongs_to+, +has_one+, +has_many+)
|
245
255
|
#
|
246
256
|
# Specifying foreign keys in fixtures can be very fragile, not to
|
247
257
|
# mention difficult to read. Since Active Record can figure out the ID of
|
248
258
|
# any fixture from its label, you can specify FK's by label instead of ID.
|
249
259
|
#
|
250
|
-
#
|
260
|
+
# ==== +belongs_to+
|
251
261
|
#
|
252
262
|
# Let's break out some more monkeys and pirates.
|
253
263
|
#
|
@@ -258,6 +268,8 @@ module ActiveRecord
|
|
258
268
|
# name: Reginald the Pirate
|
259
269
|
# monkey_id: 1
|
260
270
|
#
|
271
|
+
# <code></code>
|
272
|
+
#
|
261
273
|
# ### in monkeys.yml
|
262
274
|
#
|
263
275
|
# george:
|
@@ -275,6 +287,8 @@ module ActiveRecord
|
|
275
287
|
# name: Reginald the Pirate
|
276
288
|
# monkey: george
|
277
289
|
#
|
290
|
+
# <code></code>
|
291
|
+
#
|
278
292
|
# ### in monkeys.yml
|
279
293
|
#
|
280
294
|
# george:
|
@@ -286,7 +300,7 @@ module ActiveRecord
|
|
286
300
|
# a target *label* for the *association* (monkey: george) rather than
|
287
301
|
# a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
|
288
302
|
#
|
289
|
-
# ==== Polymorphic belongs_to
|
303
|
+
# ==== Polymorphic +belongs_to+
|
290
304
|
#
|
291
305
|
# Supporting polymorphic relationships is a little bit more complicated, since
|
292
306
|
# Active Record needs to know what type your association is pointing at. Something
|
@@ -296,6 +310,8 @@ module ActiveRecord
|
|
296
310
|
#
|
297
311
|
# belongs_to :eater, polymorphic: true
|
298
312
|
#
|
313
|
+
# <code></code>
|
314
|
+
#
|
299
315
|
# ### in fruits.yml
|
300
316
|
#
|
301
317
|
# apple:
|
@@ -311,9 +327,9 @@ module ActiveRecord
|
|
311
327
|
#
|
312
328
|
# Just provide the polymorphic target type and Active Record will take care of the rest.
|
313
329
|
#
|
314
|
-
#
|
330
|
+
# ==== +has_and_belongs_to_many+ or <tt>has_many :through</tt>
|
315
331
|
#
|
316
|
-
# Time to give our monkey some fruit.
|
332
|
+
# \Time to give our monkey some fruit.
|
317
333
|
#
|
318
334
|
# ### in monkeys.yml
|
319
335
|
#
|
@@ -321,6 +337,8 @@ module ActiveRecord
|
|
321
337
|
# id: 1
|
322
338
|
# name: George the Monkey
|
323
339
|
#
|
340
|
+
# <code></code>
|
341
|
+
#
|
324
342
|
# ### in fruits.yml
|
325
343
|
#
|
326
344
|
# apple:
|
@@ -335,6 +353,8 @@ module ActiveRecord
|
|
335
353
|
# id: 3
|
336
354
|
# name: grape
|
337
355
|
#
|
356
|
+
# <code></code>
|
357
|
+
#
|
338
358
|
# ### in fruits_monkeys.yml
|
339
359
|
#
|
340
360
|
# apple_george:
|
@@ -358,6 +378,8 @@ module ActiveRecord
|
|
358
378
|
# name: George the Monkey
|
359
379
|
# fruits: apple, orange, grape
|
360
380
|
#
|
381
|
+
# <code></code>
|
382
|
+
#
|
361
383
|
# ### in fruits.yml
|
362
384
|
#
|
363
385
|
# apple:
|
@@ -375,7 +397,7 @@ module ActiveRecord
|
|
375
397
|
# the fixture's model class and discovers the +has_and_belongs_to_many+
|
376
398
|
# associations.
|
377
399
|
#
|
378
|
-
#
|
400
|
+
# === Autofilled \Timestamp Columns
|
379
401
|
#
|
380
402
|
# If your table/model specifies any of Active Record's
|
381
403
|
# standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
|
@@ -383,7 +405,7 @@ module ActiveRecord
|
|
383
405
|
#
|
384
406
|
# If you've set specific values, they'll be left alone.
|
385
407
|
#
|
386
|
-
#
|
408
|
+
# === Fixture label interpolation
|
387
409
|
#
|
388
410
|
# The label of the current fixture is always available as a column value:
|
389
411
|
#
|
@@ -400,14 +422,18 @@ module ActiveRecord
|
|
400
422
|
# monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
|
401
423
|
# pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
|
402
424
|
#
|
403
|
-
#
|
425
|
+
# If the model uses UUID values for identifiers, add the +:uuid+ argument:
|
426
|
+
#
|
427
|
+
# ActiveRecord::FixtureSet.identify(:boaty_mcboatface, :uuid)
|
428
|
+
#
|
429
|
+
# === Support for YAML defaults
|
404
430
|
#
|
405
431
|
# You can set and reuse defaults in your fixtures YAML file.
|
406
432
|
# This is the same technique used in the +database.yml+ file to specify
|
407
433
|
# defaults:
|
408
434
|
#
|
409
435
|
# DEFAULTS: &DEFAULTS
|
410
|
-
# created_on: <%= 3.weeks.ago.
|
436
|
+
# created_on: <%= 3.weeks.ago.to_fs(:db) %>
|
411
437
|
#
|
412
438
|
# first:
|
413
439
|
# name: Smurf
|
@@ -426,7 +452,7 @@ module ActiveRecord
|
|
426
452
|
# _fixture:
|
427
453
|
# ignore:
|
428
454
|
# - base
|
429
|
-
# # or use "ignore: base" when there is only one fixture needs to be ignored.
|
455
|
+
# # or use "ignore: base" when there is only one fixture that needs to be ignored.
|
430
456
|
#
|
431
457
|
# base: &base
|
432
458
|
# admin: false
|
@@ -442,6 +468,45 @@ module ActiveRecord
|
|
442
468
|
# In the above example, 'base' will be ignored when creating fixtures.
|
443
469
|
# This can be used for common attributes inheriting.
|
444
470
|
#
|
471
|
+
# == Composite Primary Key Fixtures
|
472
|
+
#
|
473
|
+
# Fixtures for composite primary key tables are fairly similar to normal tables.
|
474
|
+
# When using an id column, the column may be omitted as usual:
|
475
|
+
#
|
476
|
+
# # app/models/book.rb
|
477
|
+
# class Book < ApplicationRecord
|
478
|
+
# self.primary_key = [:author_id, :id]
|
479
|
+
# belongs_to :author
|
480
|
+
# end
|
481
|
+
#
|
482
|
+
# <code></code>
|
483
|
+
#
|
484
|
+
# # books.yml
|
485
|
+
# alices_adventure_in_wonderland:
|
486
|
+
# author_id: <%= ActiveRecord::FixtureSet.identify(:lewis_carroll) %>
|
487
|
+
# title: "Alice's Adventures in Wonderland"
|
488
|
+
#
|
489
|
+
# However, in order to support composite primary key relationships,
|
490
|
+
# you must use the `composite_identify` method:
|
491
|
+
#
|
492
|
+
# # app/models/book_orders.rb
|
493
|
+
# class BookOrder < ApplicationRecord
|
494
|
+
# self.primary_key = [:shop_id, :id]
|
495
|
+
# belongs_to :order, query_constraints: [:shop_id, :order_id]
|
496
|
+
# belongs_to :book, query_constraints: [:author_id, :book_id]
|
497
|
+
# end
|
498
|
+
#
|
499
|
+
# <code></code>
|
500
|
+
#
|
501
|
+
# # book_orders.yml
|
502
|
+
# alices_adventure_in_wonderland_in_books:
|
503
|
+
# author: lewis_carroll
|
504
|
+
# book_id: <%= ActiveRecord::FixtureSet.composite_identify(
|
505
|
+
# :alices_adventure_in_wonderland, Book.primary_key)[:id] %>
|
506
|
+
# shop: book_store
|
507
|
+
# order_id: <%= ActiveRecord::FixtureSet.composite_identify(
|
508
|
+
# :books, Order.primary_key)[:id] %>
|
509
|
+
#
|
445
510
|
# == Configure the fixture model class
|
446
511
|
#
|
447
512
|
# It's possible to set the fixture's model class directly in the YAML file.
|
@@ -456,6 +521,10 @@ module ActiveRecord
|
|
456
521
|
#
|
457
522
|
# Any fixtures labeled "_fixture" are safely ignored.
|
458
523
|
class FixtureSet
|
524
|
+
require "active_record/fixture_set/file"
|
525
|
+
require "active_record/fixture_set/render_context"
|
526
|
+
require "active_record/fixture_set/table_rows"
|
527
|
+
|
459
528
|
#--
|
460
529
|
# An instance of FixtureSet is normally stored in a single YAML file and
|
461
530
|
# possibly in a folder with the same name.
|
@@ -467,39 +536,6 @@ module ActiveRecord
|
|
467
536
|
|
468
537
|
cattr_accessor :all_loaded_fixtures, default: {}
|
469
538
|
|
470
|
-
class ClassCache
|
471
|
-
def initialize(class_names, config)
|
472
|
-
@class_names = class_names.stringify_keys
|
473
|
-
@config = config
|
474
|
-
|
475
|
-
# Remove string values that aren't constants or subclasses of AR
|
476
|
-
@class_names.delete_if do |klass_name, klass|
|
477
|
-
!insert_class(@class_names, klass_name, klass)
|
478
|
-
end
|
479
|
-
end
|
480
|
-
|
481
|
-
def [](fs_name)
|
482
|
-
@class_names.fetch(fs_name) do
|
483
|
-
klass = default_fixture_model(fs_name, @config).safe_constantize
|
484
|
-
insert_class(@class_names, fs_name, klass)
|
485
|
-
end
|
486
|
-
end
|
487
|
-
|
488
|
-
private
|
489
|
-
def insert_class(class_names, name, klass)
|
490
|
-
# We only want to deal with AR objects.
|
491
|
-
if klass && klass < ActiveRecord::Base
|
492
|
-
class_names[name] = klass
|
493
|
-
else
|
494
|
-
class_names[name] = nil
|
495
|
-
end
|
496
|
-
end
|
497
|
-
|
498
|
-
def default_fixture_model(fs_name, config)
|
499
|
-
ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
|
500
|
-
end
|
501
|
-
end
|
502
|
-
|
503
539
|
class << self
|
504
540
|
def default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
|
505
541
|
config.pluralize_table_names ?
|
@@ -552,9 +588,9 @@ module ActiveRecord
|
|
552
588
|
end
|
553
589
|
end
|
554
590
|
|
555
|
-
def create_fixtures(
|
591
|
+
def create_fixtures(fixtures_directories, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
|
556
592
|
fixture_set_names = Array(fixture_set_names).map(&:to_s)
|
557
|
-
class_names
|
593
|
+
class_names.stringify_keys!
|
558
594
|
|
559
595
|
# FIXME: Apparently JK uses this.
|
560
596
|
connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
|
@@ -565,7 +601,7 @@ module ActiveRecord
|
|
565
601
|
|
566
602
|
if fixture_files_to_read.any?
|
567
603
|
fixtures_map = read_and_insert(
|
568
|
-
|
604
|
+
Array(fixtures_directories),
|
569
605
|
fixture_files_to_read,
|
570
606
|
class_names,
|
571
607
|
connection,
|
@@ -576,7 +612,8 @@ module ActiveRecord
|
|
576
612
|
end
|
577
613
|
|
578
614
|
# Returns a consistent, platform-independent identifier for +label+.
|
579
|
-
#
|
615
|
+
#
|
616
|
+
# \Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
|
580
617
|
def identify(label, column_type = :integer)
|
581
618
|
if column_type == :uuid
|
582
619
|
Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
|
@@ -585,29 +622,35 @@ module ActiveRecord
|
|
585
622
|
end
|
586
623
|
end
|
587
624
|
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
625
|
+
# Returns a consistent, platform-independent hash representing a mapping
|
626
|
+
# between the label and the subcomponents of the provided composite key.
|
627
|
+
#
|
628
|
+
# Example:
|
629
|
+
#
|
630
|
+
# composite_identify("label", [:a, :b, :c]) # => { a: hash_1, b: hash_2, c: hash_3 }
|
631
|
+
def composite_identify(label, key)
|
632
|
+
key
|
633
|
+
.index_with
|
634
|
+
.with_index { |sub_key, index| (identify(label) << index) % MAX_ID }
|
635
|
+
.with_indifferent_access
|
594
636
|
end
|
595
637
|
|
596
|
-
# Superclass for the evaluation contexts used by ERB fixtures.
|
638
|
+
# Superclass for the evaluation contexts used by \ERB fixtures.
|
597
639
|
def context_class
|
598
640
|
@context_class ||= Class.new
|
599
641
|
end
|
600
642
|
|
601
643
|
private
|
602
|
-
def read_and_insert(
|
644
|
+
def read_and_insert(fixtures_directories, fixture_files, class_names, connection) # :nodoc:
|
603
645
|
fixtures_map = {}
|
646
|
+
directory_glob = "{#{fixtures_directories.join(",")}}"
|
604
647
|
fixture_sets = fixture_files.map do |fixture_set_name|
|
605
648
|
klass = class_names[fixture_set_name]
|
606
649
|
fixtures_map[fixture_set_name] = new( # ActiveRecord::FixtureSet.new
|
607
650
|
nil,
|
608
651
|
fixture_set_name,
|
609
652
|
klass,
|
610
|
-
::File.join(
|
653
|
+
::File.join(directory_glob, fixture_set_name)
|
611
654
|
)
|
612
655
|
end
|
613
656
|
update_all_loaded_fixtures(fixtures_map)
|
@@ -637,6 +680,8 @@ module ActiveRecord
|
|
637
680
|
|
638
681
|
conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
|
639
682
|
|
683
|
+
check_all_foreign_keys_valid!(conn)
|
684
|
+
|
640
685
|
# Cap primary key sequences to max(pk).
|
641
686
|
if conn.respond_to?(:reset_pk_sequence!)
|
642
687
|
set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
|
@@ -644,6 +689,16 @@ module ActiveRecord
|
|
644
689
|
end
|
645
690
|
end
|
646
691
|
|
692
|
+
def check_all_foreign_keys_valid!(conn)
|
693
|
+
return unless ActiveRecord.verify_foreign_keys_for_fixtures
|
694
|
+
|
695
|
+
begin
|
696
|
+
conn.check_all_foreign_keys_valid!
|
697
|
+
rescue ActiveRecord::StatementInvalid => e
|
698
|
+
raise "Foreign key violations found in your fixture data. Ensure you aren't referring to labels that don't exist on associations. Error from database:\n\n#{e.message}"
|
699
|
+
end
|
700
|
+
end
|
701
|
+
|
647
702
|
def update_all_loaded_fixtures(fixtures_map) # :nodoc:
|
648
703
|
all_loaded_fixtures.update(fixtures_map)
|
649
704
|
end
|
@@ -657,7 +712,6 @@ module ActiveRecord
|
|
657
712
|
@config = config
|
658
713
|
|
659
714
|
self.model_class = class_name
|
660
|
-
|
661
715
|
@fixtures = read_fixture_files(path)
|
662
716
|
|
663
717
|
@table_name = model_class&.table_name || self.class.default_fixture_table_name(name, config)
|
@@ -689,7 +743,6 @@ module ActiveRecord
|
|
689
743
|
table_name,
|
690
744
|
model_class: model_class,
|
691
745
|
fixtures: fixtures,
|
692
|
-
config: config,
|
693
746
|
).to_hash
|
694
747
|
end
|
695
748
|
|
@@ -720,14 +773,18 @@ module ActiveRecord
|
|
720
773
|
# Loads the fixtures from the YAML file at +path+.
|
721
774
|
# If the file sets the +model_class+ and current instance value is not set,
|
722
775
|
# it uses the file value.
|
776
|
+
|
723
777
|
def read_fixture_files(path)
|
724
|
-
yaml_files = Dir["#{path}
|
778
|
+
yaml_files = Dir["#{path}{.yml,/{**,*}/*.yml}"].select { |f|
|
725
779
|
::File.file?(f)
|
726
|
-
}
|
780
|
+
}
|
781
|
+
|
782
|
+
raise ArgumentError, "No fixture files found for #{@name}" if yaml_files.empty?
|
727
783
|
|
728
784
|
yaml_files.each_with_object({}) do |file, fixtures|
|
729
785
|
FixtureSet::File.open(file) do |fh|
|
730
786
|
self.model_class ||= fh.model_class if fh.model_class
|
787
|
+
self.model_class ||= default_fixture_model_class
|
731
788
|
self.ignored_fixtures ||= fh.ignored_fixtures
|
732
789
|
fh.each do |fixture_name, row|
|
733
790
|
fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
|
@@ -736,18 +793,19 @@ module ActiveRecord
|
|
736
793
|
end
|
737
794
|
end
|
738
795
|
|
739
|
-
def
|
740
|
-
|
796
|
+
def default_fixture_model_class
|
797
|
+
klass = ActiveRecord::FixtureSet.default_fixture_model_name(@name, @config).safe_constantize
|
798
|
+
klass if klass && klass < ActiveRecord::Base
|
741
799
|
end
|
742
800
|
end
|
743
801
|
|
744
|
-
class Fixture
|
802
|
+
class Fixture # :nodoc:
|
745
803
|
include Enumerable
|
746
804
|
|
747
|
-
class FixtureError < StandardError
|
805
|
+
class FixtureError < StandardError # :nodoc:
|
748
806
|
end
|
749
807
|
|
750
|
-
class FormatError < FixtureError
|
808
|
+
class FormatError < FixtureError # :nodoc:
|
751
809
|
end
|
752
810
|
|
753
811
|
attr_reader :model_class, :fixture
|
@@ -761,8 +819,8 @@ module ActiveRecord
|
|
761
819
|
model_class.name if model_class
|
762
820
|
end
|
763
821
|
|
764
|
-
def each
|
765
|
-
fixture.each
|
822
|
+
def each(&block)
|
823
|
+
fixture.each(&block)
|
766
824
|
end
|
767
825
|
|
768
826
|
def [](key)
|
@@ -774,7 +832,8 @@ module ActiveRecord
|
|
774
832
|
def find
|
775
833
|
raise FixtureClassNotFound, "No class attached to find." unless model_class
|
776
834
|
object = model_class.unscoped do
|
777
|
-
|
835
|
+
pk_clauses = fixture.slice(*Array(model_class.primary_key))
|
836
|
+
model_class.find_by!(pk_clauses)
|
778
837
|
end
|
779
838
|
# Fixtures can't be eagerly loaded
|
780
839
|
object.instance_variable_set(:@strict_loading, false)
|
@@ -782,3 +841,5 @@ module ActiveRecord
|
|
782
841
|
end
|
783
842
|
end
|
784
843
|
end
|
844
|
+
|
845
|
+
ActiveSupport.run_load_hooks :active_record_fixture_set, ActiveRecord::FixtureSet
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class FutureResult # :nodoc:
|
5
|
+
class Complete
|
6
|
+
attr_reader :result
|
7
|
+
delegate :empty?, :to_a, to: :result
|
8
|
+
|
9
|
+
def initialize(result)
|
10
|
+
@result = result
|
11
|
+
end
|
12
|
+
|
13
|
+
def pending?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def canceled?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
def then(&block)
|
22
|
+
Promise::Complete.new(@result.then(&block))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class EventBuffer
|
27
|
+
def initialize(future_result, instrumenter)
|
28
|
+
@future_result = future_result
|
29
|
+
@instrumenter = instrumenter
|
30
|
+
@events = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def instrument(name, payload = {}, &block)
|
34
|
+
event = @instrumenter.new_event(name, payload)
|
35
|
+
@events << event
|
36
|
+
event.record(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def flush
|
40
|
+
events, @events = @events, []
|
41
|
+
events.each do |event|
|
42
|
+
event.payload[:lock_wait] = @future_result.lock_wait
|
43
|
+
ActiveSupport::Notifications.publish_event(event)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Canceled = Class.new(ActiveRecordError)
|
49
|
+
|
50
|
+
def self.wrap(result)
|
51
|
+
case result
|
52
|
+
when self, Complete
|
53
|
+
result
|
54
|
+
else
|
55
|
+
Complete.new(result)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
delegate :empty?, :to_a, to: :result
|
60
|
+
delegate_missing_to :result
|
61
|
+
|
62
|
+
attr_reader :lock_wait
|
63
|
+
|
64
|
+
def initialize(pool, *args, **kwargs)
|
65
|
+
@mutex = Mutex.new
|
66
|
+
|
67
|
+
@session = nil
|
68
|
+
@pool = pool
|
69
|
+
@args = args
|
70
|
+
@kwargs = kwargs
|
71
|
+
|
72
|
+
@pending = true
|
73
|
+
@error = nil
|
74
|
+
@result = nil
|
75
|
+
@instrumenter = ActiveSupport::Notifications.instrumenter
|
76
|
+
@event_buffer = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def then(&block)
|
80
|
+
Promise.new(self, block)
|
81
|
+
end
|
82
|
+
|
83
|
+
def schedule!(session)
|
84
|
+
@session = session
|
85
|
+
@pool.schedule_query(self)
|
86
|
+
end
|
87
|
+
|
88
|
+
def execute!(connection)
|
89
|
+
execute_query(connection)
|
90
|
+
end
|
91
|
+
|
92
|
+
def cancel
|
93
|
+
@pending = false
|
94
|
+
@error = Canceled
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def execute_or_skip
|
99
|
+
return unless pending?
|
100
|
+
|
101
|
+
@pool.with_connection do |connection|
|
102
|
+
return unless @mutex.try_lock
|
103
|
+
begin
|
104
|
+
if pending?
|
105
|
+
@event_buffer = EventBuffer.new(self, @instrumenter)
|
106
|
+
connection.with_instrumenter(@event_buffer) do
|
107
|
+
execute_query(connection, async: true)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
ensure
|
111
|
+
@mutex.unlock
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def result
|
117
|
+
execute_or_wait
|
118
|
+
@event_buffer&.flush
|
119
|
+
|
120
|
+
if canceled?
|
121
|
+
raise Canceled
|
122
|
+
elsif @error
|
123
|
+
raise @error
|
124
|
+
else
|
125
|
+
@result
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def pending?
|
130
|
+
@pending && (!@session || @session.active?)
|
131
|
+
end
|
132
|
+
|
133
|
+
def canceled?
|
134
|
+
@session && !@session.active?
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
def execute_or_wait
|
139
|
+
if pending?
|
140
|
+
start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
|
141
|
+
@mutex.synchronize do
|
142
|
+
if pending?
|
143
|
+
execute_query(@pool.connection)
|
144
|
+
else
|
145
|
+
@lock_wait = (Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
else
|
149
|
+
@lock_wait = 0.0
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def execute_query(connection, async: false)
|
154
|
+
@result = exec_query(connection, *@args, **@kwargs, async: async)
|
155
|
+
rescue => error
|
156
|
+
@error = error
|
157
|
+
ensure
|
158
|
+
@pending = false
|
159
|
+
end
|
160
|
+
|
161
|
+
def exec_query(connection, *args, **kwargs)
|
162
|
+
connection.internal_exec_query(*args, **kwargs)
|
163
|
+
end
|
164
|
+
|
165
|
+
class SelectAll < FutureResult # :nodoc:
|
166
|
+
private
|
167
|
+
def exec_query(*, **)
|
168
|
+
super
|
169
|
+
rescue ::RangeError
|
170
|
+
ActiveRecord::Result.empty
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|