activerecord 7.0.8.7 → 7.1.0.beta1
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 +1339 -1572
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -16
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +16 -11
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader.rb +12 -9
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +193 -97
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- 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 +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- 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 +289 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
- 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 +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- 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 +17 -12
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
- 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 +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +71 -94
- data/lib/active_record/core.rb +128 -138
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +8 -3
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- 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 +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -26
- data/lib/active_record/errors.rb +89 -15
- data/lib/active_record/explain.rb +23 -3
- 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 +119 -71
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -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 +5 -7
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -5
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +213 -109
- data/lib/active_record/model_schema.rb +47 -27
- data/lib/active_record/nested_attributes.rb +28 -3
- data/lib/active_record/normalization.rb +158 -0
- data/lib/active_record/persistence.rb +183 -33
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- data/lib/active_record/railties/databases.rake +139 -145
- 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 +169 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +152 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- data/lib/active_record/relation/finder_methods.rb +85 -15
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +351 -62
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +41 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -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 +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +10 -1
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- 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 +14 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +36 -10
- 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/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- 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 +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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 +52 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Active Record -- Object-relational mapping in Rails
|
1
|
+
= Active Record -- Object-relational mapping in \Rails
|
2
2
|
|
3
3
|
Active Record connects classes to relational database tables to establish an
|
4
4
|
almost zero-configuration persistence layer for applications. The library
|
@@ -13,29 +13,28 @@ columns. Although these mappings can be defined explicitly, it's recommended
|
|
13
13
|
to follow naming conventions, especially when getting started with the
|
14
14
|
library.
|
15
15
|
|
16
|
-
You can read more about Active Record in the {Active Record Basics}[https://
|
16
|
+
You can read more about Active Record in the {Active Record Basics}[https://guides.rubyonrails.org/active_record_basics.html] guide.
|
17
17
|
|
18
18
|
A short rundown of some of the major features:
|
19
19
|
|
20
20
|
* Automated mapping between classes and tables, attributes and columns.
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
{Learn more}[link:classes/ActiveRecord/Base.html]
|
22
|
+
class Product < ActiveRecord::Base
|
23
|
+
end
|
26
24
|
|
27
|
-
The Product class is automatically mapped to the table named "products",
|
28
|
-
which might look like this:
|
25
|
+
The Product class is automatically mapped to the table named "products",
|
26
|
+
which might look like this:
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
CREATE TABLE products (
|
29
|
+
id bigint NOT NULL auto_increment,
|
30
|
+
name varchar(255),
|
31
|
+
PRIMARY KEY (id)
|
32
|
+
);
|
35
33
|
|
36
|
-
This would also define the following accessors: <tt>Product#name</tt> and
|
37
|
-
<tt>Product#name=(new_name)</tt>.
|
34
|
+
This would also define the following accessors: <tt>Product#name</tt> and
|
35
|
+
<tt>Product#name=(new_name)</tt>.
|
38
36
|
|
37
|
+
{Learn more}[link:classes/ActiveRecord/Base.html]
|
39
38
|
|
40
39
|
* Associations between objects defined by simple class methods.
|
41
40
|
|
@@ -140,7 +139,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
|
|
140
139
|
|
141
140
|
* Database agnostic schema management with Migrations.
|
142
141
|
|
143
|
-
class AddSystemSettings < ActiveRecord::Migration[7.
|
142
|
+
class AddSystemSettings < ActiveRecord::Migration[7.1]
|
144
143
|
def up
|
145
144
|
create_table :system_settings do |t|
|
146
145
|
t.string :name
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
# See ActiveRecord::Aggregations::ClassMethods for documentation
|
5
5
|
module Aggregations
|
6
6
|
def initialize_dup(*) # :nodoc:
|
7
|
-
@aggregation_cache =
|
7
|
+
@aggregation_cache = @aggregation_cache.dup
|
8
8
|
super
|
9
9
|
end
|
10
10
|
|
@@ -19,10 +19,12 @@ module ActiveRecord
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def init_internals
|
22
|
-
@aggregation_cache = {}
|
23
22
|
super
|
23
|
+
@aggregation_cache = {}
|
24
24
|
end
|
25
25
|
|
26
|
+
# = Active Record \Aggregations
|
27
|
+
#
|
26
28
|
# Active Record implements aggregation through a macro-like class method called #composed_of
|
27
29
|
# for representing attributes as value objects. It expresses relationships like "Account [is]
|
28
30
|
# composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
|
@@ -32,8 +34,8 @@ module ActiveRecord
|
|
32
34
|
# the database).
|
33
35
|
#
|
34
36
|
# class Customer < ActiveRecord::Base
|
35
|
-
# composed_of :balance, class_name: "Money", mapping:
|
36
|
-
# composed_of :address, mapping:
|
37
|
+
# composed_of :balance, class_name: "Money", mapping: { balance: :amount }
|
38
|
+
# composed_of :address, mapping: { address_street: :street, address_city: :city }
|
37
39
|
# end
|
38
40
|
#
|
39
41
|
# The customer class now has the following methods to manipulate the value objects:
|
@@ -150,7 +152,7 @@ module ActiveRecord
|
|
150
152
|
# class NetworkResource < ActiveRecord::Base
|
151
153
|
# composed_of :cidr,
|
152
154
|
# class_name: 'NetAddr::CIDR',
|
153
|
-
# mapping:
|
155
|
+
# mapping: { network_address: :network, cidr_range: :bits },
|
154
156
|
# allow_nil: true,
|
155
157
|
# constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
|
156
158
|
# converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
|
@@ -188,10 +190,10 @@ module ActiveRecord
|
|
188
190
|
# to the Address class, but if the real class name is +CompanyAddress+, you'll have to specify it
|
189
191
|
# with this option.
|
190
192
|
# * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
|
191
|
-
# object. Each mapping is represented as
|
192
|
-
# entity attribute and the
|
193
|
+
# object. Each mapping is represented as a key-value pair where the key is the name of the
|
194
|
+
# entity attribute and the value is the name of the attribute in the value object. The
|
193
195
|
# order in which mappings are defined determines the order in which attributes are sent to the
|
194
|
-
# value class constructor.
|
196
|
+
# value class constructor. The mapping can be written as a hash or as an array of pairs.
|
195
197
|
# * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
|
196
198
|
# attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
|
197
199
|
# mapped attributes.
|
@@ -208,14 +210,15 @@ module ActiveRecord
|
|
208
210
|
# can return +nil+ to skip the assignment.
|
209
211
|
#
|
210
212
|
# Option examples:
|
211
|
-
# composed_of :temperature, mapping:
|
212
|
-
# composed_of :balance, class_name: "Money", mapping:
|
213
|
+
# composed_of :temperature, mapping: { reading: :celsius }
|
214
|
+
# composed_of :balance, class_name: "Money", mapping: { balance: :amount }
|
215
|
+
# composed_of :address, mapping: { address_street: :street, address_city: :city }
|
213
216
|
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
|
214
217
|
# composed_of :gps_location
|
215
218
|
# composed_of :gps_location, allow_nil: true
|
216
219
|
# composed_of :ip_address,
|
217
220
|
# class_name: 'IPAddr',
|
218
|
-
# mapping:
|
221
|
+
# mapping: { ip: :to_i },
|
219
222
|
# constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
|
220
223
|
# converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
|
221
224
|
#
|
@@ -249,7 +252,7 @@ module ActiveRecord
|
|
249
252
|
object = constructor.respond_to?(:call) ?
|
250
253
|
constructor.call(*attrs) :
|
251
254
|
class_name.constantize.send(constructor, *attrs)
|
252
|
-
@aggregation_cache[name] = object
|
255
|
+
@aggregation_cache[name] = object.freeze
|
253
256
|
end
|
254
257
|
@aggregation_cache[name]
|
255
258
|
end
|
@@ -275,7 +278,7 @@ module ActiveRecord
|
|
275
278
|
@aggregation_cache[name] = nil
|
276
279
|
else
|
277
280
|
mapping.each { |key, value| write_attribute(key, part.send(value)) }
|
278
|
-
@aggregation_cache[name] = part.freeze
|
281
|
+
@aggregation_cache[name] = part.dup.freeze
|
279
282
|
end
|
280
283
|
end
|
281
284
|
end
|
@@ -16,7 +16,7 @@ module ActiveRecord
|
|
16
16
|
end
|
17
17
|
|
18
18
|
%w(insert insert_all insert! insert_all! upsert upsert_all).each do |method|
|
19
|
-
class_eval <<~RUBY
|
19
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
20
20
|
def #{method}(attributes, **kwargs)
|
21
21
|
if @association.reflection.through_reflection?
|
22
22
|
raise ArgumentError, "Bulk insert or upsert is currently not supported for has_many through association"
|
@@ -19,7 +19,7 @@ module ActiveRecord
|
|
19
19
|
# Associations in Active Record are middlemen between the object that
|
20
20
|
# holds the association, known as the <tt>owner</tt>, and the associated
|
21
21
|
# result set, known as the <tt>target</tt>. Association metadata is available in
|
22
|
-
# <tt>reflection</tt>, which is an instance of
|
22
|
+
# <tt>reflection</tt>, which is an instance of +ActiveRecord::Reflection::AssociationReflection+.
|
23
23
|
#
|
24
24
|
# For example, given
|
25
25
|
#
|
@@ -45,6 +45,8 @@ module ActiveRecord
|
|
45
45
|
|
46
46
|
reset
|
47
47
|
reset_scope
|
48
|
+
|
49
|
+
@skip_strict_loading = nil
|
48
50
|
end
|
49
51
|
|
50
52
|
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
|
@@ -216,7 +218,7 @@ module ActiveRecord
|
|
216
218
|
end
|
217
219
|
|
218
220
|
def find_target
|
219
|
-
if violates_strict_loading?
|
221
|
+
if violates_strict_loading?
|
220
222
|
Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
|
221
223
|
end
|
222
224
|
|
@@ -239,7 +241,19 @@ module ActiveRecord
|
|
239
241
|
end
|
240
242
|
end
|
241
243
|
|
244
|
+
def skip_strict_loading(&block)
|
245
|
+
skip_strict_loading_was = @skip_strict_loading
|
246
|
+
@skip_strict_loading = true
|
247
|
+
yield
|
248
|
+
ensure
|
249
|
+
@skip_strict_loading = skip_strict_loading_was
|
250
|
+
end
|
251
|
+
|
242
252
|
def violates_strict_loading?
|
253
|
+
return if @skip_strict_loading
|
254
|
+
|
255
|
+
return unless owner.validation_context.nil?
|
256
|
+
|
243
257
|
return reflection.strict_loading? if reflection.options.key?(:strict_loading)
|
244
258
|
|
245
259
|
owner.strict_loading? && !owner.strict_loading_n_plus_one_only?
|
@@ -322,7 +336,8 @@ module ActiveRecord
|
|
322
336
|
|
323
337
|
# Returns true if record contains the foreign_key
|
324
338
|
def foreign_key_for?(record)
|
325
|
-
|
339
|
+
foreign_key = Array(reflection.foreign_key)
|
340
|
+
foreign_key.all? { |key| record._has_attribute?(key) }
|
326
341
|
end
|
327
342
|
|
328
343
|
# This should be implemented to return the values of the relevant key(s) on the owner,
|
@@ -35,7 +35,7 @@ module ActiveRecord
|
|
35
35
|
binds = []
|
36
36
|
last_reflection = chain.last
|
37
37
|
|
38
|
-
binds
|
38
|
+
binds.push(*last_reflection.join_id_for(owner))
|
39
39
|
if last_reflection.type
|
40
40
|
binds << owner.class.polymorphic_name
|
41
41
|
end
|
@@ -56,12 +56,15 @@ module ActiveRecord
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def last_chain_scope(scope, reflection, owner)
|
59
|
-
primary_key = reflection.join_primary_key
|
60
|
-
foreign_key = reflection.join_foreign_key
|
59
|
+
primary_key = Array(reflection.join_primary_key)
|
60
|
+
foreign_key = Array(reflection.join_foreign_key)
|
61
61
|
|
62
62
|
table = reflection.aliased_table
|
63
|
-
|
64
|
-
|
63
|
+
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
64
|
+
primary_key_foreign_key_pairs.each do |join_key, foreign_key|
|
65
|
+
value = transform_value(owner._read_attribute(foreign_key))
|
66
|
+
scope = apply_scope(scope, table, join_key, value)
|
67
|
+
end
|
65
68
|
|
66
69
|
if reflection.type
|
67
70
|
polymorphic_type = transform_value(owner.class.polymorphic_name)
|
@@ -76,19 +79,23 @@ module ActiveRecord
|
|
76
79
|
end
|
77
80
|
|
78
81
|
def next_chain_scope(scope, reflection, next_reflection)
|
79
|
-
primary_key = reflection.join_primary_key
|
80
|
-
foreign_key = reflection.join_foreign_key
|
82
|
+
primary_key = Array(reflection.join_primary_key)
|
83
|
+
foreign_key = Array(reflection.join_foreign_key)
|
81
84
|
|
82
85
|
table = reflection.aliased_table
|
83
86
|
foreign_table = next_reflection.aliased_table
|
84
|
-
|
87
|
+
|
88
|
+
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
89
|
+
constraints = primary_key_foreign_key_pairs.map do |join_primary_key, foreign_key|
|
90
|
+
table[join_primary_key].eq(foreign_table[foreign_key])
|
91
|
+
end.inject(&:and)
|
85
92
|
|
86
93
|
if reflection.type
|
87
94
|
value = transform_value(next_reflection.klass.polymorphic_name)
|
88
95
|
scope = apply_scope(scope, table, reflection.type, value)
|
89
96
|
end
|
90
97
|
|
91
|
-
scope.joins!(join(foreign_table,
|
98
|
+
scope.joins!(join(foreign_table, constraints))
|
92
99
|
end
|
93
100
|
|
94
101
|
class ReflectionProxy < SimpleDelegator # :nodoc:
|
@@ -11,8 +11,13 @@ module ActiveRecord
|
|
11
11
|
when :destroy
|
12
12
|
raise ActiveRecord::Rollback unless target.destroy
|
13
13
|
when :destroy_async
|
14
|
-
|
15
|
-
|
14
|
+
if reflection.foreign_key.is_a?(Array)
|
15
|
+
primary_key_column = reflection.active_record_primary_key.map(&:to_sym)
|
16
|
+
id = reflection.foreign_key.map { |col| owner.public_send(col.to_sym) }
|
17
|
+
else
|
18
|
+
primary_key_column = reflection.active_record_primary_key.to_sym
|
19
|
+
id = owner.public_send(reflection.foreign_key.to_sym)
|
20
|
+
end
|
16
21
|
|
17
22
|
enqueue_destroy_association(
|
18
23
|
owner_model_name: owner.class.to_s,
|
@@ -119,10 +124,13 @@ module ActiveRecord
|
|
119
124
|
end
|
120
125
|
|
121
126
|
def replace_keys(record, force: false)
|
122
|
-
|
127
|
+
target_key_values = record ? Array(primary_key(record.class)).map { |key| record._read_attribute(key) } : []
|
128
|
+
reflection_fk = Array(reflection.foreign_key)
|
123
129
|
|
124
|
-
if force || owner._read_attribute(
|
125
|
-
|
130
|
+
if force || reflection_fk.map { |fk| owner._read_attribute(fk) } != target_key_values
|
131
|
+
reflection_fk.zip(target_key_values).each do |key, value|
|
132
|
+
owner[key] = value
|
133
|
+
end
|
126
134
|
end
|
127
135
|
end
|
128
136
|
|
@@ -131,7 +139,7 @@ module ActiveRecord
|
|
131
139
|
end
|
132
140
|
|
133
141
|
def foreign_key_present?
|
134
|
-
|
142
|
+
Array(reflection.foreign_key).all? { |fk| owner._read_attribute(fk) }
|
135
143
|
end
|
136
144
|
|
137
145
|
def invertible_for?(record)
|
@@ -19,7 +19,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
19
19
|
self.extensions = []
|
20
20
|
|
21
21
|
VALID_OPTIONS = [
|
22
|
-
:class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading
|
22
|
+
:class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading, :query_constraints
|
23
23
|
].freeze # :nodoc:
|
24
24
|
|
25
25
|
def self.build(model, name, scope, options, &block)
|
@@ -128,8 +128,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
128
128
|
|
129
129
|
def self.check_dependent_options(dependent, model)
|
130
130
|
if dependent == :destroy_async && !model.destroy_association_async_job
|
131
|
-
err_message = "
|
132
|
-
raise ActiveRecord::
|
131
|
+
err_message = "A valid destroy_association_async_job is required to use `dependent: :destroy_async` on associations"
|
132
|
+
raise ActiveRecord::ConfigurationError, err_message
|
133
133
|
end
|
134
134
|
unless valid_dependent_options.include? dependent
|
135
135
|
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
|
@@ -37,10 +37,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
37
37
|
}
|
38
38
|
|
39
39
|
klass = reflection.class_name.safe_constantize
|
40
|
-
klass.
|
40
|
+
klass._counter_cache_columns |= [cache_column] if klass && klass.respond_to?(:_counter_cache_columns)
|
41
41
|
end
|
42
42
|
|
43
|
-
def self.touch_record(o, changes, foreign_key, name, touch
|
43
|
+
def self.touch_record(o, changes, foreign_key, name, touch) # :nodoc:
|
44
44
|
old_foreign_id = changes[foreign_key] && changes[foreign_key].first
|
45
45
|
|
46
46
|
if old_foreign_id
|
@@ -58,9 +58,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
58
58
|
|
59
59
|
if old_record
|
60
60
|
if touch != true
|
61
|
-
old_record.
|
61
|
+
old_record.touch_later(touch)
|
62
62
|
else
|
63
|
-
old_record.
|
63
|
+
old_record.touch_later
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
@@ -68,9 +68,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
68
68
|
record = o.public_send name
|
69
69
|
if record && record.persisted?
|
70
70
|
if touch != true
|
71
|
-
record.
|
71
|
+
record.touch_later(touch)
|
72
72
|
else
|
73
|
-
record.
|
73
|
+
record.touch_later
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
@@ -81,7 +81,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
81
81
|
touch = reflection.options[:touch]
|
82
82
|
|
83
83
|
callback = lambda { |changes_method| lambda { |record|
|
84
|
-
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch
|
84
|
+
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch)
|
85
85
|
}}
|
86
86
|
|
87
87
|
if reflection.counter_cache_column
|
@@ -123,7 +123,20 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
123
123
|
super
|
124
124
|
|
125
125
|
if required
|
126
|
-
|
126
|
+
if ActiveRecord.belongs_to_required_validates_foreign_key
|
127
|
+
model.validates_presence_of reflection.name, message: :required
|
128
|
+
else
|
129
|
+
condition = lambda { |record|
|
130
|
+
foreign_key = reflection.foreign_key
|
131
|
+
foreign_type = reflection.foreign_type
|
132
|
+
|
133
|
+
record.read_attribute(foreign_key).nil? ||
|
134
|
+
record.attribute_changed?(foreign_key) ||
|
135
|
+
(reflection.polymorphic? && (record.read_attribute(foreign_type).nil? || record.attribute_changed?(foreign_type)))
|
136
|
+
}
|
137
|
+
|
138
|
+
model.validates_presence_of reflection.name, message: :required, if: condition
|
139
|
+
end
|
127
140
|
end
|
128
141
|
end
|
129
142
|
|
@@ -20,6 +20,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
20
20
|
attr_accessor :right_reflection
|
21
21
|
end
|
22
22
|
|
23
|
+
@table_name = nil
|
23
24
|
def self.table_name
|
24
25
|
# Table name needs to be resolved lazily
|
25
26
|
# because RHS class might not have been loaded
|
@@ -44,11 +45,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
44
45
|
def self.retrieve_connection
|
45
46
|
left_model.retrieve_connection
|
46
47
|
end
|
47
|
-
|
48
|
-
private
|
49
|
-
def self.suppress_composite_primary_key(pk)
|
50
|
-
pk unless pk.is_a?(Array)
|
51
|
-
end
|
52
48
|
}
|
53
49
|
|
54
50
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
@@ -16,7 +16,7 @@ module ActiveRecord
|
|
16
16
|
#
|
17
17
|
# The CollectionAssociation class provides common methods to the collections
|
18
18
|
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
19
|
-
# the
|
19
|
+
# the <tt>:through association</tt> option.
|
20
20
|
#
|
21
21
|
# You need to be careful with assumptions regarding the target: The proxy
|
22
22
|
# does not fetch records from the database until it needs them, but new
|
@@ -61,14 +61,22 @@ module ActiveRecord
|
|
61
61
|
primary_key = reflection.association_primary_key
|
62
62
|
pk_type = klass.type_for_attribute(primary_key)
|
63
63
|
ids = Array(ids).compact_blank
|
64
|
-
ids.map! { |
|
64
|
+
ids.map! { |id| pk_type.cast(id) }
|
65
65
|
|
66
|
-
records = klass.
|
67
|
-
|
66
|
+
records = if klass.composite_primary_key?
|
67
|
+
query_records = ids.map { |values_set| klass.where(primary_key.zip(values_set).to_h) }.inject(&:or)
|
68
|
+
|
69
|
+
query_records.index_by do |record|
|
70
|
+
primary_key.map { |primary_key| record._read_attribute(primary_key) }
|
71
|
+
end
|
72
|
+
else
|
73
|
+
klass.where(primary_key => ids).index_by do |record|
|
74
|
+
record._read_attribute(primary_key)
|
75
|
+
end
|
68
76
|
end.values_at(*ids).compact
|
69
77
|
|
70
78
|
if records.size != ids.size
|
71
|
-
found_ids = records.map { |record| record.
|
79
|
+
found_ids = records.map { |record| record._read_attribute(primary_key) }
|
72
80
|
not_found_ids = ids - found_ids
|
73
81
|
klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
|
74
82
|
else
|
@@ -119,7 +127,7 @@ module ActiveRecord
|
|
119
127
|
def concat(*records)
|
120
128
|
records = records.flatten
|
121
129
|
if owner.new_record?
|
122
|
-
load_target
|
130
|
+
skip_strict_loading { load_target }
|
123
131
|
concat_records(records)
|
124
132
|
else
|
125
133
|
transaction { concat_records(records) }
|
@@ -233,7 +241,7 @@ module ActiveRecord
|
|
233
241
|
# and delete/add only records that have changed.
|
234
242
|
def replace(other_array)
|
235
243
|
other_array.each { |val| raise_on_type_mismatch!(val) }
|
236
|
-
original_target = load_target.dup
|
244
|
+
original_target = skip_strict_loading { load_target }.dup
|
237
245
|
|
238
246
|
if owner.new_record?
|
239
247
|
replace_records(other_array, original_target)
|
@@ -324,8 +332,8 @@ module ActiveRecord
|
|
324
332
|
persisted.map! do |record|
|
325
333
|
if mem_record = memory.delete(record)
|
326
334
|
|
327
|
-
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
|
328
|
-
mem_record
|
335
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save - mem_record.class._attr_readonly).each do |name|
|
336
|
+
mem_record._write_attribute(name, record[name])
|
329
337
|
end
|
330
338
|
|
331
339
|
mem_record
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
|
+
# = Active Record Collection Proxy
|
6
|
+
#
|
5
7
|
# Collection proxies in Active Record are middlemen between an
|
6
8
|
# <tt>association</tt>, and its <tt>target</tt> result set.
|
7
9
|
#
|
@@ -94,12 +96,12 @@ module ActiveRecord
|
|
94
96
|
# receive:
|
95
97
|
#
|
96
98
|
# person.pets.select(:name).first.person_id
|
97
|
-
# # => ActiveModel::MissingAttributeError: missing attribute
|
99
|
+
# # => ActiveModel::MissingAttributeError: missing attribute 'person_id' for Pet
|
98
100
|
#
|
99
|
-
# *Second:* You can pass a block so it can be used just like Array#select
|
101
|
+
# *Second:* You can pass a block so it can be used just like <tt>Array#select</tt>.
|
100
102
|
# This builds an array of objects from the database for the scope,
|
101
103
|
# converting them into an array and iterating through them using
|
102
|
-
# Array#select
|
104
|
+
# <tt>Array#select</tt>.
|
103
105
|
#
|
104
106
|
# person.pets.select { |pet| /oo/.match?(pet.name) }
|
105
107
|
# # => [
|
@@ -108,7 +110,7 @@ module ActiveRecord
|
|
108
110
|
# # ]
|
109
111
|
|
110
112
|
# Finds an object in the collection responding to the +id+. Uses the same
|
111
|
-
# rules as ActiveRecord::
|
113
|
+
# rules as ActiveRecord::FinderMethods.find. Returns ActiveRecord::RecordNotFound
|
112
114
|
# error if the object cannot be found.
|
113
115
|
#
|
114
116
|
# class Person < ActiveRecord::Base
|
@@ -218,7 +220,7 @@ module ActiveRecord
|
|
218
220
|
# :call-seq:
|
219
221
|
# third_to_last()
|
220
222
|
#
|
221
|
-
# Same as #
|
223
|
+
# Same as #last except returns only the third-to-last record.
|
222
224
|
|
223
225
|
##
|
224
226
|
# :method: second_to_last
|
@@ -226,7 +228,7 @@ module ActiveRecord
|
|
226
228
|
# :call-seq:
|
227
229
|
# second_to_last()
|
228
230
|
#
|
229
|
-
# Same as #
|
231
|
+
# Same as #last except returns only the second-to-last record.
|
230
232
|
|
231
233
|
# Returns the last record, or the last +n+ records, from the collection.
|
232
234
|
# If the collection is empty, the first form returns +nil+, and the second
|
@@ -260,7 +262,7 @@ module ActiveRecord
|
|
260
262
|
end
|
261
263
|
|
262
264
|
# Gives a record (or N records if a parameter is supplied) from the collection
|
263
|
-
# using the same rules as
|
265
|
+
# using the same rules as ActiveRecord::FinderMethods.take.
|
264
266
|
#
|
265
267
|
# class Person < ActiveRecord::Base
|
266
268
|
# has_many :pets
|
@@ -359,7 +361,7 @@ module ActiveRecord
|
|
359
361
|
# end
|
360
362
|
#
|
361
363
|
# person.pets.create!(name: nil)
|
362
|
-
# # => ActiveRecord::RecordInvalid: Validation failed: Name can
|
364
|
+
# # => ActiveRecord::RecordInvalid: Validation failed: Name can’t be blank
|
363
365
|
def create!(attributes = {}, &block)
|
364
366
|
@association.create!(attributes, &block)
|
365
367
|
end
|
@@ -382,7 +384,7 @@ module ActiveRecord
|
|
382
384
|
# # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
|
383
385
|
#
|
384
386
|
# If the supplied array has an incorrect association type, it raises
|
385
|
-
# an
|
387
|
+
# an ActiveRecord::AssociationTypeMismatch error:
|
386
388
|
#
|
387
389
|
# person.pets.replace(["doo", "ggie", "gaga"])
|
388
390
|
# # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
|
@@ -930,7 +932,7 @@ module ActiveRecord
|
|
930
932
|
@association
|
931
933
|
end
|
932
934
|
|
933
|
-
# Returns a
|
935
|
+
# Returns a Relation object for the records in this association
|
934
936
|
def scope
|
935
937
|
@scope ||= @association.scope
|
936
938
|
end
|
@@ -955,10 +957,13 @@ module ActiveRecord
|
|
955
957
|
# person.pets == other
|
956
958
|
# # => true
|
957
959
|
#
|
960
|
+
#
|
961
|
+
# Note that unpersisted records can still be seen as equal:
|
962
|
+
#
|
958
963
|
# other = [Pet.new(id: 1), Pet.new(id: 2)]
|
959
964
|
#
|
960
965
|
# person.pets == other
|
961
|
-
# # =>
|
966
|
+
# # => true
|
962
967
|
def ==(other)
|
963
968
|
load_target == other
|
964
969
|
end
|
@@ -12,7 +12,7 @@ module ActiveRecord::Associations
|
|
12
12
|
|
13
13
|
def nullified_owner_attributes
|
14
14
|
Hash.new.tap do |attrs|
|
15
|
-
|
15
|
+
Array(reflection.foreign_key).each { |foreign_key| attrs[foreign_key] = nil }
|
16
16
|
attrs[reflection.type] = nil if reflection.type.present?
|
17
17
|
end
|
18
18
|
end
|
@@ -22,8 +22,15 @@ module ActiveRecord::Associations
|
|
22
22
|
def set_owner_attributes(record)
|
23
23
|
return if options[:through]
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
primary_key_attribute_names = Array(reflection.join_primary_key)
|
26
|
+
foreign_key_attribute_names = Array(reflection.join_foreign_key)
|
27
|
+
|
28
|
+
primary_key_foreign_key_pairs = primary_key_attribute_names.zip(foreign_key_attribute_names)
|
29
|
+
|
30
|
+
primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
|
31
|
+
value = owner._read_attribute(foreign_key)
|
32
|
+
record._write_attribute(primary_key, value)
|
33
|
+
end
|
27
34
|
|
28
35
|
if reflection.type
|
29
36
|
record._write_attribute(reflection.type, owner.class.polymorphic_name)
|