activerecord 5.2.6 → 6.0.0
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 +609 -622
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations/association.rb +52 -19
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +3 -13
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -21
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -10
- data/lib/active_record/associations/has_many_through_association.rb +14 -14
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/join_dependency.rb +24 -28
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/preloader.rb +40 -32
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/associations.rb +19 -14
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +5 -9
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +94 -16
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -141
- data/lib/active_record/connection_handling.rb +149 -27
- data/lib/active_record/core.rb +100 -60
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/database_configurations.rb +233 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +37 -7
- data/lib/active_record/errors.rb +15 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +145 -472
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- data/lib/active_record/migration.rb +100 -81
- data/lib/active_record/model_schema.rb +30 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +228 -24
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +32 -20
- data/lib/active_record/railtie.rb +80 -43
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +196 -46
- data/lib/active_record/reflection.rb +32 -30
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +53 -47
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +13 -26
- data/lib/active_record/relation/merger.rb +11 -20
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +189 -63
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/relation.rb +310 -80
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping/default.rb +4 -5
- data/lib/active_record/scoping/named.rb +19 -15
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/statement_cache.rb +30 -3
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +194 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +23 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +57 -66
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +9 -2
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +51 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +108 -26
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -0,0 +1,224 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module TestFixtures
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def before_setup # :nodoc:
|
8
|
+
setup_fixtures
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def after_teardown # :nodoc:
|
13
|
+
super
|
14
|
+
teardown_fixtures
|
15
|
+
end
|
16
|
+
|
17
|
+
included do
|
18
|
+
class_attribute :fixture_path, instance_writer: false
|
19
|
+
class_attribute :fixture_table_names, default: []
|
20
|
+
class_attribute :fixture_class_names, default: {}
|
21
|
+
class_attribute :use_transactional_tests, default: true
|
22
|
+
class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
|
23
|
+
class_attribute :pre_loaded_fixtures, default: false
|
24
|
+
class_attribute :config, default: ActiveRecord::Base
|
25
|
+
class_attribute :lock_threads, default: true
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
# Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
|
30
|
+
#
|
31
|
+
# Examples:
|
32
|
+
#
|
33
|
+
# set_fixture_class some_fixture: SomeModel,
|
34
|
+
# 'namespaced/fixture' => Another::Model
|
35
|
+
#
|
36
|
+
# The keys must be the fixture names, that coincide with the short paths to the fixture files.
|
37
|
+
def set_fixture_class(class_names = {})
|
38
|
+
self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
|
39
|
+
end
|
40
|
+
|
41
|
+
def fixtures(*fixture_set_names)
|
42
|
+
if fixture_set_names.first == :all
|
43
|
+
raise StandardError, "No fixture path found. Please set `#{self}.fixture_path`." if fixture_path.blank?
|
44
|
+
fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"].uniq
|
45
|
+
fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
|
46
|
+
else
|
47
|
+
fixture_set_names = fixture_set_names.flatten.map(&:to_s)
|
48
|
+
end
|
49
|
+
|
50
|
+
self.fixture_table_names |= fixture_set_names
|
51
|
+
setup_fixture_accessors(fixture_set_names)
|
52
|
+
end
|
53
|
+
|
54
|
+
def setup_fixture_accessors(fixture_set_names = nil)
|
55
|
+
fixture_set_names = Array(fixture_set_names || fixture_table_names)
|
56
|
+
methods = Module.new do
|
57
|
+
fixture_set_names.each do |fs_name|
|
58
|
+
fs_name = fs_name.to_s
|
59
|
+
accessor_name = fs_name.tr("/", "_").to_sym
|
60
|
+
|
61
|
+
define_method(accessor_name) do |*fixture_names|
|
62
|
+
force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
|
63
|
+
return_single_record = fixture_names.size == 1
|
64
|
+
fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
|
65
|
+
|
66
|
+
@fixture_cache[fs_name] ||= {}
|
67
|
+
|
68
|
+
instances = fixture_names.map do |f_name|
|
69
|
+
f_name = f_name.to_s if f_name.is_a?(Symbol)
|
70
|
+
@fixture_cache[fs_name].delete(f_name) if force_reload
|
71
|
+
|
72
|
+
if @loaded_fixtures[fs_name][f_name]
|
73
|
+
@fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
|
74
|
+
else
|
75
|
+
raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
return_single_record ? instances.first : instances
|
80
|
+
end
|
81
|
+
private accessor_name
|
82
|
+
end
|
83
|
+
end
|
84
|
+
include methods
|
85
|
+
end
|
86
|
+
|
87
|
+
def uses_transaction(*methods)
|
88
|
+
@uses_transaction = [] unless defined?(@uses_transaction)
|
89
|
+
@uses_transaction.concat methods.map(&:to_s)
|
90
|
+
end
|
91
|
+
|
92
|
+
def uses_transaction?(method)
|
93
|
+
@uses_transaction = [] unless defined?(@uses_transaction)
|
94
|
+
@uses_transaction.include?(method.to_s)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def run_in_transaction?
|
99
|
+
use_transactional_tests &&
|
100
|
+
!self.class.uses_transaction?(method_name)
|
101
|
+
end
|
102
|
+
|
103
|
+
def setup_fixtures(config = ActiveRecord::Base)
|
104
|
+
if pre_loaded_fixtures && !use_transactional_tests
|
105
|
+
raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
|
106
|
+
end
|
107
|
+
|
108
|
+
@fixture_cache = {}
|
109
|
+
@fixture_connections = []
|
110
|
+
@@already_loaded_fixtures ||= {}
|
111
|
+
@connection_subscriber = nil
|
112
|
+
|
113
|
+
# Load fixtures once and begin transaction.
|
114
|
+
if run_in_transaction?
|
115
|
+
if @@already_loaded_fixtures[self.class]
|
116
|
+
@loaded_fixtures = @@already_loaded_fixtures[self.class]
|
117
|
+
else
|
118
|
+
@loaded_fixtures = load_fixtures(config)
|
119
|
+
@@already_loaded_fixtures[self.class] = @loaded_fixtures
|
120
|
+
end
|
121
|
+
|
122
|
+
# Begin transactions for connections already established
|
123
|
+
@fixture_connections = enlist_fixture_connections
|
124
|
+
@fixture_connections.each do |connection|
|
125
|
+
connection.begin_transaction joinable: false, _lazy: false
|
126
|
+
connection.pool.lock_thread = true if lock_threads
|
127
|
+
end
|
128
|
+
|
129
|
+
# When connections are established in the future, begin a transaction too
|
130
|
+
@connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
|
131
|
+
spec_name = payload[:spec_name] if payload.key?(:spec_name)
|
132
|
+
|
133
|
+
if spec_name
|
134
|
+
begin
|
135
|
+
connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name)
|
136
|
+
rescue ConnectionNotEstablished
|
137
|
+
connection = nil
|
138
|
+
end
|
139
|
+
|
140
|
+
if connection && !@fixture_connections.include?(connection)
|
141
|
+
connection.begin_transaction joinable: false, _lazy: false
|
142
|
+
connection.pool.lock_thread = true if lock_threads
|
143
|
+
@fixture_connections << connection
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Load fixtures for every test.
|
149
|
+
else
|
150
|
+
ActiveRecord::FixtureSet.reset_cache
|
151
|
+
@@already_loaded_fixtures[self.class] = nil
|
152
|
+
@loaded_fixtures = load_fixtures(config)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Instantiate fixtures for every test if requested.
|
156
|
+
instantiate_fixtures if use_instantiated_fixtures
|
157
|
+
end
|
158
|
+
|
159
|
+
def teardown_fixtures
|
160
|
+
# Rollback changes if a transaction is active.
|
161
|
+
if run_in_transaction?
|
162
|
+
ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
|
163
|
+
@fixture_connections.each do |connection|
|
164
|
+
connection.rollback_transaction if connection.transaction_open?
|
165
|
+
connection.pool.lock_thread = false
|
166
|
+
end
|
167
|
+
@fixture_connections.clear
|
168
|
+
else
|
169
|
+
ActiveRecord::FixtureSet.reset_cache
|
170
|
+
end
|
171
|
+
|
172
|
+
ActiveRecord::Base.clear_active_connections!
|
173
|
+
end
|
174
|
+
|
175
|
+
def enlist_fixture_connections
|
176
|
+
setup_shared_connection_pool
|
177
|
+
|
178
|
+
ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
# Shares the writing connection pool with connections on
|
184
|
+
# other handlers.
|
185
|
+
#
|
186
|
+
# In an application with a primary and replica the test fixtures
|
187
|
+
# need to share a connection pool so that the reading connection
|
188
|
+
# can see data in the open transaction on the writing connection.
|
189
|
+
def setup_shared_connection_pool
|
190
|
+
writing_handler = ActiveRecord::Base.connection_handler
|
191
|
+
|
192
|
+
ActiveRecord::Base.connection_handlers.values.each do |handler|
|
193
|
+
if handler != writing_handler
|
194
|
+
handler.connection_pool_list.each do |pool|
|
195
|
+
name = pool.spec.name
|
196
|
+
writing_connection = writing_handler.retrieve_connection_pool(name)
|
197
|
+
handler.send(:owner_to_pool)[name] = writing_connection
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def load_fixtures(config)
|
204
|
+
fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
|
205
|
+
Hash[fixtures.map { |f| [f.name, f] }]
|
206
|
+
end
|
207
|
+
|
208
|
+
def instantiate_fixtures
|
209
|
+
if pre_loaded_fixtures
|
210
|
+
raise RuntimeError, "Load fixtures before instantiating them." if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
|
211
|
+
ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
|
212
|
+
else
|
213
|
+
raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil?
|
214
|
+
@loaded_fixtures.each_value do |fixture_set|
|
215
|
+
ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def load_instances?
|
221
|
+
use_instantiated_fixtures != :no_instances
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -56,23 +56,29 @@ module ActiveRecord
|
|
56
56
|
def touch_attributes_with_time(*names, time: nil)
|
57
57
|
attribute_names = timestamp_attributes_for_update_in_model
|
58
58
|
attribute_names |= names.map(&:to_s)
|
59
|
-
time
|
60
|
-
attribute_names.each_with_object({}) { |attr_name, result| result[attr_name] = time }
|
59
|
+
attribute_names.index_with(time || current_time_from_proper_timezone)
|
61
60
|
end
|
62
61
|
|
63
|
-
|
64
|
-
|
65
|
-
timestamp_attributes_for_create
|
66
|
-
|
62
|
+
def timestamp_attributes_for_create_in_model
|
63
|
+
@timestamp_attributes_for_create_in_model ||=
|
64
|
+
(timestamp_attributes_for_create & column_names).freeze
|
65
|
+
end
|
67
66
|
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
def timestamp_attributes_for_update_in_model
|
68
|
+
@timestamp_attributes_for_update_in_model ||=
|
69
|
+
(timestamp_attributes_for_update & column_names).freeze
|
70
|
+
end
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
|
72
|
+
def all_timestamp_attributes_in_model
|
73
|
+
@all_timestamp_attributes_in_model ||=
|
74
|
+
(timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model).freeze
|
75
|
+
end
|
75
76
|
|
77
|
+
def current_time_from_proper_timezone
|
78
|
+
default_timezone == :utc ? Time.now.utc : Time.now
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
76
82
|
def timestamp_attributes_for_create
|
77
83
|
["created_at", "created_on"]
|
78
84
|
end
|
@@ -81,8 +87,11 @@ module ActiveRecord
|
|
81
87
|
["updated_at", "updated_on"]
|
82
88
|
end
|
83
89
|
|
84
|
-
def
|
85
|
-
|
90
|
+
def reload_schema_from_cache
|
91
|
+
@timestamp_attributes_for_create_in_model = nil
|
92
|
+
@timestamp_attributes_for_update_in_model = nil
|
93
|
+
@all_timestamp_attributes_in_model = nil
|
94
|
+
super
|
86
95
|
end
|
87
96
|
end
|
88
97
|
|
@@ -102,8 +111,8 @@ module ActiveRecord
|
|
102
111
|
super
|
103
112
|
end
|
104
113
|
|
105
|
-
def _update_record
|
106
|
-
if
|
114
|
+
def _update_record
|
115
|
+
if @_touch_record && should_record_timestamps?
|
107
116
|
current_time = current_time_from_proper_timezone
|
108
117
|
|
109
118
|
timestamp_attributes_for_update_in_model.each do |column|
|
@@ -111,7 +120,13 @@ module ActiveRecord
|
|
111
120
|
_write_attribute(column, current_time)
|
112
121
|
end
|
113
122
|
end
|
114
|
-
|
123
|
+
|
124
|
+
super
|
125
|
+
end
|
126
|
+
|
127
|
+
def create_or_update(touch: true, **)
|
128
|
+
@_touch_record = touch
|
129
|
+
super
|
115
130
|
end
|
116
131
|
|
117
132
|
def should_record_timestamps?
|
@@ -119,26 +134,25 @@ module ActiveRecord
|
|
119
134
|
end
|
120
135
|
|
121
136
|
def timestamp_attributes_for_create_in_model
|
122
|
-
self.class.
|
137
|
+
self.class.timestamp_attributes_for_create_in_model
|
123
138
|
end
|
124
139
|
|
125
140
|
def timestamp_attributes_for_update_in_model
|
126
|
-
self.class.
|
141
|
+
self.class.timestamp_attributes_for_update_in_model
|
127
142
|
end
|
128
143
|
|
129
144
|
def all_timestamp_attributes_in_model
|
130
|
-
self.class.
|
145
|
+
self.class.all_timestamp_attributes_in_model
|
131
146
|
end
|
132
147
|
|
133
148
|
def current_time_from_proper_timezone
|
134
|
-
self.class.
|
149
|
+
self.class.current_time_from_proper_timezone
|
135
150
|
end
|
136
151
|
|
137
|
-
def max_updated_column_timestamp
|
138
|
-
|
139
|
-
.map { |attr| self[attr] }
|
152
|
+
def max_updated_column_timestamp
|
153
|
+
timestamp_attributes_for_update_in_model
|
154
|
+
.map { |attr| self[attr]&.to_time }
|
140
155
|
.compact
|
141
|
-
.map(&:to_time)
|
142
156
|
.max
|
143
157
|
end
|
144
158
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
# = Active Record Touch Later
|
5
|
-
module TouchLater
|
5
|
+
module TouchLater # :nodoc:
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
@@ -22,7 +22,8 @@ module ActiveRecord
|
|
22
22
|
@_touch_time = current_time_from_proper_timezone
|
23
23
|
|
24
24
|
surreptitiously_touch @_defer_touch_attrs
|
25
|
-
|
25
|
+
add_to_transaction
|
26
|
+
@_new_record_before_last_commit ||= false
|
26
27
|
|
27
28
|
# touch the parents as we are not calling the after_save callbacks
|
28
29
|
self.class.reflect_on_all_associations(:belongs_to).each do |r|
|
@@ -48,6 +49,7 @@ module ActiveRecord
|
|
48
49
|
|
49
50
|
def touch_deferred_attributes
|
50
51
|
if has_defer_touch_attrs? && persisted?
|
52
|
+
@_skip_dirty_tracking = true
|
51
53
|
touch(*@_defer_touch_attrs, time: @_touch_time)
|
52
54
|
@_defer_touch_attrs, @_touch_time = nil, nil
|
53
55
|
end
|
@@ -234,6 +234,12 @@ module ActiveRecord
|
|
234
234
|
set_callback(:commit, :after, *args, &block)
|
235
235
|
end
|
236
236
|
|
237
|
+
# Shortcut for <tt>after_commit :hook, on: [ :create, :update ]</tt>.
|
238
|
+
def after_save_commit(*args, &block)
|
239
|
+
set_options_for_callbacks!(args, on: [ :create, :update ])
|
240
|
+
set_callback(:commit, :after, *args, &block)
|
241
|
+
end
|
242
|
+
|
237
243
|
# Shortcut for <tt>after_commit :hook, on: :create</tt>.
|
238
244
|
def after_create_commit(*args, &block)
|
239
245
|
set_options_for_callbacks!(args, on: :create)
|
@@ -306,9 +312,7 @@ module ActiveRecord
|
|
306
312
|
end
|
307
313
|
|
308
314
|
def save(*) #:nodoc:
|
309
|
-
|
310
|
-
with_transaction_returning_status { super }
|
311
|
-
end
|
315
|
+
with_transaction_returning_status { super }
|
312
316
|
end
|
313
317
|
|
314
318
|
def save!(*) #:nodoc:
|
@@ -319,17 +323,6 @@ module ActiveRecord
|
|
319
323
|
with_transaction_returning_status { super }
|
320
324
|
end
|
321
325
|
|
322
|
-
# Reset id and @new_record if the transaction rolls back.
|
323
|
-
def rollback_active_record_state!
|
324
|
-
remember_transaction_record_state
|
325
|
-
yield
|
326
|
-
rescue Exception
|
327
|
-
restore_transaction_record_state
|
328
|
-
raise
|
329
|
-
ensure
|
330
|
-
clear_transaction_record_state
|
331
|
-
end
|
332
|
-
|
333
326
|
def before_committed! # :nodoc:
|
334
327
|
_run_before_commit_without_transaction_enrollment_callbacks
|
335
328
|
_run_before_commit_callbacks
|
@@ -340,14 +333,14 @@ module ActiveRecord
|
|
340
333
|
# Ensure that it is not called if the object was never persisted (failed create),
|
341
334
|
# but call it after the commit of a destroyed object.
|
342
335
|
def committed!(should_run_callbacks: true) #:nodoc:
|
343
|
-
|
344
|
-
if should_run_callbacks && (destroyed? || persisted?)
|
336
|
+
if should_run_callbacks
|
345
337
|
@_committed_already_called = true
|
346
338
|
_run_commit_without_transaction_enrollment_callbacks
|
347
339
|
_run_commit_callbacks
|
348
340
|
end
|
349
341
|
ensure
|
350
342
|
@_committed_already_called = false
|
343
|
+
force_clear_transaction_record_state
|
351
344
|
end
|
352
345
|
|
353
346
|
# Call the #after_rollback callbacks. The +force_restore_state+ argument indicates if the record
|
@@ -362,18 +355,6 @@ module ActiveRecord
|
|
362
355
|
clear_transaction_record_state
|
363
356
|
end
|
364
357
|
|
365
|
-
# Add the record to the current transaction so that the #after_rollback and #after_commit callbacks
|
366
|
-
# can be called.
|
367
|
-
def add_to_transaction
|
368
|
-
if has_transactional_callbacks?
|
369
|
-
self.class.connection.add_transaction_record(self)
|
370
|
-
else
|
371
|
-
sync_with_transaction_state
|
372
|
-
set_transaction_state(self.class.connection.transaction_state)
|
373
|
-
end
|
374
|
-
remember_transaction_record_state
|
375
|
-
end
|
376
|
-
|
377
358
|
# Executes +method+ within a transaction and captures its return value as a
|
378
359
|
# status flag. If the status is true the transaction is committed, otherwise
|
379
360
|
# a ROLLBACK is issued. In any case the status flag is returned.
|
@@ -383,35 +364,40 @@ module ActiveRecord
|
|
383
364
|
def with_transaction_returning_status
|
384
365
|
status = nil
|
385
366
|
self.class.transaction do
|
386
|
-
|
367
|
+
if has_transactional_callbacks?
|
368
|
+
add_to_transaction
|
369
|
+
else
|
370
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
371
|
+
@transaction_state = self.class.connection.transaction_state
|
372
|
+
end
|
373
|
+
remember_transaction_record_state
|
374
|
+
|
387
375
|
status = yield
|
388
376
|
raise ActiveRecord::Rollback unless status
|
389
377
|
end
|
390
378
|
status
|
391
|
-
ensure
|
392
|
-
if @transaction_state && @transaction_state.committed?
|
393
|
-
clear_transaction_record_state
|
394
|
-
end
|
395
379
|
end
|
396
380
|
|
397
|
-
|
398
|
-
|
381
|
+
def trigger_transactional_callbacks? # :nodoc:
|
382
|
+
(@_new_record_before_last_commit || _trigger_update_callback) && persisted? ||
|
383
|
+
_trigger_destroy_callback && destroyed?
|
384
|
+
end
|
399
385
|
|
400
386
|
private
|
387
|
+
attr_reader :_committed_already_called, :_trigger_update_callback, :_trigger_destroy_callback
|
401
388
|
|
402
389
|
# Save the new record state and id of a record so it can be restored later if a transaction fails.
|
403
390
|
def remember_transaction_record_state
|
404
|
-
@_start_transaction_state
|
391
|
+
@_start_transaction_state ||= {
|
405
392
|
id: id,
|
406
393
|
new_record: @new_record,
|
407
394
|
destroyed: @destroyed,
|
395
|
+
attributes: @attributes,
|
408
396
|
frozen?: frozen?,
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
end
|
397
|
+
level: 0
|
398
|
+
}
|
399
|
+
@_start_transaction_state[:level] += 1
|
413
400
|
|
414
|
-
def remember_new_record_before_last_commit
|
415
401
|
if _committed_already_called
|
416
402
|
@_new_record_before_last_commit = false
|
417
403
|
else
|
@@ -421,27 +407,32 @@ module ActiveRecord
|
|
421
407
|
|
422
408
|
# Clear the new record state and id of a record.
|
423
409
|
def clear_transaction_record_state
|
424
|
-
|
410
|
+
return unless @_start_transaction_state
|
411
|
+
@_start_transaction_state[:level] -= 1
|
425
412
|
force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
|
426
413
|
end
|
427
414
|
|
428
415
|
# Force to clear the transaction record state.
|
429
416
|
def force_clear_transaction_record_state
|
430
|
-
@_start_transaction_state
|
417
|
+
@_start_transaction_state = nil
|
418
|
+
@transaction_state = nil
|
431
419
|
end
|
432
420
|
|
433
421
|
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
|
434
|
-
def restore_transaction_record_state(
|
435
|
-
|
436
|
-
|
437
|
-
if transaction_level < 1 || force
|
438
|
-
restore_state = @_start_transaction_state
|
439
|
-
thaw
|
422
|
+
def restore_transaction_record_state(force_restore_state = false)
|
423
|
+
if restore_state = @_start_transaction_state
|
424
|
+
if force_restore_state || restore_state[:level] <= 1
|
440
425
|
@new_record = restore_state[:new_record]
|
441
426
|
@destroyed = restore_state[:destroyed]
|
442
|
-
|
443
|
-
|
444
|
-
|
427
|
+
@attributes = restore_state[:attributes].map do |attr|
|
428
|
+
value = @attributes.fetch_value(attr.name)
|
429
|
+
attr = attr.with_value_from_user(value) if attr.value != value
|
430
|
+
attr
|
431
|
+
end
|
432
|
+
@mutations_from_database = nil
|
433
|
+
@mutations_before_last_save = nil
|
434
|
+
if @attributes.fetch_value(@primary_key) != restore_state[:id]
|
435
|
+
@attributes.write_from_user(@primary_key, restore_state[:id])
|
445
436
|
end
|
446
437
|
freeze if restore_state[:frozen?]
|
447
438
|
end
|
@@ -462,8 +453,10 @@ module ActiveRecord
|
|
462
453
|
end
|
463
454
|
end
|
464
455
|
|
465
|
-
|
466
|
-
|
456
|
+
# Add the record to the current transaction so that the #after_rollback and #after_commit
|
457
|
+
# callbacks can be called.
|
458
|
+
def add_to_transaction
|
459
|
+
self.class.connection.add_transaction_record(self)
|
467
460
|
end
|
468
461
|
|
469
462
|
def has_transactional_callbacks?
|
@@ -483,19 +476,17 @@ module ActiveRecord
|
|
483
476
|
# This method checks to see if the ActiveRecord object's state reflects
|
484
477
|
# the TransactionState, and rolls back or commits the Active Record object
|
485
478
|
# as appropriate.
|
486
|
-
#
|
487
|
-
# Since Active Record objects can be inside multiple transactions, this
|
488
|
-
# method recursively goes through the parent of the TransactionState and
|
489
|
-
# checks if the Active Record object reflects the state of the object.
|
490
479
|
def sync_with_transaction_state
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
480
|
+
if transaction_state = @transaction_state
|
481
|
+
if transaction_state.fully_committed?
|
482
|
+
force_clear_transaction_record_state
|
483
|
+
elsif transaction_state.committed?
|
484
|
+
clear_transaction_record_state
|
485
|
+
elsif transaction_state.rolledback?
|
486
|
+
force_restore_state = transaction_state.fully_rolledback?
|
487
|
+
restore_transaction_record_state(force_restore_state)
|
488
|
+
clear_transaction_record_state
|
489
|
+
end
|
499
490
|
end
|
500
491
|
end
|
501
492
|
end
|
@@ -52,8 +52,6 @@ module ActiveRecord
|
|
52
52
|
priority <=> other.priority
|
53
53
|
end
|
54
54
|
|
55
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
56
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
57
55
|
protected
|
58
56
|
|
59
57
|
attr_reader :name, :block, :adapter, :override
|
@@ -114,13 +112,8 @@ module ActiveRecord
|
|
114
112
|
super | 4
|
115
113
|
end
|
116
114
|
|
117
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
118
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
119
|
-
protected
|
120
|
-
|
121
|
-
attr_reader :options, :klass
|
122
|
-
|
123
115
|
private
|
116
|
+
attr_reader :options, :klass
|
124
117
|
|
125
118
|
def matches_options?(**kwargs)
|
126
119
|
options.all? do |key, value|
|
data/lib/active_record/type.rb
CHANGED
@@ -48,12 +48,11 @@ module ActiveRecord
|
|
48
48
|
|
49
49
|
private
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
def current_adapter_name
|
52
|
+
ActiveRecord::Base.connection.adapter_name.downcase.to_sym
|
53
|
+
end
|
54
54
|
end
|
55
55
|
|
56
|
-
Helpers = ActiveModel::Type::Helpers
|
57
56
|
BigInteger = ActiveModel::Type::BigInteger
|
58
57
|
Binary = ActiveModel::Type::Binary
|
59
58
|
Boolean = ActiveModel::Type::Boolean
|
@@ -8,26 +8,27 @@ module ActiveRecord
|
|
8
8
|
@table_name = table_name
|
9
9
|
end
|
10
10
|
|
11
|
-
def type_cast_for_database(
|
11
|
+
def type_cast_for_database(attr_name, value)
|
12
12
|
return value if value.is_a?(Arel::Nodes::BindParam)
|
13
|
-
|
14
|
-
|
13
|
+
type = type_for_attribute(attr_name)
|
14
|
+
type.serialize(value)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
protected
|
17
|
+
def type_for_attribute(attr_name)
|
18
|
+
schema_cache = connection.schema_cache
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
if schema_cache.data_source_exists?(table_name)
|
21
|
+
column = schema_cache.columns_hash(table_name)[attr_name.to_s]
|
22
|
+
type = connection.lookup_cast_type_from_column(column) if column
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
+
type || Type.default_value
|
26
|
+
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
28
|
+
delegate :connection, to: :@klass, private: true
|
29
|
+
|
30
|
+
private
|
31
|
+
attr_reader :table_name
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|