activerecord 6.1.3.2 → 7.0.0.alpha2
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 +734 -1058
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +35 -7
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +16 -6
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +24 -25
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +161 -49
- data/lib/active_record/associations/preloader/batch.rb +51 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +37 -11
- data/lib/active_record/associations/preloader.rb +46 -110
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +76 -81
- data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +41 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +6 -9
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +3 -18
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +11 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +1 -3
- data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
- data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +8 -5
- data/lib/active_record/connection_handling.rb +20 -38
- data/lib/active_record/core.rb +129 -117
- data/lib/active_record/database_configurations/database_config.rb +12 -0
- data/lib/active_record/database_configurations/hash_config.rb +27 -1
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +18 -9
- data/lib/active_record/delegated_type.rb +33 -11
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +80 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +44 -46
- data/lib/active_record/errors.rb +66 -3
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +40 -5
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +16 -11
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +39 -6
- data/lib/active_record/integration.rb +1 -1
- data/lib/active_record/internal_metadata.rb +3 -5
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/log_subscriber.rb +6 -2
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +83 -1
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +46 -32
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/no_touching.rb +2 -2
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +134 -45
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +203 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +117 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +83 -58
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +45 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +42 -25
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +32 -23
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +233 -50
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +22 -15
- data/lib/active_record/relation.rb +170 -87
- data/lib/active_record/result.rb +17 -2
- data/lib/active_record/runtime_registry.rb +2 -4
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +3 -3
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/default.rb +62 -15
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +40 -22
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/tasks/database_tasks.rb +107 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +45 -4
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +1 -1
- data/lib/active_record.rb +170 -2
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/composite.rb +3 -3
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +18 -22
- data/lib/arel/delete_manager.rb +2 -4
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +8 -13
- data/lib/arel/nodes/homogeneous_in.rb +4 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +3 -2
- data/lib/arel/predications.rb +3 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +2 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +6 -1
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +44 -3
- data/lib/arel.rb +1 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- metadata +55 -16
@@ -127,8 +127,7 @@ module ActiveRecord
|
|
127
127
|
# <tt>attribute :foo, :string</tt>. Defaults to false.
|
128
128
|
|
129
129
|
included do
|
130
|
-
|
131
|
-
|
130
|
+
class_attribute :primary_key_prefix_type, instance_writer: false
|
132
131
|
class_attribute :table_name_prefix, instance_writer: false, default: ""
|
133
132
|
class_attribute :table_name_suffix, instance_writer: false, default: ""
|
134
133
|
class_attribute :schema_migrations_table_name, instance_accessor: false, default: "schema_migrations"
|
@@ -137,8 +136,24 @@ module ActiveRecord
|
|
137
136
|
class_attribute :implicit_order_column, instance_accessor: false
|
138
137
|
class_attribute :immutable_strings_by_default, instance_accessor: false
|
139
138
|
|
139
|
+
# Defines the name of the table column which will store the class name on single-table
|
140
|
+
# inheritance situations.
|
141
|
+
#
|
142
|
+
# The default inheritance column name is +type+, which means it's a
|
143
|
+
# reserved word inside Active Record. To be able to use single-table
|
144
|
+
# inheritance with another column name, or to use the column +type+ in
|
145
|
+
# your own model for something else, you can set +inheritance_column+:
|
146
|
+
#
|
147
|
+
# self.inheritance_column = 'zoink'
|
148
|
+
class_attribute :inheritance_column, instance_accessor: false, default: "type"
|
149
|
+
singleton_class.class_eval do
|
150
|
+
alias_method :_inheritance_column=, :inheritance_column=
|
151
|
+
private :_inheritance_column=
|
152
|
+
alias_method :inheritance_column=, :real_inheritance_column=
|
153
|
+
end
|
154
|
+
|
140
155
|
self.protected_environments = ["production"]
|
141
|
-
|
156
|
+
|
142
157
|
self.ignored_columns = [].freeze
|
143
158
|
|
144
159
|
delegate :type_for_attribute, :column_for_attribute, to: :class
|
@@ -197,6 +212,21 @@ module ActiveRecord
|
|
197
212
|
# the table name guess for an Invoice class becomes "myapp_invoices".
|
198
213
|
# Invoice::Lineitem becomes "myapp_invoice_lineitems".
|
199
214
|
#
|
215
|
+
# Active Model Naming's +model_name+ is the base name used to guess the
|
216
|
+
# table name. In case a custom Active Model Name is defined, it will be
|
217
|
+
# used for the table name as well:
|
218
|
+
#
|
219
|
+
# class PostRecord < ActiveRecord::Base
|
220
|
+
# class << self
|
221
|
+
# def model_name
|
222
|
+
# ActiveModel::Name.new(self, nil, "Post")
|
223
|
+
# end
|
224
|
+
# end
|
225
|
+
# end
|
226
|
+
#
|
227
|
+
# PostRecord.table_name
|
228
|
+
# # => "posts"
|
229
|
+
#
|
200
230
|
# You can also set your own table name explicitly:
|
201
231
|
#
|
202
232
|
# class Mouse < ActiveRecord::Base
|
@@ -233,7 +263,7 @@ module ActiveRecord
|
|
233
263
|
end
|
234
264
|
|
235
265
|
# Computes the table name, (re)sets it internally, and returns it.
|
236
|
-
def reset_table_name
|
266
|
+
def reset_table_name # :nodoc:
|
237
267
|
self.table_name = if abstract_class?
|
238
268
|
superclass == Base ? nil : superclass.table_name
|
239
269
|
elsif superclass.abstract_class?
|
@@ -243,11 +273,11 @@ module ActiveRecord
|
|
243
273
|
end
|
244
274
|
end
|
245
275
|
|
246
|
-
def full_table_name_prefix
|
276
|
+
def full_table_name_prefix # :nodoc:
|
247
277
|
(module_parents.detect { |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
|
248
278
|
end
|
249
279
|
|
250
|
-
def full_table_name_suffix
|
280
|
+
def full_table_name_suffix # :nodoc:
|
251
281
|
(module_parents.detect { |p| p.respond_to?(:table_name_suffix) } || self).table_name_suffix
|
252
282
|
end
|
253
283
|
|
@@ -266,23 +296,8 @@ module ActiveRecord
|
|
266
296
|
@protected_environments = environments.map(&:to_s)
|
267
297
|
end
|
268
298
|
|
269
|
-
|
270
|
-
|
271
|
-
#
|
272
|
-
# The default inheritance column name is +type+, which means it's a
|
273
|
-
# reserved word inside Active Record. To be able to use single-table
|
274
|
-
# inheritance with another column name, or to use the column +type+ in
|
275
|
-
# your own model for something else, you can set +inheritance_column+:
|
276
|
-
#
|
277
|
-
# self.inheritance_column = 'zoink'
|
278
|
-
def inheritance_column
|
279
|
-
(@inheritance_column ||= nil) || superclass.inheritance_column
|
280
|
-
end
|
281
|
-
|
282
|
-
# Sets the value of inheritance_column
|
283
|
-
def inheritance_column=(value)
|
284
|
-
@inheritance_column = value.to_s
|
285
|
-
@explicit_inheritance_column = true
|
299
|
+
def real_inheritance_column=(value) # :nodoc:
|
300
|
+
self._inheritance_column = value.to_s
|
286
301
|
end
|
287
302
|
|
288
303
|
# The list of columns names the model should ignore. Ignored columns won't have attribute
|
@@ -339,7 +354,7 @@ module ActiveRecord
|
|
339
354
|
end
|
340
355
|
end
|
341
356
|
|
342
|
-
def reset_sequence_name
|
357
|
+
def reset_sequence_name # :nodoc:
|
343
358
|
@explicit_sequence_name = false
|
344
359
|
@sequence_name = connection.default_sequence_name(table_name, primary_key)
|
345
360
|
end
|
@@ -486,9 +501,9 @@ module ActiveRecord
|
|
486
501
|
#
|
487
502
|
# The most common usage pattern for this method is probably in a migration,
|
488
503
|
# when just after creating a table you want to populate it with some default
|
489
|
-
# values,
|
504
|
+
# values, e.g.:
|
490
505
|
#
|
491
|
-
# class CreateJobLevels < ActiveRecord::Migration[
|
506
|
+
# class CreateJobLevels < ActiveRecord::Migration[7.0]
|
492
507
|
# def up
|
493
508
|
# create_table :job_levels do |t|
|
494
509
|
# t.integer :id
|
@@ -574,7 +589,6 @@ module ActiveRecord
|
|
574
589
|
@content_columns = nil
|
575
590
|
@default_attributes = nil
|
576
591
|
@column_defaults = nil
|
577
|
-
@inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
|
578
592
|
@attributes_builder = nil
|
579
593
|
@columns = nil
|
580
594
|
@columns_hash = nil
|
@@ -587,8 +601,8 @@ module ActiveRecord
|
|
587
601
|
end
|
588
602
|
|
589
603
|
# Guesses the table name, but does not decorate it with prefix and suffix information.
|
590
|
-
def undecorated_table_name(
|
591
|
-
table_name =
|
604
|
+
def undecorated_table_name(model_name)
|
605
|
+
table_name = model_name.to_s.demodulize.underscore
|
592
606
|
pluralize_table_names ? table_name.pluralize : table_name
|
593
607
|
end
|
594
608
|
|
@@ -602,7 +616,7 @@ module ActiveRecord
|
|
602
616
|
contained += "_"
|
603
617
|
end
|
604
618
|
|
605
|
-
"#{full_table_name_prefix}#{contained}#{undecorated_table_name(
|
619
|
+
"#{full_table_name_prefix}#{contained}#{undecorated_table_name(model_name)}#{full_table_name_suffix}"
|
606
620
|
else
|
607
621
|
# STI subclasses always use their superclass' table.
|
608
622
|
base_class.table_name
|
@@ -619,7 +633,7 @@ module ActiveRecord
|
|
619
633
|
|
620
634
|
def warn_if_deprecated_type(column)
|
621
635
|
return if attributes_to_define_after_schema_loads.key?(column.name)
|
622
|
-
return unless column.respond_to?(:
|
636
|
+
return unless column.respond_to?(:array?)
|
623
637
|
|
624
638
|
if column.array?
|
625
639
|
array_arguments = ", array: true"
|
@@ -630,7 +644,7 @@ module ActiveRecord
|
|
630
644
|
if column.sql_type.start_with?("interval")
|
631
645
|
precision_arguments = column.precision.presence && ", precision: #{column.precision}"
|
632
646
|
ActiveSupport::Deprecation.warn(<<~WARNING)
|
633
|
-
The behavior of the `:interval` type will be changing in Rails
|
647
|
+
The behavior of the `:interval` type will be changing in Rails 7.0
|
634
648
|
to return an `ActiveSupport::Duration` object. If you'd like to keep
|
635
649
|
the old behavior, you can add this line to #{self.name} model:
|
636
650
|
|
@@ -5,7 +5,7 @@ require "active_support/core_ext/module/redefine_method"
|
|
5
5
|
require "active_support/core_ext/hash/indifferent_access"
|
6
6
|
|
7
7
|
module ActiveRecord
|
8
|
-
module NestedAttributes
|
8
|
+
module NestedAttributes # :nodoc:
|
9
9
|
class TooManyRecords < ActiveRecordError
|
10
10
|
end
|
11
11
|
|
@@ -180,7 +180,7 @@ module ActiveRecord
|
|
180
180
|
# member.posts.second.title # => '[UPDATED] other post'
|
181
181
|
#
|
182
182
|
# However, the above applies if the parent model is being updated as well.
|
183
|
-
# For example,
|
183
|
+
# For example, if you wanted to create a +member+ named _joe_ and wanted to
|
184
184
|
# update the +posts+ at the same time, that would give an
|
185
185
|
# ActiveRecord::RecordNotFound error.
|
186
186
|
#
|
@@ -486,7 +486,7 @@ module ActiveRecord
|
|
486
486
|
existing_records = if association.loaded?
|
487
487
|
association.target
|
488
488
|
else
|
489
|
-
attribute_ids = attributes_collection.
|
489
|
+
attribute_ids = attributes_collection.filter_map { |a| a["id"] || a[:id] }
|
490
490
|
attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
|
491
491
|
end
|
492
492
|
|
@@ -26,14 +26,14 @@ module ActiveRecord
|
|
26
26
|
end
|
27
27
|
|
28
28
|
class << self
|
29
|
-
def apply_to(klass)
|
29
|
+
def apply_to(klass) # :nodoc:
|
30
30
|
klasses.push(klass)
|
31
31
|
yield
|
32
32
|
ensure
|
33
33
|
klasses.pop
|
34
34
|
end
|
35
35
|
|
36
|
-
def applied_to?(klass)
|
36
|
+
def applied_to?(klass) # :nodoc:
|
37
37
|
klasses.any? { |k| k >= klass }
|
38
38
|
end
|
39
39
|
|
@@ -38,10 +38,6 @@ module ActiveRecord
|
|
38
38
|
false
|
39
39
|
end
|
40
40
|
|
41
|
-
def to_sql
|
42
|
-
""
|
43
|
-
end
|
44
|
-
|
45
41
|
def calculate(operation, _column_name)
|
46
42
|
case operation
|
47
43
|
when :count, :sum
|
@@ -60,8 +56,8 @@ module ActiveRecord
|
|
60
56
|
end
|
61
57
|
|
62
58
|
private
|
63
|
-
def
|
64
|
-
|
59
|
+
def exec_main_query(async: false)
|
60
|
+
[].freeze
|
65
61
|
end
|
66
62
|
end
|
67
63
|
end
|
@@ -91,6 +91,9 @@ module ActiveRecord
|
|
91
91
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
92
92
|
# clause entirely.
|
93
93
|
#
|
94
|
+
# You can also pass an SQL string if you need more control on the return values
|
95
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
96
|
+
#
|
94
97
|
# [:unique_by]
|
95
98
|
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
96
99
|
# by every unique index on the table. Any duplicate rows are skipped.
|
@@ -120,6 +123,14 @@ module ActiveRecord
|
|
120
123
|
# { id: 1, title: "Rework", author: "David" },
|
121
124
|
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
122
125
|
# ])
|
126
|
+
#
|
127
|
+
# # insert_all works on chained scopes, and you can use create_with
|
128
|
+
# # to set default attributes for all inserted records.
|
129
|
+
#
|
130
|
+
# author.books.create_with(created_at: Time.now).insert_all([
|
131
|
+
# { id: 1, title: "Rework" },
|
132
|
+
# { id: 2, title: "Eloquent Ruby" }
|
133
|
+
# ])
|
123
134
|
def insert_all(attributes, returning: nil, unique_by: nil)
|
124
135
|
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
|
125
136
|
end
|
@@ -160,6 +171,9 @@ module ActiveRecord
|
|
160
171
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
161
172
|
# clause entirely.
|
162
173
|
#
|
174
|
+
# You can also pass an SQL string if you need more control on the return values
|
175
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
176
|
+
#
|
163
177
|
# ==== Examples
|
164
178
|
#
|
165
179
|
# # Insert multiple records
|
@@ -184,8 +198,8 @@ module ActiveRecord
|
|
184
198
|
# go through Active Record's type casting and serialization.
|
185
199
|
#
|
186
200
|
# See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
|
187
|
-
def upsert(attributes, returning: nil, unique_by: nil)
|
188
|
-
upsert_all([ attributes ], returning: returning, unique_by: unique_by)
|
201
|
+
def upsert(attributes, on_duplicate: :update, returning: nil, unique_by: nil)
|
202
|
+
upsert_all([ attributes ], on_duplicate: on_duplicate, returning: returning, unique_by: unique_by)
|
189
203
|
end
|
190
204
|
|
191
205
|
# Updates or inserts (upserts) multiple records into the database in a
|
@@ -208,6 +222,9 @@ module ActiveRecord
|
|
208
222
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
209
223
|
# clause entirely.
|
210
224
|
#
|
225
|
+
# You can also pass an SQL string if you need more control on the return values
|
226
|
+
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
227
|
+
#
|
211
228
|
# [:unique_by]
|
212
229
|
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
213
230
|
# by every unique index on the table. Any duplicate rows are skipped.
|
@@ -228,6 +245,11 @@ module ActiveRecord
|
|
228
245
|
# <tt>:unique_by</tt> is recommended to be paired with
|
229
246
|
# Active Record's schema_cache.
|
230
247
|
#
|
248
|
+
# [:on_duplicate]
|
249
|
+
# Specify a custom SQL for updating rows on conflict.
|
250
|
+
#
|
251
|
+
# NOTE: in this case you must provide all the columns you want to update by yourself.
|
252
|
+
#
|
231
253
|
# ==== Examples
|
232
254
|
#
|
233
255
|
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
@@ -239,8 +261,8 @@ module ActiveRecord
|
|
239
261
|
# ], unique_by: :isbn)
|
240
262
|
#
|
241
263
|
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
242
|
-
def upsert_all(attributes, returning: nil, unique_by: nil)
|
243
|
-
InsertAll.new(self, attributes, on_duplicate:
|
264
|
+
def upsert_all(attributes, on_duplicate: :update, returning: nil, unique_by: nil)
|
265
|
+
InsertAll.new(self, attributes, on_duplicate: on_duplicate, returning: returning, unique_by: unique_by).execute
|
244
266
|
end
|
245
267
|
|
246
268
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
@@ -264,6 +286,7 @@ module ActiveRecord
|
|
264
286
|
# ==== Parameters
|
265
287
|
#
|
266
288
|
# * +id+ - This should be the id or an array of ids to be updated.
|
289
|
+
# Optional argument, defaults to all records in the relation.
|
267
290
|
# * +attributes+ - This should be a hash of attributes or an array of hashes.
|
268
291
|
#
|
269
292
|
# ==== Examples
|
@@ -286,6 +309,11 @@ module ActiveRecord
|
|
286
309
|
# for updating all records in a single query.
|
287
310
|
def update(id = :all, attributes)
|
288
311
|
if id.is_a?(Array)
|
312
|
+
if id.any?(ActiveRecord::Base)
|
313
|
+
raise ArgumentError,
|
314
|
+
"You are passing an array of ActiveRecord::Base instances to `update`. " \
|
315
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
316
|
+
end
|
289
317
|
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
290
318
|
object.update(attributes[idx])
|
291
319
|
}
|
@@ -303,6 +331,32 @@ module ActiveRecord
|
|
303
331
|
end
|
304
332
|
end
|
305
333
|
|
334
|
+
# Updates the object (or multiple objects) just like #update but calls #update! instead
|
335
|
+
# of +update+, so an exception is raised if the record is invalid and saving will fail.
|
336
|
+
def update!(id = :all, attributes)
|
337
|
+
if id.is_a?(Array)
|
338
|
+
if id.any?(ActiveRecord::Base)
|
339
|
+
raise ArgumentError,
|
340
|
+
"You are passing an array of ActiveRecord::Base instances to `update!`. " \
|
341
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
342
|
+
end
|
343
|
+
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
344
|
+
object.update!(attributes[idx])
|
345
|
+
}
|
346
|
+
elsif id == :all
|
347
|
+
all.each { |record| record.update!(attributes) }
|
348
|
+
else
|
349
|
+
if ActiveRecord::Base === id
|
350
|
+
raise ArgumentError,
|
351
|
+
"You are passing an instance of ActiveRecord::Base to `update!`. " \
|
352
|
+
"Please pass the id of the object by calling `.id`."
|
353
|
+
end
|
354
|
+
object = find(id)
|
355
|
+
object.update!(attributes)
|
356
|
+
object
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
306
360
|
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
307
361
|
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
308
362
|
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
@@ -356,40 +410,54 @@ module ActiveRecord
|
|
356
410
|
primary_key = self.primary_key
|
357
411
|
primary_key_value = nil
|
358
412
|
|
359
|
-
if
|
360
|
-
|
361
|
-
|
362
|
-
if !primary_key_value && prefetch_primary_key?
|
413
|
+
if prefetch_primary_key? && primary_key
|
414
|
+
values[primary_key] ||= begin
|
363
415
|
primary_key_value = next_sequence_value
|
364
|
-
|
416
|
+
_default_attributes[primary_key].with_cast_value(primary_key_value)
|
365
417
|
end
|
366
418
|
end
|
367
419
|
|
420
|
+
im = Arel::InsertManager.new(arel_table)
|
421
|
+
|
368
422
|
if values.empty?
|
369
|
-
im
|
370
|
-
im.into arel_table
|
423
|
+
im.insert(connection.empty_insert_statement_value(primary_key))
|
371
424
|
else
|
372
|
-
im
|
425
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
373
426
|
end
|
374
427
|
|
375
428
|
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
|
376
429
|
end
|
377
430
|
|
378
431
|
def _update_record(values, constraints) # :nodoc:
|
379
|
-
constraints =
|
432
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
380
433
|
|
381
|
-
|
382
|
-
constraints
|
383
|
-
|
434
|
+
if default_scopes?(all_queries: true)
|
435
|
+
constraints << default_scoped(all_queries: true).where_clause.ast
|
436
|
+
end
|
437
|
+
|
438
|
+
if current_scope = self.global_current_scope
|
439
|
+
constraints << current_scope.where_clause.ast
|
440
|
+
end
|
441
|
+
|
442
|
+
um = Arel::UpdateManager.new(arel_table)
|
443
|
+
um.set(values.transform_keys { |name| arel_table[name] })
|
444
|
+
um.wheres = constraints
|
384
445
|
|
385
446
|
connection.update(um, "#{self} Update")
|
386
447
|
end
|
387
448
|
|
388
449
|
def _delete_record(constraints) # :nodoc:
|
389
|
-
constraints =
|
450
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
451
|
+
|
452
|
+
if default_scopes?(all_queries: true)
|
453
|
+
constraints << default_scoped(all_queries: true).where_clause.ast
|
454
|
+
end
|
455
|
+
|
456
|
+
if current_scope = self.global_current_scope
|
457
|
+
constraints << current_scope.where_clause.ast
|
458
|
+
end
|
390
459
|
|
391
|
-
dm = Arel::DeleteManager.new
|
392
|
-
dm.from(arel_table)
|
460
|
+
dm = Arel::DeleteManager.new(arel_table)
|
393
461
|
dm.wheres = constraints
|
394
462
|
|
395
463
|
connection.delete(dm, "#{self} Destroy")
|
@@ -411,14 +479,6 @@ module ActiveRecord
|
|
411
479
|
def discriminate_class_for_record(record)
|
412
480
|
self
|
413
481
|
end
|
414
|
-
|
415
|
-
def _substitute_values(values)
|
416
|
-
values.map do |name, value|
|
417
|
-
attr = arel_table[name]
|
418
|
-
bind = predicate_builder.build_bind_attribute(attr.name, value)
|
419
|
-
[attr, bind]
|
420
|
-
end
|
421
|
-
end
|
422
482
|
end
|
423
483
|
|
424
484
|
# Returns true if this object hasn't been saved yet -- that is, a record
|
@@ -434,6 +494,11 @@ module ActiveRecord
|
|
434
494
|
@previously_new_record
|
435
495
|
end
|
436
496
|
|
497
|
+
# Returns true if this object was previously persisted but now it has been deleted.
|
498
|
+
def previously_persisted?
|
499
|
+
!new_record? && destroyed?
|
500
|
+
end
|
501
|
+
|
437
502
|
# Returns true if this object has been destroyed, otherwise returns false.
|
438
503
|
def destroyed?
|
439
504
|
@destroyed
|
@@ -556,17 +621,17 @@ module ActiveRecord
|
|
556
621
|
end
|
557
622
|
|
558
623
|
# Returns an instance of the specified +klass+ with the attributes of the
|
559
|
-
# current record. This is mostly useful in relation to single
|
560
|
-
# inheritance structures where you want a subclass to appear as the
|
624
|
+
# current record. This is mostly useful in relation to single table
|
625
|
+
# inheritance (STI) structures where you want a subclass to appear as the
|
561
626
|
# superclass. This can be used along with record identification in
|
562
627
|
# Action Pack to allow, say, <tt>Client < Company</tt> to do something
|
563
628
|
# like render <tt>partial: @client.becomes(Company)</tt> to render that
|
564
629
|
# instance using the companies/company partial instead of clients/client.
|
565
630
|
#
|
566
631
|
# Note: The new instance will share a link to the same attributes as the original class.
|
567
|
-
# Therefore the
|
632
|
+
# Therefore the STI column value will still be the same.
|
568
633
|
# Any change to the attributes on either instance will affect both instances.
|
569
|
-
# If you want to change the
|
634
|
+
# If you want to change the STI column as well, use #becomes! instead.
|
570
635
|
def becomes(klass)
|
571
636
|
became = klass.allocate
|
572
637
|
|
@@ -581,11 +646,11 @@ module ActiveRecord
|
|
581
646
|
became
|
582
647
|
end
|
583
648
|
|
584
|
-
# Wrapper around #becomes that also changes the instance's
|
649
|
+
# Wrapper around #becomes that also changes the instance's STI column value.
|
585
650
|
# This is especially useful if you want to persist the changed class in your
|
586
651
|
# database.
|
587
652
|
#
|
588
|
-
# Note: The old instance's
|
653
|
+
# Note: The old instance's STI column value will be changed too, as both objects
|
589
654
|
# share the same set of attributes.
|
590
655
|
def becomes!(klass)
|
591
656
|
became = becomes(klass)
|
@@ -671,14 +736,15 @@ module ActiveRecord
|
|
671
736
|
verify_readonly_attribute(name) || name
|
672
737
|
end
|
673
738
|
|
674
|
-
|
675
|
-
attributes.
|
676
|
-
|
739
|
+
update_constraints = _primary_key_constraints_hash
|
740
|
+
attributes = attributes.each_with_object({}) do |(k, v), h|
|
741
|
+
h[k] = @attributes.write_cast_value(k, v)
|
742
|
+
clear_attribute_change(k)
|
677
743
|
end
|
678
744
|
|
679
745
|
affected_rows = self.class._update_record(
|
680
746
|
attributes,
|
681
|
-
|
747
|
+
update_constraints
|
682
748
|
)
|
683
749
|
|
684
750
|
affected_rows == 1
|
@@ -800,13 +866,13 @@ module ActiveRecord
|
|
800
866
|
def reload(options = nil)
|
801
867
|
self.class.connection.clear_query_cache
|
802
868
|
|
803
|
-
fresh_object =
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
end
|
869
|
+
fresh_object = if apply_scoping?(options)
|
870
|
+
_find_record(options)
|
871
|
+
else
|
872
|
+
self.class.unscoped { _find_record(options) }
|
873
|
+
end
|
809
874
|
|
875
|
+
@association_cache = fresh_object.instance_variable_get(:@association_cache)
|
810
876
|
@attributes = fresh_object.instance_variable_get(:@attributes)
|
811
877
|
@new_record = false
|
812
878
|
@previously_new_record = false
|
@@ -865,6 +931,29 @@ module ActiveRecord
|
|
865
931
|
end
|
866
932
|
|
867
933
|
private
|
934
|
+
def strict_loaded_associations
|
935
|
+
@association_cache.find_all do |_, assoc|
|
936
|
+
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
937
|
+
end.map(&:first)
|
938
|
+
end
|
939
|
+
|
940
|
+
def _find_record(options)
|
941
|
+
if options && options[:lock]
|
942
|
+
self.class.preload(strict_loaded_associations).lock(options[:lock]).find(id)
|
943
|
+
else
|
944
|
+
self.class.preload(strict_loaded_associations).find(id)
|
945
|
+
end
|
946
|
+
end
|
947
|
+
|
948
|
+
def apply_scoping?(options)
|
949
|
+
!(options && options[:unscoped]) &&
|
950
|
+
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
951
|
+
end
|
952
|
+
|
953
|
+
def _primary_key_constraints_hash
|
954
|
+
{ @primary_key => id_in_database }
|
955
|
+
end
|
956
|
+
|
868
957
|
# A hook to be overridden by association modules.
|
869
958
|
def destroy_associations
|
870
959
|
end
|
@@ -874,7 +963,7 @@ module ActiveRecord
|
|
874
963
|
end
|
875
964
|
|
876
965
|
def _delete_row
|
877
|
-
self.class._delete_record(
|
966
|
+
self.class._delete_record(_primary_key_constraints_hash)
|
878
967
|
end
|
879
968
|
|
880
969
|
def _touch_row(attribute_names, time)
|
@@ -890,7 +979,7 @@ module ActiveRecord
|
|
890
979
|
def _update_row(attribute_names, attempted_action = "update")
|
891
980
|
self.class._update_record(
|
892
981
|
attributes_with_values(attribute_names),
|
893
|
-
|
982
|
+
_primary_key_constraints_hash
|
894
983
|
)
|
895
984
|
end
|
896
985
|
|