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
@@ -7,17 +7,14 @@ module ActiveRecord
7
7
 
8
8
  module ClassMethods # :nodoc:
9
9
  private
10
-
11
- def define_method_attribute(name)
10
+ def define_method_attribute(name, owner:)
12
11
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
13
- generated_attribute_methods, name
12
+ owner, name
14
13
  ) do |temp_method_name, attr_name_expr|
15
- generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
16
- def #{temp_method_name}
17
- name = #{attr_name_expr}
18
- _read_attribute(name) { |n| missing_attribute(n, caller) }
19
- end
20
- RUBY
14
+ owner <<
15
+ "def #{temp_method_name}" <<
16
+ " _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
17
+ "end"
21
18
  end
22
19
  end
23
20
  end
@@ -30,14 +27,13 @@ module ActiveRecord
30
27
  name = self.class.attribute_aliases[name] || name
31
28
 
32
29
  name = @primary_key if name == "id" && @primary_key
33
- _read_attribute(name, &block)
30
+ @attributes.fetch_value(name, &block)
34
31
  end
35
32
 
36
33
  # This method exists to avoid the expensive primary_key check internally, without
37
34
  # breaking compatibility with the read_attribute API
38
35
  def _read_attribute(attr_name, &block) # :nodoc
39
- sync_with_transaction_state if @transaction_state&.finalized?
40
- @attributes.fetch_value(attr_name.to_s, &block)
36
+ @attributes.fetch_value(attr_name, &block)
41
37
  end
42
38
 
43
39
  alias :attribute :_read_attribute
@@ -41,6 +41,12 @@ module ActiveRecord
41
41
  # * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
42
42
  # or a class name that the object type should be equal to.
43
43
  #
44
+ # ==== Options
45
+ #
46
+ # +default+ The default value to use when no value is provided. If this option
47
+ # is not passed, the previous default value (if any) will be used.
48
+ # Otherwise, the default will be +nil+.
49
+ #
44
50
  # ==== Example
45
51
  #
46
52
  # # Serialize a preferences attribute.
@@ -57,7 +63,7 @@ module ActiveRecord
57
63
  # class User < ActiveRecord::Base
58
64
  # serialize :preferences, Hash
59
65
  # end
60
- def serialize(attr_name, class_name_or_coder = Object)
66
+ def serialize(attr_name, class_name_or_coder = Object, **options)
61
67
  # When ::JSON is used, force it to go through the Active Support JSON encoder
62
68
  # to ensure special objects (e.g. Active Record models) are dumped correctly
63
69
  # using the #as_json hook.
@@ -69,17 +75,16 @@ module ActiveRecord
69
75
  Coders::YAMLColumn.new(attr_name, class_name_or_coder)
70
76
  end
71
77
 
72
- decorate_attribute_type(attr_name, :serialize) do |type|
73
- if type_incompatible_with_serialize?(type, class_name_or_coder)
74
- raise ColumnNotSerializableError.new(attr_name, type)
78
+ decorate_attribute_type(attr_name.to_s, **options) do |cast_type|
79
+ if type_incompatible_with_serialize?(cast_type, class_name_or_coder)
80
+ raise ColumnNotSerializableError.new(attr_name, cast_type)
75
81
  end
76
82
 
77
- Type::Serialized.new(type, coder)
83
+ Type::Serialized.new(cast_type, coder)
78
84
  end
79
85
  end
80
86
 
81
87
  private
82
-
83
88
  def type_incompatible_with_serialize?(type, class_name)
84
89
  type.is_a?(ActiveRecord::Type::Json) && class_name == ::JSON ||
85
90
  type.respond_to?(:type_cast_array, true) && class_name == ::Array
@@ -1,9 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/object/try"
4
+
3
5
  module ActiveRecord
4
6
  module AttributeMethods
5
7
  module TimeZoneConversion
6
8
  class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc:
9
+ def self.new(subtype)
10
+ self === subtype ? subtype : super
11
+ end
12
+
7
13
  def deserialize(value)
8
14
  convert_time_to_time_zone(super)
9
15
  end
@@ -25,7 +31,6 @@ module ActiveRecord
25
31
  end
26
32
 
27
33
  private
28
-
29
34
  def convert_time_to_time_zone(value)
30
35
  return if value.nil?
31
36
 
@@ -63,22 +68,14 @@ module ActiveRecord
63
68
  end
64
69
 
65
70
  module ClassMethods # :nodoc:
66
- private
67
-
68
- def inherited(subclass)
69
- super
70
- # We need to apply this decorator here, rather than on module inclusion. The closure
71
- # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
72
- # sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
73
- # `skip_time_zone_conversion_for_attributes` would not be picked up.
74
- subclass.class_eval do
75
- matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
76
- decorate_matching_attribute_types(matcher, "_time_zone_conversion") do |type|
77
- TimeZoneConverter.new(type)
78
- end
79
- end
71
+ def define_attribute(name, cast_type, **)
72
+ if create_time_zone_conversion_attribute?(name, cast_type)
73
+ cast_type = TimeZoneConverter.new(cast_type)
80
74
  end
75
+ super
76
+ end
81
77
 
78
+ private
82
79
  def create_time_zone_conversion_attribute?(name, cast_type)
83
80
  enabled_for_column = time_zone_aware_attributes &&
84
81
  !skip_time_zone_conversion_for_attributes.include?(name.to_sym)
@@ -11,17 +11,14 @@ module ActiveRecord
11
11
 
12
12
  module ClassMethods # :nodoc:
13
13
  private
14
-
15
- def define_method_attribute=(name)
14
+ def define_method_attribute=(name, owner:)
16
15
  ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
17
- generated_attribute_methods, name, writer: true,
16
+ owner, name, writer: true,
18
17
  ) do |temp_method_name, attr_name_expr|
19
- generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
20
- def #{temp_method_name}(value)
21
- name = #{attr_name_expr}
22
- _write_attribute(name, value)
23
- end
24
- RUBY
18
+ owner <<
19
+ "def #{temp_method_name}(value)" <<
20
+ " _write_attribute(#{attr_name_expr}, value)" <<
21
+ "end"
25
22
  end
26
23
  end
27
24
  end
@@ -34,27 +31,21 @@ module ActiveRecord
34
31
  name = self.class.attribute_aliases[name] || name
35
32
 
36
33
  name = @primary_key if name == "id" && @primary_key
37
- _write_attribute(name, value)
34
+ @attributes.write_from_user(name, value)
38
35
  end
39
36
 
40
37
  # This method exists to avoid the expensive primary_key check internally, without
41
38
  # breaking compatibility with the write_attribute API
42
39
  def _write_attribute(attr_name, value) # :nodoc:
43
- sync_with_transaction_state if @transaction_state&.finalized?
44
- @attributes.write_from_user(attr_name.to_s, value)
45
- value
40
+ @attributes.write_from_user(attr_name, value)
46
41
  end
47
42
 
43
+ alias :attribute= :_write_attribute
44
+ private :attribute=
45
+
48
46
  private
49
47
  def write_attribute_without_type_cast(attr_name, value)
50
- sync_with_transaction_state if @transaction_state&.finalized?
51
- @attributes.write_cast_value(attr_name.to_s, value)
52
- value
53
- end
54
-
55
- # Dispatch target for <tt>*=</tt> attribute methods.
56
- def attribute=(attribute_name, value)
57
- _write_attribute(attribute_name, value)
48
+ @attributes.write_cast_value(attr_name, value)
58
49
  end
59
50
  end
60
51
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "mutex_m"
4
+ require "active_support/core_ext/enumerable"
4
5
 
5
6
  module ActiveRecord
6
7
  # = Active Record Attribute Methods
@@ -18,8 +19,6 @@ module ActiveRecord
18
19
  include TimeZoneConversion
19
20
  include Dirty
20
21
  include Serialization
21
-
22
- delegate :column_for_attribute, to: :class
23
22
  end
24
23
 
25
24
  RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
@@ -28,6 +27,17 @@ module ActiveRecord
28
27
  include Mutex_m
29
28
  end
30
29
 
30
+ class << self
31
+ def dangerous_attribute_methods # :nodoc:
32
+ @dangerous_attribute_methods ||= (
33
+ Base.instance_methods +
34
+ Base.private_instance_methods -
35
+ Base.superclass.instance_methods -
36
+ Base.superclass.private_instance_methods
37
+ ).map { |m| -m.to_s }.to_set.freeze
38
+ end
39
+ end
40
+
31
41
  module ClassMethods
32
42
  def inherited(child_class) #:nodoc:
33
43
  child_class.initialize_generated_modules
@@ -97,7 +107,7 @@ module ActiveRecord
97
107
  # A method name is 'dangerous' if it is already (re)defined by Active Record, but
98
108
  # not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
99
109
  def dangerous_attribute_method?(name) # :nodoc:
100
- method_defined_within?(name, Base)
110
+ ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(name.to_s)
101
111
  end
102
112
 
103
113
  def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
@@ -115,13 +125,11 @@ module ActiveRecord
115
125
  # A class method is 'dangerous' if it is already (re)defined by Active Record, but
116
126
  # not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
117
127
  def dangerous_class_method?(method_name)
118
- RESTRICTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
119
- end
128
+ return true if RESTRICTED_CLASS_METHODS.include?(method_name.to_s)
120
129
 
121
- def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
122
- if klass.respond_to?(name, true)
123
- if superklass.respond_to?(name, true)
124
- klass.method(name).owner != superklass.method(name).owner
130
+ if Base.respond_to?(method_name, true)
131
+ if Object.respond_to?(method_name, true)
132
+ Base.method(method_name).owner != Object.method(method_name).owner
125
133
  else
126
134
  true
127
135
  end
@@ -140,7 +148,7 @@ module ActiveRecord
140
148
  # Person.attribute_method?(:age=) # => true
141
149
  # Person.attribute_method?(:nothing) # => false
142
150
  def attribute_method?(attribute)
143
- super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, "")))
151
+ super || (table_exists? && column_names.include?(attribute.to_s.delete_suffix("=")))
144
152
  end
145
153
 
146
154
  # Returns an array of column names as strings if it's not an abstract class and
@@ -156,39 +164,27 @@ module ActiveRecord
156
164
  attribute_types.keys
157
165
  else
158
166
  []
159
- end
167
+ end.freeze
160
168
  end
161
169
 
162
170
  # Returns true if the given attribute exists, otherwise false.
163
171
  #
164
172
  # class Person < ActiveRecord::Base
173
+ # alias_attribute :new_name, :name
165
174
  # end
166
175
  #
167
- # Person.has_attribute?('name') # => true
168
- # Person.has_attribute?(:age) # => true
169
- # Person.has_attribute?(:nothing) # => false
176
+ # Person.has_attribute?('name') # => true
177
+ # Person.has_attribute?('new_name') # => true
178
+ # Person.has_attribute?(:age) # => true
179
+ # Person.has_attribute?(:nothing) # => false
170
180
  def has_attribute?(attr_name)
171
- attribute_types.key?(attr_name.to_s)
181
+ attr_name = attr_name.to_s
182
+ attr_name = attribute_aliases[attr_name] || attr_name
183
+ attribute_types.key?(attr_name)
172
184
  end
173
185
 
174
- # Returns the column object for the named attribute.
175
- # Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
176
- # named attribute does not exist.
177
- #
178
- # class Person < ActiveRecord::Base
179
- # end
180
- #
181
- # person = Person.new
182
- # person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
183
- # # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
184
- #
185
- # person.column_for_attribute(:nothing)
186
- # # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
187
- def column_for_attribute(name)
188
- name = name.to_s
189
- columns_hash.fetch(name) do
190
- ConnectionAdapters::NullColumn.new(name)
191
- end
186
+ def _has_attribute?(attr_name) # :nodoc:
187
+ attribute_types.key?(attr_name)
192
188
  end
193
189
  end
194
190
 
@@ -217,7 +213,7 @@ module ActiveRecord
217
213
  # have been allocated but not yet initialized.
218
214
  if defined?(@attributes)
219
215
  if name = self.class.symbol_column_to_string(name.to_sym)
220
- return has_attribute?(name)
216
+ return _has_attribute?(name)
221
217
  end
222
218
  end
223
219
 
@@ -227,14 +223,22 @@ module ActiveRecord
227
223
  # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
228
224
  #
229
225
  # class Person < ActiveRecord::Base
226
+ # alias_attribute :new_name, :name
230
227
  # end
231
228
  #
232
229
  # person = Person.new
233
- # person.has_attribute?(:name) # => true
234
- # person.has_attribute?('age') # => true
235
- # person.has_attribute?(:nothing) # => false
230
+ # person.has_attribute?(:name) # => true
231
+ # person.has_attribute?(:new_name) # => true
232
+ # person.has_attribute?('age') # => true
233
+ # person.has_attribute?(:nothing) # => false
236
234
  def has_attribute?(attr_name)
237
- @attributes.key?(attr_name.to_s)
235
+ attr_name = attr_name.to_s
236
+ attr_name = self.class.attribute_aliases[attr_name] || attr_name
237
+ @attributes.key?(attr_name)
238
+ end
239
+
240
+ def _has_attribute?(attr_name) # :nodoc:
241
+ @attributes.key?(attr_name)
238
242
  end
239
243
 
240
244
  # Returns an array of names for the attributes available on this object.
@@ -278,8 +282,10 @@ module ActiveRecord
278
282
  # person.attribute_for_inspect(:tag_ids)
279
283
  # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
280
284
  def attribute_for_inspect(attr_name)
285
+ attr_name = attr_name.to_s
286
+ attr_name = self.class.attribute_aliases[attr_name] || attr_name
281
287
  value = _read_attribute(attr_name)
282
- format_for_inspect(value)
288
+ format_for_inspect(attr_name, value)
283
289
  end
284
290
 
285
291
  # Returns +true+ if the specified +attribute+ has been set by the user or by a
@@ -297,8 +303,10 @@ module ActiveRecord
297
303
  # task.is_done = true
298
304
  # task.attribute_present?(:title) # => true
299
305
  # task.attribute_present?(:is_done) # => true
300
- def attribute_present?(attribute)
301
- value = _read_attribute(attribute)
306
+ def attribute_present?(attr_name)
307
+ attr_name = attr_name.to_s
308
+ attr_name = self.class.attribute_aliases[attr_name] || attr_name
309
+ value = _read_attribute(attr_name)
302
310
  !value.nil? && !(value.respond_to?(:empty?) && value.empty?)
303
311
  end
304
312
 
@@ -377,8 +385,8 @@ module ActiveRecord
377
385
  end
378
386
 
379
387
  def attributes_with_values(attribute_names)
380
- attribute_names.each_with_object({}) do |name, attrs|
381
- attrs[name] = _read_attribute(name)
388
+ attribute_names.index_with do |name|
389
+ _read_attribute(name)
382
390
  end
383
391
  end
384
392
 
@@ -386,7 +394,7 @@ module ActiveRecord
386
394
  def attributes_for_update(attribute_names)
387
395
  attribute_names &= self.class.column_names
388
396
  attribute_names.delete_if do |name|
389
- readonly_attribute?(name)
397
+ self.class.readonly_attribute?(name)
390
398
  end
391
399
  end
392
400
 
@@ -399,18 +407,20 @@ module ActiveRecord
399
407
  end
400
408
  end
401
409
 
402
- def format_for_inspect(value)
403
- if value.is_a?(String) && value.length > 50
404
- "#{value[0, 50]}...".inspect
405
- elsif value.is_a?(Date) || value.is_a?(Time)
406
- %("#{value.to_s(:db)}")
407
- else
410
+ def format_for_inspect(name, value)
411
+ if value.nil?
408
412
  value.inspect
409
- end
410
- end
413
+ else
414
+ inspected_value = if value.is_a?(String) && value.length > 50
415
+ "#{value[0, 50]}...".inspect
416
+ elsif value.is_a?(Date) || value.is_a?(Time)
417
+ %("#{value.to_s(:inspect)}")
418
+ else
419
+ value.inspect
420
+ end
411
421
 
412
- def readonly_attribute?(name)
413
- self.class.readonly_attributes.include?(name)
422
+ inspection_filter.filter_param(name, inspected_value)
423
+ end
414
424
  end
415
425
 
416
426
  def pk_attribute?(name)
@@ -12,6 +12,9 @@ module ActiveRecord
12
12
  end
13
13
 
14
14
  module ClassMethods
15
+ ##
16
+ # :call-seq: attribute(name, cast_type = nil, **options)
17
+ #
15
18
  # Defines an attribute with a type on this model. It will override the
16
19
  # type of existing attributes if needed. This allows control over how
17
20
  # values are converted to and from SQL when assigned to a model. It also
@@ -205,13 +208,13 @@ module ActiveRecord
205
208
  # tracking is performed. The methods +changed?+ and +changed_in_place?+
206
209
  # will be called from ActiveModel::Dirty. See the documentation for those
207
210
  # methods in ActiveModel::Type::Value for more details.
208
- def attribute(name, cast_type = Type::Value.new, **options)
211
+ def attribute(name, cast_type = nil, **options, &block)
209
212
  name = name.to_s
210
213
  reload_schema_from_cache
211
214
 
212
215
  self.attributes_to_define_after_schema_loads =
213
216
  attributes_to_define_after_schema_loads.merge(
214
- name => [cast_type, options]
217
+ name => [cast_type || block, options]
215
218
  )
216
219
  end
217
220
 
@@ -246,16 +249,11 @@ module ActiveRecord
246
249
  def load_schema! # :nodoc:
247
250
  super
248
251
  attributes_to_define_after_schema_loads.each do |name, (type, options)|
249
- if type.is_a?(Symbol)
250
- type = ActiveRecord::Type.lookup(type, **options.except(:default))
251
- end
252
-
253
- define_attribute(name, type, **options.slice(:default))
252
+ define_attribute(name, _lookup_cast_type(name, type, options), **options.slice(:default))
254
253
  end
255
254
  end
256
255
 
257
256
  private
258
-
259
257
  NO_DEFAULT_PROVIDED = Object.new # :nodoc:
260
258
  private_constant :NO_DEFAULT_PROVIDED
261
259
 
@@ -274,6 +272,32 @@ module ActiveRecord
274
272
  end
275
273
  _default_attributes[name] = default_attribute
276
274
  end
275
+
276
+ def decorate_attribute_type(attr_name, **default)
277
+ type, options = attributes_to_define_after_schema_loads[attr_name]
278
+
279
+ default.with_defaults!(default: options[:default]) if options&.key?(:default)
280
+
281
+ attribute(attr_name, **default) do |cast_type|
282
+ if type && !type.is_a?(Proc)
283
+ cast_type = _lookup_cast_type(attr_name, type, options)
284
+ end
285
+
286
+ yield cast_type
287
+ end
288
+ end
289
+
290
+ def _lookup_cast_type(name, type, options)
291
+ case type
292
+ when Symbol
293
+ adapter_name = ActiveRecord::Type.adapter_name_from(self)
294
+ ActiveRecord::Type.lookup(type, **options.except(:default), adapter: adapter_name)
295
+ when Proc
296
+ type[type_for_attribute(name)]
297
+ else
298
+ type || type_for_attribute(name)
299
+ end
300
+ end
277
301
  end
278
302
  end
279
303
  end