activerecord 6.1.7.10 → 7.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +726 -1404
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +31 -9
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- 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 +2 -2
- data/lib/active_record/associations/collection_association.rb +14 -23
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +161 -47
- data/lib/active_record/associations/preloader/batch.rb +51 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +37 -11
- data/lib/active_record/associations/preloader.rb +46 -110
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +76 -81
- data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +41 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +6 -9
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +3 -18
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +2 -14
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
- data/lib/active_record/connection_adapters/abstract/transaction.rb +3 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +112 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +1 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -14
- 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/range.rb +1 -1
- 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 +28 -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 +6 -32
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +159 -102
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -37
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +20 -38
- data/lib/active_record/core.rb +111 -125
- data/lib/active_record/database_configurations/connection_url_resolver.rb +0 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -0
- data/lib/active_record/database_configurations/hash_config.rb +27 -1
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +17 -9
- data/lib/active_record/delegated_type.rb +33 -11
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- 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/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -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 +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -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 +80 -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 +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +41 -41
- data/lib/active_record/errors.rb +66 -3
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +40 -5
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +16 -11
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +34 -5
- data/lib/active_record/integration.rb +1 -1
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/log_subscriber.rb +6 -2
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +89 -10
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +45 -31
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/no_touching.rb +2 -2
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +134 -45
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +203 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +117 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +72 -48
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +45 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +39 -26
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -22
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +230 -49
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +8 -4
- data/lib/active_record/relation.rb +166 -77
- data/lib/active_record/result.rb +17 -2
- data/lib/active_record/runtime_registry.rb +2 -4
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +3 -3
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +40 -22
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/store.rb +1 -6
- data/lib/active_record/tasks/database_tasks.rb +106 -22
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +9 -13
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +170 -2
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +18 -22
- data/lib/arel/delete_manager.rb +2 -4
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +8 -13
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +3 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +2 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +6 -1
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +43 -2
- data/lib/arel.rb +1 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- 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
- metadata +52 -14
data/lib/active_record/errors.rb
CHANGED
@@ -63,6 +63,30 @@ module ActiveRecord
|
|
63
63
|
class ConnectionTimeoutError < ConnectionNotEstablished
|
64
64
|
end
|
65
65
|
|
66
|
+
# Raised when connection to the database could not been established because it was not
|
67
|
+
# able to connect to the host or when the authorization failed.
|
68
|
+
class DatabaseConnectionError < ConnectionNotEstablished
|
69
|
+
def initialize(message = nil)
|
70
|
+
super(message || "Database connection error")
|
71
|
+
end
|
72
|
+
|
73
|
+
class << self
|
74
|
+
def hostname_error(hostname)
|
75
|
+
DatabaseConnectionError.new(<<~MSG)
|
76
|
+
There is an issue connecting with your hostname: #{hostname}.\n
|
77
|
+
Please check your database configuration and ensure there is a valid connection to your database.
|
78
|
+
MSG
|
79
|
+
end
|
80
|
+
|
81
|
+
def username_error(username)
|
82
|
+
DatabaseConnectionError.new(<<~MSG)
|
83
|
+
There is an issue connecting to your database with your username/password, username: #{username}.\n
|
84
|
+
Please check your database configuration to ensure the username/password are valid.
|
85
|
+
MSG
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
66
90
|
# Raised when a pool was unable to get ahold of all its connections
|
67
91
|
# to perform a "group" action such as
|
68
92
|
# {ActiveRecord::Base.connection_pool.disconnect!}[rdoc-ref:ConnectionAdapters::ConnectionPool#disconnect!]
|
@@ -100,7 +124,7 @@ module ActiveRecord
|
|
100
124
|
end
|
101
125
|
|
102
126
|
# Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
|
103
|
-
# when a call to {#destroy}[rdoc-ref:Persistence#destroy
|
127
|
+
# when a call to {#destroy}[rdoc-ref:Persistence#destroy]
|
104
128
|
# would return false.
|
105
129
|
#
|
106
130
|
# begin
|
@@ -118,6 +142,16 @@ module ActiveRecord
|
|
118
142
|
end
|
119
143
|
end
|
120
144
|
|
145
|
+
# Raised when Active Record finds multiple records but only expected one.
|
146
|
+
class SoleRecordExceeded < ActiveRecordError
|
147
|
+
attr_reader :record
|
148
|
+
|
149
|
+
def initialize(record = nil)
|
150
|
+
@record = record
|
151
|
+
super "Wanted only one #{record&.name || "record"}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
121
155
|
# Superclass for all database execution errors.
|
122
156
|
#
|
123
157
|
# Wraps the underlying database error as +cause+.
|
@@ -202,6 +236,30 @@ module ActiveRecord
|
|
202
236
|
|
203
237
|
# Raised when a given database does not exist.
|
204
238
|
class NoDatabaseError < StatementInvalid
|
239
|
+
include ActiveSupport::ActionableError
|
240
|
+
|
241
|
+
action "Create database" do
|
242
|
+
ActiveRecord::Tasks::DatabaseTasks.create_current
|
243
|
+
end
|
244
|
+
|
245
|
+
def initialize(message = nil)
|
246
|
+
super(message || "Database not found")
|
247
|
+
end
|
248
|
+
|
249
|
+
class << self
|
250
|
+
def db_error(db_name)
|
251
|
+
NoDatabaseError.new(<<~MSG)
|
252
|
+
We could not find your database: #{db_name}. Which can be found in the database configuration file located at config/database.yml.
|
253
|
+
|
254
|
+
To resolve this issue:
|
255
|
+
|
256
|
+
- Did you create the database for this app, or delete it? You may need to create your database.
|
257
|
+
- Has the database name changed? Check your database.yml config has the correct database name.
|
258
|
+
|
259
|
+
To create your database, run:\n\n bin/rails db:create
|
260
|
+
MSG
|
261
|
+
end
|
262
|
+
end
|
205
263
|
end
|
206
264
|
|
207
265
|
# Raised when creating a database if it exists.
|
@@ -363,6 +421,11 @@ module ActiveRecord
|
|
363
421
|
class TransactionRollbackError < StatementInvalid
|
364
422
|
end
|
365
423
|
|
424
|
+
# AsynchronousQueryInsideTransactionError will be raised when attempting
|
425
|
+
# to perform an asynchronous query from inside a transaction
|
426
|
+
class AsynchronousQueryInsideTransactionError < ActiveRecordError
|
427
|
+
end
|
428
|
+
|
366
429
|
# SerializationFailure will be raised when a transaction is rolled
|
367
430
|
# back by the database due to a serialization failure.
|
368
431
|
class SerializationFailure < TransactionRollbackError
|
@@ -409,12 +472,12 @@ module ActiveRecord
|
|
409
472
|
#
|
410
473
|
# For example, the following code would raise this exception:
|
411
474
|
#
|
412
|
-
# Post.order("
|
475
|
+
# Post.order("REPLACE(title, 'misc', 'zzzz') asc").pluck(:id)
|
413
476
|
#
|
414
477
|
# The desired result can be accomplished by wrapping the known-safe string
|
415
478
|
# in Arel.sql:
|
416
479
|
#
|
417
|
-
# Post.order(Arel.sql("
|
480
|
+
# Post.order(Arel.sql("REPLACE(title, 'misc', 'zzzz') asc")).pluck(:id)
|
418
481
|
#
|
419
482
|
# Again, such a workaround should *not* be used when passing user-provided
|
420
483
|
# values, such as request parameters or model attributes to query methods.
|
@@ -41,7 +41,7 @@ module ActiveRecord
|
|
41
41
|
@config_row ||= begin
|
42
42
|
row = raw_rows.find { |fixture_name, _| fixture_name == "_fixture" }
|
43
43
|
if row
|
44
|
-
row.last
|
44
|
+
validate_config_row(row.last)
|
45
45
|
else
|
46
46
|
{ 'model_class': nil, 'ignore': nil }
|
47
47
|
end
|
@@ -58,6 +58,20 @@ module ActiveRecord
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
def validate_config_row(data)
|
62
|
+
unless Hash === data
|
63
|
+
raise Fixture::FormatError, "Invalid `_fixture` section: `_fixture` must be a hash: #{@file}"
|
64
|
+
end
|
65
|
+
|
66
|
+
begin
|
67
|
+
data.assert_valid_keys("model_class", "ignore")
|
68
|
+
rescue ArgumentError => error
|
69
|
+
raise Fixture::FormatError, "Invalid `_fixture` section: #{error.message}: #{@file}"
|
70
|
+
end
|
71
|
+
|
72
|
+
data
|
73
|
+
end
|
74
|
+
|
61
75
|
# Validate our unmarshalled data.
|
62
76
|
def validate(data)
|
63
77
|
unless Hash === data || YAML::Omap === data
|
@@ -33,6 +33,33 @@ module ActiveRecord
|
|
33
33
|
def join_table
|
34
34
|
@association.through_reflection.table_name
|
35
35
|
end
|
36
|
+
|
37
|
+
def timestamp_column_names
|
38
|
+
@association.through_reflection.klass.all_timestamp_attributes_in_model
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class PrimaryKeyError < StandardError # :nodoc:
|
43
|
+
def initialize(label, association, value)
|
44
|
+
super(<<~MSG)
|
45
|
+
Unable to set #{association.name} to #{value} because the association has a
|
46
|
+
custom primary key (#{association.join_primary_key}) that does not match the
|
47
|
+
associated table's primary key (#{association.klass.primary_key}).
|
48
|
+
|
49
|
+
To fix this, change your fixture from
|
50
|
+
|
51
|
+
#{label}:
|
52
|
+
#{association.name}: #{value}
|
53
|
+
|
54
|
+
to
|
55
|
+
|
56
|
+
#{label}:
|
57
|
+
#{association.foreign_key}: **value**
|
58
|
+
|
59
|
+
where **value** is the #{association.join_primary_key} value for the
|
60
|
+
associated #{association.klass.name} record.
|
61
|
+
MSG
|
62
|
+
end
|
36
63
|
end
|
37
64
|
|
38
65
|
def initialize(fixture, table_rows:, label:, now:)
|
@@ -115,9 +142,13 @@ module ActiveRecord
|
|
115
142
|
fk_name = association.join_foreign_key
|
116
143
|
|
117
144
|
if association.name.to_s != fk_name && value = @row.delete(association.name.to_s)
|
118
|
-
if association.polymorphic?
|
119
|
-
|
120
|
-
|
145
|
+
if association.polymorphic?
|
146
|
+
if value.sub!(/\s*\(([^)]*)\)\s*$/, "")
|
147
|
+
# support polymorphic belongs_to as "label (Type)"
|
148
|
+
@row[association.join_foreign_type] = $1
|
149
|
+
end
|
150
|
+
elsif association.join_primary_key != association.klass.primary_key
|
151
|
+
raise PrimaryKeyError.new(@label, association, value)
|
121
152
|
end
|
122
153
|
|
123
154
|
fk_type = reflection_class.type_for_attribute(fk_name).type
|
@@ -141,8 +172,12 @@ module ActiveRecord
|
|
141
172
|
|
142
173
|
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
|
143
174
|
joins = targets.map do |target|
|
144
|
-
{ lhs_key => @row[model_metadata.primary_key_name],
|
145
|
-
|
175
|
+
join = { lhs_key => @row[model_metadata.primary_key_name],
|
176
|
+
rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
|
177
|
+
association.timestamp_column_names.each do |col|
|
178
|
+
join[col] = @now
|
179
|
+
end
|
180
|
+
join
|
146
181
|
end
|
147
182
|
@table_rows.tables[table_name].concat(joins)
|
148
183
|
end
|
@@ -6,7 +6,7 @@ require "active_record/fixture_set/model_metadata"
|
|
6
6
|
module ActiveRecord
|
7
7
|
class FixtureSet
|
8
8
|
class TableRows # :nodoc:
|
9
|
-
def initialize(table_name, model_class:, fixtures
|
9
|
+
def initialize(table_name, model_class:, fixtures:)
|
10
10
|
@model_class = model_class
|
11
11
|
|
12
12
|
# track any join tables we need to insert later
|
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
# ensure this table is loaded before any HABTM associations
|
16
16
|
@tables[table_name] = nil
|
17
17
|
|
18
|
-
build_table_rows_from(table_name, fixtures
|
18
|
+
build_table_rows_from(table_name, fixtures)
|
19
19
|
end
|
20
20
|
|
21
21
|
attr_reader :tables, :model_class
|
@@ -29,8 +29,8 @@ module ActiveRecord
|
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
32
|
-
def build_table_rows_from(table_name, fixtures
|
33
|
-
now =
|
32
|
+
def build_table_rows_from(table_name, fixtures)
|
33
|
+
now = ActiveRecord.default_timezone == :utc ? Time.now.utc : Time.now
|
34
34
|
|
35
35
|
@tables[table_name] = fixtures.map do |label, fixture|
|
36
36
|
TableRow.new(
|
@@ -12,7 +12,7 @@ require "active_record/fixture_set/table_rows"
|
|
12
12
|
require "active_record/test_fixtures"
|
13
13
|
|
14
14
|
module ActiveRecord
|
15
|
-
class FixtureClassNotFound < ActiveRecord::ActiveRecordError
|
15
|
+
class FixtureClassNotFound < ActiveRecord::ActiveRecordError # :nodoc:
|
16
16
|
end
|
17
17
|
|
18
18
|
# \Fixtures are a way of organizing data that you want to test against; in short, sample data.
|
@@ -35,7 +35,7 @@ module ActiveRecord
|
|
35
35
|
# name: Google
|
36
36
|
# url: http://www.google.com
|
37
37
|
#
|
38
|
-
# This fixture file includes two fixtures. Each YAML fixture (
|
38
|
+
# This fixture file includes two fixtures. Each YAML fixture (i.e. record) is given a name and
|
39
39
|
# is followed by an indented list of key/value pairs in the "key: value" format. Records are
|
40
40
|
# separated by a blank line for your viewing pleasure.
|
41
41
|
#
|
@@ -152,7 +152,7 @@ module ActiveRecord
|
|
152
152
|
# - define a helper method in <tt>test_helper.rb</tt>
|
153
153
|
# module FixtureFileHelpers
|
154
154
|
# def file_sha(path)
|
155
|
-
# Digest::
|
155
|
+
# OpenSSL::Digest::SHA256.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
|
156
156
|
# end
|
157
157
|
# end
|
158
158
|
# ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers
|
@@ -311,7 +311,7 @@ module ActiveRecord
|
|
311
311
|
#
|
312
312
|
# Just provide the polymorphic target type and Active Record will take care of the rest.
|
313
313
|
#
|
314
|
-
# === has_and_belongs_to_many
|
314
|
+
# === has_and_belongs_to_many or has_many :through
|
315
315
|
#
|
316
316
|
# Time to give our monkey some fruit.
|
317
317
|
#
|
@@ -426,7 +426,7 @@ module ActiveRecord
|
|
426
426
|
# _fixture:
|
427
427
|
# ignore:
|
428
428
|
# - base
|
429
|
-
# # or use "ignore: base" when there is only one fixture needs to be ignored.
|
429
|
+
# # or use "ignore: base" when there is only one fixture that needs to be ignored.
|
430
430
|
#
|
431
431
|
# base: &base
|
432
432
|
# admin: false
|
@@ -637,6 +637,10 @@ module ActiveRecord
|
|
637
637
|
|
638
638
|
conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
|
639
639
|
|
640
|
+
if ActiveRecord.verify_foreign_keys_for_fixtures && !conn.all_foreign_keys_valid?
|
641
|
+
raise "Foreign key violations found in your fixture data. Ensure you aren't referring to labels that don't exist on associations."
|
642
|
+
end
|
643
|
+
|
640
644
|
# Cap primary key sequences to max(pk).
|
641
645
|
if conn.respond_to?(:reset_pk_sequence!)
|
642
646
|
set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
|
@@ -689,7 +693,6 @@ module ActiveRecord
|
|
689
693
|
table_name,
|
690
694
|
model_class: model_class,
|
691
695
|
fixtures: fixtures,
|
692
|
-
config: config,
|
693
696
|
).to_hash
|
694
697
|
end
|
695
698
|
|
@@ -741,13 +744,13 @@ module ActiveRecord
|
|
741
744
|
end
|
742
745
|
end
|
743
746
|
|
744
|
-
class Fixture
|
747
|
+
class Fixture # :nodoc:
|
745
748
|
include Enumerable
|
746
749
|
|
747
|
-
class FixtureError < StandardError
|
750
|
+
class FixtureError < StandardError # :nodoc:
|
748
751
|
end
|
749
752
|
|
750
|
-
class FormatError < FixtureError
|
753
|
+
class FormatError < FixtureError # :nodoc:
|
751
754
|
end
|
752
755
|
|
753
756
|
attr_reader :model_class, :fixture
|
@@ -761,8 +764,8 @@ module ActiveRecord
|
|
761
764
|
model_class.name if model_class
|
762
765
|
end
|
763
766
|
|
764
|
-
def each
|
765
|
-
fixture.each
|
767
|
+
def each(&block)
|
768
|
+
fixture.each(&block)
|
766
769
|
end
|
767
770
|
|
768
771
|
def [](key)
|
@@ -782,3 +785,5 @@ module ActiveRecord
|
|
782
785
|
end
|
783
786
|
end
|
784
787
|
end
|
788
|
+
|
789
|
+
ActiveSupport.run_load_hooks :active_record_fixture_set, ActiveRecord::FixtureSet
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class FutureResult # :nodoc:
|
5
|
+
class EventBuffer
|
6
|
+
def initialize(future_result, instrumenter)
|
7
|
+
@future_result = future_result
|
8
|
+
@instrumenter = instrumenter
|
9
|
+
@events = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def instrument(name, payload = {}, &block)
|
13
|
+
event = @instrumenter.new_event(name, payload)
|
14
|
+
@events << event
|
15
|
+
event.record(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def flush
|
19
|
+
events, @events = @events, []
|
20
|
+
events.each do |event|
|
21
|
+
event.payload[:lock_wait] = @future_result.lock_wait
|
22
|
+
ActiveSupport::Notifications.publish_event(event)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Canceled = Class.new(ActiveRecordError)
|
28
|
+
|
29
|
+
delegate :empty?, :to_a, to: :result
|
30
|
+
|
31
|
+
attr_reader :lock_wait
|
32
|
+
|
33
|
+
def initialize(pool, *args, **kwargs)
|
34
|
+
@mutex = Mutex.new
|
35
|
+
|
36
|
+
@session = nil
|
37
|
+
@pool = pool
|
38
|
+
@args = args
|
39
|
+
@kwargs = kwargs
|
40
|
+
|
41
|
+
@pending = true
|
42
|
+
@error = nil
|
43
|
+
@result = nil
|
44
|
+
@instrumenter = ActiveSupport::Notifications.instrumenter
|
45
|
+
@event_buffer = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def schedule!(session)
|
49
|
+
@session = session
|
50
|
+
@pool.schedule_query(self)
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute!(connection)
|
54
|
+
execute_query(connection)
|
55
|
+
end
|
56
|
+
|
57
|
+
def cancel
|
58
|
+
@pending = false
|
59
|
+
@error = Canceled
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def execute_or_skip
|
64
|
+
return unless pending?
|
65
|
+
|
66
|
+
@pool.with_connection do |connection|
|
67
|
+
return unless @mutex.try_lock
|
68
|
+
begin
|
69
|
+
if pending?
|
70
|
+
@event_buffer = EventBuffer.new(self, @instrumenter)
|
71
|
+
connection.with_instrumenter(@event_buffer) do
|
72
|
+
execute_query(connection, async: true)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
ensure
|
76
|
+
@mutex.unlock
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def result
|
82
|
+
execute_or_wait
|
83
|
+
@event_buffer&.flush
|
84
|
+
|
85
|
+
if canceled?
|
86
|
+
raise Canceled
|
87
|
+
elsif @error
|
88
|
+
raise @error
|
89
|
+
else
|
90
|
+
@result
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def pending?
|
95
|
+
@pending && (!@session || @session.active?)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def canceled?
|
100
|
+
@session && !@session.active?
|
101
|
+
end
|
102
|
+
|
103
|
+
def execute_or_wait
|
104
|
+
if pending?
|
105
|
+
start = Concurrent.monotonic_time
|
106
|
+
@mutex.synchronize do
|
107
|
+
if pending?
|
108
|
+
execute_query(@pool.connection)
|
109
|
+
else
|
110
|
+
@lock_wait = (Concurrent.monotonic_time - start) * 1_000
|
111
|
+
end
|
112
|
+
end
|
113
|
+
else
|
114
|
+
@lock_wait = 0.0
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def execute_query(connection, async: false)
|
119
|
+
@result = exec_query(connection, *@args, **@kwargs, async: async)
|
120
|
+
rescue => error
|
121
|
+
@error = error
|
122
|
+
ensure
|
123
|
+
@pending = false
|
124
|
+
end
|
125
|
+
|
126
|
+
def exec_query(connection, *args, **kwargs)
|
127
|
+
connection.exec_query(*args, **kwargs)
|
128
|
+
end
|
129
|
+
|
130
|
+
class SelectAll < FutureResult # :nodoc:
|
131
|
+
private
|
132
|
+
def exec_query(*, **)
|
133
|
+
super
|
134
|
+
rescue ::RangeError
|
135
|
+
ActiveRecord::Result.empty
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/inflector"
|
3
4
|
require "active_support/core_ext/hash/indifferent_access"
|
4
5
|
|
5
6
|
module ActiveRecord
|
@@ -43,6 +44,8 @@ module ActiveRecord
|
|
43
44
|
# Determines whether to store the full constant name including namespace when using STI.
|
44
45
|
# This is true, by default.
|
45
46
|
class_attribute :store_full_sti_class, instance_writer: false, default: true
|
47
|
+
|
48
|
+
set_base_class
|
46
49
|
end
|
47
50
|
|
48
51
|
module ClassMethods
|
@@ -85,7 +88,7 @@ module ActiveRecord
|
|
85
88
|
end
|
86
89
|
end
|
87
90
|
|
88
|
-
def finder_needs_type_condition?
|
91
|
+
def finder_needs_type_condition? # :nodoc:
|
89
92
|
# This is like this because benchmarking justifies the strange :false stuff
|
90
93
|
:true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
|
91
94
|
end
|
@@ -98,17 +101,7 @@ module ActiveRecord
|
|
98
101
|
#
|
99
102
|
# If B < A and C < B and if A is an abstract_class then both B.base_class
|
100
103
|
# and C.base_class would return B as the answer since A is an abstract_class.
|
101
|
-
|
102
|
-
unless self < Base
|
103
|
-
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
|
104
|
-
end
|
105
|
-
|
106
|
-
if superclass == Base || superclass.abstract_class?
|
107
|
-
self
|
108
|
-
else
|
109
|
-
superclass.base_class
|
110
|
-
end
|
111
|
-
end
|
104
|
+
attr_reader :base_class
|
112
105
|
|
113
106
|
# Returns whether the class is a base class.
|
114
107
|
# See #base_class for more information.
|
@@ -164,6 +157,21 @@ module ActiveRecord
|
|
164
157
|
defined?(@abstract_class) && @abstract_class == true
|
165
158
|
end
|
166
159
|
|
160
|
+
# Sets the application record class for Active Record
|
161
|
+
#
|
162
|
+
# This is useful if your application uses a different class than
|
163
|
+
# ApplicationRecord for your primary abstract class. This class
|
164
|
+
# will share a database connection with Active Record. It is the class
|
165
|
+
# that connects to your primary database.
|
166
|
+
def primary_abstract_class
|
167
|
+
if ActiveRecord.application_record_class && ActiveRecord.application_record_class.name != name
|
168
|
+
raise ArgumentError, "The `primary_abstract_class` is already set to #{ActiveRecord.application_record_class.inspect}. There can only be one `primary_abstract_class` in an application."
|
169
|
+
end
|
170
|
+
|
171
|
+
self.abstract_class = true
|
172
|
+
ActiveRecord.application_record_class = self
|
173
|
+
end
|
174
|
+
|
167
175
|
# Returns the value to be stored in the inheritance column for STI.
|
168
176
|
def sti_name
|
169
177
|
store_full_sti_class && store_full_class_name ? name : name.demodulize
|
@@ -174,7 +182,7 @@ module ActiveRecord
|
|
174
182
|
# It is used to find the class correspondent to the value stored in the inheritance column.
|
175
183
|
def sti_class_for(type_name)
|
176
184
|
if store_full_sti_class && store_full_class_name
|
177
|
-
|
185
|
+
type_name.constantize
|
178
186
|
else
|
179
187
|
compute_type(type_name)
|
180
188
|
end
|
@@ -196,17 +204,31 @@ module ActiveRecord
|
|
196
204
|
# It is used to find the class correspondent to the value stored in the polymorphic type column.
|
197
205
|
def polymorphic_class_for(name)
|
198
206
|
if store_full_class_name
|
199
|
-
|
207
|
+
name.constantize
|
200
208
|
else
|
201
209
|
compute_type(name)
|
202
210
|
end
|
203
211
|
end
|
204
212
|
|
205
213
|
def inherited(subclass)
|
214
|
+
subclass.set_base_class
|
206
215
|
subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
|
207
216
|
super
|
208
217
|
end
|
209
218
|
|
219
|
+
def dup # :nodoc:
|
220
|
+
# `initialize_dup` / `initialize_copy` don't work when defined
|
221
|
+
# in the `singleton_class`.
|
222
|
+
other = super
|
223
|
+
other.set_base_class
|
224
|
+
other
|
225
|
+
end
|
226
|
+
|
227
|
+
def initialize_clone(other) # :nodoc:
|
228
|
+
super
|
229
|
+
set_base_class
|
230
|
+
end
|
231
|
+
|
210
232
|
protected
|
211
233
|
# Returns the class type of the record using the current module as a prefix. So descendants of
|
212
234
|
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
|
@@ -214,10 +236,10 @@ module ActiveRecord
|
|
214
236
|
if type_name.start_with?("::")
|
215
237
|
# If the type is prefixed with a scope operator then we assume that
|
216
238
|
# the type_name is an absolute reference.
|
217
|
-
|
239
|
+
type_name.constantize
|
218
240
|
else
|
219
241
|
type_candidate = @_type_candidates_cache[type_name]
|
220
|
-
if type_candidate && type_constant =
|
242
|
+
if type_candidate && type_constant = type_candidate.safe_constantize
|
221
243
|
return type_constant
|
222
244
|
end
|
223
245
|
|
@@ -227,7 +249,7 @@ module ActiveRecord
|
|
227
249
|
candidates << type_name
|
228
250
|
|
229
251
|
candidates.each do |candidate|
|
230
|
-
constant =
|
252
|
+
constant = candidate.safe_constantize
|
231
253
|
if candidate == constant.to_s
|
232
254
|
@_type_candidates_cache[type_name] = candidate
|
233
255
|
return constant
|
@@ -238,6 +260,22 @@ module ActiveRecord
|
|
238
260
|
end
|
239
261
|
end
|
240
262
|
|
263
|
+
def set_base_class # :nodoc:
|
264
|
+
@base_class = if self == Base
|
265
|
+
self
|
266
|
+
else
|
267
|
+
unless self < Base
|
268
|
+
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
|
269
|
+
end
|
270
|
+
|
271
|
+
if superclass == Base || superclass.abstract_class?
|
272
|
+
self
|
273
|
+
else
|
274
|
+
superclass.base_class
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
241
279
|
private
|
242
280
|
# Called by +instantiate+ to decide which class to use for a new
|
243
281
|
# record instance. For single-table inheritance, we check the record
|