activerecord 6.1.7.6 → 7.0.0
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 +1055 -1180
- 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 +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- 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 +10 -3
- 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 +18 -19
- 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/join_dependency.rb +6 -2
- data/lib/active_record/associations/preloader/association.rb +186 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +90 -82
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -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 +49 -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 +13 -14
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +6 -21
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +2 -14
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -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 +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +43 -82
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -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 +50 -76
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +27 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -107
- data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
- 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 +6 -5
- data/lib/active_record/connection_handling.rb +47 -53
- data/lib/active_record/core.rb +121 -146
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +15 -32
- data/lib/active_record/delegated_type.rb +52 -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 +28 -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 +90 -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 +49 -42
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +17 -20
- 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 +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +9 -3
- data/lib/active_record/log_subscriber.rb +14 -3
- 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/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +89 -10
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +110 -80
- data/lib/active_record/model_schema.rb +45 -58
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +219 -52
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +127 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +66 -129
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +67 -50
- 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 +40 -36
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -35
- 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 +235 -63
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +169 -84
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +10 -3
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/store.rb +1 -6
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +116 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +9 -13
- 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/uniqueness.rb +1 -1
- data/lib/active_record.rb +204 -28
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -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 +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -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 +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -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
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +58 -14
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module"
|
4
|
+
require "active_support/core_ext/array"
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module Encryption
|
8
|
+
extend ActiveSupport::Autoload
|
9
|
+
|
10
|
+
eager_autoload do
|
11
|
+
autoload :Cipher
|
12
|
+
autoload :Config
|
13
|
+
autoload :Configurable
|
14
|
+
autoload :Context
|
15
|
+
autoload :Contexts
|
16
|
+
autoload :DerivedSecretKeyProvider
|
17
|
+
autoload :EncryptableRecord
|
18
|
+
autoload :EncryptedAttributeType
|
19
|
+
autoload :EncryptedFixtures
|
20
|
+
autoload :EncryptingOnlyEncryptor
|
21
|
+
autoload :DeterministicKeyProvider
|
22
|
+
autoload :Encryptor
|
23
|
+
autoload :EnvelopeEncryptionKeyProvider
|
24
|
+
autoload :Errors
|
25
|
+
autoload :ExtendedDeterministicQueries
|
26
|
+
autoload :ExtendedDeterministicUniquenessValidator
|
27
|
+
autoload :Key
|
28
|
+
autoload :KeyGenerator
|
29
|
+
autoload :KeyProvider
|
30
|
+
autoload :Message
|
31
|
+
autoload :MessageSerializer
|
32
|
+
autoload :NullEncryptor
|
33
|
+
autoload :Properties
|
34
|
+
autoload :ReadOnlyNullEncryptor
|
35
|
+
autoload :Scheme
|
36
|
+
end
|
37
|
+
|
38
|
+
class Cipher
|
39
|
+
extend ActiveSupport::Autoload
|
40
|
+
|
41
|
+
eager_autoload do
|
42
|
+
autoload :Aes256Gcm
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
include Configurable
|
47
|
+
include Contexts
|
48
|
+
|
49
|
+
def self.eager_load!
|
50
|
+
super
|
51
|
+
|
52
|
+
Cipher.eager_load!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/active_record/enum.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/hash/slice"
|
3
4
|
require "active_support/core_ext/object/deep_dup"
|
4
5
|
|
5
6
|
module ActiveRecord
|
@@ -7,7 +8,7 @@ module ActiveRecord
|
|
7
8
|
# but can be queried by name. Example:
|
8
9
|
#
|
9
10
|
# class Conversation < ActiveRecord::Base
|
10
|
-
# enum status
|
11
|
+
# enum :status, [ :active, :archived ]
|
11
12
|
# end
|
12
13
|
#
|
13
14
|
# # conversation.update! status: 0
|
@@ -41,26 +42,33 @@ module ActiveRecord
|
|
41
42
|
# Conversation.where(status: [:active, :archived])
|
42
43
|
# Conversation.where.not(status: :active)
|
43
44
|
#
|
44
|
-
# Defining scopes can be disabled by setting +:
|
45
|
+
# Defining scopes can be disabled by setting +:scopes+ to +false+.
|
45
46
|
#
|
46
47
|
# class Conversation < ActiveRecord::Base
|
47
|
-
# enum status
|
48
|
+
# enum :status, [ :active, :archived ], scopes: false
|
48
49
|
# end
|
49
50
|
#
|
50
|
-
# You can set the default enum value by setting +:
|
51
|
+
# You can set the default enum value by setting +:default+, like:
|
51
52
|
#
|
52
53
|
# class Conversation < ActiveRecord::Base
|
53
|
-
# enum status
|
54
|
+
# enum :status, [ :active, :archived ], default: :active
|
54
55
|
# end
|
55
56
|
#
|
56
57
|
# conversation = Conversation.new
|
57
58
|
# conversation.status # => "active"
|
58
59
|
#
|
59
|
-
#
|
60
|
+
# It's possible to explicitly map the relation between attribute and
|
60
61
|
# database integer with a hash:
|
61
62
|
#
|
62
63
|
# class Conversation < ActiveRecord::Base
|
63
|
-
# enum status
|
64
|
+
# enum :status, active: 0, archived: 1
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# Finally it's also possible to use a string column to persist the enumerated value.
|
68
|
+
# Note that this will likely lead to slower database queries:
|
69
|
+
#
|
70
|
+
# class Conversation < ActiveRecord::Base
|
71
|
+
# enum :status, active: "active", archived: "archived"
|
64
72
|
# end
|
65
73
|
#
|
66
74
|
# Note that when an array is used, the implicit mapping from the values to database
|
@@ -85,14 +93,14 @@ module ActiveRecord
|
|
85
93
|
#
|
86
94
|
# Conversation.where("status <> ?", Conversation.statuses[:archived])
|
87
95
|
#
|
88
|
-
# You can use the +:
|
96
|
+
# You can use the +:prefix+ or +:suffix+ options when you need to define
|
89
97
|
# multiple enums with same values. If the passed value is +true+, the methods
|
90
98
|
# are prefixed/suffixed with the name of the enum. It is also possible to
|
91
99
|
# supply a custom value:
|
92
100
|
#
|
93
101
|
# class Conversation < ActiveRecord::Base
|
94
|
-
# enum status
|
95
|
-
# enum comments_status
|
102
|
+
# enum :status, [ :active, :archived ], suffix: true
|
103
|
+
# enum :comments_status, [ :active, :inactive ], prefix: :comments
|
96
104
|
# end
|
97
105
|
#
|
98
106
|
# With the above example, the bang and predicate methods along with the
|
@@ -103,7 +111,6 @@ module ActiveRecord
|
|
103
111
|
#
|
104
112
|
# conversation.comments_inactive!
|
105
113
|
# conversation.comments_active? # => false
|
106
|
-
|
107
114
|
module Enum
|
108
115
|
def self.extended(base) # :nodoc:
|
109
116
|
base.class_attribute(:defined_enums, instance_writer: false, default: {})
|
@@ -128,10 +135,8 @@ module ActiveRecord
|
|
128
135
|
value.to_s
|
129
136
|
elsif mapping.has_value?(value)
|
130
137
|
mapping.key(value)
|
131
|
-
elsif value.blank?
|
132
|
-
nil
|
133
138
|
else
|
134
|
-
|
139
|
+
value.presence
|
135
140
|
end
|
136
141
|
end
|
137
142
|
|
@@ -140,7 +145,11 @@ module ActiveRecord
|
|
140
145
|
end
|
141
146
|
|
142
147
|
def serialize(value)
|
143
|
-
mapping.fetch(value, value)
|
148
|
+
subtype.serialize(mapping.fetch(value, value))
|
149
|
+
end
|
150
|
+
|
151
|
+
def serializable?(value, &block)
|
152
|
+
subtype.serializable?(mapping.fetch(value, value), &block)
|
144
153
|
end
|
145
154
|
|
146
155
|
def assert_valid_value(value)
|
@@ -155,15 +164,20 @@ module ActiveRecord
|
|
155
164
|
attr_reader :name, :mapping
|
156
165
|
end
|
157
166
|
|
158
|
-
def enum(
|
159
|
-
|
160
|
-
|
161
|
-
|
167
|
+
def enum(name = nil, values = nil, **options)
|
168
|
+
if name
|
169
|
+
values, options = options, {} unless values
|
170
|
+
return _enum(name, values, **options)
|
171
|
+
end
|
172
|
+
|
173
|
+
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default)
|
174
|
+
options.transform_keys! { |key| :"#{key[1..-1]}" }
|
162
175
|
|
163
|
-
|
164
|
-
|
176
|
+
definitions.each { |name, values| _enum(name, values, **options) }
|
177
|
+
end
|
165
178
|
|
166
|
-
|
179
|
+
private
|
180
|
+
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, **options)
|
167
181
|
assert_valid_enum_definition_values(values)
|
168
182
|
# statuses = { }
|
169
183
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
@@ -177,24 +191,19 @@ module ActiveRecord
|
|
177
191
|
detect_enum_conflict!(name, name)
|
178
192
|
detect_enum_conflict!(name, "#{name}=")
|
179
193
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
EnumType.new(attr, enum_values, subtype)
|
194
|
+
attribute(name, **options) do |subtype|
|
195
|
+
subtype = subtype.subtype if EnumType === subtype
|
196
|
+
EnumType.new(name, enum_values, subtype)
|
184
197
|
end
|
185
198
|
|
186
199
|
value_method_names = []
|
187
200
|
_enum_methods_module.module_eval do
|
188
|
-
prefix = if
|
189
|
-
"#{name}_"
|
190
|
-
elsif enum_prefix
|
191
|
-
"#{enum_prefix}_"
|
201
|
+
prefix = if prefix
|
202
|
+
prefix == true ? "#{name}_" : "#{prefix}_"
|
192
203
|
end
|
193
204
|
|
194
|
-
suffix = if
|
195
|
-
"_#{name}"
|
196
|
-
elsif enum_suffix
|
197
|
-
"_#{enum_suffix}"
|
205
|
+
suffix = if suffix
|
206
|
+
suffix == true ? "_#{name}" : "_#{suffix}"
|
198
207
|
end
|
199
208
|
|
200
209
|
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
|
@@ -204,23 +213,21 @@ module ActiveRecord
|
|
204
213
|
|
205
214
|
value_method_name = "#{prefix}#{label}#{suffix}"
|
206
215
|
value_method_names << value_method_name
|
207
|
-
define_enum_methods(name, value_method_name, value,
|
216
|
+
define_enum_methods(name, value_method_name, value, scopes)
|
208
217
|
|
209
218
|
method_friendly_label = label.gsub(/[\W&&[:ascii:]]+/, "_")
|
210
219
|
value_method_alias = "#{prefix}#{method_friendly_label}#{suffix}"
|
211
220
|
|
212
221
|
if value_method_alias != value_method_name && !value_method_names.include?(value_method_alias)
|
213
222
|
value_method_names << value_method_alias
|
214
|
-
define_enum_methods(name, value_method_alias, value,
|
223
|
+
define_enum_methods(name, value_method_alias, value, scopes)
|
215
224
|
end
|
216
225
|
end
|
217
226
|
end
|
218
|
-
detect_negative_enum_conditions!(value_method_names) if
|
227
|
+
detect_negative_enum_conditions!(value_method_names) if scopes
|
219
228
|
enum_values.freeze
|
220
229
|
end
|
221
|
-
end
|
222
230
|
|
223
|
-
private
|
224
231
|
class EnumMethods < Module # :nodoc:
|
225
232
|
def initialize(klass)
|
226
233
|
@klass = klass
|
@@ -229,7 +236,7 @@ module ActiveRecord
|
|
229
236
|
private
|
230
237
|
attr_reader :klass
|
231
238
|
|
232
|
-
def define_enum_methods(name, value_method_name, value,
|
239
|
+
def define_enum_methods(name, value_method_name, value, scopes)
|
233
240
|
# def active?() status_for_database == 0 end
|
234
241
|
klass.send(:detect_enum_conflict!, name, "#{value_method_name}?")
|
235
242
|
define_method("#{value_method_name}?") { public_send(:"#{name}_for_database") == value }
|
@@ -240,7 +247,7 @@ module ActiveRecord
|
|
240
247
|
|
241
248
|
# scope :active, -> { where(status: 0) }
|
242
249
|
# scope :not_active, -> { where.not(status: 0) }
|
243
|
-
if
|
250
|
+
if scopes
|
244
251
|
klass.send(:detect_enum_conflict!, name, value_method_name, true)
|
245
252
|
klass.scope value_method_name, -> { where(name => value) }
|
246
253
|
|
@@ -260,7 +267,7 @@ module ActiveRecord
|
|
260
267
|
end
|
261
268
|
|
262
269
|
def assert_valid_enum_definition_values(values)
|
263
|
-
unless values.is_a?(Hash) || values.all?
|
270
|
+
unless values.is_a?(Hash) || values.all?(Symbol) || values.all?(String)
|
264
271
|
error_message = <<~MSG
|
265
272
|
Enum values #{values} must be either a hash, an array of symbols, or an array of strings.
|
266
273
|
MSG
|
data/lib/active_record/errors.rb
CHANGED
@@ -63,6 +63,30 @@ module ActiveRecord
|
|
63
63
|
class ConnectionTimeoutError < ConnectionNotEstablished
|
64
64
|
end
|
65
65
|
|
66
|
+
# Raised when connection to the database could not been established because it was not
|
67
|
+
# able to connect to the host or when the authorization failed.
|
68
|
+
class DatabaseConnectionError < ConnectionNotEstablished
|
69
|
+
def initialize(message = nil)
|
70
|
+
super(message || "Database connection error")
|
71
|
+
end
|
72
|
+
|
73
|
+
class << self
|
74
|
+
def hostname_error(hostname)
|
75
|
+
DatabaseConnectionError.new(<<~MSG)
|
76
|
+
There is an issue connecting with your hostname: #{hostname}.\n
|
77
|
+
Please check your database configuration and ensure there is a valid connection to your database.
|
78
|
+
MSG
|
79
|
+
end
|
80
|
+
|
81
|
+
def username_error(username)
|
82
|
+
DatabaseConnectionError.new(<<~MSG)
|
83
|
+
There is an issue connecting to your database with your username/password, username: #{username}.\n
|
84
|
+
Please check your database configuration to ensure the username/password are valid.
|
85
|
+
MSG
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
66
90
|
# Raised when a pool was unable to get ahold of all its connections
|
67
91
|
# to perform a "group" action such as
|
68
92
|
# {ActiveRecord::Base.connection_pool.disconnect!}[rdoc-ref:ConnectionAdapters::ConnectionPool#disconnect!]
|
@@ -100,7 +124,7 @@ module ActiveRecord
|
|
100
124
|
end
|
101
125
|
|
102
126
|
# Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
|
103
|
-
# when a call to {#destroy}[rdoc-ref:Persistence#destroy
|
127
|
+
# when a call to {#destroy}[rdoc-ref:Persistence#destroy]
|
104
128
|
# would return false.
|
105
129
|
#
|
106
130
|
# begin
|
@@ -118,6 +142,16 @@ module ActiveRecord
|
|
118
142
|
end
|
119
143
|
end
|
120
144
|
|
145
|
+
# Raised when Active Record finds multiple records but only expected one.
|
146
|
+
class SoleRecordExceeded < ActiveRecordError
|
147
|
+
attr_reader :record
|
148
|
+
|
149
|
+
def initialize(record = nil)
|
150
|
+
@record = record
|
151
|
+
super "Wanted only one #{record&.name || "record"}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
121
155
|
# Superclass for all database execution errors.
|
122
156
|
#
|
123
157
|
# Wraps the underlying database error as +cause+.
|
@@ -202,6 +236,30 @@ module ActiveRecord
|
|
202
236
|
|
203
237
|
# Raised when a given database does not exist.
|
204
238
|
class NoDatabaseError < StatementInvalid
|
239
|
+
include ActiveSupport::ActionableError
|
240
|
+
|
241
|
+
action "Create database" do
|
242
|
+
ActiveRecord::Tasks::DatabaseTasks.create_current
|
243
|
+
end
|
244
|
+
|
245
|
+
def initialize(message = nil)
|
246
|
+
super(message || "Database not found")
|
247
|
+
end
|
248
|
+
|
249
|
+
class << self
|
250
|
+
def db_error(db_name)
|
251
|
+
NoDatabaseError.new(<<~MSG)
|
252
|
+
We could not find your database: #{db_name}. Which can be found in the database configuration file located at config/database.yml.
|
253
|
+
|
254
|
+
To resolve this issue:
|
255
|
+
|
256
|
+
- Did you create the database for this app, or delete it? You may need to create your database.
|
257
|
+
- Has the database name changed? Check your database.yml config has the correct database name.
|
258
|
+
|
259
|
+
To create your database, run:\n\n bin/rails db:create
|
260
|
+
MSG
|
261
|
+
end
|
262
|
+
end
|
205
263
|
end
|
206
264
|
|
207
265
|
# Raised when creating a database if it exists.
|
@@ -268,7 +326,7 @@ module ActiveRecord
|
|
268
326
|
# # The system must fail on Friday so that our support department
|
269
327
|
# # won't be out of job. We silently rollback this transaction
|
270
328
|
# # without telling the user.
|
271
|
-
# raise ActiveRecord::Rollback
|
329
|
+
# raise ActiveRecord::Rollback
|
272
330
|
# end
|
273
331
|
# end
|
274
332
|
# # ActiveRecord::Rollback is the only exception that won't be passed on
|
@@ -363,6 +421,11 @@ module ActiveRecord
|
|
363
421
|
class TransactionRollbackError < StatementInvalid
|
364
422
|
end
|
365
423
|
|
424
|
+
# AsynchronousQueryInsideTransactionError will be raised when attempting
|
425
|
+
# to perform an asynchronous query from inside a transaction
|
426
|
+
class AsynchronousQueryInsideTransactionError < ActiveRecordError
|
427
|
+
end
|
428
|
+
|
366
429
|
# SerializationFailure will be raised when a transaction is rolled
|
367
430
|
# back by the database due to a serialization failure.
|
368
431
|
class SerializationFailure < TransactionRollbackError
|
@@ -409,12 +472,12 @@ module ActiveRecord
|
|
409
472
|
#
|
410
473
|
# For example, the following code would raise this exception:
|
411
474
|
#
|
412
|
-
# Post.order("
|
475
|
+
# Post.order("REPLACE(title, 'misc', 'zzzz') asc").pluck(:id)
|
413
476
|
#
|
414
477
|
# The desired result can be accomplished by wrapping the known-safe string
|
415
478
|
# in Arel.sql:
|
416
479
|
#
|
417
|
-
# Post.order(Arel.sql("
|
480
|
+
# Post.order(Arel.sql("REPLACE(title, 'misc', 'zzzz') asc")).pluck(:id)
|
418
481
|
#
|
419
482
|
# Again, such a workaround should *not* be used when passing user-provided
|
420
483
|
# values, such as request parameters or model attributes to query methods.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support/
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
# This is a thread locals registry for EXPLAIN. For example
|
@@ -8,13 +8,18 @@ module ActiveRecord
|
|
8
8
|
# ActiveRecord::ExplainRegistry.queries
|
9
9
|
#
|
10
10
|
# returns the collected queries local to the current thread.
|
11
|
-
#
|
12
|
-
# See the documentation of ActiveSupport::PerThreadRegistry
|
13
|
-
# for further details.
|
14
11
|
class ExplainRegistry # :nodoc:
|
15
|
-
|
12
|
+
class << self
|
13
|
+
delegate :reset, :collect, :collect=, :collect?, :queries, to: :instance
|
14
|
+
|
15
|
+
private
|
16
|
+
def instance
|
17
|
+
ActiveSupport::IsolatedExecutionState[:active_record_explain_registry] ||= new
|
18
|
+
end
|
19
|
+
end
|
16
20
|
|
17
|
-
attr_accessor :
|
21
|
+
attr_accessor :collect
|
22
|
+
attr_reader :queries
|
18
23
|
|
19
24
|
def initialize
|
20
25
|
reset
|
@@ -41,7 +41,7 @@ module ActiveRecord
|
|
41
41
|
@config_row ||= begin
|
42
42
|
row = raw_rows.find { |fixture_name, _| fixture_name == "_fixture" }
|
43
43
|
if row
|
44
|
-
row.last
|
44
|
+
validate_config_row(row.last)
|
45
45
|
else
|
46
46
|
{ 'model_class': nil, 'ignore': nil }
|
47
47
|
end
|
@@ -58,6 +58,20 @@ module ActiveRecord
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
def validate_config_row(data)
|
62
|
+
unless Hash === data
|
63
|
+
raise Fixture::FormatError, "Invalid `_fixture` section: `_fixture` must be a hash: #{@file}"
|
64
|
+
end
|
65
|
+
|
66
|
+
begin
|
67
|
+
data.assert_valid_keys("model_class", "ignore")
|
68
|
+
rescue ArgumentError => error
|
69
|
+
raise Fixture::FormatError, "Invalid `_fixture` section: #{error.message}: #{@file}"
|
70
|
+
end
|
71
|
+
|
72
|
+
data
|
73
|
+
end
|
74
|
+
|
61
75
|
# Validate our unmarshalled data.
|
62
76
|
def validate(data)
|
63
77
|
unless Hash === data || YAML::Omap === data
|
@@ -33,6 +33,33 @@ module ActiveRecord
|
|
33
33
|
def join_table
|
34
34
|
@association.through_reflection.table_name
|
35
35
|
end
|
36
|
+
|
37
|
+
def timestamp_column_names
|
38
|
+
@association.through_reflection.klass.all_timestamp_attributes_in_model
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class PrimaryKeyError < StandardError # :nodoc:
|
43
|
+
def initialize(label, association, value)
|
44
|
+
super(<<~MSG)
|
45
|
+
Unable to set #{association.name} to #{value} because the association has a
|
46
|
+
custom primary key (#{association.join_primary_key}) that does not match the
|
47
|
+
associated table's primary key (#{association.klass.primary_key}).
|
48
|
+
|
49
|
+
To fix this, change your fixture from
|
50
|
+
|
51
|
+
#{label}:
|
52
|
+
#{association.name}: #{value}
|
53
|
+
|
54
|
+
to
|
55
|
+
|
56
|
+
#{label}:
|
57
|
+
#{association.foreign_key}: **value**
|
58
|
+
|
59
|
+
where **value** is the #{association.join_primary_key} value for the
|
60
|
+
associated #{association.klass.name} record.
|
61
|
+
MSG
|
62
|
+
end
|
36
63
|
end
|
37
64
|
|
38
65
|
def initialize(fixture, table_rows:, label:, now:)
|
@@ -99,7 +126,7 @@ module ActiveRecord
|
|
99
126
|
end
|
100
127
|
|
101
128
|
def resolve_enums
|
102
|
-
|
129
|
+
reflection_class.defined_enums.each do |name, values|
|
103
130
|
if @row.include?(name)
|
104
131
|
@row[name] = values.fetch(@row[name], @row[name])
|
105
132
|
end
|
@@ -115,9 +142,13 @@ module ActiveRecord
|
|
115
142
|
fk_name = association.join_foreign_key
|
116
143
|
|
117
144
|
if association.name.to_s != fk_name && value = @row.delete(association.name.to_s)
|
118
|
-
if association.polymorphic?
|
119
|
-
|
120
|
-
|
145
|
+
if association.polymorphic?
|
146
|
+
if value.sub!(/\s*\(([^)]*)\)\s*$/, "")
|
147
|
+
# support polymorphic belongs_to as "label (Type)"
|
148
|
+
@row[association.join_foreign_type] = $1
|
149
|
+
end
|
150
|
+
elsif association.join_primary_key != association.klass.primary_key
|
151
|
+
raise PrimaryKeyError.new(@label, association, value)
|
121
152
|
end
|
122
153
|
|
123
154
|
fk_type = reflection_class.type_for_attribute(fk_name).type
|
@@ -141,8 +172,12 @@ module ActiveRecord
|
|
141
172
|
|
142
173
|
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
|
143
174
|
joins = targets.map do |target|
|
144
|
-
{ lhs_key => @row[model_metadata.primary_key_name],
|
145
|
-
|
175
|
+
join = { lhs_key => @row[model_metadata.primary_key_name],
|
176
|
+
rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
|
177
|
+
association.timestamp_column_names.each do |col|
|
178
|
+
join[col] = @now
|
179
|
+
end
|
180
|
+
join
|
146
181
|
end
|
147
182
|
@table_rows.tables[table_name].concat(joins)
|
148
183
|
end
|
@@ -6,7 +6,7 @@ require "active_record/fixture_set/model_metadata"
|
|
6
6
|
module ActiveRecord
|
7
7
|
class FixtureSet
|
8
8
|
class TableRows # :nodoc:
|
9
|
-
def initialize(table_name, model_class:, fixtures
|
9
|
+
def initialize(table_name, model_class:, fixtures:)
|
10
10
|
@model_class = model_class
|
11
11
|
|
12
12
|
# track any join tables we need to insert later
|
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
# ensure this table is loaded before any HABTM associations
|
16
16
|
@tables[table_name] = nil
|
17
17
|
|
18
|
-
build_table_rows_from(table_name, fixtures
|
18
|
+
build_table_rows_from(table_name, fixtures)
|
19
19
|
end
|
20
20
|
|
21
21
|
attr_reader :tables, :model_class
|
@@ -29,8 +29,8 @@ module ActiveRecord
|
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
32
|
-
def build_table_rows_from(table_name, fixtures
|
33
|
-
now =
|
32
|
+
def build_table_rows_from(table_name, fixtures)
|
33
|
+
now = ActiveRecord.default_timezone == :utc ? Time.now.utc : Time.now
|
34
34
|
|
35
35
|
@tables[table_name] = fixtures.map do |label, fixture|
|
36
36
|
TableRow.new(
|
@@ -12,7 +12,7 @@ require "active_record/fixture_set/table_rows"
|
|
12
12
|
require "active_record/test_fixtures"
|
13
13
|
|
14
14
|
module ActiveRecord
|
15
|
-
class FixtureClassNotFound < ActiveRecord::ActiveRecordError
|
15
|
+
class FixtureClassNotFound < ActiveRecord::ActiveRecordError # :nodoc:
|
16
16
|
end
|
17
17
|
|
18
18
|
# \Fixtures are a way of organizing data that you want to test against; in short, sample data.
|
@@ -35,7 +35,7 @@ module ActiveRecord
|
|
35
35
|
# name: Google
|
36
36
|
# url: http://www.google.com
|
37
37
|
#
|
38
|
-
# This fixture file includes two fixtures. Each YAML fixture (
|
38
|
+
# This fixture file includes two fixtures. Each YAML fixture (i.e. record) is given a name and
|
39
39
|
# is followed by an indented list of key/value pairs in the "key: value" format. Records are
|
40
40
|
# separated by a blank line for your viewing pleasure.
|
41
41
|
#
|
@@ -152,7 +152,7 @@ module ActiveRecord
|
|
152
152
|
# - define a helper method in <tt>test_helper.rb</tt>
|
153
153
|
# module FixtureFileHelpers
|
154
154
|
# def file_sha(path)
|
155
|
-
# Digest::
|
155
|
+
# OpenSSL::Digest::SHA256.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
|
156
156
|
# end
|
157
157
|
# end
|
158
158
|
# ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers
|
@@ -311,7 +311,7 @@ module ActiveRecord
|
|
311
311
|
#
|
312
312
|
# Just provide the polymorphic target type and Active Record will take care of the rest.
|
313
313
|
#
|
314
|
-
# === has_and_belongs_to_many
|
314
|
+
# === has_and_belongs_to_many or has_many :through
|
315
315
|
#
|
316
316
|
# Time to give our monkey some fruit.
|
317
317
|
#
|
@@ -407,7 +407,7 @@ module ActiveRecord
|
|
407
407
|
# defaults:
|
408
408
|
#
|
409
409
|
# DEFAULTS: &DEFAULTS
|
410
|
-
# created_on: <%= 3.weeks.ago.
|
410
|
+
# created_on: <%= 3.weeks.ago.to_formatted_s(:db) %>
|
411
411
|
#
|
412
412
|
# first:
|
413
413
|
# name: Smurf
|
@@ -426,7 +426,7 @@ module ActiveRecord
|
|
426
426
|
# _fixture:
|
427
427
|
# ignore:
|
428
428
|
# - base
|
429
|
-
# # or use "ignore: base" when there is only one fixture needs to be ignored.
|
429
|
+
# # or use "ignore: base" when there is only one fixture that needs to be ignored.
|
430
430
|
#
|
431
431
|
# base: &base
|
432
432
|
# admin: false
|
@@ -585,14 +585,6 @@ module ActiveRecord
|
|
585
585
|
end
|
586
586
|
end
|
587
587
|
|
588
|
-
def signed_global_id(fixture_set_name, label, column_type: :integer, **options)
|
589
|
-
identifier = identify(label, column_type)
|
590
|
-
model_name = default_fixture_model_name(fixture_set_name)
|
591
|
-
uri = URI::GID.build([GlobalID.app, model_name, identifier, {}])
|
592
|
-
|
593
|
-
SignedGlobalID.new(uri, **options)
|
594
|
-
end
|
595
|
-
|
596
588
|
# Superclass for the evaluation contexts used by ERB fixtures.
|
597
589
|
def context_class
|
598
590
|
@context_class ||= Class.new
|
@@ -637,6 +629,10 @@ module ActiveRecord
|
|
637
629
|
|
638
630
|
conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
|
639
631
|
|
632
|
+
if ActiveRecord.verify_foreign_keys_for_fixtures && !conn.all_foreign_keys_valid?
|
633
|
+
raise "Foreign key violations found in your fixture data. Ensure you aren't referring to labels that don't exist on associations."
|
634
|
+
end
|
635
|
+
|
640
636
|
# Cap primary key sequences to max(pk).
|
641
637
|
if conn.respond_to?(:reset_pk_sequence!)
|
642
638
|
set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
|
@@ -689,7 +685,6 @@ module ActiveRecord
|
|
689
685
|
table_name,
|
690
686
|
model_class: model_class,
|
691
687
|
fixtures: fixtures,
|
692
|
-
config: config,
|
693
688
|
).to_hash
|
694
689
|
end
|
695
690
|
|
@@ -741,13 +736,13 @@ module ActiveRecord
|
|
741
736
|
end
|
742
737
|
end
|
743
738
|
|
744
|
-
class Fixture
|
739
|
+
class Fixture # :nodoc:
|
745
740
|
include Enumerable
|
746
741
|
|
747
|
-
class FixtureError < StandardError
|
742
|
+
class FixtureError < StandardError # :nodoc:
|
748
743
|
end
|
749
744
|
|
750
|
-
class FormatError < FixtureError
|
745
|
+
class FormatError < FixtureError # :nodoc:
|
751
746
|
end
|
752
747
|
|
753
748
|
attr_reader :model_class, :fixture
|
@@ -761,8 +756,8 @@ module ActiveRecord
|
|
761
756
|
model_class.name if model_class
|
762
757
|
end
|
763
758
|
|
764
|
-
def each
|
765
|
-
fixture.each
|
759
|
+
def each(&block)
|
760
|
+
fixture.each(&block)
|
766
761
|
end
|
767
762
|
|
768
763
|
def [](key)
|
@@ -782,3 +777,5 @@ module ActiveRecord
|
|
782
777
|
end
|
783
778
|
end
|
784
779
|
end
|
780
|
+
|
781
|
+
ActiveSupport.run_load_hooks :active_record_fixture_set, ActiveRecord::FixtureSet
|