activerecord 7.0.8 → 7.1.3.4
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 +1554 -1452
- data/MIT-LICENSE +1 -1
- data/README.rdoc +16 -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 +20 -4
- 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 +15 -9
- data/lib/active_record/associations/collection_proxy.rb +15 -10
- 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 +31 -7
- data/lib/active_record/associations/preloader.rb +13 -10
- 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 +313 -217
- 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 +52 -34
- 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 +74 -51
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- 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 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -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 +18 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -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 +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -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 +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
- 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 +52 -39
- 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 +209 -79
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +175 -153
- 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 +9 -4
- 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 +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/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 +112 -28
- data/lib/active_record/errors.rb +112 -18
- 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 +135 -71
- data/lib/active_record/future_result.rb +31 -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 +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -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 +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +104 -5
- data/lib/active_record/migration/compatibility.rb +139 -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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +219 -111
- data/lib/active_record/model_schema.rb +64 -44
- data/lib/active_record/nested_attributes.rb +24 -6
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +188 -37
- 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 +109 -47
- data/lib/active_record/railties/controller_runtime.rb +12 -6
- data/lib/active_record/railties/databases.rake +142 -148
- 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 +174 -44
- 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 +187 -63
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- 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/polymorphic_array_value.rb +4 -6
- 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 +352 -63
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +91 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -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 +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +27 -15
- 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 +1 -9
- 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 +48 -12
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
data/lib/active_record/core.rb
CHANGED
@@ -1,29 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/enumerable"
|
4
|
-
require "active_support/core_ext/
|
5
|
-
require "active_support/core_ext/string/filters"
|
4
|
+
require "active_support/core_ext/module/delegation"
|
6
5
|
require "active_support/parameter_filter"
|
7
6
|
require "concurrent/map"
|
8
7
|
|
9
8
|
module ActiveRecord
|
9
|
+
# = Active Record \Core
|
10
10
|
module Core
|
11
11
|
extend ActiveSupport::Concern
|
12
|
+
include ActiveModel::Access
|
12
13
|
|
13
14
|
included do
|
14
15
|
##
|
15
16
|
# :singleton-method:
|
16
17
|
#
|
17
|
-
# Accepts a logger conforming to the interface of Log4r
|
18
|
-
# passed on to any new database
|
19
|
-
#
|
18
|
+
# Accepts a logger conforming to the interface of Log4r or the default
|
19
|
+
# Ruby +Logger+ class, which is then passed on to any new database
|
20
|
+
# connections made. You can retrieve this logger by calling +logger+ on
|
21
|
+
# either an Active Record model class or an Active Record model instance.
|
20
22
|
class_attribute :logger, instance_writer: false
|
21
23
|
|
24
|
+
class_attribute :_destroy_association_async_job, instance_accessor: false, default: "ActiveRecord::DestroyAssociationAsyncJob"
|
25
|
+
|
26
|
+
# The job class used to destroy associations in the background.
|
27
|
+
def self.destroy_association_async_job
|
28
|
+
if _destroy_association_async_job.is_a?(String)
|
29
|
+
self._destroy_association_async_job = _destroy_association_async_job.constantize
|
30
|
+
end
|
31
|
+
_destroy_association_async_job
|
32
|
+
rescue NameError => error
|
33
|
+
raise NameError, "Unable to load destroy_association_async_job: #{error.message}"
|
34
|
+
end
|
35
|
+
|
36
|
+
singleton_class.alias_method :destroy_association_async_job=, :_destroy_association_async_job=
|
37
|
+
delegate :destroy_association_async_job, to: :class
|
38
|
+
|
22
39
|
##
|
23
40
|
# :singleton-method:
|
24
41
|
#
|
25
|
-
# Specifies the
|
26
|
-
|
42
|
+
# Specifies the maximum number of records that will be destroyed in a
|
43
|
+
# single background job by the <tt>dependent: :destroy_async</tt>
|
44
|
+
# association option. When +nil+ (default), all dependent records will be
|
45
|
+
# destroyed in a single background job. If specified, the records to be
|
46
|
+
# destroyed will be split into multiple background jobs.
|
47
|
+
class_attribute :destroy_association_async_batch_size, instance_writer: false, instance_predicate: false, default: nil
|
27
48
|
|
28
49
|
##
|
29
50
|
# Contains the database configuration - as is typically stored in config/database.yml -
|
@@ -33,19 +54,19 @@ module ActiveRecord
|
|
33
54
|
#
|
34
55
|
# development:
|
35
56
|
# adapter: sqlite3
|
36
|
-
# database:
|
57
|
+
# database: storage/development.sqlite3
|
37
58
|
#
|
38
59
|
# production:
|
39
60
|
# adapter: sqlite3
|
40
|
-
# database:
|
61
|
+
# database: storage/production.sqlite3
|
41
62
|
#
|
42
63
|
# ...would result in ActiveRecord::Base.configurations to look like this:
|
43
64
|
#
|
44
65
|
# #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
|
45
66
|
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
|
46
|
-
# @name="primary", @config={adapter: "sqlite3", database: "
|
67
|
+
# @name="primary", @config={adapter: "sqlite3", database: "storage/development.sqlite3"}>,
|
47
68
|
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
|
48
|
-
# @name="primary", @config={adapter: "sqlite3", database: "
|
69
|
+
# @name="primary", @config={adapter: "sqlite3", database: "storage/production.sqlite3"}>
|
49
70
|
# ]>
|
50
71
|
def self.configurations=(config)
|
51
72
|
@@configurations = ActiveRecord::DatabaseConfigurations.new(config)
|
@@ -71,6 +92,8 @@ module ActiveRecord
|
|
71
92
|
|
72
93
|
class_attribute :has_many_inversing, instance_accessor: false, default: false
|
73
94
|
|
95
|
+
class_attribute :run_commit_callbacks_on_first_saved_instances_in_transaction, instance_accessor: false, default: true
|
96
|
+
|
74
97
|
class_attribute :default_connection_handler, instance_writer: false
|
75
98
|
|
76
99
|
class_attribute :default_role, instance_writer: false
|
@@ -99,33 +122,6 @@ module ActiveRecord
|
|
99
122
|
ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler
|
100
123
|
end
|
101
124
|
|
102
|
-
def self.connection_handlers
|
103
|
-
if ActiveRecord.legacy_connection_handling
|
104
|
-
else
|
105
|
-
raise NotImplementedError, "The new connection handling does not support accessing multiple connection handlers."
|
106
|
-
end
|
107
|
-
|
108
|
-
@@connection_handlers ||= {}
|
109
|
-
end
|
110
|
-
|
111
|
-
def self.connection_handlers=(handlers)
|
112
|
-
if ActiveRecord.legacy_connection_handling
|
113
|
-
ActiveSupport::Deprecation.warn(<<~MSG)
|
114
|
-
Using legacy connection handling is deprecated. Please set
|
115
|
-
`legacy_connection_handling` to `false` in your application.
|
116
|
-
|
117
|
-
The new connection handling does not support `connection_handlers`
|
118
|
-
getter and setter.
|
119
|
-
|
120
|
-
Read more about how to migrate at: https://guides.rubyonrails.org/active_record_multiple_databases.html#migrate-to-the-new-connection-handling
|
121
|
-
MSG
|
122
|
-
else
|
123
|
-
raise NotImplementedError, "The new connection handling does not support multiple connection handlers."
|
124
|
-
end
|
125
|
-
|
126
|
-
@@connection_handlers = handlers
|
127
|
-
end
|
128
|
-
|
129
125
|
def self.asynchronous_queries_session # :nodoc:
|
130
126
|
asynchronous_queries_tracker.current_session
|
131
127
|
end
|
@@ -145,16 +141,12 @@ module ActiveRecord
|
|
145
141
|
# ActiveRecord::Base.current_role #=> :reading
|
146
142
|
# end
|
147
143
|
def self.current_role
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
connected_to_stack.reverse_each do |hash|
|
152
|
-
return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
|
153
|
-
return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
|
154
|
-
end
|
155
|
-
|
156
|
-
default_role
|
144
|
+
connected_to_stack.reverse_each do |hash|
|
145
|
+
return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
|
146
|
+
return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
|
157
147
|
end
|
148
|
+
|
149
|
+
default_role
|
158
150
|
end
|
159
151
|
|
160
152
|
# Returns the symbol representing the current connected shard.
|
@@ -186,16 +178,12 @@ module ActiveRecord
|
|
186
178
|
# ActiveRecord::Base.current_preventing_writes #=> false
|
187
179
|
# end
|
188
180
|
def self.current_preventing_writes
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
connected_to_stack.reverse_each do |hash|
|
193
|
-
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
|
194
|
-
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
|
195
|
-
end
|
196
|
-
|
197
|
-
false
|
181
|
+
connected_to_stack.reverse_each do |hash|
|
182
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
|
183
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
|
198
184
|
end
|
185
|
+
|
186
|
+
false
|
199
187
|
end
|
200
188
|
|
201
189
|
def self.connected_to_stack # :nodoc:
|
@@ -252,19 +240,6 @@ module ActiveRecord
|
|
252
240
|
@find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
|
253
241
|
end
|
254
242
|
|
255
|
-
def inherited(child_class) # :nodoc:
|
256
|
-
# initialize cache at class definition for thread safety
|
257
|
-
child_class.initialize_find_by_cache
|
258
|
-
unless child_class.base_class?
|
259
|
-
klass = self
|
260
|
-
until klass.base_class?
|
261
|
-
klass.initialize_find_by_cache
|
262
|
-
klass = klass.superclass
|
263
|
-
end
|
264
|
-
end
|
265
|
-
super
|
266
|
-
end
|
267
|
-
|
268
243
|
def find(*ids) # :nodoc:
|
269
244
|
# We don't have cache keys for this stuff yet
|
270
245
|
return super unless ids.length == 1
|
@@ -274,14 +249,8 @@ module ActiveRecord
|
|
274
249
|
|
275
250
|
return super if StatementCache.unsupported_value?(id)
|
276
251
|
|
277
|
-
|
278
|
-
|
279
|
-
statement = cached_find_by_statement(key) { |params|
|
280
|
-
where(key => params.bind).limit(1)
|
281
|
-
}
|
282
|
-
|
283
|
-
statement.execute([id], connection).first ||
|
284
|
-
raise(RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id))
|
252
|
+
cached_find_by([primary_key], [id]) ||
|
253
|
+
raise(RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}", name, primary_key, id))
|
285
254
|
end
|
286
255
|
|
287
256
|
def find_by(*args) # :nodoc:
|
@@ -303,58 +272,38 @@ module ActiveRecord
|
|
303
272
|
elsif reflection.belongs_to? && !reflection.polymorphic?
|
304
273
|
key = reflection.join_foreign_key
|
305
274
|
pkey = reflection.join_primary_key
|
306
|
-
|
275
|
+
|
276
|
+
if pkey.is_a?(Array)
|
277
|
+
if pkey.all? { |attribute| value.respond_to?(attribute) }
|
278
|
+
value = pkey.map do |attribute|
|
279
|
+
if attribute == "id"
|
280
|
+
value.id_value
|
281
|
+
else
|
282
|
+
value.public_send(attribute)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
composite_primary_key = true
|
286
|
+
end
|
287
|
+
else
|
288
|
+
value = value.public_send(pkey) if value.respond_to?(pkey)
|
289
|
+
end
|
307
290
|
end
|
308
291
|
|
309
|
-
if !
|
292
|
+
if !composite_primary_key &&
|
293
|
+
(!columns_hash.key?(key) || StatementCache.unsupported_value?(value))
|
310
294
|
return super
|
311
295
|
end
|
312
296
|
|
313
297
|
h[key] = value
|
314
298
|
end
|
315
299
|
|
316
|
-
keys
|
317
|
-
statement = cached_find_by_statement(keys) { |params|
|
318
|
-
wheres = keys.index_with { params.bind }
|
319
|
-
where(wheres).limit(1)
|
320
|
-
}
|
321
|
-
|
322
|
-
begin
|
323
|
-
statement.execute(hash.values, connection).first
|
324
|
-
rescue TypeError
|
325
|
-
raise ActiveRecord::StatementInvalid
|
326
|
-
end
|
300
|
+
cached_find_by(hash.keys, hash.values)
|
327
301
|
end
|
328
302
|
|
329
303
|
def find_by!(*args) # :nodoc:
|
330
304
|
find_by(*args) || where(*args).raise_record_not_found_exception!
|
331
305
|
end
|
332
306
|
|
333
|
-
%w(
|
334
|
-
reading_role writing_role legacy_connection_handling default_timezone index_nested_attribute_errors
|
335
|
-
verbose_query_logs queues warn_on_records_fetched_greater_than maintain_test_schema
|
336
|
-
application_record_class action_on_strict_loading_violation schema_format error_on_ignored_order
|
337
|
-
timestamped_migrations dump_schema_after_migration dump_schemas suppress_multiple_database_warning
|
338
|
-
).each do |attr|
|
339
|
-
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
340
|
-
def #{attr}
|
341
|
-
ActiveSupport::Deprecation.warn(<<~MSG)
|
342
|
-
ActiveRecord::Base.#{attr} is deprecated and will be removed in Rails 7.1.
|
343
|
-
Use `ActiveRecord.#{attr}` instead.
|
344
|
-
MSG
|
345
|
-
ActiveRecord.#{attr}
|
346
|
-
end
|
347
|
-
|
348
|
-
def #{attr}=(value)
|
349
|
-
ActiveSupport::Deprecation.warn(<<~MSG)
|
350
|
-
ActiveRecord::Base.#{attr}= is deprecated and will be removed in Rails 7.1.
|
351
|
-
Use `ActiveRecord.#{attr}=` instead.
|
352
|
-
MSG
|
353
|
-
ActiveRecord.#{attr} = value
|
354
|
-
end
|
355
|
-
RUBY
|
356
|
-
end
|
357
|
-
|
358
307
|
def initialize_generated_modules # :nodoc:
|
359
308
|
generated_association_methods
|
360
309
|
end
|
@@ -371,10 +320,10 @@ module ActiveRecord
|
|
371
320
|
|
372
321
|
# Returns columns which shouldn't be exposed while calling +#inspect+.
|
373
322
|
def filter_attributes
|
374
|
-
if
|
375
|
-
@filter_attributes
|
376
|
-
else
|
323
|
+
if @filter_attributes.nil?
|
377
324
|
superclass.filter_attributes
|
325
|
+
else
|
326
|
+
@filter_attributes
|
378
327
|
end
|
379
328
|
end
|
380
329
|
|
@@ -385,13 +334,13 @@ module ActiveRecord
|
|
385
334
|
end
|
386
335
|
|
387
336
|
def inspection_filter # :nodoc:
|
388
|
-
if
|
337
|
+
if @filter_attributes.nil?
|
338
|
+
superclass.inspection_filter
|
339
|
+
else
|
389
340
|
@inspection_filter ||= begin
|
390
341
|
mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
|
391
342
|
ActiveSupport::ParameterFilter.new(@filter_attributes, mask: mask)
|
392
343
|
end
|
393
|
-
else
|
394
|
-
superclass.inspection_filter
|
395
344
|
end
|
396
345
|
end
|
397
346
|
|
@@ -411,12 +360,7 @@ module ActiveRecord
|
|
411
360
|
end
|
412
361
|
end
|
413
362
|
|
414
|
-
#
|
415
|
-
def ===(object) # :nodoc:
|
416
|
-
object.is_a?(self)
|
417
|
-
end
|
418
|
-
|
419
|
-
# Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
|
363
|
+
# Returns an instance of +Arel::Table+ loaded with the current table name.
|
420
364
|
def arel_table # :nodoc:
|
421
365
|
@arel_table ||= Arel::Table.new(table_name, klass: self)
|
422
366
|
end
|
@@ -435,6 +379,28 @@ module ActiveRecord
|
|
435
379
|
end
|
436
380
|
|
437
381
|
private
|
382
|
+
def inherited(subclass)
|
383
|
+
super
|
384
|
+
|
385
|
+
# initialize cache at class definition for thread safety
|
386
|
+
subclass.initialize_find_by_cache
|
387
|
+
unless subclass.base_class?
|
388
|
+
klass = self
|
389
|
+
until klass.base_class?
|
390
|
+
klass.initialize_find_by_cache
|
391
|
+
klass = klass.superclass
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
subclass.class_eval do
|
396
|
+
@arel_table = nil
|
397
|
+
@predicate_builder = nil
|
398
|
+
@inspection_filter = nil
|
399
|
+
@filter_attributes = nil
|
400
|
+
@generated_association_methods = nil
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
438
404
|
def relation
|
439
405
|
relation = Relation.create(self)
|
440
406
|
|
@@ -448,6 +414,25 @@ module ActiveRecord
|
|
448
414
|
def table_metadata
|
449
415
|
TableMetadata.new(self, arel_table)
|
450
416
|
end
|
417
|
+
|
418
|
+
def cached_find_by(keys, values)
|
419
|
+
statement = cached_find_by_statement(keys) { |params|
|
420
|
+
wheres = keys.index_with do |key|
|
421
|
+
if key.is_a?(Array)
|
422
|
+
[key.map { params.bind }]
|
423
|
+
else
|
424
|
+
params.bind
|
425
|
+
end
|
426
|
+
end
|
427
|
+
where(wheres).limit(1)
|
428
|
+
}
|
429
|
+
|
430
|
+
begin
|
431
|
+
statement.execute(values.flatten, connection).first
|
432
|
+
rescue TypeError
|
433
|
+
raise ActiveRecord::StatementInvalid
|
434
|
+
end
|
435
|
+
end
|
451
436
|
end
|
452
437
|
|
453
438
|
# New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
|
@@ -455,7 +440,7 @@ module ActiveRecord
|
|
455
440
|
# In both instances, valid attribute keys are determined by the column names of the associated table --
|
456
441
|
# hence you can't have attributes that aren't part of the table columns.
|
457
442
|
#
|
458
|
-
# ==== Example
|
443
|
+
# ==== Example
|
459
444
|
# # Instantiates a single new object
|
460
445
|
# User.new(first_name: 'Jamie')
|
461
446
|
def initialize(attributes = nil)
|
@@ -533,12 +518,17 @@ module ActiveRecord
|
|
533
518
|
# only, not its associations. The extent of a "deep" copy is application
|
534
519
|
# specific and is therefore left to the application to implement according
|
535
520
|
# to its need.
|
536
|
-
# The dup method does not preserve the timestamps (created|updated)_(at|on)
|
521
|
+
# The dup method does not preserve the timestamps (created|updated)_(at|on)
|
522
|
+
# and locking column.
|
537
523
|
|
538
524
|
##
|
539
525
|
def initialize_dup(other) # :nodoc:
|
540
526
|
@attributes = @attributes.deep_dup
|
541
|
-
|
527
|
+
if self.class.composite_primary_key?
|
528
|
+
@primary_key.each { |key| @attributes.reset(key) }
|
529
|
+
else
|
530
|
+
@attributes.reset(@primary_key)
|
531
|
+
end
|
542
532
|
|
543
533
|
_run_initialize_callbacks
|
544
534
|
|
@@ -568,6 +558,35 @@ module ActiveRecord
|
|
568
558
|
coder["active_record_yaml_version"] = 2
|
569
559
|
end
|
570
560
|
|
561
|
+
##
|
562
|
+
# :method: slice
|
563
|
+
#
|
564
|
+
# :call-seq: slice(*methods)
|
565
|
+
#
|
566
|
+
# Returns a hash of the given methods with their names as keys and returned
|
567
|
+
# values as values.
|
568
|
+
#
|
569
|
+
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
570
|
+
# topic.slice(:title, :author_name)
|
571
|
+
# => { "title" => "Budget", "author_name" => "Jason" }
|
572
|
+
#
|
573
|
+
#--
|
574
|
+
# Implemented by ActiveModel::Access#slice.
|
575
|
+
|
576
|
+
##
|
577
|
+
# :method: values_at
|
578
|
+
#
|
579
|
+
# :call-seq: values_at(*methods)
|
580
|
+
#
|
581
|
+
# Returns an array of the values returned by the given methods.
|
582
|
+
#
|
583
|
+
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
584
|
+
# topic.values_at(:title, :author_name)
|
585
|
+
# => ["Budget", "Jason"]
|
586
|
+
#
|
587
|
+
#--
|
588
|
+
# Implemented by ActiveModel::Access#values_at.
|
589
|
+
|
571
590
|
# Returns true if +comparison_object+ is the same exact object, or +comparison_object+
|
572
591
|
# is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
|
573
592
|
#
|
@@ -580,7 +599,7 @@ module ActiveRecord
|
|
580
599
|
def ==(comparison_object)
|
581
600
|
super ||
|
582
601
|
comparison_object.instance_of?(self.class) &&
|
583
|
-
|
602
|
+
primary_key_values_present? &&
|
584
603
|
comparison_object.id == id
|
585
604
|
end
|
586
605
|
alias :eql? :==
|
@@ -590,7 +609,7 @@ module ActiveRecord
|
|
590
609
|
def hash
|
591
610
|
id = self.id
|
592
611
|
|
593
|
-
if
|
612
|
+
if primary_key_values_present?
|
594
613
|
self.class.hash ^ id.hash
|
595
614
|
else
|
596
615
|
super
|
@@ -642,25 +661,33 @@ module ActiveRecord
|
|
642
661
|
#
|
643
662
|
# user = User.first
|
644
663
|
# user.strict_loading! # => true
|
645
|
-
# user.
|
664
|
+
# user.address.city
|
665
|
+
# => ActiveRecord::StrictLoadingViolationError
|
666
|
+
# user.comments.to_a
|
646
667
|
# => ActiveRecord::StrictLoadingViolationError
|
647
668
|
#
|
648
|
-
#
|
669
|
+
# ==== Parameters
|
649
670
|
#
|
650
|
-
# * value - Boolean specifying whether to enable or disable strict loading.
|
651
|
-
# * mode - Symbol specifying strict loading mode. Defaults to :all. Using
|
652
|
-
#
|
653
|
-
#
|
671
|
+
# * +value+ - Boolean specifying whether to enable or disable strict loading.
|
672
|
+
# * <tt>:mode</tt> - Symbol specifying strict loading mode. Defaults to :all. Using
|
673
|
+
# :n_plus_one_only mode will only raise an error if an association that
|
674
|
+
# will lead to an n plus one query is lazily loaded.
|
654
675
|
#
|
655
|
-
#
|
676
|
+
# ==== Examples
|
656
677
|
#
|
657
678
|
# user = User.first
|
658
679
|
# user.strict_loading!(false) # => false
|
659
|
-
# user.
|
660
|
-
# => #<
|
680
|
+
# user.address.city # => "Tatooine"
|
681
|
+
# user.comments.to_a # => [#<Comment:0x00...]
|
682
|
+
#
|
683
|
+
# user.strict_loading!(mode: :n_plus_one_only)
|
684
|
+
# user.address.city # => "Tatooine"
|
685
|
+
# user.comments.to_a # => [#<Comment:0x00...]
|
686
|
+
# user.comments.first.ratings.to_a
|
687
|
+
# => ActiveRecord::StrictLoadingViolationError
|
661
688
|
def strict_loading!(value = true, mode: :all)
|
662
689
|
unless [:all, :n_plus_one_only].include?(mode)
|
663
|
-
raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only]."
|
690
|
+
raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
|
664
691
|
end
|
665
692
|
|
666
693
|
@strict_loading_mode = mode
|
@@ -675,6 +702,10 @@ module ActiveRecord
|
|
675
702
|
end
|
676
703
|
|
677
704
|
# Marks this record as read only.
|
705
|
+
#
|
706
|
+
# customer = Customer.first
|
707
|
+
# customer.readonly!
|
708
|
+
# customer.save # Raises an ActiveRecord::ReadOnlyRecord
|
678
709
|
def readonly!
|
679
710
|
@readonly = true
|
680
711
|
end
|
@@ -688,7 +719,7 @@ module ActiveRecord
|
|
688
719
|
# We check defined?(@attributes) not to issue warnings if the object is
|
689
720
|
# allocated but not initialized.
|
690
721
|
inspection = if defined?(@attributes) && @attributes
|
691
|
-
|
722
|
+
attribute_names.filter_map do |name|
|
692
723
|
if _has_attribute?(name)
|
693
724
|
"#{name}: #{attribute_for_inspect(name)}"
|
694
725
|
end
|
@@ -725,16 +756,6 @@ module ActiveRecord
|
|
725
756
|
end
|
726
757
|
end
|
727
758
|
|
728
|
-
# Returns a hash of the given methods with their names as keys and returned values as values.
|
729
|
-
def slice(*methods)
|
730
|
-
methods.flatten.index_with { |method| public_send(method) }.with_indifferent_access
|
731
|
-
end
|
732
|
-
|
733
|
-
# Returns an array of the values returned by the given methods.
|
734
|
-
def values_at(*methods)
|
735
|
-
methods.flatten.map! { |method| public_send(method) }
|
736
|
-
end
|
737
|
-
|
738
759
|
private
|
739
760
|
# +Array#flatten+ will call +#to_ary+ (recursively) on each of the elements of
|
740
761
|
# the array, and then rescues from the possible +NoMethodError+. If those elements are
|
@@ -763,6 +784,7 @@ module ActiveRecord
|
|
763
784
|
@strict_loading_mode = :all
|
764
785
|
|
765
786
|
klass.define_attribute_methods
|
787
|
+
klass.generate_alias_attributes
|
766
788
|
end
|
767
789
|
|
768
790
|
def initialize_internals_callback
|
@@ -5,6 +5,10 @@ module ActiveRecord
|
|
5
5
|
module CounterCache
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
|
+
included do
|
9
|
+
class_attribute :_counter_cache_columns, instance_accessor: false, default: []
|
10
|
+
end
|
11
|
+
|
8
12
|
module ClassMethods
|
9
13
|
# Resets one or more counter caches to their correct value using an SQL
|
10
14
|
# count query. This is useful when adding new counter caches, or if the
|
@@ -29,6 +33,7 @@ module ActiveRecord
|
|
29
33
|
def reset_counters(id, *counters, touch: nil)
|
30
34
|
object = find(id)
|
31
35
|
|
36
|
+
updates = {}
|
32
37
|
counters.each do |counter_association|
|
33
38
|
has_many_association = _reflect_on_association(counter_association)
|
34
39
|
unless has_many_association
|
@@ -47,19 +52,21 @@ module ActiveRecord
|
|
47
52
|
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
48
53
|
counter_name = reflection.counter_cache_column
|
49
54
|
|
50
|
-
|
51
|
-
|
52
|
-
if
|
53
|
-
|
54
|
-
names = Array.wrap(names)
|
55
|
-
options = names.extract_options!
|
56
|
-
touch_updates = touch_attributes_with_time(*names, **options)
|
57
|
-
updates.merge!(touch_updates)
|
58
|
-
end
|
55
|
+
count_was = object.send(counter_name)
|
56
|
+
count = object.send(counter_association).count(:all)
|
57
|
+
updates[counter_name] = count if count != count_was
|
58
|
+
end
|
59
59
|
|
60
|
-
|
60
|
+
if touch
|
61
|
+
names = touch if touch != true
|
62
|
+
names = Array.wrap(names)
|
63
|
+
options = names.extract_options!
|
64
|
+
touch_updates = touch_attributes_with_time(*names, **options)
|
65
|
+
updates.merge!(touch_updates)
|
61
66
|
end
|
62
67
|
|
68
|
+
unscoped.where(primary_key => object.id).update_all(updates) if updates.any?
|
69
|
+
|
63
70
|
true
|
64
71
|
end
|
65
72
|
|
@@ -80,28 +87,28 @@ module ActiveRecord
|
|
80
87
|
#
|
81
88
|
# ==== Examples
|
82
89
|
#
|
83
|
-
# # For the Post with id of 5, decrement the
|
84
|
-
# # increment the
|
85
|
-
# Post.update_counters 5,
|
90
|
+
# # For the Post with id of 5, decrement the comments_count by 1, and
|
91
|
+
# # increment the actions_count by 1
|
92
|
+
# Post.update_counters 5, comments_count: -1, actions_count: 1
|
86
93
|
# # Executes the following SQL:
|
87
94
|
# # UPDATE posts
|
88
|
-
# # SET
|
89
|
-
# #
|
95
|
+
# # SET comments_count = COALESCE(comments_count, 0) - 1,
|
96
|
+
# # actions_count = COALESCE(actions_count, 0) + 1
|
90
97
|
# # WHERE id = 5
|
91
98
|
#
|
92
|
-
# # For the Posts with id of 10 and 15, increment the
|
93
|
-
# Post.update_counters [10, 15],
|
99
|
+
# # For the Posts with id of 10 and 15, increment the comments_count by 1
|
100
|
+
# Post.update_counters [10, 15], comments_count: 1
|
94
101
|
# # Executes the following SQL:
|
95
102
|
# # UPDATE posts
|
96
|
-
# # SET
|
103
|
+
# # SET comments_count = COALESCE(comments_count, 0) + 1
|
97
104
|
# # WHERE id IN (10, 15)
|
98
105
|
#
|
99
|
-
# # For the Posts with id of 10 and 15, increment the
|
106
|
+
# # For the Posts with id of 10 and 15, increment the comments_count by 1
|
100
107
|
# # and update the updated_at value for each counter.
|
101
|
-
# Post.update_counters [10, 15],
|
108
|
+
# Post.update_counters [10, 15], comments_count: 1, touch: true
|
102
109
|
# # Executes the following SQL:
|
103
110
|
# # UPDATE posts
|
104
|
-
# # SET
|
111
|
+
# # SET comments_count = COALESCE(comments_count, 0) + 1,
|
105
112
|
# # `updated_at` = '2016-10-13T09:59:23-05:00'
|
106
113
|
# # WHERE id IN (10, 15)
|
107
114
|
def update_counters(id, counters)
|
@@ -119,6 +126,7 @@ module ActiveRecord
|
|
119
126
|
#
|
120
127
|
# * +counter_name+ - The name of the field that should be incremented.
|
121
128
|
# * +id+ - The id of the object that should be incremented or an array of ids.
|
129
|
+
# * <tt>:by</tt> - The amount by which to increment the value. Defaults to +1+.
|
122
130
|
# * <tt>:touch</tt> - Touch timestamp columns when updating.
|
123
131
|
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
124
132
|
# touch that column or an array of symbols to touch just those ones.
|
@@ -129,10 +137,14 @@ module ActiveRecord
|
|
129
137
|
# DiscussionBoard.increment_counter(:posts_count, 5)
|
130
138
|
#
|
131
139
|
# # Increment the posts_count column for the record with an id of 5
|
140
|
+
# # by a specific amount.
|
141
|
+
# DiscussionBoard.increment_counter(:posts_count, 5, by: 3)
|
142
|
+
#
|
143
|
+
# # Increment the posts_count column for the record with an id of 5
|
132
144
|
# # and update the updated_at value.
|
133
145
|
# DiscussionBoard.increment_counter(:posts_count, 5, touch: true)
|
134
|
-
def increment_counter(counter_name, id, touch: nil)
|
135
|
-
update_counters(id, counter_name =>
|
146
|
+
def increment_counter(counter_name, id, by: 1, touch: nil)
|
147
|
+
update_counters(id, counter_name => by, touch: touch)
|
136
148
|
end
|
137
149
|
|
138
150
|
# Decrement a numeric field by one, via a direct SQL update.
|
@@ -144,6 +156,7 @@ module ActiveRecord
|
|
144
156
|
#
|
145
157
|
# * +counter_name+ - The name of the field that should be decremented.
|
146
158
|
# * +id+ - The id of the object that should be decremented or an array of ids.
|
159
|
+
# * <tt>:by</tt> - The amount by which to decrement the value. Defaults to +1+.
|
147
160
|
# * <tt>:touch</tt> - Touch timestamp columns when updating.
|
148
161
|
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
149
162
|
# touch that column or an array of symbols to touch just those ones.
|
@@ -154,10 +167,18 @@ module ActiveRecord
|
|
154
167
|
# DiscussionBoard.decrement_counter(:posts_count, 5)
|
155
168
|
#
|
156
169
|
# # Decrement the posts_count column for the record with an id of 5
|
170
|
+
# by a specific amount.
|
171
|
+
# DiscussionBoard.decrement_counter(:posts_count, 5, by: 3)
|
172
|
+
#
|
173
|
+
# # Decrement the posts_count column for the record with an id of 5
|
157
174
|
# # and update the updated_at value.
|
158
175
|
# DiscussionBoard.decrement_counter(:posts_count, 5, touch: true)
|
159
|
-
def decrement_counter(counter_name, id, touch: nil)
|
160
|
-
update_counters(id, counter_name => -
|
176
|
+
def decrement_counter(counter_name, id, by: 1, touch: nil)
|
177
|
+
update_counters(id, counter_name => -by, touch: touch)
|
178
|
+
end
|
179
|
+
|
180
|
+
def counter_cache_column?(name) # :nodoc:
|
181
|
+
_counter_cache_columns.include?(name)
|
161
182
|
end
|
162
183
|
end
|
163
184
|
|