activerecord 6.0.1 → 6.1.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1363 -647
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -4
- data/lib/active_record/aggregations.rb +5 -6
- data/lib/active_record/association_relation.rb +26 -15
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +55 -37
- data/lib/active_record/associations/association_scope.rb +19 -15
- data/lib/active_record/associations/belongs_to_association.rb +23 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +38 -13
- data/lib/active_record/associations/collection_proxy.rb +14 -7
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -3
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +73 -42
- data/lib/active_record/associations/preloader/association.rb +49 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/preloader.rb +12 -7
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +119 -12
- data/lib/active_record/attribute_assignment.rb +10 -9
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
- data/lib/active_record/attribute_methods/dirty.rb +3 -13
- data/lib/active_record/attribute_methods/primary_key.rb +6 -4
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -12
- data/lib/active_record/attribute_methods/serialization.rb +11 -6
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
- data/lib/active_record/attribute_methods/write.rb +12 -21
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +33 -9
- data/lib/active_record/autosave_association.rb +56 -41
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +153 -24
- data/lib/active_record/coders/yaml_column.rb +24 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -38
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -35
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +145 -52
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
- data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
- data/lib/active_record/connection_adapters/abstract_adapter.rb +63 -77
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +80 -66
- data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
- data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +218 -87
- data/lib/active_record/core.rb +276 -68
- data/lib/active_record/counter_cache.rb +4 -1
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -41
- data/lib/active_record/database_configurations.rb +125 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +2 -3
- data/lib/active_record/enum.rb +80 -38
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -5
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -3
- data/lib/active_record/fixture_set/table_rows.rb +0 -1
- data/lib/active_record/fixtures.rb +58 -12
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -21
- data/lib/active_record/insert_all.rb +42 -9
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +18 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +33 -18
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +28 -9
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
- data/lib/active_record/middleware/database_selector.rb +4 -2
- data/lib/active_record/migration/command_recorder.rb +53 -45
- data/lib/active_record/migration/compatibility.rb +75 -21
- data/lib/active_record/migration/join_table.rb +0 -1
- data/lib/active_record/migration.rb +115 -85
- data/lib/active_record/model_schema.rb +117 -15
- data/lib/active_record/nested_attributes.rb +2 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +0 -1
- data/lib/active_record/persistence.rb +50 -46
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +12 -7
- data/lib/active_record/railtie.rb +65 -45
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +280 -99
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +77 -63
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -32
- data/lib/active_record/relation/calculations.rb +106 -45
- data/lib/active_record/relation/delegation.rb +9 -7
- data/lib/active_record/relation/finder_methods.rb +45 -16
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +27 -26
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +59 -40
- data/lib/active_record/relation/query_methods.rb +341 -188
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -8
- data/lib/active_record/relation/where_clause.rb +111 -62
- data/lib/active_record/relation.rb +116 -83
- data/lib/active_record/result.rb +41 -34
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/default.rb +1 -4
- data/lib/active_record/scoping/named.rb +7 -18
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +9 -4
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -36
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +87 -20
- data/lib/active_record/timestamp.rb +4 -7
- data/lib/active_record/touch_later.rb +20 -21
- data/lib/active_record/transactions.rb +25 -72
- data/lib/active_record/type/adapter_specific_registry.rb +2 -5
- data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
- data/lib/active_record/type/serialized.rb +6 -3
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type/type_map.rb +0 -1
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +8 -2
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -2
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +3 -3
- data/lib/active_record.rb +7 -13
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +17 -24
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -3
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -5
- data/lib/arel/visitors/sqlite.rb +0 -1
- data/lib/arel/visitors/to_sql.rb +89 -79
- data/lib/arel/visitors/visitor.rb +0 -1
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -9
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +6 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +30 -29
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -132,7 +132,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
|
|
132
132
|
SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
|
133
133
|
|
134
134
|
|
135
|
-
* Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[
|
135
|
+
* Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[https://ruby-doc.org/stdlib/libdoc/logger/rdoc/].
|
136
136
|
|
137
137
|
ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
|
138
138
|
ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
|
@@ -140,7 +140,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
|
|
140
140
|
|
141
141
|
* Database agnostic schema management with Migrations.
|
142
142
|
|
143
|
-
class AddSystemSettings < ActiveRecord::Migration[
|
143
|
+
class AddSystemSettings < ActiveRecord::Migration[6.0]
|
144
144
|
def up
|
145
145
|
create_table :system_settings do |t|
|
146
146
|
t.string :name
|
@@ -194,7 +194,7 @@ The latest version of Active Record can be installed with RubyGems:
|
|
194
194
|
|
195
195
|
Source code can be downloaded as part of the Rails project on GitHub:
|
196
196
|
|
197
|
-
* https://github.com/rails/rails/tree/
|
197
|
+
* https://github.com/rails/rails/tree/main/activerecord
|
198
198
|
|
199
199
|
|
200
200
|
== License
|
@@ -216,4 +216,4 @@ Bug reports for the Ruby on Rails project can be filed here:
|
|
216
216
|
|
217
217
|
Feature requests should be discussed on the rails-core mailing list here:
|
218
218
|
|
219
|
-
* https://
|
219
|
+
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
@@ -14,7 +14,6 @@ module ActiveRecord
|
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
17
|
-
|
18
17
|
def clear_aggregation_cache
|
19
18
|
@aggregation_cache.clear if persisted?
|
20
19
|
end
|
@@ -142,7 +141,7 @@ module ActiveRecord
|
|
142
141
|
# converted to an instance of value class if necessary.
|
143
142
|
#
|
144
143
|
# For example, the +NetworkResource+ model has +network_address+ and +cidr_range+ attributes that should be
|
145
|
-
# aggregated using the +NetAddr::CIDR+ value class (
|
144
|
+
# aggregated using the +NetAddr::CIDR+ value class (https://www.rubydoc.info/gems/netaddr/1.5.0/NetAddr/CIDR).
|
146
145
|
# The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
|
147
146
|
# New values can be assigned to the value object using either another +NetAddr::CIDR+ object, a string
|
148
147
|
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
|
@@ -245,8 +244,8 @@ module ActiveRecord
|
|
245
244
|
private
|
246
245
|
def reader_method(name, class_name, mapping, allow_nil, constructor)
|
247
246
|
define_method(name) do
|
248
|
-
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? { |key, _| !
|
249
|
-
attrs = mapping.collect { |key, _|
|
247
|
+
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? { |key, _| !read_attribute(key).nil? })
|
248
|
+
attrs = mapping.collect { |key, _| read_attribute(key) }
|
250
249
|
object = constructor.respond_to?(:call) ?
|
251
250
|
constructor.call(*attrs) :
|
252
251
|
class_name.constantize.send(constructor, *attrs)
|
@@ -272,10 +271,10 @@ module ActiveRecord
|
|
272
271
|
end
|
273
272
|
|
274
273
|
if part.nil? && allow_nil
|
275
|
-
mapping.each { |key, _|
|
274
|
+
mapping.each { |key, _| write_attribute(key, nil) }
|
276
275
|
@aggregation_cache[name] = nil
|
277
276
|
else
|
278
|
-
mapping.each { |key, value|
|
277
|
+
mapping.each { |key, value| write_attribute(key, part.send(value)) }
|
279
278
|
@aggregation_cache[name] = part.freeze
|
280
279
|
end
|
281
280
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
-
class AssociationRelation < Relation
|
5
|
-
def initialize(klass, association)
|
4
|
+
class AssociationRelation < Relation # :nodoc:
|
5
|
+
def initialize(klass, association, **)
|
6
6
|
super(klass)
|
7
7
|
@association = association
|
8
8
|
end
|
@@ -15,29 +15,40 @@ module ActiveRecord
|
|
15
15
|
other == records
|
16
16
|
end
|
17
17
|
|
18
|
+
%w(insert insert_all insert! insert_all! upsert upsert_all).each do |method|
|
19
|
+
class_eval <<~RUBY
|
20
|
+
def #{method}(attributes, **kwargs)
|
21
|
+
if @association.reflection.through_reflection?
|
22
|
+
raise ArgumentError, "Bulk insert or upsert is currently not supported for has_many through association"
|
23
|
+
end
|
24
|
+
|
25
|
+
scoping { klass.#{method}(attributes, **kwargs) }
|
26
|
+
end
|
27
|
+
RUBY
|
28
|
+
end
|
29
|
+
|
18
30
|
def build(attributes = nil, &block)
|
19
|
-
|
20
|
-
|
21
|
-
|
31
|
+
if attributes.is_a?(Array)
|
32
|
+
attributes.collect { |attr| build(attr, &block) }
|
33
|
+
else
|
34
|
+
block = current_scope_restoring_block(&block)
|
35
|
+
scoping { _new(attributes, &block) }
|
22
36
|
end
|
23
37
|
end
|
24
38
|
alias new build
|
25
39
|
|
26
|
-
|
27
|
-
|
28
|
-
|
40
|
+
private
|
41
|
+
def _new(attributes, &block)
|
42
|
+
@association.build(attributes, &block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def _create(attributes, &block)
|
29
46
|
@association.create(attributes, &block)
|
30
47
|
end
|
31
|
-
end
|
32
48
|
|
33
|
-
|
34
|
-
block = _deprecated_scope_block("create!", &block)
|
35
|
-
@association.scoping(self) do
|
49
|
+
def _create!(attributes, &block)
|
36
50
|
@association.create!(attributes, &block)
|
37
51
|
end
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
52
|
|
42
53
|
def exec_queries
|
43
54
|
super do |record|
|
@@ -6,9 +6,14 @@ module ActiveRecord
|
|
6
6
|
module Associations
|
7
7
|
# Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
|
8
8
|
class AliasTracker # :nodoc:
|
9
|
-
def self.create(connection, initial_table, joins)
|
9
|
+
def self.create(connection, initial_table, joins, aliases = nil)
|
10
10
|
if joins.empty?
|
11
|
-
aliases
|
11
|
+
aliases ||= Hash.new(0)
|
12
|
+
elsif aliases
|
13
|
+
default_proc = aliases.default_proc || proc { 0 }
|
14
|
+
aliases.default_proc = proc { |h, k|
|
15
|
+
h[k] = initial_count_for(connection, k, joins) + default_proc.call(h, k)
|
16
|
+
}
|
12
17
|
else
|
13
18
|
aliases = Hash.new { |h, k|
|
14
19
|
h[k] = initial_count_for(connection, k, joins)
|
@@ -32,8 +37,6 @@ module ActiveRecord
|
|
32
37
|
).size
|
33
38
|
elsif join.is_a?(Arel::Nodes::Join)
|
34
39
|
join.left.name == name ? 1 : 0
|
35
|
-
elsif join.is_a?(Hash)
|
36
|
-
join[name]
|
37
40
|
else
|
38
41
|
raise ArgumentError, "joins list should be initialized by list of Arel::Nodes::Join"
|
39
42
|
end
|
@@ -48,31 +51,31 @@ module ActiveRecord
|
|
48
51
|
@connection = connection
|
49
52
|
end
|
50
53
|
|
51
|
-
def aliased_table_for(
|
52
|
-
|
54
|
+
def aliased_table_for(arel_table, table_name = nil)
|
55
|
+
table_name ||= arel_table.name
|
56
|
+
|
57
|
+
if aliases[table_name] == 0
|
53
58
|
# If it's zero, we can have our table_name
|
54
59
|
aliases[table_name] = 1
|
55
|
-
|
60
|
+
arel_table = arel_table.alias(table_name) if arel_table.name != table_name
|
56
61
|
else
|
57
62
|
# Otherwise, we need to use an alias
|
58
|
-
aliased_name = @connection.table_alias_for(
|
63
|
+
aliased_name = @connection.table_alias_for(yield)
|
59
64
|
|
60
65
|
# Update the count
|
61
|
-
aliases[aliased_name] += 1
|
66
|
+
count = aliases[aliased_name] += 1
|
62
67
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
aliased_name
|
67
|
-
end
|
68
|
-
Arel::Table.new(table_name, type_caster: type_caster).alias(table_alias)
|
68
|
+
aliased_name = "#{truncate(aliased_name)}_#{count}" if count > 1
|
69
|
+
|
70
|
+
arel_table = arel_table.alias(aliased_name)
|
69
71
|
end
|
72
|
+
|
73
|
+
arel_table
|
70
74
|
end
|
71
75
|
|
72
76
|
attr_reader :aliases
|
73
77
|
|
74
78
|
private
|
75
|
-
|
76
79
|
def truncate(name)
|
77
80
|
name.slice(0, @connection.table_alias_length - 2)
|
78
81
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/core_ext/array/wrap"
|
4
|
-
|
5
3
|
module ActiveRecord
|
6
4
|
module Associations
|
7
5
|
# = Active Record Associations
|
@@ -43,7 +41,6 @@ module ActiveRecord
|
|
43
41
|
reflection.check_validity!
|
44
42
|
|
45
43
|
@owner, @reflection = owner, reflection
|
46
|
-
@_scope = nil
|
47
44
|
|
48
45
|
reset
|
49
46
|
reset_scope
|
@@ -57,6 +54,10 @@ module ActiveRecord
|
|
57
54
|
@inversed = false
|
58
55
|
end
|
59
56
|
|
57
|
+
def reset_negative_cache # :nodoc:
|
58
|
+
reset if loaded? && target.nil?
|
59
|
+
end
|
60
|
+
|
60
61
|
# Reloads the \target and returns +self+ on success.
|
61
62
|
# The QueryCache is cleared if +force+ is true.
|
62
63
|
def reload(force = false)
|
@@ -96,7 +97,11 @@ module ActiveRecord
|
|
96
97
|
end
|
97
98
|
|
98
99
|
def scope
|
99
|
-
|
100
|
+
if (scope = klass.current_scope) && scope.try(:proxy_association) == self
|
101
|
+
scope.spawn
|
102
|
+
else
|
103
|
+
target_scope.merge!(association_scope)
|
104
|
+
end
|
100
105
|
end
|
101
106
|
|
102
107
|
def reset_scope
|
@@ -129,7 +134,15 @@ module ActiveRecord
|
|
129
134
|
self.target = record
|
130
135
|
@inversed = !!record
|
131
136
|
end
|
132
|
-
|
137
|
+
|
138
|
+
def inversed_from_queries(record)
|
139
|
+
if inversable?(record)
|
140
|
+
self.target = record
|
141
|
+
@inversed = true
|
142
|
+
else
|
143
|
+
@inversed = false
|
144
|
+
end
|
145
|
+
end
|
133
146
|
|
134
147
|
# Returns the class of the target. belongs_to polymorphic overrides this to look at the
|
135
148
|
# polymorphic_type field on the owner.
|
@@ -188,34 +201,36 @@ module ActiveRecord
|
|
188
201
|
set_inverse_instance(record)
|
189
202
|
end
|
190
203
|
|
191
|
-
def create(attributes =
|
204
|
+
def create(attributes = nil, &block)
|
192
205
|
_create_record(attributes, &block)
|
193
206
|
end
|
194
207
|
|
195
|
-
def create!(attributes =
|
208
|
+
def create!(attributes = nil, &block)
|
196
209
|
_create_record(attributes, true, &block)
|
197
210
|
end
|
198
211
|
|
199
|
-
def scoping(relation, &block)
|
200
|
-
@_scope = relation
|
201
|
-
relation.scoping(&block)
|
202
|
-
ensure
|
203
|
-
@_scope = nil
|
204
|
-
end
|
205
|
-
|
206
212
|
private
|
207
213
|
def find_target
|
214
|
+
if strict_loading? && owner.validation_context.nil?
|
215
|
+
Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
|
216
|
+
end
|
217
|
+
|
208
218
|
scope = self.scope
|
209
219
|
return scope.to_a if skip_statement_cache?(scope)
|
210
220
|
|
211
|
-
|
212
|
-
sc = reflection.association_scope_cache(conn, owner) do |params|
|
221
|
+
sc = reflection.association_scope_cache(klass, owner) do |params|
|
213
222
|
as = AssociationScope.create { params.bind }
|
214
223
|
target_scope.merge!(as.scope(self))
|
215
224
|
end
|
216
225
|
|
217
226
|
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
218
|
-
sc.execute(binds,
|
227
|
+
sc.execute(binds, klass.connection) { |record| set_inverse_instance(record) }
|
228
|
+
end
|
229
|
+
|
230
|
+
def strict_loading?
|
231
|
+
return reflection.strict_loading? if reflection.options.key?(:strict_loading)
|
232
|
+
|
233
|
+
owner.strict_loading?
|
219
234
|
end
|
220
235
|
|
221
236
|
# The scope for this association.
|
@@ -244,25 +259,6 @@ module ActiveRecord
|
|
244
259
|
!loaded? && (!owner.new_record? || foreign_key_present?) && klass
|
245
260
|
end
|
246
261
|
|
247
|
-
def creation_attributes
|
248
|
-
attributes = {}
|
249
|
-
|
250
|
-
if (reflection.has_one? || reflection.collection?) && !options[:through]
|
251
|
-
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
|
252
|
-
|
253
|
-
if reflection.type
|
254
|
-
attributes[reflection.type] = owner.class.polymorphic_name
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
attributes
|
259
|
-
end
|
260
|
-
|
261
|
-
# Sets the owner attributes on the given record
|
262
|
-
def set_owner_attributes(record)
|
263
|
-
creation_attributes.each { |key, value| record[key] = value }
|
264
|
-
end
|
265
|
-
|
266
262
|
# Returns true if there is a foreign key present on the owner which
|
267
263
|
# references the target. This is used to determine whether we can load
|
268
264
|
# the target if the owner is currently a new record (and therefore
|
@@ -310,7 +306,7 @@ module ActiveRecord
|
|
310
306
|
|
311
307
|
# Returns true if record contains the foreign_key
|
312
308
|
def foreign_key_for?(record)
|
313
|
-
record.
|
309
|
+
record._has_attribute?(reflection.foreign_key)
|
314
310
|
end
|
315
311
|
|
316
312
|
# This should be implemented to return the values of the relevant key(s) on the owner,
|
@@ -335,6 +331,28 @@ module ActiveRecord
|
|
335
331
|
klass.scope_attributes? ||
|
336
332
|
reflection.source_reflection.active_record.default_scopes.any?
|
337
333
|
end
|
334
|
+
|
335
|
+
def enqueue_destroy_association(options)
|
336
|
+
job_class = owner.class.destroy_association_async_job
|
337
|
+
|
338
|
+
if job_class
|
339
|
+
owner._after_commit_jobs.push([job_class, options])
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def inversable?(record)
|
344
|
+
record &&
|
345
|
+
((!record.persisted? || !owner.persisted?) || matches_foreign_key?(record))
|
346
|
+
end
|
347
|
+
|
348
|
+
def matches_foreign_key?(record)
|
349
|
+
if foreign_key_for?(record)
|
350
|
+
record.read_attribute(reflection.foreign_key) == owner.id ||
|
351
|
+
(foreign_key_for?(owner) && owner.read_attribute(reflection.foreign_key) == record.id)
|
352
|
+
else
|
353
|
+
owner.read_attribute(reflection.foreign_key) == record.id
|
354
|
+
end
|
355
|
+
end
|
338
356
|
end
|
339
357
|
end
|
340
358
|
end
|
@@ -52,17 +52,16 @@ module ActiveRecord
|
|
52
52
|
attr_reader :value_transformation
|
53
53
|
|
54
54
|
def join(table, constraint)
|
55
|
-
|
55
|
+
Arel::Nodes::LeadingJoin.new(table, Arel::Nodes::On.new(constraint))
|
56
56
|
end
|
57
57
|
|
58
58
|
def last_chain_scope(scope, reflection, owner)
|
59
|
-
|
60
|
-
|
61
|
-
foreign_key = join_keys.foreign_key
|
59
|
+
primary_key = reflection.join_primary_key
|
60
|
+
foreign_key = reflection.join_foreign_key
|
62
61
|
|
63
62
|
table = reflection.aliased_table
|
64
63
|
value = transform_value(owner[foreign_key])
|
65
|
-
scope = apply_scope(scope, table,
|
64
|
+
scope = apply_scope(scope, table, primary_key, value)
|
66
65
|
|
67
66
|
if reflection.type
|
68
67
|
polymorphic_type = transform_value(owner.class.polymorphic_name)
|
@@ -77,13 +76,12 @@ module ActiveRecord
|
|
77
76
|
end
|
78
77
|
|
79
78
|
def next_chain_scope(scope, reflection, next_reflection)
|
80
|
-
|
81
|
-
|
82
|
-
foreign_key = join_keys.foreign_key
|
79
|
+
primary_key = reflection.join_primary_key
|
80
|
+
foreign_key = reflection.join_foreign_key
|
83
81
|
|
84
82
|
table = reflection.aliased_table
|
85
83
|
foreign_table = next_reflection.aliased_table
|
86
|
-
constraint = table[
|
84
|
+
constraint = table[primary_key].eq(foreign_table[foreign_key])
|
87
85
|
|
88
86
|
if reflection.type
|
89
87
|
value = transform_value(next_reflection.klass.polymorphic_name)
|
@@ -108,11 +106,9 @@ module ActiveRecord
|
|
108
106
|
name = reflection.name
|
109
107
|
chain = [Reflection::RuntimeReflection.new(reflection, association)]
|
110
108
|
reflection.chain.drop(1).each do |refl|
|
111
|
-
aliased_table = tracker.aliased_table_for(
|
112
|
-
refl.
|
113
|
-
|
114
|
-
refl.klass.type_caster
|
115
|
-
)
|
109
|
+
aliased_table = tracker.aliased_table_for(refl.klass.arel_table) do
|
110
|
+
refl.alias_candidate(name)
|
111
|
+
end
|
116
112
|
chain << ReflectionProxy.new(refl, aliased_table)
|
117
113
|
end
|
118
114
|
chain
|
@@ -134,10 +130,18 @@ module ActiveRecord
|
|
134
130
|
|
135
131
|
if scope_chain_item == chain_head.scope
|
136
132
|
scope.merge! item.except(:where, :includes, :unscope, :order)
|
133
|
+
elsif !item.references_values.empty?
|
134
|
+
scope.merge! item.only(:joins, :left_outer_joins)
|
135
|
+
|
136
|
+
associations = item.eager_load_values | item.includes_values
|
137
|
+
|
138
|
+
unless associations.empty?
|
139
|
+
scope.joins! item.construct_join_dependency(associations, Arel::Nodes::OuterJoin)
|
140
|
+
end
|
137
141
|
end
|
138
142
|
|
139
143
|
reflection.all_includes do
|
140
|
-
scope.
|
144
|
+
scope.includes_values |= item.includes_values
|
141
145
|
end
|
142
146
|
|
143
147
|
scope.unscope!(*item.unscope_values)
|
@@ -9,10 +9,21 @@ module ActiveRecord
|
|
9
9
|
|
10
10
|
case options[:dependent]
|
11
11
|
when :destroy
|
12
|
-
target.destroy
|
13
|
-
|
12
|
+
raise ActiveRecord::Rollback unless target.destroy
|
13
|
+
when :destroy_async
|
14
|
+
id = owner.public_send(reflection.foreign_key.to_sym)
|
15
|
+
primary_key_column = reflection.active_record_primary_key.to_sym
|
16
|
+
|
17
|
+
enqueue_destroy_association(
|
18
|
+
owner_model_name: owner.class.to_s,
|
19
|
+
owner_id: owner.id,
|
20
|
+
association_class: reflection.klass.to_s,
|
21
|
+
association_ids: [id],
|
22
|
+
association_primary_key_column: primary_key_column,
|
23
|
+
ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
|
24
|
+
)
|
14
25
|
else
|
15
|
-
target.
|
26
|
+
target.public_send(options[:dependent])
|
16
27
|
end
|
17
28
|
end
|
18
29
|
|
@@ -44,7 +55,7 @@ module ActiveRecord
|
|
44
55
|
|
45
56
|
def decrement_counters_before_last_save
|
46
57
|
if reflection.polymorphic?
|
47
|
-
model_was = owner.attribute_before_last_save(reflection.foreign_type)
|
58
|
+
model_was = owner.attribute_before_last_save(reflection.foreign_type)&.constantize
|
48
59
|
else
|
49
60
|
model_was = klass
|
50
61
|
end
|
@@ -68,7 +79,7 @@ module ActiveRecord
|
|
68
79
|
@updated = true
|
69
80
|
end
|
70
81
|
|
71
|
-
replace_keys(record)
|
82
|
+
replace_keys(record, force: true)
|
72
83
|
|
73
84
|
self.target = record
|
74
85
|
end
|
@@ -96,8 +107,12 @@ module ActiveRecord
|
|
96
107
|
reflection.counter_cache_column && owner.persisted?
|
97
108
|
end
|
98
109
|
|
99
|
-
def replace_keys(record)
|
100
|
-
|
110
|
+
def replace_keys(record, force: false)
|
111
|
+
target_key = record ? record._read_attribute(primary_key(record.class)) : nil
|
112
|
+
|
113
|
+
if force || owner[reflection.foreign_key] != target_key
|
114
|
+
owner[reflection.foreign_key] = target_key
|
115
|
+
end
|
101
116
|
end
|
102
117
|
|
103
118
|
def primary_key(klass)
|
@@ -108,11 +123,9 @@ module ActiveRecord
|
|
108
123
|
owner._read_attribute(reflection.foreign_key)
|
109
124
|
end
|
110
125
|
|
111
|
-
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
112
|
-
# has_one associations.
|
113
126
|
def invertible_for?(record)
|
114
127
|
inverse = inverse_reflection_for(record)
|
115
|
-
inverse && inverse.has_one?
|
128
|
+
inverse && (inverse.has_one? || ActiveRecord::Base.has_many_inversing)
|
116
129
|
end
|
117
130
|
|
118
131
|
def stale_state
|
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
6
6
|
class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
|
7
7
|
def klass
|
8
8
|
type = owner[reflection.foreign_type]
|
9
|
-
type.presence && type
|
9
|
+
type.presence && owner.class.polymorphic_class_for(type)
|
10
10
|
end
|
11
11
|
|
12
12
|
def target_changed?
|
@@ -14,9 +14,14 @@ module ActiveRecord
|
|
14
14
|
end
|
15
15
|
|
16
16
|
private
|
17
|
-
def replace_keys(record)
|
17
|
+
def replace_keys(record, force: false)
|
18
18
|
super
|
19
|
-
|
19
|
+
|
20
|
+
target_type = record ? record.class.polymorphic_name : nil
|
21
|
+
|
22
|
+
if force || owner[reflection.foreign_type] != target_type
|
23
|
+
owner[reflection.foreign_type] = target_type
|
24
|
+
end
|
20
25
|
end
|
21
26
|
|
22
27
|
def inverse_reflection_for(record)
|
@@ -18,7 +18,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
18
18
|
end
|
19
19
|
self.extensions = []
|
20
20
|
|
21
|
-
VALID_OPTIONS = [
|
21
|
+
VALID_OPTIONS = [
|
22
|
+
:class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading
|
23
|
+
].freeze # :nodoc:
|
22
24
|
|
23
25
|
def self.build(model, name, scope, options, &block)
|
24
26
|
if model.dangerous_attribute_method?(name)
|
@@ -72,8 +74,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
72
74
|
|
73
75
|
def self.define_callbacks(model, reflection)
|
74
76
|
if dependent = reflection.options[:dependent]
|
75
|
-
check_dependent_options(dependent)
|
77
|
+
check_dependent_options(dependent, model)
|
76
78
|
add_destroy_callbacks(model, reflection)
|
79
|
+
add_after_commit_jobs_callback(model, dependent)
|
77
80
|
end
|
78
81
|
|
79
82
|
Association.extensions.each do |extension|
|
@@ -118,7 +121,11 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
118
121
|
raise NotImplementedError
|
119
122
|
end
|
120
123
|
|
121
|
-
def self.check_dependent_options(dependent)
|
124
|
+
def self.check_dependent_options(dependent, model)
|
125
|
+
if dependent == :destroy_async && !model.destroy_association_async_job
|
126
|
+
err_message = "ActiveJob is required to use destroy_async on associations"
|
127
|
+
raise ActiveRecord::ActiveJobRequiredError, err_message
|
128
|
+
end
|
122
129
|
unless valid_dependent_options.include? dependent
|
123
130
|
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
|
124
131
|
end
|
@@ -126,11 +133,31 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
126
133
|
|
127
134
|
def self.add_destroy_callbacks(model, reflection)
|
128
135
|
name = reflection.name
|
129
|
-
model.before_destroy
|
136
|
+
model.before_destroy(->(o) { o.association(name).handle_dependency })
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.add_after_commit_jobs_callback(model, dependent)
|
140
|
+
if dependent == :destroy_async
|
141
|
+
mixin = model.generated_association_methods
|
142
|
+
|
143
|
+
unless mixin.method_defined?(:_after_commit_jobs)
|
144
|
+
model.after_commit(-> do
|
145
|
+
_after_commit_jobs.each do |job_class, job_arguments|
|
146
|
+
job_class.perform_later(**job_arguments)
|
147
|
+
end
|
148
|
+
end)
|
149
|
+
|
150
|
+
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
151
|
+
def _after_commit_jobs
|
152
|
+
@_after_commit_jobs ||= []
|
153
|
+
end
|
154
|
+
CODE
|
155
|
+
end
|
156
|
+
end
|
130
157
|
end
|
131
158
|
|
132
159
|
private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
|
133
160
|
:define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
|
134
|
-
:valid_dependent_options, :check_dependent_options, :add_destroy_callbacks
|
161
|
+
:valid_dependent_options, :check_dependent_options, :add_destroy_callbacks, :add_after_commit_jobs_callback
|
135
162
|
end
|
136
163
|
end
|
@@ -7,11 +7,14 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
super + [:polymorphic, :
|
10
|
+
valid = super + [:polymorphic, :counter_cache, :optional, :default]
|
11
|
+
valid += [:foreign_type] if options[:polymorphic]
|
12
|
+
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
13
|
+
valid
|
11
14
|
end
|
12
15
|
|
13
16
|
def self.valid_dependent_options
|
14
|
-
[:destroy, :delete]
|
17
|
+
[:destroy, :delete, :destroy_async]
|
15
18
|
end
|
16
19
|
|
17
20
|
def self.define_callbacks(model, reflection)
|
@@ -55,19 +58,19 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
55
58
|
|
56
59
|
if old_record
|
57
60
|
if touch != true
|
58
|
-
old_record.
|
61
|
+
old_record.public_send(touch_method, touch)
|
59
62
|
else
|
60
|
-
old_record.
|
63
|
+
old_record.public_send(touch_method)
|
61
64
|
end
|
62
65
|
end
|
63
66
|
end
|
64
67
|
|
65
|
-
record = o.
|
68
|
+
record = o.public_send name
|
66
69
|
if record && record.persisted?
|
67
70
|
if touch != true
|
68
|
-
record.
|
71
|
+
record.public_send(touch_method, touch)
|
69
72
|
else
|
70
|
-
record.
|
73
|
+
record.public_send(touch_method)
|
71
74
|
end
|
72
75
|
end
|
73
76
|
end
|