activerecord 5.2.8.1 → 6.0.0.beta1

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -816
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +35 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  12. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  14. data/lib/active_record/associations/collection_association.rb +11 -25
  15. data/lib/active_record/associations/collection_proxy.rb +32 -6
  16. data/lib/active_record/associations/foreign_association.rb +7 -0
  17. data/lib/active_record/associations/has_many_association.rb +1 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  19. data/lib/active_record/associations/has_one_association.rb +28 -30
  20. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  21. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  22. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/preloader/association.rb +1 -2
  25. data/lib/active_record/associations/preloader.rb +32 -29
  26. data/lib/active_record/associations/singular_association.rb +2 -16
  27. data/lib/active_record/associations.rb +16 -12
  28. data/lib/active_record/attribute_assignment.rb +7 -10
  29. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  31. data/lib/active_record/attribute_methods/read.rb +16 -48
  32. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  34. data/lib/active_record/attribute_methods/write.rb +15 -16
  35. data/lib/active_record/attribute_methods.rb +34 -56
  36. data/lib/active_record/autosave_association.rb +7 -21
  37. data/lib/active_record/base.rb +2 -2
  38. data/lib/active_record/callbacks.rb +3 -17
  39. data/lib/active_record/coders/yaml_column.rb +1 -13
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +75 -52
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations/database_config.rb +37 -0
  83. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  84. data/lib/active_record/database_configurations/url_config.rb +74 -0
  85. data/lib/active_record/database_configurations.rb +184 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration/command_recorder.rb +35 -5
  102. data/lib/active_record/migration/compatibility.rb +34 -16
  103. data/lib/active_record/migration.rb +38 -37
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -60
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation/batches.rb +13 -10
  116. data/lib/active_record/relation/calculations.rb +38 -28
  117. data/lib/active_record/relation/delegation.rb +4 -13
  118. data/lib/active_record/relation/finder_methods.rb +12 -25
  119. data/lib/active_record/relation/merger.rb +2 -6
  120. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  121. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  122. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  123. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  124. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  125. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  126. data/lib/active_record/relation/predicate_builder.rb +4 -6
  127. data/lib/active_record/relation/query_attribute.rb +15 -12
  128. data/lib/active_record/relation/query_methods.rb +29 -52
  129. data/lib/active_record/relation/where_clause.rb +4 -0
  130. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  131. data/lib/active_record/relation.rb +150 -69
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping/default.rb +10 -3
  138. data/lib/active_record/scoping/named.rb +10 -14
  139. data/lib/active_record/scoping.rb +9 -8
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  153. data/lib/active_record/type.rb +3 -4
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/active_record.rb +2 -1
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes/attribute.rb +37 -0
  160. data/lib/arel/attributes.rb +22 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes/and.rb +32 -0
  174. data/lib/arel/nodes/ascending.rb +23 -0
  175. data/lib/arel/nodes/binary.rb +52 -0
  176. data/lib/arel/nodes/bind_param.rb +36 -0
  177. data/lib/arel/nodes/case.rb +55 -0
  178. data/lib/arel/nodes/casted.rb +50 -0
  179. data/lib/arel/nodes/count.rb +12 -0
  180. data/lib/arel/nodes/delete_statement.rb +45 -0
  181. data/lib/arel/nodes/descending.rb +23 -0
  182. data/lib/arel/nodes/equality.rb +18 -0
  183. data/lib/arel/nodes/extract.rb +24 -0
  184. data/lib/arel/nodes/false.rb +16 -0
  185. data/lib/arel/nodes/full_outer_join.rb +8 -0
  186. data/lib/arel/nodes/function.rb +44 -0
  187. data/lib/arel/nodes/grouping.rb +8 -0
  188. data/lib/arel/nodes/in.rb +8 -0
  189. data/lib/arel/nodes/infix_operation.rb +80 -0
  190. data/lib/arel/nodes/inner_join.rb +8 -0
  191. data/lib/arel/nodes/insert_statement.rb +37 -0
  192. data/lib/arel/nodes/join_source.rb +20 -0
  193. data/lib/arel/nodes/matches.rb +18 -0
  194. data/lib/arel/nodes/named_function.rb +23 -0
  195. data/lib/arel/nodes/node.rb +50 -0
  196. data/lib/arel/nodes/node_expression.rb +13 -0
  197. data/lib/arel/nodes/outer_join.rb +8 -0
  198. data/lib/arel/nodes/over.rb +15 -0
  199. data/lib/arel/nodes/regexp.rb +16 -0
  200. data/lib/arel/nodes/right_outer_join.rb +8 -0
  201. data/lib/arel/nodes/select_core.rb +63 -0
  202. data/lib/arel/nodes/select_statement.rb +41 -0
  203. data/lib/arel/nodes/sql_literal.rb +16 -0
  204. data/lib/arel/nodes/string_join.rb +11 -0
  205. data/lib/arel/nodes/table_alias.rb +27 -0
  206. data/lib/arel/nodes/terminal.rb +16 -0
  207. data/lib/arel/nodes/true.rb +16 -0
  208. data/lib/arel/nodes/unary.rb +44 -0
  209. data/lib/arel/nodes/unary_operation.rb +20 -0
  210. data/lib/arel/nodes/unqualified_column.rb +22 -0
  211. data/lib/arel/nodes/update_statement.rb +41 -0
  212. data/lib/arel/nodes/values.rb +16 -0
  213. data/lib/arel/nodes/values_list.rb +24 -0
  214. data/lib/arel/nodes/window.rb +126 -0
  215. data/lib/arel/nodes/with.rb +11 -0
  216. data/lib/arel/nodes.rb +67 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors/depth_first.rb +199 -0
  224. data/lib/arel/visitors/dot.rb +292 -0
  225. data/lib/arel/visitors/ibm_db.rb +21 -0
  226. data/lib/arel/visitors/informix.rb +56 -0
  227. data/lib/arel/visitors/mssql.rb +143 -0
  228. data/lib/arel/visitors/mysql.rb +83 -0
  229. data/lib/arel/visitors/oracle.rb +159 -0
  230. data/lib/arel/visitors/oracle12.rb +67 -0
  231. data/lib/arel/visitors/postgresql.rb +116 -0
  232. data/lib/arel/visitors/sqlite.rb +39 -0
  233. data/lib/arel/visitors/to_sql.rb +913 -0
  234. data/lib/arel/visitors/visitor.rb +42 -0
  235. data/lib/arel/visitors/where_sql.rb +23 -0
  236. data/lib/arel/visitors.rb +20 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/arel.rb +44 -0
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/migration.rb +14 -1
  241. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  242. metadata +107 -29
@@ -36,11 +36,12 @@ module ActiveRecord
36
36
  end
37
37
  end
38
38
 
39
- # Did this attribute change when we last saved? This method can be invoked
40
- # as +saved_change_to_name?+ instead of <tt>saved_change_to_attribute?("name")</tt>.
41
- # Behaves similarly to +attribute_changed?+. This method is useful in
42
- # after callbacks to determine if the call to save changed a certain
43
- # attribute.
39
+ # Did this attribute change when we last saved?
40
+ #
41
+ # This method is useful in after callbacks to determine if an attribute
42
+ # was changed during the save that triggered the callbacks to run. It can
43
+ # be invoked as +saved_change_to_name?+ instead of
44
+ # <tt>saved_change_to_attribute?("name")</tt>.
44
45
  #
45
46
  # ==== Options
46
47
  #
@@ -57,19 +58,20 @@ module ActiveRecord
57
58
  # attribute was changed, the result will be an array containing the
58
59
  # original value and the saved value.
59
60
  #
60
- # Behaves similarly to +attribute_change+. This method is useful in after
61
- # callbacks, to see the change in an attribute that just occurred
62
- #
63
- # This method can be invoked as +saved_change_to_name+ in instead of
64
- # <tt>saved_change_to_attribute("name")</tt>
61
+ # This method is useful in after callbacks, to see the change in an
62
+ # attribute during the save that triggered the callbacks to run. It can be
63
+ # invoked as +saved_change_to_name+ instead of
64
+ # <tt>saved_change_to_attribute("name")</tt>.
65
65
  def saved_change_to_attribute(attr_name)
66
66
  mutations_before_last_save.change_to_attribute(attr_name)
67
67
  end
68
68
 
69
69
  # Returns the original value of an attribute before the last save.
70
- # Behaves similarly to +attribute_was+. This method is useful in after
71
- # callbacks to get the original value of an attribute before the save that
72
- # just occurred
70
+ #
71
+ # This method is useful in after callbacks to get the original value of an
72
+ # attribute before the save that triggered the callbacks to run. It can be
73
+ # invoked as +name_before_last_save+ instead of
74
+ # <tt>attribute_before_last_save("name")</tt>.
73
75
  def attribute_before_last_save(attr_name)
74
76
  mutations_before_last_save.original_value(attr_name)
75
77
  end
@@ -84,37 +86,73 @@ module ActiveRecord
84
86
  mutations_before_last_save.changes
85
87
  end
86
88
 
87
- # Alias for +attribute_changed?+
89
+ # Will this attribute change the next time we save?
90
+ #
91
+ # This method is useful in validations and before callbacks to determine
92
+ # if the next call to +save+ will change a particular attribute. It can be
93
+ # invoked as +will_save_change_to_name?+ instead of
94
+ # <tt>will_save_change_to_attribute("name")</tt>.
95
+ #
96
+ # ==== Options
97
+ #
98
+ # +from+ When passed, this method will return false unless the original
99
+ # value is equal to the given option
100
+ #
101
+ # +to+ When passed, this method will return false unless the value will be
102
+ # changed to the given value
88
103
  def will_save_change_to_attribute?(attr_name, **options)
89
104
  mutations_from_database.changed?(attr_name, **options)
90
105
  end
91
106
 
92
- # Alias for +attribute_change+
107
+ # Returns the change to an attribute that will be persisted during the
108
+ # next save.
109
+ #
110
+ # This method is useful in validations and before callbacks, to see the
111
+ # change to an attribute that will occur when the record is saved. It can
112
+ # be invoked as +name_change_to_be_saved+ instead of
113
+ # <tt>attribute_change_to_be_saved("name")</tt>.
114
+ #
115
+ # If the attribute will change, the result will be an array containing the
116
+ # original value and the new value about to be saved.
93
117
  def attribute_change_to_be_saved(attr_name)
94
118
  mutations_from_database.change_to_attribute(attr_name)
95
119
  end
96
120
 
97
- # Alias for +attribute_was+
121
+ # Returns the value of an attribute in the database, as opposed to the
122
+ # in-memory value that will be persisted the next time the record is
123
+ # saved.
124
+ #
125
+ # This method is useful in validations and before callbacks, to see the
126
+ # original value of an attribute prior to any changes about to be
127
+ # saved. It can be invoked as +name_in_database+ instead of
128
+ # <tt>attribute_in_database("name")</tt>.
98
129
  def attribute_in_database(attr_name)
99
130
  mutations_from_database.original_value(attr_name)
100
131
  end
101
132
 
102
- # Alias for +changed?+
133
+ # Will the next call to +save+ have any changes to persist?
103
134
  def has_changes_to_save?
104
135
  mutations_from_database.any_changes?
105
136
  end
106
137
 
107
- # Alias for +changes+
138
+ # Returns a hash containing all the changes that will be persisted during
139
+ # the next save.
108
140
  def changes_to_save
109
141
  mutations_from_database.changes
110
142
  end
111
143
 
112
- # Alias for +changed+
144
+ # Returns an array of the names of any attributes that will change when
145
+ # the record is next saved.
113
146
  def changed_attribute_names_to_save
114
147
  mutations_from_database.changed_attribute_names
115
148
  end
116
149
 
117
- # Alias for +changed_attributes+
150
+ # Returns a hash of the attributes that will change when the record is
151
+ # next saved.
152
+ #
153
+ # The hash keys are the attribute names, and the hash values are the
154
+ # original attribute values in the database (as opposed to the in-memory
155
+ # values about to be saved).
118
156
  def attributes_in_database
119
157
  mutations_from_database.changed_values
120
158
  end
@@ -130,20 +168,20 @@ module ActiveRecord
130
168
  result
131
169
  end
132
170
 
133
- def _update_record(*)
134
- affected_rows = partial_writes? ? super(keys_for_partial_write) : super
171
+ def _update_record(attribute_names = attribute_names_for_partial_writes)
172
+ affected_rows = super
135
173
  changes_applied
136
174
  affected_rows
137
175
  end
138
176
 
139
- def _create_record(*)
140
- id = partial_writes? ? super(keys_for_partial_write) : super
177
+ def _create_record(attribute_names = attribute_names_for_partial_writes)
178
+ id = super
141
179
  changes_applied
142
180
  id
143
181
  end
144
182
 
145
- def keys_for_partial_write
146
- changed_attribute_names_to_save & self.class.column_names
183
+ def attribute_names_for_partial_writes
184
+ partial_writes? ? changed_attribute_names_to_save : attribute_names
147
185
  end
148
186
  end
149
187
  end
@@ -14,38 +14,39 @@ 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
19
  sync_with_transaction_state
20
20
  primary_key = self.class.primary_key
21
21
  _read_attribute(primary_key) if primary_key
22
22
  end
23
23
 
24
- # Sets the primary key value.
24
+ # Sets the primary key column's value.
25
25
  def id=(value)
26
26
  sync_with_transaction_state
27
27
  primary_key = self.class.primary_key
28
28
  _write_attribute(primary_key, value) if primary_key
29
29
  end
30
30
 
31
- # Queries the primary key value.
31
+ # Queries the primary key column's value.
32
32
  def id?
33
33
  sync_with_transaction_state
34
34
  query_attribute(self.class.primary_key)
35
35
  end
36
36
 
37
- # Returns the primary key value before type cast.
37
+ # Returns the primary key column's value before type cast.
38
38
  def id_before_type_cast
39
39
  sync_with_transaction_state
40
40
  read_attribute_before_type_cast(self.class.primary_key)
41
41
  end
42
42
 
43
- # Returns the primary key previous value.
43
+ # Returns the primary key column's previous value.
44
44
  def id_was
45
45
  sync_with_transaction_state
46
46
  attribute_was(self.class.primary_key)
47
47
  end
48
48
 
49
+ # Returns the primary key column's value from the database.
49
50
  def id_in_database
50
51
  sync_with_transaction_state
51
52
  attribute_in_database(self.class.primary_key)
@@ -83,7 +84,7 @@ module ActiveRecord
83
84
  end
84
85
 
85
86
  def reset_primary_key #:nodoc:
86
- if self == base_class
87
+ if base_class?
87
88
  self.primary_key = get_primary_key(base_class.name)
88
89
  else
89
90
  self.primary_key = base_class.primary_key
@@ -131,7 +132,7 @@ module ActiveRecord
131
132
  def suppress_composite_primary_key(pk)
132
133
  return pk unless pk.is_a?(Array)
133
134
 
134
- warn <<-WARNING.strip_heredoc
135
+ warn <<~WARNING
135
136
  WARNING: Active Record does not support composite primary key.
136
137
 
137
138
  #{table_name} has composite primary key. Composite primary key is ignored.
@@ -8,42 +8,19 @@ 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
12
  sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
35
13
 
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
14
+ ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
15
+ generated_attribute_methods, name
16
+ ) do |temp_method_name, attr_name_expr|
17
+ generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
18
+ def #{temp_method_name}
19
+ #{sync_with_transaction_state}
20
+ name = #{attr_name_expr}
21
+ _read_attribute(name) { |n| missing_attribute(n, caller) }
22
+ end
23
+ RUBY
47
24
  end
48
25
  end
49
26
  end
@@ -52,30 +29,21 @@ module ActiveRecord
52
29
  # it has been typecast (for example, "2004-12-12" in a date column is cast
53
30
  # to a date object, like Date.new(2004, 12, 12)).
54
31
  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
32
+ name = attr_name.to_s
33
+ if self.class.attribute_alias?(name)
34
+ name = self.class.attribute_alias(name)
59
35
  end
60
36
 
61
37
  primary_key = self.class.primary_key
62
- name = primary_key if name == "id".freeze && primary_key
38
+ name = primary_key if name == "id" && primary_key
63
39
  sync_with_transaction_state if name == primary_key
64
40
  _read_attribute(name, &block)
65
41
  end
66
42
 
67
43
  # This method exists to avoid the expensive primary_key check internally, without
68
44
  # 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
45
+ def _read_attribute(attr_name, &block) # :nodoc
46
+ @attributes.fetch_value(attr_name.to_s, &block)
79
47
  end
80
48
 
81
49
  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,19 @@ 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
16
  sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
19
17
 
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
18
+ ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
19
+ generated_attribute_methods, name, writer: true,
20
+ ) do |temp_method_name, attr_name_expr|
21
+ generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
22
+ def #{temp_method_name}(value)
23
+ name = #{attr_name_expr}
24
+ #{sync_with_transaction_state}
25
+ _write_attribute(name, value)
26
+ end
27
+ RUBY
28
+ end
29
29
  end
30
30
  end
31
31
 
@@ -33,14 +33,13 @@ module ActiveRecord
33
33
  # specified +value+. Empty strings for Integer and Float columns are
34
34
  # turned into +nil+.
35
35
  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
36
+ name = attr_name.to_s
37
+ if self.class.attribute_alias?(name)
38
+ name = self.class.attribute_alias(name)
40
39
  end
41
40
 
42
41
  primary_key = self.class.primary_key
43
- name = primary_key if name == "id".freeze && primary_key
42
+ name = primary_key if name == "id" && primary_key
44
43
  sync_with_transaction_state if name == primary_key
45
44
  _write_attribute(name, value)
46
45
  end
@@ -22,16 +22,7 @@ module ActiveRecord
22
22
  delegate :column_for_attribute, to: :class
23
23
  end
24
24
 
25
- AttrNames = Module.new {
26
- def self.set_name_cache(name, value)
27
- const_name = "ATTR_#{name}"
28
- unless const_defined? const_name
29
- const_set const_name, value.dup.freeze
30
- end
31
- end
32
- }
33
-
34
- BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
25
+ RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
35
26
 
36
27
  class GeneratedAttributeMethods < Module #:nodoc:
37
28
  include Mutex_m
@@ -59,7 +50,7 @@ module ActiveRecord
59
50
  # attribute methods.
60
51
  generated_attribute_methods.synchronize do
61
52
  return false if @attribute_methods_generated
62
- superclass.define_attribute_methods unless self == base_class
53
+ superclass.define_attribute_methods unless base_class?
63
54
  super(attribute_names)
64
55
  @attribute_methods_generated = true
65
56
  end
@@ -123,7 +114,7 @@ module ActiveRecord
123
114
  # A class method is 'dangerous' if it is already (re)defined by Active Record, but
124
115
  # not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
125
116
  def dangerous_class_method?(method_name)
126
- BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
117
+ RESTRICTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
127
118
  end
128
119
 
129
120
  def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
@@ -167,12 +158,14 @@ module ActiveRecord
167
158
  end
168
159
  end
169
160
 
170
- # Regexp whitelist. Matches the following:
161
+ # Regexp for column names (with or without a table name prefix). Matches
162
+ # the following:
171
163
  # "#{table_name}.#{column_name}"
172
164
  # "#{column_name}"
173
- COLUMN_NAME_WHITELIST = /\A(?:\w+\.)?\w+\z/i
165
+ COLUMN_NAME = /\A(?:\w+\.)?\w+\z/i
174
166
 
175
- # Regexp whitelist. Matches the following:
167
+ # Regexp for column names with order (with or without a table name
168
+ # prefix, with or without various order modifiers). Matches the following:
176
169
  # "#{table_name}.#{column_name}"
177
170
  # "#{table_name}.#{column_name} #{direction}"
178
171
  # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
@@ -181,7 +174,7 @@ module ActiveRecord
181
174
  # "#{column_name} #{direction}"
182
175
  # "#{column_name} #{direction} NULLS FIRST"
183
176
  # "#{column_name} NULLS LAST"
184
- COLUMN_NAME_ORDER_WHITELIST = /
177
+ COLUMN_NAME_WITH_ORDER = /
185
178
  \A
186
179
  (?:\w+\.)?
187
180
  \w+
@@ -190,12 +183,10 @@ module ActiveRecord
190
183
  \z
191
184
  /ix
192
185
 
193
- def enforce_raw_sql_whitelist(args, whitelist: COLUMN_NAME_WHITELIST) # :nodoc:
186
+ def disallow_raw_sql!(args, permit: COLUMN_NAME) # :nodoc:
194
187
  unexpected = args.reject do |arg|
195
- arg.kind_of?(Arel::Node) ||
196
- arg.is_a?(Arel::Nodes::SqlLiteral) ||
197
- arg.is_a?(Arel::Attributes::Attribute) ||
198
- arg.to_s.split(/\s*,\s*/).all? { |part| whitelist.match?(part) }
188
+ Arel.arel_node?(arg) ||
189
+ arg.to_s.split(/\s*,\s*/).all? { |part| permit.match?(part) }
199
190
  end
200
191
 
201
192
  return if unexpected.none?
@@ -270,21 +261,14 @@ module ActiveRecord
270
261
  def respond_to?(name, include_private = false)
271
262
  return false unless super
272
263
 
273
- case name
274
- when :to_partial_path
275
- name = "to_partial_path".freeze
276
- when :to_model
277
- name = "to_model".freeze
278
- else
279
- name = name.to_s
280
- end
281
-
282
264
  # If the result is true then check for the select case.
283
265
  # For queries selecting a subset of columns, return false for unselected columns.
284
266
  # We check defined?(@attributes) not to issue warnings if called on objects that
285
267
  # have been allocated but not yet initialized.
286
- if defined?(@attributes) && self.class.column_names.include?(name)
287
- return has_attribute?(name)
268
+ if defined?(@attributes)
269
+ if name = self.class.symbol_column_to_string(name.to_sym)
270
+ return has_attribute?(name)
271
+ end
288
272
  end
289
273
 
290
274
  true
@@ -344,15 +328,8 @@ module ActiveRecord
344
328
  # person.attribute_for_inspect(:tag_ids)
345
329
  # # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
346
330
  def attribute_for_inspect(attr_name)
347
- value = read_attribute(attr_name)
348
-
349
- if value.is_a?(String) && value.length > 50
350
- "#{value[0, 50]}...".inspect
351
- elsif value.is_a?(Date) || value.is_a?(Time)
352
- %("#{value.to_s(:db)}")
353
- else
354
- value.inspect
355
- end
331
+ value = _read_attribute(attr_name)
332
+ format_for_inspect(value)
356
333
  end
357
334
 
358
335
  # Returns +true+ if the specified +attribute+ has been set by the user or by a
@@ -443,23 +420,12 @@ module ActiveRecord
443
420
  @attributes.accessed
444
421
  end
445
422
 
446
- protected
447
-
448
- def attribute_method?(attr_name) # :nodoc:
423
+ private
424
+ def attribute_method?(attr_name)
449
425
  # We check defined? because Syck calls respond_to? before actually calling initialize.
450
426
  defined?(@attributes) && @attributes.key?(attr_name)
451
427
  end
452
428
 
453
- private
454
-
455
- def attributes_with_values_for_create(attribute_names)
456
- attributes_with_values(attributes_for_create(attribute_names))
457
- end
458
-
459
- def attributes_with_values_for_update(attribute_names)
460
- attributes_with_values(attributes_for_update(attribute_names))
461
- end
462
-
463
429
  def attributes_with_values(attribute_names)
464
430
  attribute_names.each_with_object({}) do |name, attrs|
465
431
  attrs[name] = _read_attribute(name)
@@ -468,7 +434,8 @@ module ActiveRecord
468
434
 
469
435
  # Filters the primary keys and readonly attributes from the attribute names.
470
436
  def attributes_for_update(attribute_names)
471
- attribute_names.reject do |name|
437
+ attribute_names &= self.class.column_names
438
+ attribute_names.delete_if do |name|
472
439
  readonly_attribute?(name)
473
440
  end
474
441
  end
@@ -476,11 +443,22 @@ module ActiveRecord
476
443
  # Filters out the primary keys, from the attribute names, when the primary
477
444
  # key is to be generated (e.g. the id attribute has no value).
478
445
  def attributes_for_create(attribute_names)
479
- attribute_names.reject do |name|
446
+ attribute_names &= self.class.column_names
447
+ attribute_names.delete_if do |name|
480
448
  pk_attribute?(name) && id.nil?
481
449
  end
482
450
  end
483
451
 
452
+ def format_for_inspect(value)
453
+ if value.is_a?(String) && value.length > 50
454
+ "#{value[0, 50]}...".inspect
455
+ elsif value.is_a?(Date) || value.is_a?(Time)
456
+ %("#{value.to_s(:db)}")
457
+ else
458
+ value.inspect
459
+ end
460
+ end
461
+
484
462
  def readonly_attribute?(name)
485
463
  self.class.readonly_attributes.include?(name)
486
464
  end
@@ -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 || custom_validation_context?
275
+ if new_record
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 && (record.changed_for_autosave? || custom_validation_context?)
307
+ association_valid?(reflection, record) if record
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 if custom_validation_context?
327
+ context = validation_context unless [:create, :update].include?(validation_context)
328
328
 
329
329
  unless valid = record.valid?(context)
330
330
  if reflection.options[:autosave]
@@ -382,14 +382,10 @@ 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
-
389
385
  # reconstruct the scope now that we know the owner's id
390
386
  association.reset_scope
391
387
 
392
- if records = associated_records_to_validate_or_save(association, new_record_before_save, autosave)
388
+ if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
393
389
  if autosave
394
390
  records_to_destroy = records.select(&:marked_for_destruction?)
395
391
  records_to_destroy.each { |record| association.destroy(record) }
@@ -401,7 +397,7 @@ module ActiveRecord
401
397
 
402
398
  saved = true
403
399
 
404
- if autosave != false && (new_record_before_save || record.new_record?)
400
+ if autosave != false && (@new_record_before_save || record.new_record?)
405
401
  if autosave
406
402
  saved = association.insert_record(record, false)
407
403
  elsif !reflection.nested?
@@ -461,16 +457,10 @@ module ActiveRecord
461
457
  # If the record is new or it has changed, returns true.
462
458
  def record_changed?(reflection, record, key)
463
459
  record.new_record? ||
464
- association_foreign_key_changed?(reflection, record, key) ||
460
+ (record.has_attribute?(reflection.foreign_key) && record[reflection.foreign_key] != key) ||
465
461
  record.will_save_change_to_attribute?(reflection.foreign_key)
466
462
  end
467
463
 
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
-
474
464
  # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
475
465
  #
476
466
  # In addition, it will destroy the association if it was marked for destruction.
@@ -499,10 +489,6 @@ module ActiveRecord
499
489
  end
500
490
  end
501
491
 
502
- def custom_validation_context?
503
- validation_context && [:create, :update].exclude?(validation_context)
504
- end
505
-
506
492
  def _ensure_no_duplicate_errors
507
493
  errors.messages.each_key do |attribute|
508
494
  errors[attribute].uniq!