activerecord 5.2.1.1 → 6.0.1

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +738 -445
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +9 -2
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/association_relation.rb +18 -9
  9. data/lib/active_record/associations.rb +20 -15
  10. data/lib/active_record/associations/association.rb +69 -20
  11. data/lib/active_record/associations/association_scope.rb +4 -6
  12. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  14. data/lib/active_record/associations/builder/association.rb +14 -18
  15. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +35 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +15 -29
  22. data/lib/active_record/associations/collection_proxy.rb +19 -48
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +11 -10
  25. data/lib/active_record/associations/has_many_through_association.rb +42 -25
  26. data/lib/active_record/associations/has_one_association.rb +28 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  28. data/lib/active_record/associations/join_dependency.rb +28 -28
  29. data/lib/active_record/associations/join_dependency/join_association.rb +27 -7
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +39 -31
  32. data/lib/active_record/associations/preloader/association.rb +38 -36
  33. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/attribute_assignment.rb +7 -10
  36. data/lib/active_record/attribute_methods.rb +28 -100
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  38. data/lib/active_record/attribute_methods/dirty.rb +114 -38
  39. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  40. data/lib/active_record/attribute_methods/query.rb +2 -3
  41. data/lib/active_record/attribute_methods/read.rb +15 -53
  42. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  44. data/lib/active_record/attribute_methods/write.rb +17 -24
  45. data/lib/active_record/attributes.rb +13 -0
  46. data/lib/active_record/autosave_association.rb +27 -13
  47. data/lib/active_record/base.rb +2 -3
  48. data/lib/active_record/callbacks.rb +6 -20
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +140 -27
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +22 -4
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +116 -127
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +26 -11
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +135 -56
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +189 -43
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +151 -198
  61. data/lib/active_record/connection_adapters/column.rb +17 -13
  62. data/lib/active_record/connection_adapters/connection_specification.rb +55 -45
  63. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +9 -4
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  66. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  67. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  68. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  69. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  70. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  71. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  72. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +8 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  81. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  82. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  83. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +47 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  86. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +65 -77
  87. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +172 -74
  90. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  91. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  92. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  93. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -5
  94. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  95. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +131 -143
  96. data/lib/active_record/connection_handling.rb +155 -26
  97. data/lib/active_record/core.rb +104 -59
  98. data/lib/active_record/counter_cache.rb +4 -29
  99. data/lib/active_record/database_configurations.rb +233 -0
  100. data/lib/active_record/database_configurations/database_config.rb +37 -0
  101. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  102. data/lib/active_record/database_configurations/url_config.rb +79 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/enum.rb +38 -7
  105. data/lib/active_record/errors.rb +30 -16
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  108. data/lib/active_record/fixture_set/render_context.rb +17 -0
  109. data/lib/active_record/fixture_set/table_row.rb +153 -0
  110. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  111. data/lib/active_record/fixtures.rb +145 -472
  112. data/lib/active_record/gem_version.rb +3 -3
  113. data/lib/active_record/inheritance.rb +13 -3
  114. data/lib/active_record/insert_all.rb +179 -0
  115. data/lib/active_record/integration.rb +68 -16
  116. data/lib/active_record/internal_metadata.rb +10 -2
  117. data/lib/active_record/locking/optimistic.rb +5 -6
  118. data/lib/active_record/locking/pessimistic.rb +3 -3
  119. data/lib/active_record/log_subscriber.rb +7 -26
  120. data/lib/active_record/middleware/database_selector.rb +75 -0
  121. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  122. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  123. data/lib/active_record/migration.rb +100 -81
  124. data/lib/active_record/migration/command_recorder.rb +50 -6
  125. data/lib/active_record/migration/compatibility.rb +91 -64
  126. data/lib/active_record/model_schema.rb +34 -10
  127. data/lib/active_record/nested_attributes.rb +2 -2
  128. data/lib/active_record/no_touching.rb +7 -0
  129. data/lib/active_record/persistence.rb +233 -28
  130. data/lib/active_record/query_cache.rb +11 -4
  131. data/lib/active_record/querying.rb +33 -21
  132. data/lib/active_record/railtie.rb +81 -46
  133. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  134. data/lib/active_record/railties/controller_runtime.rb +30 -35
  135. data/lib/active_record/railties/databases.rake +196 -46
  136. data/lib/active_record/reflection.rb +42 -44
  137. data/lib/active_record/relation.rb +320 -70
  138. data/lib/active_record/relation/batches.rb +13 -10
  139. data/lib/active_record/relation/calculations.rb +67 -57
  140. data/lib/active_record/relation/delegation.rb +48 -35
  141. data/lib/active_record/relation/finder_methods.rb +30 -30
  142. data/lib/active_record/relation/merger.rb +19 -25
  143. data/lib/active_record/relation/predicate_builder.rb +18 -15
  144. data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -6
  145. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  146. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  147. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  148. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  149. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  150. data/lib/active_record/relation/query_attribute.rb +17 -10
  151. data/lib/active_record/relation/query_methods.rb +236 -73
  152. data/lib/active_record/relation/spawn_methods.rb +1 -1
  153. data/lib/active_record/relation/where_clause.rb +14 -10
  154. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  155. data/lib/active_record/result.rb +30 -11
  156. data/lib/active_record/sanitization.rb +32 -40
  157. data/lib/active_record/schema.rb +2 -11
  158. data/lib/active_record/schema_dumper.rb +22 -7
  159. data/lib/active_record/schema_migration.rb +5 -1
  160. data/lib/active_record/scoping.rb +8 -8
  161. data/lib/active_record/scoping/default.rb +6 -7
  162. data/lib/active_record/scoping/named.rb +21 -15
  163. data/lib/active_record/statement_cache.rb +32 -5
  164. data/lib/active_record/store.rb +87 -8
  165. data/lib/active_record/table_metadata.rb +10 -17
  166. data/lib/active_record/tasks/database_tasks.rb +195 -26
  167. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  168. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  169. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  170. data/lib/active_record/test_databases.rb +23 -0
  171. data/lib/active_record/test_fixtures.rb +224 -0
  172. data/lib/active_record/timestamp.rb +39 -25
  173. data/lib/active_record/touch_later.rb +4 -2
  174. data/lib/active_record/transactions.rb +57 -66
  175. data/lib/active_record/translation.rb +1 -1
  176. data/lib/active_record/type.rb +3 -4
  177. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  178. data/lib/active_record/type_caster/connection.rb +15 -14
  179. data/lib/active_record/type_caster/map.rb +1 -4
  180. data/lib/active_record/validations.rb +1 -0
  181. data/lib/active_record/validations/uniqueness.rb +15 -27
  182. data/lib/arel.rb +58 -0
  183. data/lib/arel/alias_predication.rb +9 -0
  184. data/lib/arel/attributes.rb +22 -0
  185. data/lib/arel/attributes/attribute.rb +37 -0
  186. data/lib/arel/collectors/bind.rb +24 -0
  187. data/lib/arel/collectors/composite.rb +31 -0
  188. data/lib/arel/collectors/plain_string.rb +20 -0
  189. data/lib/arel/collectors/sql_string.rb +20 -0
  190. data/lib/arel/collectors/substitute_binds.rb +28 -0
  191. data/lib/arel/crud.rb +42 -0
  192. data/lib/arel/delete_manager.rb +18 -0
  193. data/lib/arel/errors.rb +9 -0
  194. data/lib/arel/expressions.rb +29 -0
  195. data/lib/arel/factory_methods.rb +49 -0
  196. data/lib/arel/insert_manager.rb +49 -0
  197. data/lib/arel/math.rb +45 -0
  198. data/lib/arel/nodes.rb +68 -0
  199. data/lib/arel/nodes/and.rb +32 -0
  200. data/lib/arel/nodes/ascending.rb +23 -0
  201. data/lib/arel/nodes/binary.rb +52 -0
  202. data/lib/arel/nodes/bind_param.rb +36 -0
  203. data/lib/arel/nodes/case.rb +55 -0
  204. data/lib/arel/nodes/casted.rb +50 -0
  205. data/lib/arel/nodes/comment.rb +29 -0
  206. data/lib/arel/nodes/count.rb +12 -0
  207. data/lib/arel/nodes/delete_statement.rb +45 -0
  208. data/lib/arel/nodes/descending.rb +23 -0
  209. data/lib/arel/nodes/equality.rb +18 -0
  210. data/lib/arel/nodes/extract.rb +24 -0
  211. data/lib/arel/nodes/false.rb +16 -0
  212. data/lib/arel/nodes/full_outer_join.rb +8 -0
  213. data/lib/arel/nodes/function.rb +44 -0
  214. data/lib/arel/nodes/grouping.rb +8 -0
  215. data/lib/arel/nodes/in.rb +8 -0
  216. data/lib/arel/nodes/infix_operation.rb +80 -0
  217. data/lib/arel/nodes/inner_join.rb +8 -0
  218. data/lib/arel/nodes/insert_statement.rb +37 -0
  219. data/lib/arel/nodes/join_source.rb +20 -0
  220. data/lib/arel/nodes/matches.rb +18 -0
  221. data/lib/arel/nodes/named_function.rb +23 -0
  222. data/lib/arel/nodes/node.rb +50 -0
  223. data/lib/arel/nodes/node_expression.rb +13 -0
  224. data/lib/arel/nodes/outer_join.rb +8 -0
  225. data/lib/arel/nodes/over.rb +15 -0
  226. data/lib/arel/nodes/regexp.rb +16 -0
  227. data/lib/arel/nodes/right_outer_join.rb +8 -0
  228. data/lib/arel/nodes/select_core.rb +67 -0
  229. data/lib/arel/nodes/select_statement.rb +41 -0
  230. data/lib/arel/nodes/sql_literal.rb +16 -0
  231. data/lib/arel/nodes/string_join.rb +11 -0
  232. data/lib/arel/nodes/table_alias.rb +27 -0
  233. data/lib/arel/nodes/terminal.rb +16 -0
  234. data/lib/arel/nodes/true.rb +16 -0
  235. data/lib/arel/nodes/unary.rb +45 -0
  236. data/lib/arel/nodes/unary_operation.rb +20 -0
  237. data/lib/arel/nodes/unqualified_column.rb +22 -0
  238. data/lib/arel/nodes/update_statement.rb +41 -0
  239. data/lib/arel/nodes/values_list.rb +9 -0
  240. data/lib/arel/nodes/window.rb +126 -0
  241. data/lib/arel/nodes/with.rb +11 -0
  242. data/lib/arel/order_predications.rb +13 -0
  243. data/lib/arel/predications.rb +257 -0
  244. data/lib/arel/select_manager.rb +271 -0
  245. data/lib/arel/table.rb +110 -0
  246. data/lib/arel/tree_manager.rb +72 -0
  247. data/lib/arel/update_manager.rb +34 -0
  248. data/lib/arel/visitors.rb +20 -0
  249. data/lib/arel/visitors/depth_first.rb +204 -0
  250. data/lib/arel/visitors/dot.rb +297 -0
  251. data/lib/arel/visitors/ibm_db.rb +34 -0
  252. data/lib/arel/visitors/informix.rb +62 -0
  253. data/lib/arel/visitors/mssql.rb +157 -0
  254. data/lib/arel/visitors/mysql.rb +83 -0
  255. data/lib/arel/visitors/oracle.rb +159 -0
  256. data/lib/arel/visitors/oracle12.rb +66 -0
  257. data/lib/arel/visitors/postgresql.rb +110 -0
  258. data/lib/arel/visitors/sqlite.rb +39 -0
  259. data/lib/arel/visitors/to_sql.rb +889 -0
  260. data/lib/arel/visitors/visitor.rb +46 -0
  261. data/lib/arel/visitors/where_sql.rb +23 -0
  262. data/lib/arel/window_predications.rb +9 -0
  263. data/lib/rails/generators/active_record/migration.rb +14 -1
  264. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  265. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  266. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  267. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  268. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  269. metadata +111 -27
  270. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -14,41 +14,34 @@ module ActiveRecord
14
14
  [key] if key
15
15
  end
16
16
 
17
- # Returns the primary key value.
17
+ # Returns the primary key column's value.
18
18
  def id
19
- sync_with_transaction_state
20
- primary_key = self.class.primary_key
21
- _read_attribute(primary_key) if primary_key
19
+ _read_attribute(@primary_key)
22
20
  end
23
21
 
24
- # Sets the primary key value.
22
+ # Sets the primary key column's value.
25
23
  def id=(value)
26
- sync_with_transaction_state
27
- primary_key = self.class.primary_key
28
- _write_attribute(primary_key, value) if primary_key
24
+ _write_attribute(@primary_key, value)
29
25
  end
30
26
 
31
- # Queries the primary key value.
27
+ # Queries the primary key column's value.
32
28
  def id?
33
- sync_with_transaction_state
34
- query_attribute(self.class.primary_key)
29
+ query_attribute(@primary_key)
35
30
  end
36
31
 
37
- # Returns the primary key value before type cast.
32
+ # Returns the primary key column's value before type cast.
38
33
  def id_before_type_cast
39
- sync_with_transaction_state
40
- read_attribute_before_type_cast(self.class.primary_key)
34
+ read_attribute_before_type_cast(@primary_key)
41
35
  end
42
36
 
43
- # Returns the primary key previous value.
37
+ # Returns the primary key column's previous value.
44
38
  def id_was
45
- sync_with_transaction_state
46
- attribute_was(self.class.primary_key)
39
+ attribute_was(@primary_key)
47
40
  end
48
41
 
42
+ # Returns the primary key column's value from the database.
49
43
  def id_in_database
50
- sync_with_transaction_state
51
- attribute_in_database(self.class.primary_key)
44
+ attribute_in_database(@primary_key)
52
45
  end
53
46
 
54
47
  private
@@ -83,7 +76,7 @@ module ActiveRecord
83
76
  end
84
77
 
85
78
  def reset_primary_key #:nodoc:
86
- if self == base_class
79
+ if base_class?
87
80
  self.primary_key = get_primary_key(base_class.name)
88
81
  else
89
82
  self.primary_key = base_class.primary_key
@@ -121,7 +114,7 @@ module ActiveRecord
121
114
  #
122
115
  # Project.primary_key # => "foo_id"
123
116
  def primary_key=(value)
124
- @primary_key = value && value.to_s
117
+ @primary_key = value && -value.to_s
125
118
  @quoted_primary_key = nil
126
119
  @attributes_builder = nil
127
120
  end
@@ -131,7 +124,7 @@ module ActiveRecord
131
124
  def suppress_composite_primary_key(pk)
132
125
  return pk unless pk.is_a?(Array)
133
126
 
134
- warn <<-WARNING.strip_heredoc
127
+ warn <<~WARNING
135
128
  WARNING: Active Record does not support composite primary key.
136
129
 
137
130
  #{table_name} has composite primary key. Composite primary key is ignored.
@@ -16,8 +16,7 @@ module ActiveRecord
16
16
  when true then true
17
17
  when false, nil then false
18
18
  else
19
- column = self.class.columns_hash[attr_name]
20
- if column.nil?
19
+ if !type_for_attribute(attr_name) { false }
21
20
  if Numeric === value || value !~ /[^0-9]/
22
21
  !value.to_i.zero?
23
22
  else
@@ -33,7 +32,7 @@ module ActiveRecord
33
32
  end
34
33
 
35
34
  private
36
- # Handle *? for method_missing.
35
+ # Dispatch target for <tt>*?</tt> attribute methods.
37
36
  def attribute?(attribute_name)
38
37
  query_attribute(attribute_name)
39
38
  end
@@ -8,42 +8,16 @@ module ActiveRecord
8
8
  module ClassMethods # :nodoc:
9
9
  private
10
10
 
11
- # We want to generate the methods via module_eval rather than
12
- # define_method, because define_method is slower on dispatch.
13
- # Evaluating many similar methods may use more memory as the instruction
14
- # sequences are duplicated and cached (in MRI). define_method may
15
- # be slower on dispatch, but if you're careful about the closure
16
- # created, then define_method will consume much less memory.
17
- #
18
- # But sometimes the database might return columns with
19
- # characters that are not allowed in normal method names (like
20
- # 'my_column(omg)'. So to work around this we first define with
21
- # the __temp__ identifier, and then use alias method to rename
22
- # it to what we want.
23
- #
24
- # We are also defining a constant to hold the frozen string of
25
- # the attribute name. Using a constant means that we do not have
26
- # to allocate an object on each call to the attribute method.
27
- # Making it frozen means that it doesn't get duped when used to
28
- # key the @attributes in read_attribute.
29
11
  def define_method_attribute(name)
30
- safe_name = name.unpack("h*".freeze).first
31
- temp_method = "__temp__#{safe_name}"
32
-
33
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
34
- sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
35
-
36
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
37
- def #{temp_method}
38
- #{sync_with_transaction_state}
39
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
40
- _read_attribute(name) { |n| missing_attribute(n, caller) }
41
- end
42
- STR
43
-
44
- generated_attribute_methods.module_eval do
45
- alias_method name, temp_method
46
- undef_method temp_method
12
+ ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
13
+ generated_attribute_methods, name
14
+ ) 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
47
21
  end
48
22
  end
49
23
  end
@@ -52,30 +26,18 @@ module ActiveRecord
52
26
  # it has been typecast (for example, "2004-12-12" in a date column is cast
53
27
  # to a date object, like Date.new(2004, 12, 12)).
54
28
  def read_attribute(attr_name, &block)
55
- name = if self.class.attribute_alias?(attr_name)
56
- self.class.attribute_alias(attr_name).to_s
57
- else
58
- attr_name.to_s
59
- end
29
+ name = attr_name.to_s
30
+ name = self.class.attribute_aliases[name] || name
60
31
 
61
- primary_key = self.class.primary_key
62
- name = primary_key if name == "id".freeze && primary_key
63
- sync_with_transaction_state if name == primary_key
32
+ name = @primary_key if name == "id" && @primary_key
64
33
  _read_attribute(name, &block)
65
34
  end
66
35
 
67
36
  # This method exists to avoid the expensive primary_key check internally, without
68
37
  # breaking compatibility with the read_attribute API
69
- if defined?(JRUBY_VERSION)
70
- # This form is significantly faster on JRuby, and this is one of our biggest hotspots.
71
- # https://github.com/jruby/jruby/pull/2562
72
- def _read_attribute(attr_name, &block) # :nodoc:
73
- @attributes.fetch_value(attr_name.to_s, &block)
74
- end
75
- else
76
- def _read_attribute(attr_name) # :nodoc:
77
- @attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
78
- end
38
+ 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)
79
41
  end
80
42
 
81
43
  alias :attribute :_read_attribute
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
 
8
8
  class ColumnNotSerializableError < StandardError
9
9
  def initialize(name, type)
10
- super <<-EOS.strip_heredoc
10
+ super <<~EOS
11
11
  Column `#{name}` of type #{type.class} does not support `serialize` feature.
12
12
  Usually it means that you are trying to use `serialize`
13
13
  on a column that already implements serialization natively.
@@ -73,7 +73,7 @@ module ActiveRecord
73
73
  # `skip_time_zone_conversion_for_attributes` would not be picked up.
74
74
  subclass.class_eval do
75
75
  matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
76
- decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
76
+ decorate_matching_attribute_types(matcher, "_time_zone_conversion") do |type|
77
77
  TimeZoneConverter.new(type)
78
78
  end
79
79
  end
@@ -13,19 +13,16 @@ module ActiveRecord
13
13
  private
14
14
 
15
15
  def define_method_attribute=(name)
16
- safe_name = name.unpack("h*".freeze).first
17
- ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
18
- sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
19
-
20
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
21
- def __temp__#{safe_name}=(value)
22
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
23
- #{sync_with_transaction_state}
24
- _write_attribute(name, value)
25
- end
26
- alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
27
- undef_method :__temp__#{safe_name}=
28
- STR
16
+ ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
17
+ generated_attribute_methods, name, writer: true,
18
+ ) 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
25
+ end
29
26
  end
30
27
  end
31
28
 
@@ -33,33 +30,29 @@ module ActiveRecord
33
30
  # specified +value+. Empty strings for Integer and Float columns are
34
31
  # turned into +nil+.
35
32
  def write_attribute(attr_name, value)
36
- name = if self.class.attribute_alias?(attr_name)
37
- self.class.attribute_alias(attr_name).to_s
38
- else
39
- attr_name.to_s
40
- end
33
+ name = attr_name.to_s
34
+ name = self.class.attribute_aliases[name] || name
41
35
 
42
- primary_key = self.class.primary_key
43
- name = primary_key if name == "id".freeze && primary_key
44
- sync_with_transaction_state if name == primary_key
36
+ name = @primary_key if name == "id" && @primary_key
45
37
  _write_attribute(name, value)
46
38
  end
47
39
 
48
40
  # This method exists to avoid the expensive primary_key check internally, without
49
41
  # breaking compatibility with the write_attribute API
50
42
  def _write_attribute(attr_name, value) # :nodoc:
43
+ sync_with_transaction_state if @transaction_state&.finalized?
51
44
  @attributes.write_from_user(attr_name.to_s, value)
52
45
  value
53
46
  end
54
47
 
55
48
  private
56
49
  def write_attribute_without_type_cast(attr_name, value)
57
- name = attr_name.to_s
58
- @attributes.write_cast_value(name, value)
50
+ sync_with_transaction_state if @transaction_state&.finalized?
51
+ @attributes.write_cast_value(attr_name.to_s, value)
59
52
  value
60
53
  end
61
54
 
62
- # Handle *= for method_missing.
55
+ # Dispatch target for <tt>*=</tt> attribute methods.
63
56
  def attribute=(attribute_name, value)
64
57
  _write_attribute(attribute_name, value)
65
58
  end
@@ -41,6 +41,9 @@ module ActiveRecord
41
41
  # +range+ (PostgreSQL only) specifies that the type should be a range (see the
42
42
  # examples below).
43
43
  #
44
+ # When using a symbol for +cast_type+, extra options are forwarded to the
45
+ # constructor of the type object.
46
+ #
44
47
  # ==== Examples
45
48
  #
46
49
  # The type detected by Active Record can be overridden.
@@ -112,6 +115,16 @@ module ActiveRecord
112
115
  # my_float_range: 1.0..3.5
113
116
  # }
114
117
  #
118
+ # Passing options to the type constructor
119
+ #
120
+ # # app/models/my_model.rb
121
+ # class MyModel < ActiveRecord::Base
122
+ # attribute :small_int, :integer, limit: 2
123
+ # end
124
+ #
125
+ # MyModel.create(small_int: 65537)
126
+ # # => Error: 65537 is out of range for the limit of two bytes
127
+ #
115
128
  # ==== Creating Custom Types
116
129
  #
117
130
  # Users may also define their own custom types, as long as they respond
@@ -149,7 +149,7 @@ module ActiveRecord
149
149
  private
150
150
 
151
151
  def define_non_cyclic_method(name, &block)
152
- return if method_defined?(name)
152
+ return if instance_methods(false).include?(name)
153
153
  define_method(name) do |*args|
154
154
  result = true; @_already_called ||= {}
155
155
  # Loop prevention for validation of associations
@@ -272,7 +272,7 @@ module ActiveRecord
272
272
  # or saved. If +autosave+ is +false+ only new records will be returned,
273
273
  # unless the parent is/was a new record itself.
274
274
  def associated_records_to_validate_or_save(association, new_record, autosave)
275
- if new_record
275
+ if new_record || custom_validation_context?
276
276
  association && association.target
277
277
  elsif autosave
278
278
  association.target.find_all(&:changed_for_autosave?)
@@ -304,7 +304,7 @@ module ActiveRecord
304
304
  def validate_single_association(reflection)
305
305
  association = association_instance_get(reflection.name)
306
306
  record = association && association.reader
307
- association_valid?(reflection, record) if record
307
+ association_valid?(reflection, record) if record && (record.changed_for_autosave? || custom_validation_context?)
308
308
  end
309
309
 
310
310
  # Validate the associated records if <tt>:validate</tt> or
@@ -324,7 +324,7 @@ module ActiveRecord
324
324
  def association_valid?(reflection, record, index = nil)
325
325
  return true if record.destroyed? || (reflection.options[:autosave] && record.marked_for_destruction?)
326
326
 
327
- context = validation_context unless [:create, :update].include?(validation_context)
327
+ context = validation_context if custom_validation_context?
328
328
 
329
329
  unless valid = record.valid?(context)
330
330
  if reflection.options[:autosave]
@@ -382,37 +382,41 @@ module ActiveRecord
382
382
  if association = association_instance_get(reflection.name)
383
383
  autosave = reflection.options[:autosave]
384
384
 
385
+ # By saving the instance variable in a local variable,
386
+ # we make the whole callback re-entrant.
387
+ new_record_before_save = @new_record_before_save
388
+
385
389
  # reconstruct the scope now that we know the owner's id
386
390
  association.reset_scope
387
391
 
388
- if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
392
+ if records = associated_records_to_validate_or_save(association, new_record_before_save, autosave)
389
393
  if autosave
390
394
  records_to_destroy = records.select(&:marked_for_destruction?)
391
395
  records_to_destroy.each { |record| association.destroy(record) }
392
396
  records -= records_to_destroy
393
397
  end
394
398
 
395
- records.each_with_index do |record, index|
399
+ records.each do |record|
396
400
  next if record.destroyed?
397
401
 
398
402
  saved = true
399
403
 
400
- if autosave != false && (@new_record_before_save || record.new_record?)
404
+ if autosave != false && (new_record_before_save || record.new_record?)
401
405
  if autosave
402
406
  saved = association.insert_record(record, false)
403
407
  elsif !reflection.nested?
408
+ association_saved = association.insert_record(record)
409
+
404
410
  if reflection.validate?
405
- valid = association_valid?(reflection, record, index)
406
- saved = valid ? association.insert_record(record, false) : false
407
- else
408
- association.insert_record(record)
411
+ errors.add(reflection.name) unless association_saved
412
+ saved = association_saved
409
413
  end
410
414
  end
411
415
  elsif autosave
412
416
  saved = record.save(validate: false)
413
417
  end
414
418
 
415
- raise ActiveRecord::Rollback unless saved
419
+ raise(RecordInvalid.new(association.owner)) unless saved
416
420
  end
417
421
  end
418
422
  end
@@ -457,10 +461,16 @@ module ActiveRecord
457
461
  # If the record is new or it has changed, returns true.
458
462
  def record_changed?(reflection, record, key)
459
463
  record.new_record? ||
460
- (record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key) ||
464
+ association_foreign_key_changed?(reflection, record, key) ||
461
465
  record.will_save_change_to_attribute?(reflection.foreign_key)
462
466
  end
463
467
 
468
+ def association_foreign_key_changed?(reflection, record, key)
469
+ return false if reflection.through_reflection?
470
+
471
+ record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key
472
+ end
473
+
464
474
  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
465
475
  #
466
476
  # In addition, it will destroy the association if it was marked for destruction.
@@ -489,6 +499,10 @@ module ActiveRecord
489
499
  end
490
500
  end
491
501
 
502
+ def custom_validation_context?
503
+ validation_context && [:create, :update].exclude?(validation_context)
504
+ end
505
+
492
506
  def _ensure_no_duplicate_errors
493
507
  errors.messages.each_key do |attribute|
494
508
  errors[attribute].uniq!
@@ -9,7 +9,6 @@ require "active_support/core_ext/module/attribute_accessors"
9
9
  require "active_support/core_ext/array/extract_options"
10
10
  require "active_support/core_ext/hash/deep_merge"
11
11
  require "active_support/core_ext/hash/slice"
12
- require "active_support/core_ext/hash/transform_values"
13
12
  require "active_support/core_ext/string/behavior"
14
13
  require "active_support/core_ext/kernel/singleton_class"
15
14
  require "active_support/core_ext/module/introspection"
@@ -23,6 +22,7 @@ require "active_record/explain_subscriber"
23
22
  require "active_record/relation/delegation"
24
23
  require "active_record/attributes"
25
24
  require "active_record/type_caster"
25
+ require "active_record/database_configurations"
26
26
 
27
27
  module ActiveRecord #:nodoc:
28
28
  # = Active Record
@@ -288,7 +288,7 @@ module ActiveRecord #:nodoc:
288
288
  extend Explain
289
289
  extend Enum
290
290
  extend Delegation::DelegateCache
291
- extend CollectionCacheKey
291
+ extend Aggregations::ClassMethods
292
292
 
293
293
  include Core
294
294
  include Persistence
@@ -314,7 +314,6 @@ module ActiveRecord #:nodoc:
314
314
  include ActiveModel::SecurePassword
315
315
  include AutosaveAssociation
316
316
  include NestedAttributes
317
- include Aggregations
318
317
  include Transactions
319
318
  include TouchLater
320
319
  include NoTouching