activerecord 7.1.5.2 → 8.1.2.1
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 +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +250 -315
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "concurrent/atomic/atomic_boolean"
|
|
4
|
+
require "concurrent/atomic/read_write_lock"
|
|
5
|
+
|
|
3
6
|
module ActiveRecord
|
|
4
7
|
class AsynchronousQueriesTracker # :nodoc:
|
|
5
|
-
module NullSession # :nodoc:
|
|
6
|
-
class << self
|
|
7
|
-
def active?
|
|
8
|
-
true
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def finalize
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
8
|
class Session # :nodoc:
|
|
17
9
|
def initialize
|
|
18
|
-
@active = true
|
|
10
|
+
@active = Concurrent::AtomicBoolean.new(true)
|
|
11
|
+
@lock = Concurrent::ReadWriteLock.new
|
|
19
12
|
end
|
|
20
13
|
|
|
21
14
|
def active?
|
|
22
|
-
@active
|
|
15
|
+
@active.true?
|
|
23
16
|
end
|
|
24
17
|
|
|
25
|
-
def
|
|
26
|
-
@
|
|
18
|
+
def synchronize(&block)
|
|
19
|
+
@lock.with_read_lock(&block)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def finalize(wait = false)
|
|
23
|
+
@active.make_false
|
|
24
|
+
if wait
|
|
25
|
+
# Wait until all thread with a read lock are done
|
|
26
|
+
@lock.with_write_lock { }
|
|
27
|
+
end
|
|
27
28
|
end
|
|
28
29
|
end
|
|
29
30
|
|
|
@@ -33,7 +34,7 @@ module ActiveRecord
|
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def run
|
|
36
|
-
ActiveRecord::Base.asynchronous_queries_tracker.start_session
|
|
37
|
+
ActiveRecord::Base.asynchronous_queries_tracker.tap(&:start_session)
|
|
37
38
|
end
|
|
38
39
|
|
|
39
40
|
def complete(asynchronous_queries_tracker)
|
|
@@ -41,20 +42,23 @@ module ActiveRecord
|
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
|
|
44
|
-
attr_reader :current_session
|
|
45
|
-
|
|
46
45
|
def initialize
|
|
47
|
-
@
|
|
46
|
+
@stack = []
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def current_session
|
|
50
|
+
@stack.last or raise ActiveRecordError, "Can't perform asynchronous queries without a query session"
|
|
48
51
|
end
|
|
49
52
|
|
|
50
53
|
def start_session
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
session = Session.new
|
|
55
|
+
@stack << session
|
|
53
56
|
end
|
|
54
57
|
|
|
55
|
-
def finalize_session
|
|
56
|
-
@
|
|
57
|
-
|
|
58
|
+
def finalize_session(wait = false)
|
|
59
|
+
session = @stack.pop
|
|
60
|
+
session&.finalize(wait)
|
|
61
|
+
self
|
|
58
62
|
end
|
|
59
63
|
end
|
|
60
64
|
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module AttributeMethods
|
|
5
|
+
module CompositePrimaryKey # :nodoc:
|
|
6
|
+
# Returns the primary key column's value. If the primary key is composite,
|
|
7
|
+
# returns an array of the primary key column values.
|
|
8
|
+
def id
|
|
9
|
+
if self.class.composite_primary_key?
|
|
10
|
+
@primary_key.map { |pk| _read_attribute(pk) }
|
|
11
|
+
else
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def primary_key_values_present? # :nodoc:
|
|
17
|
+
if self.class.composite_primary_key?
|
|
18
|
+
id.all?
|
|
19
|
+
else
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Sets the primary key column's value. If the primary key is composite,
|
|
25
|
+
# raises TypeError when the set value not enumerable.
|
|
26
|
+
def id=(value)
|
|
27
|
+
if self.class.composite_primary_key?
|
|
28
|
+
raise TypeError, "Expected value matching #{self.class.primary_key.inspect}, got #{value.inspect}." unless value.is_a?(Enumerable)
|
|
29
|
+
@primary_key.zip(value) { |attr, value| _write_attribute(attr, value) }
|
|
30
|
+
else
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Queries the primary key column's value. If the primary key is composite,
|
|
36
|
+
# all primary key column values must be queryable.
|
|
37
|
+
def id?
|
|
38
|
+
if self.class.composite_primary_key?
|
|
39
|
+
@primary_key.all? { |col| _query_attribute(col) }
|
|
40
|
+
else
|
|
41
|
+
super
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns the primary key column's value before type cast. If the primary key is composite,
|
|
46
|
+
# returns an array of primary key column values before type cast.
|
|
47
|
+
def id_before_type_cast
|
|
48
|
+
if self.class.composite_primary_key?
|
|
49
|
+
@primary_key.map { |col| attribute_before_type_cast(col) }
|
|
50
|
+
else
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Returns the primary key column's previous value. If the primary key is composite,
|
|
56
|
+
# returns an array of primary key column previous values.
|
|
57
|
+
def id_was
|
|
58
|
+
if self.class.composite_primary_key?
|
|
59
|
+
@primary_key.map { |col| attribute_was(col) }
|
|
60
|
+
else
|
|
61
|
+
super
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns the primary key column's value from the database. If the primary key is composite,
|
|
66
|
+
# returns an array of primary key column values from database.
|
|
67
|
+
def id_in_database
|
|
68
|
+
if self.class.composite_primary_key?
|
|
69
|
+
@primary_key.map { |col| attribute_in_database(col) }
|
|
70
|
+
else
|
|
71
|
+
super
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def id_for_database # :nodoc:
|
|
76
|
+
if self.class.composite_primary_key?
|
|
77
|
+
@primary_key.map { |col| @attributes[col].value_for_database }
|
|
78
|
+
else
|
|
79
|
+
super
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "set"
|
|
4
|
-
|
|
5
3
|
module ActiveRecord
|
|
6
4
|
module AttributeMethods
|
|
7
5
|
# = Active Record Attribute Methods Primary Key
|
|
@@ -18,74 +16,45 @@ module ActiveRecord
|
|
|
18
16
|
# Returns the primary key column's value. If the primary key is composite,
|
|
19
17
|
# returns an array of the primary key column values.
|
|
20
18
|
def id
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@primary_key.map { |pk| _read_attribute(pk) }
|
|
19
|
+
_read_attribute(@primary_key)
|
|
24
20
|
end
|
|
25
21
|
|
|
26
22
|
def primary_key_values_present? # :nodoc:
|
|
27
|
-
return id.all? if self.class.composite_primary_key?
|
|
28
|
-
|
|
29
23
|
!!id
|
|
30
24
|
end
|
|
31
25
|
|
|
32
26
|
# Sets the primary key column's value. If the primary key is composite,
|
|
33
27
|
# raises TypeError when the set value not enumerable.
|
|
34
28
|
def id=(value)
|
|
35
|
-
|
|
36
|
-
raise TypeError, "Expected value matching #{self.class.primary_key.inspect}, got #{value.inspect}." unless value.is_a?(Enumerable)
|
|
37
|
-
@primary_key.zip(value) { |attr, value| _write_attribute(attr, value) }
|
|
38
|
-
else
|
|
39
|
-
_write_attribute(@primary_key, value)
|
|
40
|
-
end
|
|
29
|
+
_write_attribute(@primary_key, value)
|
|
41
30
|
end
|
|
42
31
|
|
|
43
32
|
# Queries the primary key column's value. If the primary key is composite,
|
|
44
33
|
# all primary key column values must be queryable.
|
|
45
34
|
def id?
|
|
46
|
-
|
|
47
|
-
@primary_key.all? { |col| _query_attribute(col) }
|
|
48
|
-
else
|
|
49
|
-
_query_attribute(@primary_key)
|
|
50
|
-
end
|
|
35
|
+
_query_attribute(@primary_key)
|
|
51
36
|
end
|
|
52
37
|
|
|
53
38
|
# Returns the primary key column's value before type cast. If the primary key is composite,
|
|
54
39
|
# returns an array of primary key column values before type cast.
|
|
55
40
|
def id_before_type_cast
|
|
56
|
-
|
|
57
|
-
@primary_key.map { |col| attribute_before_type_cast(col) }
|
|
58
|
-
else
|
|
59
|
-
attribute_before_type_cast(@primary_key)
|
|
60
|
-
end
|
|
41
|
+
attribute_before_type_cast(@primary_key)
|
|
61
42
|
end
|
|
62
43
|
|
|
63
44
|
# Returns the primary key column's previous value. If the primary key is composite,
|
|
64
45
|
# returns an array of primary key column previous values.
|
|
65
46
|
def id_was
|
|
66
|
-
|
|
67
|
-
@primary_key.map { |col| attribute_was(col) }
|
|
68
|
-
else
|
|
69
|
-
attribute_was(@primary_key)
|
|
70
|
-
end
|
|
47
|
+
attribute_was(@primary_key)
|
|
71
48
|
end
|
|
72
49
|
|
|
73
50
|
# Returns the primary key column's value from the database. If the primary key is composite,
|
|
74
51
|
# returns an array of primary key column values from database.
|
|
75
52
|
def id_in_database
|
|
76
|
-
|
|
77
|
-
@primary_key.map { |col| attribute_in_database(col) }
|
|
78
|
-
else
|
|
79
|
-
attribute_in_database(@primary_key)
|
|
80
|
-
end
|
|
53
|
+
attribute_in_database(@primary_key)
|
|
81
54
|
end
|
|
82
55
|
|
|
83
56
|
def id_for_database # :nodoc:
|
|
84
|
-
|
|
85
|
-
@primary_key.map { |col| @attributes[col].value_for_database }
|
|
86
|
-
else
|
|
87
|
-
@attributes[@primary_key].value_for_database
|
|
88
|
-
end
|
|
57
|
+
@attributes[@primary_key].value_for_database
|
|
89
58
|
end
|
|
90
59
|
|
|
91
60
|
private
|
|
@@ -109,20 +78,18 @@ module ActiveRecord
|
|
|
109
78
|
# Overwriting will negate any effect of the +primary_key_prefix_type+
|
|
110
79
|
# setting, though.
|
|
111
80
|
def primary_key
|
|
112
|
-
if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
|
|
113
|
-
@primary_key = reset_primary_key
|
|
114
|
-
end
|
|
81
|
+
reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
|
|
115
82
|
@primary_key
|
|
116
83
|
end
|
|
117
84
|
|
|
118
85
|
def composite_primary_key? # :nodoc:
|
|
119
|
-
|
|
86
|
+
reset_primary_key if PRIMARY_KEY_NOT_SET.equal?(@primary_key)
|
|
87
|
+
@composite_primary_key
|
|
120
88
|
end
|
|
121
89
|
|
|
122
|
-
# Returns a quoted version of the primary key name
|
|
123
|
-
# SQL statements.
|
|
90
|
+
# Returns a quoted version of the primary key name.
|
|
124
91
|
def quoted_primary_key
|
|
125
|
-
|
|
92
|
+
adapter_class.quote_column_name(primary_key)
|
|
126
93
|
end
|
|
127
94
|
|
|
128
95
|
def reset_primary_key # :nodoc:
|
|
@@ -138,12 +105,10 @@ module ActiveRecord
|
|
|
138
105
|
base_name.foreign_key(false)
|
|
139
106
|
elsif base_name && primary_key_prefix_type == :table_name_with_underscore
|
|
140
107
|
base_name.foreign_key
|
|
108
|
+
elsif ActiveRecord::Base != self && table_exists?
|
|
109
|
+
schema_cache.primary_keys(table_name)
|
|
141
110
|
else
|
|
142
|
-
|
|
143
|
-
connection.schema_cache.primary_keys(table_name)
|
|
144
|
-
else
|
|
145
|
-
"id"
|
|
146
|
-
end
|
|
111
|
+
"id"
|
|
147
112
|
end
|
|
148
113
|
end
|
|
149
114
|
|
|
@@ -163,25 +128,24 @@ module ActiveRecord
|
|
|
163
128
|
#
|
|
164
129
|
# Project.primary_key # => "foo_id"
|
|
165
130
|
def primary_key=(value)
|
|
166
|
-
@primary_key
|
|
167
|
-
|
|
131
|
+
@primary_key = if value.is_a?(Array)
|
|
132
|
+
include CompositePrimaryKey
|
|
133
|
+
@primary_key = value.map { |v| -v.to_s }.freeze
|
|
134
|
+
elsif value
|
|
135
|
+
-value.to_s
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
@composite_primary_key = value.is_a?(Array)
|
|
168
139
|
@attributes_builder = nil
|
|
169
140
|
end
|
|
170
141
|
|
|
171
142
|
private
|
|
172
|
-
def derive_primary_key(value)
|
|
173
|
-
return unless value
|
|
174
|
-
|
|
175
|
-
return -value.to_s unless value.is_a?(Array)
|
|
176
|
-
|
|
177
|
-
value.map { |v| -v.to_s }.freeze
|
|
178
|
-
end
|
|
179
|
-
|
|
180
143
|
def inherited(base)
|
|
181
144
|
super
|
|
182
145
|
base.class_eval do
|
|
183
146
|
@primary_key = PRIMARY_KEY_NOT_SET
|
|
184
|
-
@
|
|
147
|
+
@composite_primary_key = false
|
|
148
|
+
@attributes_builder = nil
|
|
185
149
|
end
|
|
186
150
|
end
|
|
187
151
|
end
|
|
@@ -3,6 +3,38 @@
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
module AttributeMethods
|
|
5
5
|
# = Active Record Attribute Methods \Query
|
|
6
|
+
#
|
|
7
|
+
# Adds query methods for attributes that return either +true+ or +false+
|
|
8
|
+
# depending on the attribute type and value.
|
|
9
|
+
#
|
|
10
|
+
# For Boolean attributes this will return +true+ if the value is present
|
|
11
|
+
# and return +false+ otherwise:
|
|
12
|
+
#
|
|
13
|
+
# class Product < ActiveRecord::Base
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# product = Product.new(archived: false)
|
|
17
|
+
# product.archived? # => false
|
|
18
|
+
# product.archived = true
|
|
19
|
+
# product.archived? # => true
|
|
20
|
+
#
|
|
21
|
+
# For Numeric attributes this will return +true+ if the value is a non-zero
|
|
22
|
+
# number and return +false+ otherwise:
|
|
23
|
+
#
|
|
24
|
+
# product.inventory_count = 0
|
|
25
|
+
# product.inventory_count? # => false
|
|
26
|
+
# product.inventory_count = 1
|
|
27
|
+
# product.inventory_count? # => true
|
|
28
|
+
#
|
|
29
|
+
# For other attributes it will return +true+ if the value is present
|
|
30
|
+
# and return +false+ otherwise:
|
|
31
|
+
#
|
|
32
|
+
# product.name = nil
|
|
33
|
+
# product.name? # => false
|
|
34
|
+
# product.name = " "
|
|
35
|
+
# product.name? # => false
|
|
36
|
+
# product.name = "Orange"
|
|
37
|
+
# product.name? # => true
|
|
6
38
|
module Query
|
|
7
39
|
extend ActiveSupport::Concern
|
|
8
40
|
|
|
@@ -10,6 +42,8 @@ module ActiveRecord
|
|
|
10
42
|
attribute_method_suffix "?", parameters: false
|
|
11
43
|
end
|
|
12
44
|
|
|
45
|
+
# Returns +true+ or +false+ for the attribute identified by +attr_name+,
|
|
46
|
+
# depending on the attribute type and value.
|
|
13
47
|
def query_attribute(attr_name)
|
|
14
48
|
value = self.public_send(attr_name)
|
|
15
49
|
|
|
@@ -30,19 +30,7 @@ module ActiveRecord
|
|
|
30
30
|
name = attr_name.to_s
|
|
31
31
|
name = self.class.attribute_aliases[name] || name
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if self.class.composite_primary_key?
|
|
36
|
-
@attributes.fetch_value("id", &block)
|
|
37
|
-
else
|
|
38
|
-
if @primary_key != "id"
|
|
39
|
-
ActiveRecord.deprecator.warn(<<-MSG.squish)
|
|
40
|
-
Using read_attribute(:id) to read the primary key value is deprecated.
|
|
41
|
-
Use #id instead.
|
|
42
|
-
MSG
|
|
43
|
-
end
|
|
44
|
-
@attributes.fetch_value(@primary_key, &block)
|
|
45
|
-
end
|
|
33
|
+
@attributes.fetch_value(name, &block)
|
|
46
34
|
end
|
|
47
35
|
|
|
48
36
|
# This method exists to avoid the expensive primary_key check internally, without
|
|
@@ -51,6 +51,16 @@ module ActiveRecord
|
|
|
51
51
|
# ActiveRecord::SerializationTypeMismatch error.
|
|
52
52
|
# * If the column is +NULL+ or starting from a new record, the default value
|
|
53
53
|
# will set to +type.new+
|
|
54
|
+
# * +comparable+ - Specify whether the deserialized object is safely comparable
|
|
55
|
+
# for the purpose of detecting changes. Defaults to +false+
|
|
56
|
+
# When set to +false+ the old and new values will be compared by their serialized
|
|
57
|
+
# representation (e.g. JSON or YAML), which can sometimes cause two objects that are
|
|
58
|
+
# semantically equal to be considered different.
|
|
59
|
+
# For instance two hashes with the same keys and values but a different order have a
|
|
60
|
+
# different serialized representation, but are semantically equal once deserialized.
|
|
61
|
+
# If set to +true+ the comparison will be done on the deserialized object.
|
|
62
|
+
# This options should only be enabled if the +type+ is known to have
|
|
63
|
+
# a proper <tt>==</tt> method that deeply compare the objects.
|
|
54
64
|
# * +yaml+ - Optional. Yaml specific options. The allowed config is:
|
|
55
65
|
# * +:permitted_classes+ - +Array+ with the permitted classes.
|
|
56
66
|
# * +:unsafe_load+ - Unsafely load YAML blobs, allow YAML to load any class.
|
|
@@ -130,7 +140,7 @@ module ActiveRecord
|
|
|
130
140
|
# silently cast unsupported types to +String+:
|
|
131
141
|
#
|
|
132
142
|
# >> JSON.parse(JSON.dump(Struct.new(:foo)))
|
|
133
|
-
# => "#<Class:0x000000013090b4c0>"
|
|
143
|
+
# # => "#<Class:0x000000013090b4c0>"
|
|
134
144
|
#
|
|
135
145
|
# ==== Examples
|
|
136
146
|
#
|
|
@@ -180,29 +190,7 @@ module ActiveRecord
|
|
|
180
190
|
# serialize :preferences, coder: Rot13JSON
|
|
181
191
|
# end
|
|
182
192
|
#
|
|
183
|
-
def serialize(attr_name,
|
|
184
|
-
unless class_name_or_coder.nil?
|
|
185
|
-
if class_name_or_coder == ::JSON || [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
|
|
186
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
|
187
|
-
Passing the coder as positional argument is deprecated and will be removed in Rails 7.2.
|
|
188
|
-
|
|
189
|
-
Please pass the coder as a keyword argument:
|
|
190
|
-
|
|
191
|
-
serialize #{attr_name.inspect}, coder: #{class_name_or_coder}
|
|
192
|
-
MSG
|
|
193
|
-
coder = class_name_or_coder
|
|
194
|
-
else
|
|
195
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
|
196
|
-
Passing the class as positional argument is deprecated and will be removed in Rails 7.2.
|
|
197
|
-
|
|
198
|
-
Please pass the class as a keyword argument:
|
|
199
|
-
|
|
200
|
-
serialize #{attr_name.inspect}, type: #{class_name_or_coder.name}
|
|
201
|
-
MSG
|
|
202
|
-
type = class_name_or_coder
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
|
193
|
+
def serialize(attr_name, coder: nil, type: Object, comparable: false, yaml: {}, **options)
|
|
206
194
|
coder ||= default_column_serializer
|
|
207
195
|
unless coder
|
|
208
196
|
raise ArgumentError, <<~MSG.squish
|
|
@@ -214,13 +202,15 @@ module ActiveRecord
|
|
|
214
202
|
|
|
215
203
|
column_serializer = build_column_serializer(attr_name, coder, type, yaml)
|
|
216
204
|
|
|
217
|
-
attribute(attr_name, **options)
|
|
205
|
+
attribute(attr_name, **options)
|
|
206
|
+
|
|
207
|
+
decorate_attributes([attr_name]) do |attr_name, cast_type|
|
|
218
208
|
if type_incompatible_with_serialize?(cast_type, coder, type)
|
|
219
209
|
raise ColumnNotSerializableError.new(attr_name, cast_type)
|
|
220
210
|
end
|
|
221
211
|
|
|
222
212
|
cast_type = cast_type.subtype if Type::Serialized === cast_type
|
|
223
|
-
Type::Serialized.new(cast_type, column_serializer)
|
|
213
|
+
Type::Serialized.new(cast_type, column_serializer, comparable: comparable)
|
|
224
214
|
end
|
|
225
215
|
end
|
|
226
216
|
|
|
@@ -229,7 +219,10 @@ module ActiveRecord
|
|
|
229
219
|
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
|
230
220
|
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
|
231
221
|
# using the #as_json hook.
|
|
232
|
-
|
|
222
|
+
|
|
223
|
+
if coder == ::JSON || coder == Coders::JSON
|
|
224
|
+
coder = Coders::JSON.new
|
|
225
|
+
end
|
|
233
226
|
|
|
234
227
|
if coder == ::YAML || coder == Coders::YAMLColumn
|
|
235
228
|
Coders::YAMLColumn.new(attr_name, type, **(yaml || {}))
|
|
@@ -21,14 +21,18 @@ module ActiveRecord
|
|
|
21
21
|
set_time_zone_without_conversion(super)
|
|
22
22
|
elsif value.respond_to?(:in_time_zone)
|
|
23
23
|
begin
|
|
24
|
-
super(user_input_in_time_zone(value)) || super
|
|
24
|
+
result = super(user_input_in_time_zone(value)) || super
|
|
25
|
+
if result && type == :time
|
|
26
|
+
result = result.change(year: 2000, month: 1, day: 1)
|
|
27
|
+
end
|
|
28
|
+
result
|
|
25
29
|
rescue ArgumentError
|
|
26
30
|
nil
|
|
27
31
|
end
|
|
28
32
|
elsif value.respond_to?(:infinite?) && value.infinite?
|
|
29
33
|
value
|
|
30
34
|
else
|
|
31
|
-
|
|
35
|
+
map(super) { |v| cast(v) }
|
|
32
36
|
end
|
|
33
37
|
end
|
|
34
38
|
|
|
@@ -41,27 +45,21 @@ module ActiveRecord
|
|
|
41
45
|
return if value.nil?
|
|
42
46
|
|
|
43
47
|
if value.acts_like?(:time)
|
|
44
|
-
value.in_time_zone
|
|
48
|
+
converted = value.in_time_zone
|
|
49
|
+
if type == :time && converted
|
|
50
|
+
converted = converted.change(year: 2000, month: 1, day: 1)
|
|
51
|
+
end
|
|
52
|
+
converted
|
|
45
53
|
elsif value.respond_to?(:infinite?) && value.infinite?
|
|
46
54
|
value
|
|
47
55
|
else
|
|
48
|
-
|
|
56
|
+
map(value) { |v| convert_time_to_time_zone(v) }
|
|
49
57
|
end
|
|
50
58
|
end
|
|
51
59
|
|
|
52
60
|
def set_time_zone_without_conversion(value)
|
|
53
61
|
::Time.zone.local_to_utc(value).try(:in_time_zone) if value
|
|
54
62
|
end
|
|
55
|
-
|
|
56
|
-
def map_avoiding_infinite_recursion(value)
|
|
57
|
-
map(value) do |v|
|
|
58
|
-
if value.equal?(v)
|
|
59
|
-
nil
|
|
60
|
-
else
|
|
61
|
-
yield(v)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
63
|
end
|
|
66
64
|
|
|
67
65
|
extend ActiveSupport::Concern
|
|
@@ -73,14 +71,15 @@ module ActiveRecord
|
|
|
73
71
|
end
|
|
74
72
|
|
|
75
73
|
module ClassMethods # :nodoc:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
private
|
|
75
|
+
def hook_attribute_type(name, cast_type)
|
|
76
|
+
if create_time_zone_conversion_attribute?(name, cast_type)
|
|
77
|
+
cast_type = TimeZoneConverter.new(cast_type)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
super
|
|
79
81
|
end
|
|
80
|
-
super
|
|
81
|
-
end
|
|
82
82
|
|
|
83
|
-
private
|
|
84
83
|
def create_time_zone_conversion_attribute?(name, cast_type)
|
|
85
84
|
enabled_for_column = time_zone_aware_attributes &&
|
|
86
85
|
!skip_time_zone_conversion_for_attributes.include?(name.to_sym)
|