activerecord 6.0.1 → 6.1.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.

Files changed (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +843 -626
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -2
  6. data/lib/active_record/association_relation.rb +18 -17
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +49 -37
  9. data/lib/active_record/associations/association_scope.rb +17 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -3
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +25 -8
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +73 -42
  28. data/lib/active_record/associations/preloader/association.rb +51 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +12 -7
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +115 -12
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +32 -8
  45. data/lib/active_record/autosave_association.rb +56 -41
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +1 -2
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +82 -37
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -8
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +263 -107
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +60 -73
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +28 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  93. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  95. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  96. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  97. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  98. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  100. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +77 -57
  103. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  104. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +36 -12
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  107. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
  110. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  111. data/lib/active_record/connection_adapters.rb +50 -0
  112. data/lib/active_record/connection_handling.rb +210 -87
  113. data/lib/active_record/core.rb +229 -65
  114. data/lib/active_record/counter_cache.rb +4 -1
  115. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  116. data/lib/active_record/database_configurations/database_config.rb +52 -9
  117. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  118. data/lib/active_record/database_configurations/url_config.rb +15 -41
  119. data/lib/active_record/database_configurations.rb +124 -85
  120. data/lib/active_record/delegated_type.rb +209 -0
  121. data/lib/active_record/destroy_association_async_job.rb +36 -0
  122. data/lib/active_record/dynamic_matchers.rb +2 -3
  123. data/lib/active_record/enum.rb +40 -16
  124. data/lib/active_record/errors.rb +47 -12
  125. data/lib/active_record/explain.rb +9 -5
  126. data/lib/active_record/explain_subscriber.rb +1 -1
  127. data/lib/active_record/fixture_set/file.rb +10 -17
  128. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  129. data/lib/active_record/fixture_set/render_context.rb +1 -1
  130. data/lib/active_record/fixture_set/table_row.rb +2 -3
  131. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  132. data/lib/active_record/fixtures.rb +54 -11
  133. data/lib/active_record/gem_version.rb +2 -2
  134. data/lib/active_record/inheritance.rb +40 -21
  135. data/lib/active_record/insert_all.rb +38 -9
  136. data/lib/active_record/integration.rb +3 -5
  137. data/lib/active_record/internal_metadata.rb +16 -7
  138. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  139. data/lib/active_record/locking/optimistic.rb +22 -17
  140. data/lib/active_record/locking/pessimistic.rb +6 -2
  141. data/lib/active_record/log_subscriber.rb +27 -9
  142. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  143. data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/migration/command_recorder.rb +53 -45
  146. data/lib/active_record/migration/compatibility.rb +70 -20
  147. data/lib/active_record/migration/join_table.rb +0 -1
  148. data/lib/active_record/migration.rb +114 -84
  149. data/lib/active_record/model_schema.rb +117 -15
  150. data/lib/active_record/nested_attributes.rb +2 -5
  151. data/lib/active_record/no_touching.rb +1 -1
  152. data/lib/active_record/null_relation.rb +0 -1
  153. data/lib/active_record/persistence.rb +50 -46
  154. data/lib/active_record/query_cache.rb +15 -5
  155. data/lib/active_record/querying.rb +12 -7
  156. data/lib/active_record/railtie.rb +65 -45
  157. data/lib/active_record/railties/databases.rake +267 -93
  158. data/lib/active_record/readonly_attributes.rb +4 -0
  159. data/lib/active_record/reflection.rb +77 -63
  160. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  161. data/lib/active_record/relation/batches.rb +38 -32
  162. data/lib/active_record/relation/calculations.rb +102 -45
  163. data/lib/active_record/relation/delegation.rb +9 -7
  164. data/lib/active_record/relation/finder_methods.rb +45 -16
  165. data/lib/active_record/relation/from_clause.rb +5 -1
  166. data/lib/active_record/relation/merger.rb +27 -26
  167. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  168. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  169. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  170. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  171. data/lib/active_record/relation/predicate_builder.rb +55 -35
  172. data/lib/active_record/relation/query_methods.rb +335 -187
  173. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  174. data/lib/active_record/relation/spawn_methods.rb +8 -8
  175. data/lib/active_record/relation/where_clause.rb +104 -58
  176. data/lib/active_record/relation.rb +108 -68
  177. data/lib/active_record/result.rb +41 -34
  178. data/lib/active_record/runtime_registry.rb +2 -2
  179. data/lib/active_record/sanitization.rb +6 -17
  180. data/lib/active_record/schema_dumper.rb +34 -4
  181. data/lib/active_record/schema_migration.rb +2 -8
  182. data/lib/active_record/scoping/default.rb +0 -1
  183. data/lib/active_record/scoping/named.rb +7 -18
  184. data/lib/active_record/scoping.rb +0 -1
  185. data/lib/active_record/secure_token.rb +16 -8
  186. data/lib/active_record/serialization.rb +5 -3
  187. data/lib/active_record/signed_id.rb +116 -0
  188. data/lib/active_record/statement_cache.rb +20 -4
  189. data/lib/active_record/store.rb +3 -3
  190. data/lib/active_record/suppressor.rb +2 -2
  191. data/lib/active_record/table_metadata.rb +39 -36
  192. data/lib/active_record/tasks/database_tasks.rb +139 -113
  193. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  194. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  195. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  196. data/lib/active_record/test_databases.rb +5 -4
  197. data/lib/active_record/test_fixtures.rb +38 -16
  198. data/lib/active_record/timestamp.rb +4 -7
  199. data/lib/active_record/touch_later.rb +20 -21
  200. data/lib/active_record/transactions.rb +21 -70
  201. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  202. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  203. data/lib/active_record/type/serialized.rb +6 -3
  204. data/lib/active_record/type/time.rb +10 -0
  205. data/lib/active_record/type/type_map.rb +0 -1
  206. data/lib/active_record/type/unsigned_integer.rb +0 -1
  207. data/lib/active_record/type.rb +8 -2
  208. data/lib/active_record/type_caster/connection.rb +0 -1
  209. data/lib/active_record/type_caster/map.rb +8 -5
  210. data/lib/active_record/validations/associated.rb +1 -2
  211. data/lib/active_record/validations/numericality.rb +35 -0
  212. data/lib/active_record/validations/uniqueness.rb +24 -4
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record.rb +7 -13
  215. data/lib/arel/attributes/attribute.rb +4 -0
  216. data/lib/arel/collectors/bind.rb +5 -0
  217. data/lib/arel/collectors/composite.rb +8 -0
  218. data/lib/arel/collectors/sql_string.rb +7 -0
  219. data/lib/arel/collectors/substitute_binds.rb +7 -0
  220. data/lib/arel/nodes/binary.rb +82 -8
  221. data/lib/arel/nodes/bind_param.rb +8 -0
  222. data/lib/arel/nodes/casted.rb +21 -9
  223. data/lib/arel/nodes/equality.rb +6 -9
  224. data/lib/arel/nodes/grouping.rb +3 -0
  225. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  226. data/lib/arel/nodes/in.rb +8 -1
  227. data/lib/arel/nodes/infix_operation.rb +13 -1
  228. data/lib/arel/nodes/join_source.rb +1 -1
  229. data/lib/arel/nodes/node.rb +7 -6
  230. data/lib/arel/nodes/ordering.rb +27 -0
  231. data/lib/arel/nodes/sql_literal.rb +3 -0
  232. data/lib/arel/nodes/table_alias.rb +7 -3
  233. data/lib/arel/nodes/unary.rb +0 -1
  234. data/lib/arel/nodes.rb +3 -1
  235. data/lib/arel/predications.rb +17 -24
  236. data/lib/arel/select_manager.rb +1 -2
  237. data/lib/arel/table.rb +13 -5
  238. data/lib/arel/visitors/dot.rb +14 -3
  239. data/lib/arel/visitors/mysql.rb +11 -1
  240. data/lib/arel/visitors/postgresql.rb +15 -5
  241. data/lib/arel/visitors/sqlite.rb +0 -1
  242. data/lib/arel/visitors/to_sql.rb +89 -79
  243. data/lib/arel/visitors/visitor.rb +0 -1
  244. data/lib/arel/visitors.rb +0 -7
  245. data/lib/arel.rb +5 -9
  246. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  247. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  248. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  249. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  250. data/lib/rails/generators/active_record/migration.rb +6 -2
  251. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  252. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  253. metadata +26 -26
  254. data/lib/active_record/attribute_decorators.rb +0 -90
  255. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  256. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  257. data/lib/active_record/define_callbacks.rb +0 -22
  258. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  259. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  260. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  261. data/lib/arel/attributes.rb +0 -22
  262. data/lib/arel/visitors/depth_first.rb +0 -204
  263. data/lib/arel/visitors/ibm_db.rb +0 -34
  264. data/lib/arel/visitors/informix.rb +0 -62
  265. data/lib/arel/visitors/mssql.rb +0 -157
  266. data/lib/arel/visitors/oracle.rb +0 -159
  267. data/lib/arel/visitors/oracle12.rb +0 -66
  268. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -23,6 +23,12 @@ module ActiveRecord
23
23
  # should not be dumped to db/schema.rb.
24
24
  cattr_accessor :fk_ignore_pattern, default: /^fk_rails_[0-9a-f]{10}$/
25
25
 
26
+ ##
27
+ # :singleton-method:
28
+ # Specify a custom regular expression matching check constraints which name
29
+ # should not be dumped to db/schema.rb.
30
+ cattr_accessor :chk_ignore_pattern, default: /^chk_rails_[0-9a-f]{10}$/
31
+
26
32
  class << self
27
33
  def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
28
34
  connection.create_schema_dumper(generate_options(config)).dump(stream)
@@ -72,8 +78,8 @@ module ActiveRecord
72
78
  # of editing this file, please use the migrations feature of Active Record to
73
79
  # incrementally modify your database, and then regenerate this schema definition.
74
80
  #
75
- # This file is the source Rails uses to define your schema when running `rails
76
- # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
81
+ # This file is the source Rails uses to define your schema when running `bin/rails
82
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
77
83
  # be faster and is potentially less error prone than running all of your
78
84
  # migrations from scratch. Old migrations may fail to apply correctly if those
79
85
  # migrations use external dependencies or application code.
@@ -125,7 +131,10 @@ HEADER
125
131
  tbl.print ", primary_key: #{pk.inspect}" unless pk == "id"
126
132
  pkcol = columns.detect { |c| c.name == pk }
127
133
  pkcolspec = column_spec_for_primary_key(pkcol)
128
- if pkcolspec.present?
134
+ unless pkcolspec.empty?
135
+ if pkcolspec != pkcolspec.slice(:id, :default)
136
+ pkcolspec = { id: { type: pkcolspec.delete(:id), **pkcolspec }.compact }
137
+ end
129
138
  tbl.print ", #{format_colspec(pkcolspec)}"
130
139
  end
131
140
  when Array
@@ -156,6 +165,7 @@ HEADER
156
165
  end
157
166
 
158
167
  indexes_in_create(table, tbl)
168
+ check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
159
169
 
160
170
  tbl.puts " end"
161
171
  tbl.puts
@@ -209,6 +219,24 @@ HEADER
209
219
  index_parts
210
220
  end
211
221
 
222
+ def check_constraints_in_create(table, stream)
223
+ if (check_constraints = @connection.check_constraints(table)).any?
224
+ add_check_constraint_statements = check_constraints.map do |check_constraint|
225
+ parts = [
226
+ "t.check_constraint #{check_constraint.expression.inspect}"
227
+ ]
228
+
229
+ if check_constraint.export_name_on_schema_dump?
230
+ parts << "name: #{check_constraint.name.inspect}"
231
+ end
232
+
233
+ " #{parts.join(', ')}"
234
+ end
235
+
236
+ stream.puts add_check_constraint_statements.sort.join("\n")
237
+ end
238
+ end
239
+
212
240
  def foreign_keys(table, stream)
213
241
  if (foreign_keys = @connection.foreign_keys(table)).any?
214
242
  add_foreign_key_statements = foreign_keys.map do |foreign_key|
@@ -240,7 +268,9 @@ HEADER
240
268
  end
241
269
 
242
270
  def format_colspec(colspec)
243
- colspec.map { |key, value| "#{key}: #{value}" }.join(", ")
271
+ colspec.map do |key, value|
272
+ "#{key}: #{ value.is_a?(Hash) ? "{ #{format_colspec(value)} }" : value }"
273
+ end.join(", ")
244
274
  end
245
275
 
246
276
  def format_options(options)
@@ -22,16 +22,10 @@ module ActiveRecord
22
22
  "#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
23
23
  end
24
24
 
25
- def table_exists?
26
- connection.table_exists?(table_name)
27
- end
28
-
29
25
  def create_table
30
- unless table_exists?
31
- version_options = connection.internal_string_options_for_primary_key
32
-
26
+ unless connection.table_exists?(table_name)
33
27
  connection.create_table(table_name, id: false) do |t|
34
- t.string :version, version_options
28
+ t.string :version, **connection.internal_string_options_for_primary_key
35
29
  end
36
30
  end
37
31
  end
@@ -44,7 +44,6 @@ module ActiveRecord
44
44
  end
45
45
 
46
46
  private
47
-
48
47
  # Use this macro in your model to set a default scope for all operations on
49
48
  # the model.
50
49
  #
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/array"
4
- require "active_support/core_ext/hash/except"
5
- require "active_support/core_ext/kernel/singleton_class"
6
-
7
3
  module ActiveRecord
8
4
  # = Active Record \Named \Scopes
9
5
  module Scoping
@@ -27,14 +23,6 @@ module ActiveRecord
27
23
  scope = current_scope
28
24
 
29
25
  if scope
30
- if scope._deprecated_scope_source
31
- ActiveSupport::Deprecation.warn(<<~MSG.squish)
32
- Class level methods will no longer inherit scoping from `#{scope._deprecated_scope_source}`
33
- in Rails 6.1. To continue using the scoped relation, pass it into the block directly.
34
- To instead access the full set of models, as Rails 6.1 will, use `#{name}.unscoped`.
35
- MSG
36
- end
37
-
38
26
  if self == scope.klass
39
27
  scope.clone
40
28
  else
@@ -53,7 +41,8 @@ module ActiveRecord
53
41
  end
54
42
  end
55
43
 
56
- def default_scoped(scope = relation) # :nodoc:
44
+ # Returns a scope for the model with default scopes.
45
+ def default_scoped(scope = relation)
57
46
  build_default_scope(scope) || scope
58
47
  end
59
48
 
@@ -83,10 +72,6 @@ module ActiveRecord
83
72
  # <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
84
73
  # represents the query <tt>Shirt.where(color: 'red')</tt>.
85
74
  #
86
- # You should always pass a callable object to the scopes defined
87
- # with #scope. This ensures that the scope is re-evaluated each
88
- # time it is called.
89
- #
90
75
  # Note that this is simply 'syntactic sugar' for defining an actual
91
76
  # class method:
92
77
  #
@@ -188,7 +173,7 @@ module ActiveRecord
188
173
 
189
174
  if body.respond_to?(:to_proc)
190
175
  singleton_class.define_method(name) do |*args|
191
- scope = all._exec_scope(name, *args, &body)
176
+ scope = all._exec_scope(*args, &body)
192
177
  scope = scope.extending(extension) if extension
193
178
  scope
194
179
  end
@@ -199,11 +184,15 @@ module ActiveRecord
199
184
  scope
200
185
  end
201
186
  end
187
+ singleton_class.send(:ruby2_keywords, name) if respond_to?(:ruby2_keywords, true)
202
188
 
203
189
  generate_relation_method(name)
204
190
  end
205
191
 
206
192
  private
193
+ def singleton_method_added(name)
194
+ generate_relation_method(name) if Kernel.respond_to?(name) && !ActiveRecord::Relation.method_defined?(name)
195
+ end
207
196
 
208
197
  def valid_scope_name?(name)
209
198
  if respond_to?(name, true) && logger
@@ -95,7 +95,6 @@ module ActiveRecord
95
95
  end
96
96
 
97
97
  private
98
-
99
98
  def raise_invalid_scope_type!(scope_type)
100
99
  if !VALID_SCOPE_TYPES.include?(scope_type)
101
100
  raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
@@ -2,6 +2,10 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module SecureToken
5
+ class MinimumLengthError < StandardError; end
6
+
7
+ MINIMUM_TOKEN_LENGTH = 24
8
+
5
9
  extend ActiveSupport::Concern
6
10
 
7
11
  module ClassMethods
@@ -10,30 +14,34 @@ module ActiveRecord
10
14
  # # Schema: User(token:string, auth_token:string)
11
15
  # class User < ActiveRecord::Base
12
16
  # has_secure_token
13
- # has_secure_token :auth_token
17
+ # has_secure_token :auth_token, length: 36
14
18
  # end
15
19
  #
16
20
  # user = User.new
17
21
  # user.save
18
22
  # user.token # => "pX27zsMN2ViQKta1bGfLmVJE"
19
- # user.auth_token # => "77TMHrHJFvFDwodq8w7Ev2m7"
23
+ # user.auth_token # => "tU9bLuZseefXQ4yQxQo8wjtBvsAfPc78os6R"
20
24
  # user.regenerate_token # => true
21
25
  # user.regenerate_auth_token # => true
22
26
  #
23
- # <tt>SecureRandom::base58</tt> is used to generate the 24-character unique token, so collisions are highly unlikely.
27
+ # <tt>SecureRandom::base58</tt> is used to generate at minimum a 24-character unique token, so collisions are highly unlikely.
24
28
  #
25
29
  # Note that it's still possible to generate a race condition in the database in the same way that
26
30
  # {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
27
31
  # You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
28
- def has_secure_token(attribute = :token)
32
+ def has_secure_token(attribute = :token, length: MINIMUM_TOKEN_LENGTH)
33
+ if length < MINIMUM_TOKEN_LENGTH
34
+ raise MinimumLengthError, "Token requires a minimum length of #{MINIMUM_TOKEN_LENGTH} characters."
35
+ end
36
+
29
37
  # Load securerandom only when has_secure_token is used.
30
38
  require "active_support/core_ext/securerandom"
31
- define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
32
- before_create { send("#{attribute}=", self.class.generate_unique_secure_token) unless send("#{attribute}?") }
39
+ define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token(length: length) }
40
+ before_create { send("#{attribute}=", self.class.generate_unique_secure_token(length: length)) unless send("#{attribute}?") }
33
41
  end
34
42
 
35
- def generate_unique_secure_token
36
- SecureRandom.base58(24)
43
+ def generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)
44
+ SecureRandom.base58(length)
37
45
  end
38
46
  end
39
47
  end
@@ -11,10 +11,12 @@ module ActiveRecord #:nodoc:
11
11
  end
12
12
 
13
13
  def serializable_hash(options = nil)
14
- options = options.try(:dup) || {}
14
+ if self.class._has_attribute?(self.class.inheritance_column)
15
+ options = options ? options.dup : {}
15
16
 
16
- options[:except] = Array(options[:except]).map(&:to_s)
17
- options[:except] |= Array(self.class.inheritance_column)
17
+ options[:except] = Array(options[:except]).map(&:to_s)
18
+ options[:except] |= Array(self.class.inheritance_column)
19
+ end
18
20
 
19
21
  super(options)
20
22
  end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # = Active Record Signed Id
5
+ module SignedId
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ ##
10
+ # :singleton-method:
11
+ # Set the secret used for the signed id verifier instance when using Active Record outside of Rails.
12
+ # Within Rails, this is automatically set using the Rails application key generator.
13
+ mattr_accessor :signed_id_verifier_secret, instance_writer: false
14
+ end
15
+
16
+ module ClassMethods
17
+ # Lets you find a record based on a signed id that's safe to put into the world without risk of tampering.
18
+ # This is particularly useful for things like password reset or email verification, where you want
19
+ # the bearer of the signed id to be able to interact with the underlying record, but usually only within
20
+ # a certain time period.
21
+ #
22
+ # You set the time period that the signed id is valid for during generation, using the instance method
23
+ # +signed_id(expires_in: 15.minutes)+. If the time has elapsed before a signed find is attempted,
24
+ # the signed id will no longer be valid, and nil is returned.
25
+ #
26
+ # It's possible to further restrict the use of a signed id with a purpose. This helps when you have a
27
+ # general base model, like a User, which might have signed ids for several things, like password reset
28
+ # or email verification. The purpose that was set during generation must match the purpose set when
29
+ # finding. If there's a mismatch, nil is again returned.
30
+ #
31
+ # ==== Examples
32
+ #
33
+ # signed_id = User.first.signed_id expires_in: 15.minutes, purpose: :password_reset
34
+ #
35
+ # User.find_signed signed_id # => nil, since the purpose does not match
36
+ #
37
+ # travel 16.minutes
38
+ # User.find_signed signed_id, purpose: :password_reset # => nil, since the signed id has expired
39
+ #
40
+ # travel_back
41
+ # User.find_signed signed_id, purpose: :password_reset # => User.first
42
+ def find_signed(signed_id, purpose: nil)
43
+ raise UnknownPrimaryKey.new(self) if primary_key.nil?
44
+
45
+ if id = signed_id_verifier.verified(signed_id, purpose: combine_signed_id_purposes(purpose))
46
+ find_by primary_key => id
47
+ end
48
+ end
49
+
50
+ # Works like +find_signed+, but will raise an +ActiveSupport::MessageVerifier::InvalidSignature+
51
+ # exception if the +signed_id+ has either expired, has a purpose mismatch, is for another record,
52
+ # or has been tampered with. It will also raise an +ActiveRecord::RecordNotFound+ exception if
53
+ # the valid signed id can't find a record.
54
+ #
55
+ # === Examples
56
+ #
57
+ # User.find_signed! "bad data" # => ActiveSupport::MessageVerifier::InvalidSignature
58
+ #
59
+ # signed_id = User.first.signed_id
60
+ # User.first.destroy
61
+ # User.find_signed! signed_id # => ActiveRecord::RecordNotFound
62
+ def find_signed!(signed_id, purpose: nil)
63
+ if id = signed_id_verifier.verify(signed_id, purpose: combine_signed_id_purposes(purpose))
64
+ find(id)
65
+ end
66
+ end
67
+
68
+ # The verifier instance that all signed ids are generated and verified from. By default, it'll be initialized
69
+ # with the class-level +signed_id_verifier_secret+, which within Rails comes from the
70
+ # Rails.application.key_generator. By default, it's SHA256 for the digest and JSON for the serialization.
71
+ def signed_id_verifier
72
+ @signed_id_verifier ||= begin
73
+ secret = signed_id_verifier_secret
74
+ secret = secret.call if secret.respond_to?(:call)
75
+
76
+ if secret.nil?
77
+ raise ArgumentError, "You must set ActiveRecord::Base.signed_id_verifier_secret to use signed ids"
78
+ else
79
+ ActiveSupport::MessageVerifier.new secret, digest: "SHA256", serializer: JSON
80
+ end
81
+ end
82
+ end
83
+
84
+ # Allows you to pass in a custom verifier used for the signed ids. This also allows you to use different
85
+ # verifiers for different classes. This is also helpful if you need to rotate keys, as you can prepare
86
+ # your custom verifier for that in advance. See +ActiveSupport::MessageVerifier+ for details.
87
+ def signed_id_verifier=(verifier)
88
+ @signed_id_verifier = verifier
89
+ end
90
+
91
+ # :nodoc:
92
+ def combine_signed_id_purposes(purpose)
93
+ [ base_class.name.underscore, purpose.to_s ].compact_blank.join("/")
94
+ end
95
+ end
96
+
97
+
98
+ # Returns a signed id that's generated using a preconfigured +ActiveSupport::MessageVerifier+ instance.
99
+ # This signed id is tamper proof, so it's safe to send in an email or otherwise share with the outside world.
100
+ # It can further more be set to expire (the default is not to expire), and scoped down with a specific purpose.
101
+ # If the expiration date has been exceeded before +find_signed+ is called, the id won't find the designated
102
+ # record. If a purpose is set, this too must match.
103
+ #
104
+ # If you accidentally let a signed id out in the wild that you wish to retract sooner than its expiration date
105
+ # (or maybe you forgot to set an expiration date while meaning to!), you can use the purpose to essentially
106
+ # version the signed_id, like so:
107
+ #
108
+ # user.signed_id purpose: :v2
109
+ #
110
+ # And you then change your +find_signed+ calls to require this new purpose. Any old signed ids that were not
111
+ # created with the purpose will no longer find the record.
112
+ def signed_id(expires_in: nil, purpose: nil)
113
+ self.class.signed_id_verifier.generate id, expires_in: expires_in, purpose: self.class.combine_signed_id_purposes(purpose)
114
+ end
115
+ end
116
+ end
@@ -50,13 +50,20 @@ module ActiveRecord
50
50
 
51
51
  def sql_for(binds, connection)
52
52
  val = @values.dup
53
- casted_binds = binds.map(&:value_for_database)
54
- @indexes.each { |i| val[i] = connection.quote(casted_binds.shift) }
53
+ @indexes.each do |i|
54
+ value = binds.shift
55
+ if ActiveModel::Attribute === value
56
+ value = value.value_for_database
57
+ end
58
+ val[i] = connection.quote(value)
59
+ end
55
60
  val.join
56
61
  end
57
62
  end
58
63
 
59
64
  class PartialQueryCollector
65
+ attr_accessor :preparable
66
+
60
67
  def initialize
61
68
  @parts = []
62
69
  @binds = []
@@ -73,6 +80,15 @@ module ActiveRecord
73
80
  self
74
81
  end
75
82
 
83
+ def add_binds(binds)
84
+ @binds.concat binds
85
+ binds.size.times do |i|
86
+ @parts << ", " unless i == 0
87
+ @parts << Substitute.new
88
+ end
89
+ self
90
+ end
91
+
76
92
  def value
77
93
  [@parts, @binds]
78
94
  end
@@ -100,7 +116,7 @@ module ActiveRecord
100
116
  @bound_attributes = bound_attributes
101
117
 
102
118
  bound_attributes.each_with_index do |attr, i|
103
- if Substitute === attr.value
119
+ if ActiveModel::Attribute === attr && Substitute === attr.value
104
120
  @indexes << i
105
121
  end
106
122
  end
@@ -133,7 +149,7 @@ module ActiveRecord
133
149
 
134
150
  klass.find_by_sql(sql, bind_values, preparable: true, &block)
135
151
  rescue ::RangeError
136
- nil
152
+ []
137
153
  end
138
154
 
139
155
  def self.unsupported_value?(value)
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
  # You can set custom coder to encode/decode your serialized attributes to/from different formats.
24
24
  # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
25
25
  #
26
- # NOTE: If you are using structured database data types (eg. PostgreSQL +hstore+/+json+, or MySQL 5.7+
26
+ # NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+, or MySQL 5.7+
27
27
  # +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
28
28
  # Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
29
29
  # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
@@ -103,7 +103,7 @@ module ActiveRecord
103
103
  module ClassMethods
104
104
  def store(store_attribute, options = {})
105
105
  serialize store_attribute, IndifferentCoder.new(store_attribute, options[:coder])
106
- store_accessor(store_attribute, options[:accessors], options.slice(:prefix, :suffix)) if options.has_key? :accessors
106
+ store_accessor(store_attribute, options[:accessors], **options.slice(:prefix, :suffix)) if options.has_key? :accessors
107
107
  end
108
108
 
109
109
  def store_accessor(store_attribute, *keys, prefix: nil, suffix: nil)
@@ -251,7 +251,7 @@ module ActiveRecord
251
251
  attribute = object.send(store_attribute)
252
252
  unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
253
253
  attribute = IndifferentCoder.as_indifferent_hash(attribute)
254
- object.send :"#{store_attribute}=", attribute
254
+ object.public_send :"#{store_attribute}=", attribute
255
255
  end
256
256
  attribute
257
257
  end
@@ -40,11 +40,11 @@ module ActiveRecord
40
40
  end
41
41
  end
42
42
 
43
- def save(*) # :nodoc:
43
+ def save(**) # :nodoc:
44
44
  SuppressorRegistry.suppressed[self.class.name] ? true : super
45
45
  end
46
46
 
47
- def save!(*) # :nodoc:
47
+ def save!(**) # :nodoc:
48
48
  SuppressorRegistry.suppressed[self.class.name] ? true : super
49
49
  end
50
50
  end
@@ -2,74 +2,77 @@
2
2
 
3
3
  module ActiveRecord
4
4
  class TableMetadata # :nodoc:
5
- delegate :foreign_type, :foreign_key, :join_primary_key, :join_foreign_key, to: :association, prefix: true
5
+ delegate :join_primary_key, :join_foreign_key, :join_foreign_type, to: :reflection
6
6
 
7
- def initialize(klass, arel_table, association = nil, types = klass)
7
+ def initialize(klass, arel_table, reflection = nil)
8
8
  @klass = klass
9
- @types = types
10
9
  @arel_table = arel_table
11
- @association = association
10
+ @reflection = reflection
12
11
  end
13
12
 
14
- def resolve_column_aliases(hash)
15
- new_hash = hash.dup
16
- hash.each_key do |key|
17
- if key.is_a?(Symbol) && new_key = klass.attribute_aliases[key.to_s]
18
- new_hash[new_key] = new_hash.delete(key)
19
- end
20
- end
21
- new_hash
22
- end
23
-
24
- def arel_attribute(column_name)
25
- if klass
26
- klass.arel_attribute(column_name, arel_table)
27
- else
28
- arel_table[column_name]
29
- end
13
+ def primary_key
14
+ klass&.primary_key
30
15
  end
31
16
 
32
17
  def type(column_name)
33
- types.type_for_attribute(column_name)
18
+ arel_table.type_for_attribute(column_name)
34
19
  end
35
20
 
36
21
  def has_column?(column_name)
37
- klass && klass.columns_hash.key?(column_name.to_s)
22
+ klass&.columns_hash.key?(column_name)
38
23
  end
39
24
 
40
- def associated_with?(association_name)
41
- klass && klass._reflect_on_association(association_name)
25
+ def associated_with?(table_name)
26
+ klass&._reflect_on_association(table_name) || klass&._reflect_on_association(table_name.singularize)
42
27
  end
43
28
 
44
29
  def associated_table(table_name)
45
- association = klass._reflect_on_association(table_name) || klass._reflect_on_association(table_name.to_s.singularize)
30
+ reflection = klass._reflect_on_association(table_name) || klass._reflect_on_association(table_name.singularize)
46
31
 
47
- if !association && table_name == arel_table.name
32
+ if !reflection && table_name == arel_table.name
48
33
  return self
49
- elsif association && !association.polymorphic?
50
- association_klass = association.klass
51
- arel_table = association_klass.arel_table.alias(table_name)
52
- TableMetadata.new(association_klass, arel_table, association)
34
+ end
35
+
36
+ reflection ||= yield table_name if block_given?
37
+
38
+ if reflection && !reflection.polymorphic?
39
+ association_klass = reflection.klass
40
+ arel_table = association_klass.arel_table
41
+ arel_table = arel_table.alias(table_name) if arel_table.name != table_name
42
+ TableMetadata.new(association_klass, arel_table, reflection)
53
43
  else
54
44
  type_caster = TypeCaster::Connection.new(klass, table_name)
55
45
  arel_table = Arel::Table.new(table_name, type_caster: type_caster)
56
- TableMetadata.new(nil, arel_table, association, type_caster)
46
+ TableMetadata.new(nil, arel_table, reflection)
57
47
  end
58
48
  end
59
49
 
60
50
  def polymorphic_association?
61
- association && association.polymorphic?
51
+ reflection&.polymorphic?
62
52
  end
63
53
 
64
- def aggregated_with?(aggregation_name)
65
- klass && reflect_on_aggregation(aggregation_name)
54
+ def through_association?
55
+ reflection&.through_reflection?
66
56
  end
67
57
 
68
58
  def reflect_on_aggregation(aggregation_name)
69
- klass.reflect_on_aggregation(aggregation_name)
59
+ klass&.reflect_on_aggregation(aggregation_name)
70
60
  end
61
+ alias :aggregated_with? :reflect_on_aggregation
62
+
63
+ def predicate_builder
64
+ if klass
65
+ predicate_builder = klass.predicate_builder.dup
66
+ predicate_builder.instance_variable_set(:@table, self)
67
+ predicate_builder
68
+ else
69
+ PredicateBuilder.new(self)
70
+ end
71
+ end
72
+
73
+ attr_reader :arel_table
71
74
 
72
75
  private
73
- attr_reader :klass, :types, :arel_table, :association
76
+ attr_reader :klass, :reflection
74
77
  end
75
78
  end