activerecord 7.0.8.6 → 7.2.2.1
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 +631 -1939
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +26 -14
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -33
- data/lib/active_record/attributes.rb +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- data/lib/active_record/callbacks.rb +11 -25
- 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 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
- 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 +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +96 -104
- data/lib/active_record/core.rb +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +39 -10
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +129 -28
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- 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 +29 -8
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- 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 +4 -0
- data/lib/active_record/middleware/database_selector.rb +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +234 -117
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +129 -85
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +267 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +250 -93
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -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 +28 -16
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +576 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +580 -90
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +63 -14
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +27 -6
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +106 -24
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +9 -3
- 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 +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -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
- metadata +56 -14
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -3,8 +3,6 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Tasks # :nodoc:
|
5
5
|
class SQLiteDatabaseTasks # :nodoc:
|
6
|
-
delegate :connection, :establish_connection, to: ActiveRecord::Base
|
7
|
-
|
8
6
|
def self.using_database_configurations?
|
9
7
|
true
|
10
8
|
end
|
@@ -17,25 +15,26 @@ module ActiveRecord
|
|
17
15
|
def create
|
18
16
|
raise DatabaseAlreadyExists if File.exist?(db_config.database)
|
19
17
|
|
20
|
-
establish_connection
|
18
|
+
establish_connection
|
21
19
|
connection
|
22
20
|
end
|
23
21
|
|
24
22
|
def drop
|
25
|
-
|
26
|
-
|
27
|
-
file = path.absolute? ? path.to_s : File.join(root, path)
|
28
|
-
|
23
|
+
db_path = db_config.database
|
24
|
+
file = File.absolute_path?(db_path) ? db_path : File.join(root, db_path)
|
29
25
|
FileUtils.rm(file)
|
26
|
+
FileUtils.rm_f(["#{file}-shm", "#{file}-wal"])
|
30
27
|
rescue Errno::ENOENT => error
|
31
28
|
raise NoDatabaseError.new(error.message)
|
32
29
|
end
|
33
30
|
|
34
31
|
def purge
|
32
|
+
connection.disconnect!
|
35
33
|
drop
|
36
34
|
rescue NoDatabaseError
|
37
35
|
ensure
|
38
36
|
create
|
37
|
+
connection.reconnect!
|
39
38
|
end
|
40
39
|
|
41
40
|
def charset
|
@@ -49,6 +48,7 @@ module ActiveRecord
|
|
49
48
|
|
50
49
|
ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
|
51
50
|
if ignore_tables.any?
|
51
|
+
ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
|
52
52
|
condition = ignore_tables.map { |table| connection.quote(table) }.join(", ")
|
53
53
|
args << "SELECT sql FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
|
54
54
|
else
|
@@ -65,6 +65,15 @@ module ActiveRecord
|
|
65
65
|
private
|
66
66
|
attr_reader :db_config, :root
|
67
67
|
|
68
|
+
def connection
|
69
|
+
ActiveRecord::Base.lease_connection
|
70
|
+
end
|
71
|
+
|
72
|
+
def establish_connection(config = db_config)
|
73
|
+
ActiveRecord::Base.establish_connection(config)
|
74
|
+
connection.connect!
|
75
|
+
end
|
76
|
+
|
68
77
|
def run_cmd(cmd, args, out)
|
69
78
|
fail run_cmd_error(cmd, args) unless Kernel.system(cmd, *args, out: out)
|
70
79
|
end
|
@@ -13,17 +13,31 @@ module ActiveRecord
|
|
13
13
|
|
14
14
|
def after_teardown # :nodoc:
|
15
15
|
super
|
16
|
+
ensure
|
16
17
|
teardown_fixtures
|
17
18
|
end
|
18
19
|
|
19
20
|
included do
|
20
|
-
|
21
|
+
##
|
22
|
+
# :singleton-method: fixture_paths
|
23
|
+
#
|
24
|
+
# Returns the ActiveRecord::FixtureSet collection
|
25
|
+
|
26
|
+
##
|
27
|
+
# :singleton-method: fixture_paths=
|
28
|
+
#
|
29
|
+
# :call-seq:
|
30
|
+
# fixture_paths=(fixture_paths)
|
31
|
+
class_attribute :fixture_paths, instance_writer: false, default: []
|
21
32
|
class_attribute :fixture_table_names, default: []
|
22
33
|
class_attribute :fixture_class_names, default: {}
|
23
34
|
class_attribute :use_transactional_tests, default: true
|
24
35
|
class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
|
25
36
|
class_attribute :pre_loaded_fixtures, default: false
|
26
37
|
class_attribute :lock_threads, default: true
|
38
|
+
class_attribute :fixture_sets, default: {}
|
39
|
+
|
40
|
+
ActiveSupport.run_load_hooks(:active_record_fixtures, self)
|
27
41
|
end
|
28
42
|
|
29
43
|
module ClassMethods
|
@@ -41,49 +55,31 @@ module ActiveRecord
|
|
41
55
|
|
42
56
|
def fixtures(*fixture_set_names)
|
43
57
|
if fixture_set_names.first == :all
|
44
|
-
raise StandardError, "No fixture path found. Please set `#{self}.
|
45
|
-
fixture_set_names =
|
46
|
-
|
47
|
-
|
58
|
+
raise StandardError, "No fixture path found. Please set `#{self}.fixture_paths`." if fixture_paths.blank?
|
59
|
+
fixture_set_names = fixture_paths.flat_map do |path|
|
60
|
+
names = Dir[::File.join(path, "{**,*}/*.{yml}")].uniq
|
61
|
+
names.reject! { |f| f.start_with?(file_fixture_path.to_s) } if defined?(file_fixture_path) && file_fixture_path
|
62
|
+
names.map! { |f| f[path.to_s.size..-5].delete_prefix("/") }
|
63
|
+
end.uniq
|
48
64
|
else
|
49
65
|
fixture_set_names = fixture_set_names.flatten.map(&:to_s)
|
50
66
|
end
|
51
67
|
|
52
|
-
self.fixture_table_names
|
68
|
+
self.fixture_table_names = (fixture_table_names | fixture_set_names).sort
|
53
69
|
setup_fixture_accessors(fixture_set_names)
|
54
70
|
end
|
55
71
|
|
56
72
|
def setup_fixture_accessors(fixture_set_names = nil)
|
57
73
|
fixture_set_names = Array(fixture_set_names || fixture_table_names)
|
58
|
-
|
74
|
+
unless fixture_set_names.empty?
|
75
|
+
self.fixture_sets = fixture_sets.dup
|
59
76
|
fixture_set_names.each do |fs_name|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
|
65
|
-
return_single_record = fixture_names.size == 1
|
66
|
-
fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
|
67
|
-
|
68
|
-
@fixture_cache[fs_name] ||= {}
|
69
|
-
|
70
|
-
instances = fixture_names.map do |f_name|
|
71
|
-
f_name = f_name.to_s if f_name.is_a?(Symbol)
|
72
|
-
@fixture_cache[fs_name].delete(f_name) if force_reload
|
73
|
-
|
74
|
-
if @loaded_fixtures[fs_name][f_name]
|
75
|
-
@fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
|
76
|
-
else
|
77
|
-
raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
return_single_record ? instances.first : instances
|
82
|
-
end
|
83
|
-
private accessor_name
|
77
|
+
key = fs_name.to_s.include?("/") ? -fs_name.to_s.tr("/", "_") : fs_name
|
78
|
+
key = -key.to_s if key.is_a?(Symbol)
|
79
|
+
fs_name = -fs_name.to_s if fs_name.is_a?(Symbol)
|
80
|
+
fixture_sets[key] = fs_name
|
84
81
|
end
|
85
82
|
end
|
86
|
-
include methods
|
87
83
|
end
|
88
84
|
|
89
85
|
# Prevents automatically wrapping each specified test in a transaction,
|
@@ -100,99 +96,109 @@ module ActiveRecord
|
|
100
96
|
end
|
101
97
|
end
|
102
98
|
|
103
|
-
|
104
|
-
|
105
|
-
|
99
|
+
# Generic fixture accessor for fixture names that may conflict with other methods.
|
100
|
+
#
|
101
|
+
# assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
|
102
|
+
# assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).name
|
103
|
+
def fixture(fixture_set_name, *fixture_names)
|
104
|
+
active_record_fixture(fixture_set_name, *fixture_names)
|
106
105
|
end
|
107
106
|
|
108
|
-
|
109
|
-
|
110
|
-
|
107
|
+
private
|
108
|
+
def run_in_transaction?
|
109
|
+
use_transactional_tests &&
|
110
|
+
!self.class.uses_transaction?(name)
|
111
111
|
end
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
113
|
+
def setup_fixtures(config = ActiveRecord::Base)
|
114
|
+
if pre_loaded_fixtures && !use_transactional_tests
|
115
|
+
raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
|
116
|
+
end
|
117
|
+
|
118
|
+
@fixture_cache = {}
|
119
|
+
@fixture_cache_key = [self.class.fixture_table_names.dup, self.class.fixture_paths.dup, self.class.fixture_class_names.dup]
|
120
|
+
@fixture_connection_pools = []
|
121
|
+
@@already_loaded_fixtures ||= {}
|
122
|
+
@connection_subscriber = nil
|
123
|
+
@saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
|
124
|
+
|
125
|
+
if run_in_transaction?
|
126
|
+
# Load fixtures once and begin transaction.
|
127
|
+
@loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key]
|
128
|
+
unless @loaded_fixtures
|
129
|
+
@@already_loaded_fixtures.clear
|
130
|
+
@loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key] = load_fixtures(config)
|
131
|
+
end
|
132
|
+
|
133
|
+
setup_transactional_fixtures
|
124
134
|
else
|
135
|
+
# Load fixtures for every test.
|
136
|
+
ActiveRecord::FixtureSet.reset_cache
|
137
|
+
invalidate_already_loaded_fixtures
|
125
138
|
@loaded_fixtures = load_fixtures(config)
|
126
|
-
@@already_loaded_fixtures[self.class] = @loaded_fixtures
|
127
139
|
end
|
128
140
|
|
141
|
+
# Instantiate fixtures for every test if requested.
|
142
|
+
instantiate_fixtures if use_instantiated_fixtures
|
143
|
+
end
|
144
|
+
|
145
|
+
def teardown_fixtures
|
146
|
+
# Rollback changes if a transaction is active.
|
147
|
+
if run_in_transaction?
|
148
|
+
teardown_transactional_fixtures
|
149
|
+
else
|
150
|
+
ActiveRecord::FixtureSet.reset_cache
|
151
|
+
invalidate_already_loaded_fixtures
|
152
|
+
end
|
153
|
+
|
154
|
+
ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
|
155
|
+
end
|
156
|
+
|
157
|
+
def invalidate_already_loaded_fixtures
|
158
|
+
@@already_loaded_fixtures.clear
|
159
|
+
end
|
160
|
+
|
161
|
+
def setup_transactional_fixtures
|
162
|
+
setup_shared_connection_pool
|
163
|
+
|
129
164
|
# Begin transactions for connections already established
|
130
|
-
@
|
131
|
-
@
|
132
|
-
|
133
|
-
|
165
|
+
@fixture_connection_pools = ActiveRecord::Base.connection_handler.connection_pool_list(:writing)
|
166
|
+
@fixture_connection_pools.each do |pool|
|
167
|
+
pool.pin_connection!(lock_threads)
|
168
|
+
pool.lease_connection
|
134
169
|
end
|
135
170
|
|
136
171
|
# When connections are established in the future, begin a transaction too
|
137
172
|
@connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
|
138
|
-
|
173
|
+
connection_name = payload[:connection_name] if payload.key?(:connection_name)
|
139
174
|
shard = payload[:shard] if payload.key?(:shard)
|
140
|
-
setup_shared_connection_pool if ActiveRecord.legacy_connection_handling
|
141
175
|
|
142
|
-
if
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
connection = nil
|
147
|
-
end
|
148
|
-
|
149
|
-
if connection
|
150
|
-
setup_shared_connection_pool unless ActiveRecord.legacy_connection_handling
|
176
|
+
if connection_name
|
177
|
+
pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(connection_name, shard: shard)
|
178
|
+
if pool
|
179
|
+
setup_shared_connection_pool
|
151
180
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
@
|
181
|
+
unless @fixture_connection_pools.include?(pool)
|
182
|
+
pool.pin_connection!(lock_threads)
|
183
|
+
pool.lease_connection
|
184
|
+
@fixture_connection_pools << pool
|
156
185
|
end
|
157
186
|
end
|
158
187
|
end
|
159
188
|
end
|
160
|
-
|
161
|
-
# Load fixtures for every test.
|
162
|
-
else
|
163
|
-
ActiveRecord::FixtureSet.reset_cache
|
164
|
-
@@already_loaded_fixtures[self.class] = nil
|
165
|
-
@loaded_fixtures = load_fixtures(config)
|
166
189
|
end
|
167
190
|
|
168
|
-
|
169
|
-
instantiate_fixtures if use_instantiated_fixtures
|
170
|
-
end
|
171
|
-
|
172
|
-
def teardown_fixtures
|
173
|
-
# Rollback changes if a transaction is active.
|
174
|
-
if run_in_transaction?
|
191
|
+
def teardown_transactional_fixtures
|
175
192
|
ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
|
176
|
-
@
|
177
|
-
|
178
|
-
|
193
|
+
unless @fixture_connection_pools.map(&:unpin_connection!).all?
|
194
|
+
# Something caused the transaction to be committed or rolled back
|
195
|
+
# We can no longer trust the database is in a clean state.
|
196
|
+
@@already_loaded_fixtures.clear
|
179
197
|
end
|
180
|
-
@
|
198
|
+
@fixture_connection_pools.clear
|
181
199
|
teardown_shared_connection_pool
|
182
|
-
else
|
183
|
-
ActiveRecord::FixtureSet.reset_cache
|
184
200
|
end
|
185
201
|
|
186
|
-
ActiveRecord::Base.clear_active_connections!
|
187
|
-
end
|
188
|
-
|
189
|
-
def enlist_fixture_connections
|
190
|
-
setup_shared_connection_pool
|
191
|
-
|
192
|
-
ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
|
193
|
-
end
|
194
|
-
|
195
|
-
private
|
196
202
|
# Shares the writing connection pool with connections on
|
197
203
|
# other handlers.
|
198
204
|
#
|
@@ -200,79 +206,43 @@ module ActiveRecord
|
|
200
206
|
# need to share a connection pool so that the reading connection
|
201
207
|
# can see data in the open transaction on the writing connection.
|
202
208
|
def setup_shared_connection_pool
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
pool_config = pool_manager.get_pool_config(nil, shard_name)
|
217
|
-
next if pool_config == writing_pool_config
|
218
|
-
|
219
|
-
@legacy_saved_pool_configs[handler][name][shard_name] = pool_config
|
220
|
-
pool_manager.set_pool_config(nil, shard_name, writing_pool_config)
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
else
|
226
|
-
handler = ActiveRecord::Base.connection_handler
|
227
|
-
|
228
|
-
handler.connection_pool_names.each do |name|
|
229
|
-
pool_manager = handler.send(:owner_to_pool_manager)[name]
|
230
|
-
pool_manager.shard_names.each do |shard_name|
|
231
|
-
writing_pool_config = pool_manager.get_pool_config(ActiveRecord.writing_role, shard_name)
|
232
|
-
@saved_pool_configs[name][shard_name] ||= {}
|
233
|
-
pool_manager.role_names.each do |role|
|
234
|
-
next unless pool_config = pool_manager.get_pool_config(role, shard_name)
|
235
|
-
next if pool_config == writing_pool_config
|
236
|
-
|
237
|
-
@saved_pool_configs[name][shard_name][role] = pool_config
|
238
|
-
pool_manager.set_pool_config(role, shard_name, writing_pool_config)
|
239
|
-
end
|
209
|
+
handler = ActiveRecord::Base.connection_handler
|
210
|
+
|
211
|
+
handler.connection_pool_names.each do |name|
|
212
|
+
pool_manager = handler.send(:connection_name_to_pool_manager)[name]
|
213
|
+
pool_manager.shard_names.each do |shard_name|
|
214
|
+
writing_pool_config = pool_manager.get_pool_config(ActiveRecord.writing_role, shard_name)
|
215
|
+
@saved_pool_configs[name][shard_name] ||= {}
|
216
|
+
pool_manager.role_names.each do |role|
|
217
|
+
next unless pool_config = pool_manager.get_pool_config(role, shard_name)
|
218
|
+
next if pool_config == writing_pool_config
|
219
|
+
|
220
|
+
@saved_pool_configs[name][shard_name][role] = pool_config
|
221
|
+
pool_manager.set_pool_config(role, shard_name, writing_pool_config)
|
240
222
|
end
|
241
223
|
end
|
242
224
|
end
|
243
225
|
end
|
244
226
|
|
245
227
|
def teardown_shared_connection_pool
|
246
|
-
|
247
|
-
@legacy_saved_pool_configs.each_pair do |handler, names|
|
248
|
-
names.each_pair do |name, shards|
|
249
|
-
shards.each_pair do |shard_name, pool_config|
|
250
|
-
pool_manager = handler.send(:owner_to_pool_manager)[name]
|
251
|
-
pool_manager.set_pool_config(nil, shard_name, pool_config)
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end
|
255
|
-
else
|
256
|
-
handler = ActiveRecord::Base.connection_handler
|
228
|
+
handler = ActiveRecord::Base.connection_handler
|
257
229
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
230
|
+
@saved_pool_configs.each_pair do |name, shards|
|
231
|
+
pool_manager = handler.send(:connection_name_to_pool_manager)[name]
|
232
|
+
shards.each_pair do |shard_name, roles|
|
233
|
+
roles.each_pair do |role, pool_config|
|
234
|
+
next unless pool_manager.get_pool_config(role, shard_name)
|
263
235
|
|
264
|
-
|
265
|
-
end
|
236
|
+
pool_manager.set_pool_config(role, shard_name, pool_config)
|
266
237
|
end
|
267
238
|
end
|
268
239
|
end
|
269
240
|
|
270
|
-
@legacy_saved_pool_configs.clear
|
271
241
|
@saved_pool_configs.clear
|
272
242
|
end
|
273
243
|
|
274
244
|
def load_fixtures(config)
|
275
|
-
ActiveRecord::FixtureSet.create_fixtures(
|
245
|
+
ActiveRecord::FixtureSet.create_fixtures(fixture_paths, fixture_table_names, fixture_class_names, config).index_by(&:name)
|
276
246
|
end
|
277
247
|
|
278
248
|
def instantiate_fixtures
|
@@ -290,5 +260,50 @@ module ActiveRecord
|
|
290
260
|
def load_instances?
|
291
261
|
use_instantiated_fixtures != :no_instances
|
292
262
|
end
|
263
|
+
|
264
|
+
def method_missing(method, ...)
|
265
|
+
if fixture_sets.key?(method.name)
|
266
|
+
active_record_fixture(method, ...)
|
267
|
+
else
|
268
|
+
super
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def respond_to_missing?(method, include_private = false)
|
273
|
+
if include_private && fixture_sets.key?(method.name)
|
274
|
+
true
|
275
|
+
else
|
276
|
+
super
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
def active_record_fixture(fixture_set_name, *fixture_names)
|
281
|
+
if fs_name = fixture_sets[fixture_set_name.name]
|
282
|
+
access_fixture(fs_name, *fixture_names)
|
283
|
+
else
|
284
|
+
raise StandardError, "No fixture set named '#{fixture_set_name.inspect}'"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def access_fixture(fs_name, *fixture_names)
|
289
|
+
force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
|
290
|
+
return_single_record = fixture_names.size == 1
|
291
|
+
|
292
|
+
fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
|
293
|
+
@fixture_cache[fs_name] ||= {}
|
294
|
+
|
295
|
+
instances = fixture_names.map do |f_name|
|
296
|
+
f_name = f_name.to_s if f_name.is_a?(Symbol)
|
297
|
+
@fixture_cache[fs_name].delete(f_name) if force_reload
|
298
|
+
|
299
|
+
if @loaded_fixtures[fs_name][f_name]
|
300
|
+
@fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
|
301
|
+
else
|
302
|
+
raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
return_single_record ? instances.first : instances
|
307
|
+
end
|
293
308
|
end
|
294
309
|
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Assertions
|
5
|
+
module QueryAssertions
|
6
|
+
# Asserts that the number of SQL queries executed in the given block matches the expected count.
|
7
|
+
#
|
8
|
+
# # Check for exact number of queries
|
9
|
+
# assert_queries_count(1) { Post.first }
|
10
|
+
#
|
11
|
+
# # Check for any number of queries
|
12
|
+
# assert_queries_count { Post.first }
|
13
|
+
#
|
14
|
+
# If the +:include_schema+ option is provided, any queries (including schema related) are counted.
|
15
|
+
#
|
16
|
+
# assert_queries_count(1, include_schema: true) { Post.columns }
|
17
|
+
#
|
18
|
+
def assert_queries_count(count = nil, include_schema: false, &block)
|
19
|
+
ActiveRecord::Base.lease_connection.materialize_transactions
|
20
|
+
|
21
|
+
counter = SQLCounter.new
|
22
|
+
ActiveSupport::Notifications.subscribed(counter, "sql.active_record") do
|
23
|
+
result = _assert_nothing_raised_or_warn("assert_queries_count", &block)
|
24
|
+
queries = include_schema ? counter.log_all : counter.log
|
25
|
+
if count
|
26
|
+
assert_equal count, queries.size, "#{queries.size} instead of #{count} queries were executed. Queries: #{queries.join("\n\n")}"
|
27
|
+
else
|
28
|
+
assert_operator queries.size, :>=, 1, "1 or more queries expected, but none were executed.#{queries.empty? ? '' : "\nQueries:\n#{queries.join("\n")}"}"
|
29
|
+
end
|
30
|
+
result
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Asserts that no SQL queries are executed in the given block.
|
35
|
+
#
|
36
|
+
# assert_no_queries { post.comments }
|
37
|
+
#
|
38
|
+
# If the +:include_schema+ option is provided, any queries (including schema related) are counted.
|
39
|
+
#
|
40
|
+
# assert_no_queries(include_schema: true) { Post.columns }
|
41
|
+
#
|
42
|
+
def assert_no_queries(include_schema: false, &block)
|
43
|
+
assert_queries_count(0, include_schema: include_schema, &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Asserts that the SQL queries executed in the given block match expected pattern.
|
47
|
+
#
|
48
|
+
# # Check for exact number of queries
|
49
|
+
# assert_queries_match(/LIMIT \?/, count: 1) { Post.first }
|
50
|
+
#
|
51
|
+
# # Check for any number of queries
|
52
|
+
# assert_queries_match(/LIMIT \?/) { Post.first }
|
53
|
+
#
|
54
|
+
# If the +:include_schema+ option is provided, any queries (including schema related)
|
55
|
+
# that match the matcher are considered.
|
56
|
+
#
|
57
|
+
# assert_queries_match(/FROM pg_attribute/i, include_schema: true) { Post.columns }
|
58
|
+
#
|
59
|
+
def assert_queries_match(match, count: nil, include_schema: false, &block)
|
60
|
+
ActiveRecord::Base.lease_connection.materialize_transactions
|
61
|
+
|
62
|
+
counter = SQLCounter.new
|
63
|
+
ActiveSupport::Notifications.subscribed(counter, "sql.active_record") do
|
64
|
+
result = _assert_nothing_raised_or_warn("assert_queries_match", &block)
|
65
|
+
queries = include_schema ? counter.log_all : counter.log
|
66
|
+
matched_queries = queries.select { |query| match === query }
|
67
|
+
|
68
|
+
if count
|
69
|
+
assert_equal count, matched_queries.size, "#{matched_queries.size} instead of #{count} queries were executed.#{queries.empty? ? '' : "\nQueries:\n#{queries.join("\n")}"}"
|
70
|
+
else
|
71
|
+
assert_operator matched_queries.size, :>=, 1, "1 or more queries expected, but none were executed.#{queries.empty? ? '' : "\nQueries:\n#{queries.join("\n")}"}"
|
72
|
+
end
|
73
|
+
|
74
|
+
result
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Asserts that no SQL queries matching the pattern are executed in the given block.
|
79
|
+
#
|
80
|
+
# assert_no_queries_match(/SELECT/i) { post.comments }
|
81
|
+
#
|
82
|
+
# If the +:include_schema+ option is provided, any queries (including schema related)
|
83
|
+
# that match the matcher are counted.
|
84
|
+
#
|
85
|
+
# assert_no_queries_match(/FROM pg_attribute/i, include_schema: true) { Post.columns }
|
86
|
+
#
|
87
|
+
def assert_no_queries_match(match, include_schema: false, &block)
|
88
|
+
assert_queries_match(match, count: 0, include_schema: include_schema, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
class SQLCounter # :nodoc:
|
92
|
+
attr_reader :log_full, :log_all
|
93
|
+
|
94
|
+
def initialize
|
95
|
+
@log_full = []
|
96
|
+
@log_all = []
|
97
|
+
end
|
98
|
+
|
99
|
+
def log
|
100
|
+
@log_full.map(&:first)
|
101
|
+
end
|
102
|
+
|
103
|
+
def call(*, payload)
|
104
|
+
return if payload[:cached]
|
105
|
+
|
106
|
+
sql = payload[:sql]
|
107
|
+
@log_all << sql
|
108
|
+
|
109
|
+
unless payload[:name] == "SCHEMA"
|
110
|
+
bound_values = (payload[:binds] || []).map do |value|
|
111
|
+
value = value.value_for_database if value.respond_to?(:value_for_database)
|
112
|
+
value
|
113
|
+
end
|
114
|
+
|
115
|
+
@log_full << [sql, bound_values]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|