activerecord 6.1.4.6 → 7.0.2.3

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.

Files changed (240) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1188 -932
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +33 -17
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +34 -27
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/join_dependency.rb +6 -2
  25. data/lib/active_record/associations/preloader/association.rb +187 -55
  26. data/lib/active_record/associations/preloader/batch.rb +48 -0
  27. data/lib/active_record/associations/preloader/branch.rb +147 -0
  28. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  29. data/lib/active_record/associations/preloader.rb +39 -113
  30. data/lib/active_record/associations/singular_association.rb +8 -2
  31. data/lib/active_record/associations/through_association.rb +3 -3
  32. data/lib/active_record/associations.rb +119 -90
  33. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  34. data/lib/active_record/attribute_assignment.rb +1 -1
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  37. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  38. data/lib/active_record/attribute_methods/query.rb +2 -2
  39. data/lib/active_record/attribute_methods/read.rb +7 -5
  40. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  42. data/lib/active_record/attribute_methods/write.rb +7 -10
  43. data/lib/active_record/attribute_methods.rb +13 -14
  44. data/lib/active_record/attributes.rb +24 -35
  45. data/lib/active_record/autosave_association.rb +8 -23
  46. data/lib/active_record/base.rb +19 -1
  47. data/lib/active_record/callbacks.rb +2 -2
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +78 -22
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +38 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +5 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +50 -50
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +35 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -107
  87. data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +27 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +16 -14
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +89 -30
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +47 -53
  94. data/lib/active_record/core.rb +122 -132
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +16 -32
  100. data/lib/active_record/delegated_type.rb +52 -11
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +61 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +49 -42
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +17 -20
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +3 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +9 -3
  147. data/lib/active_record/log_subscriber.rb +14 -3
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +8 -3
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +4 -4
  152. data/lib/active_record/migration/compatibility.rb +107 -3
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +109 -79
  155. data/lib/active_record/model_schema.rb +45 -58
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +219 -52
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +138 -0
  162. data/lib/active_record/querying.rb +15 -5
  163. data/lib/active_record/railtie.rb +127 -17
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +66 -129
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +67 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +3 -3
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +249 -61
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +184 -84
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +11 -7
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +61 -12
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +1 -1
  191. data/lib/active_record/signed_id.rb +1 -1
  192. data/lib/active_record/suppressor.rb +11 -15
  193. data/lib/active_record/tasks/database_tasks.rb +120 -58
  194. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  195. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
  196. data/lib/active_record/test_databases.rb +1 -1
  197. data/lib/active_record/test_fixtures.rb +4 -4
  198. data/lib/active_record/timestamp.rb +3 -4
  199. data/lib/active_record/transactions.rb +9 -14
  200. data/lib/active_record/translation.rb +2 -2
  201. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  202. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  203. data/lib/active_record/type/internal/timezone.rb +2 -2
  204. data/lib/active_record/type/serialized.rb +1 -1
  205. data/lib/active_record/type/type_map.rb +17 -20
  206. data/lib/active_record/type.rb +1 -2
  207. data/lib/active_record/validations/associated.rb +1 -1
  208. data/lib/active_record/validations/uniqueness.rb +1 -1
  209. data/lib/active_record.rb +204 -28
  210. data/lib/arel/attributes/attribute.rb +0 -8
  211. data/lib/arel/crud.rb +28 -22
  212. data/lib/arel/delete_manager.rb +18 -4
  213. data/lib/arel/filter_predications.rb +9 -0
  214. data/lib/arel/insert_manager.rb +2 -3
  215. data/lib/arel/nodes/casted.rb +1 -1
  216. data/lib/arel/nodes/delete_statement.rb +12 -13
  217. data/lib/arel/nodes/filter.rb +10 -0
  218. data/lib/arel/nodes/function.rb +1 -0
  219. data/lib/arel/nodes/insert_statement.rb +2 -2
  220. data/lib/arel/nodes/select_core.rb +2 -2
  221. data/lib/arel/nodes/select_statement.rb +2 -2
  222. data/lib/arel/nodes/update_statement.rb +8 -3
  223. data/lib/arel/nodes.rb +1 -0
  224. data/lib/arel/predications.rb +11 -3
  225. data/lib/arel/select_manager.rb +10 -4
  226. data/lib/arel/table.rb +0 -1
  227. data/lib/arel/tree_manager.rb +0 -12
  228. data/lib/arel/update_manager.rb +18 -4
  229. data/lib/arel/visitors/dot.rb +80 -90
  230. data/lib/arel/visitors/mysql.rb +8 -2
  231. data/lib/arel/visitors/postgresql.rb +0 -10
  232. data/lib/arel/visitors/to_sql.rb +58 -2
  233. data/lib/arel.rb +2 -1
  234. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  235. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  236. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  237. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  238. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  239. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  240. metadata +56 -11
@@ -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
@@ -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: [ :active, :archived ]
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 +:_scopes+ to +false+.
45
+ # Defining scopes can be disabled by setting +:scopes+ to +false+.
45
46
  #
46
47
  # class Conversation < ActiveRecord::Base
47
- # enum status: [ :active, :archived ], _scopes: false
48
+ # enum :status, [ :active, :archived ], scopes: false
48
49
  # end
49
50
  #
50
- # You can set the default enum value by setting +:_default+, like:
51
+ # You can set the default enum value by setting +:default+, like:
51
52
  #
52
53
  # class Conversation < ActiveRecord::Base
53
- # enum status: [ :active, :archived ], _default: "active"
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
- # Finally, it's also possible to explicitly map the relation between attribute and
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: { active: 0, archived: 1 }
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 +:_prefix+ or +:_suffix+ options when you need to define
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: [:active, :archived], _suffix: true
95
- # enum comments_status: [:active, :inactive], _prefix: :comments
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
- assert_valid_value(value)
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(definitions)
159
- enum_prefix = definitions.delete(:_prefix)
160
- enum_suffix = definitions.delete(:_suffix)
161
- enum_scopes = definitions.delete(:_scopes)
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
- default = {}
164
- default[:default] = definitions.delete(:_default) if definitions.key?(:_default)
176
+ definitions.each { |name, values| _enum(name, values, **options) }
177
+ end
165
178
 
166
- definitions.each do |name, values|
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
- attr = attribute_alias?(name) ? attribute_alias(name) : name
181
-
182
- decorate_attribute_type(attr, **default) do |subtype|
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 enum_prefix == true
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 enum_suffix == true
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, enum_scopes)
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, enum_scopes)
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 enum_scopes != false
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, enum_scopes)
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 enum_scopes != false
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? { |v| v.is_a?(Symbol) } || values.all? { |v| v.is_a?(String) }
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
@@ -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, "Call tech support!"
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("length(title)").first
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("length(title)")).first
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/per_thread_registry"
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
- extend ActiveSupport::PerThreadRegistry
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 :queries, :collect
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
- model_class.defined_enums.each do |name, values|
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? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
119
- # support polymorphic belongs_to as "label (Type)"
120
- @row[association.join_foreign_type] = $1
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
- rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
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:, config:)
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, config)
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, config)
33
- now = config.default_timezone == :utc ? Time.now.utc : Time.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 #:nodoc:
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 (ie. record) is given a name and
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::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
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.to_s(:db) %>
410
+ # created_on: <%= 3.weeks.ago.to_fs(: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 #:nodoc:
739
+ class Fixture # :nodoc:
745
740
  include Enumerable
746
741
 
747
- class FixtureError < StandardError #:nodoc:
742
+ class FixtureError < StandardError # :nodoc:
748
743
  end
749
744
 
750
- class FormatError < FixtureError #:nodoc:
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 { |item| yield item }
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