activerecord 5.2.3.rc1 → 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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +326 -696
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +2 -1
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/associations.rb +16 -12
  9. data/lib/active_record/associations/association.rb +35 -19
  10. data/lib/active_record/associations/association_scope.rb +4 -6
  11. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  14. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  16. data/lib/active_record/associations/collection_association.rb +11 -25
  17. data/lib/active_record/associations/collection_proxy.rb +32 -6
  18. data/lib/active_record/associations/foreign_association.rb +7 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  21. data/lib/active_record/associations/has_one_association.rb +28 -30
  22. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/join_dependency/join_association.rb +6 -0
  25. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  26. data/lib/active_record/associations/preloader.rb +31 -28
  27. data/lib/active_record/associations/preloader/association.rb +1 -2
  28. data/lib/active_record/associations/singular_association.rb +2 -16
  29. data/lib/active_record/attribute_assignment.rb +7 -10
  30. data/lib/active_record/attribute_methods.rb +34 -56
  31. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  32. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  33. data/lib/active_record/attribute_methods/read.rb +16 -48
  34. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  36. data/lib/active_record/attribute_methods/write.rb +15 -16
  37. data/lib/active_record/autosave_association.rb +1 -1
  38. data/lib/active_record/base.rb +2 -2
  39. data/lib/active_record/callbacks.rb +3 -17
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +10 -14
  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 +14 -11
  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 +40 -26
  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/point.rb +1 -1
  67. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  68. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  69. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  70. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  72. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  74. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  75. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  76. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  77. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +93 -60
  78. data/lib/active_record/connection_handling.rb +132 -26
  79. data/lib/active_record/core.rb +76 -43
  80. data/lib/active_record/counter_cache.rb +4 -29
  81. data/lib/active_record/database_configurations.rb +184 -0
  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/enum.rb +22 -7
  86. data/lib/active_record/errors.rb +24 -21
  87. data/lib/active_record/explain.rb +1 -1
  88. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  89. data/lib/active_record/fixture_set/render_context.rb +17 -0
  90. data/lib/active_record/fixture_set/table_row.rb +153 -0
  91. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  92. data/lib/active_record/fixtures.rb +140 -472
  93. data/lib/active_record/gem_version.rb +4 -4
  94. data/lib/active_record/inheritance.rb +12 -2
  95. data/lib/active_record/integration.rb +56 -16
  96. data/lib/active_record/internal_metadata.rb +5 -1
  97. data/lib/active_record/locking/optimistic.rb +2 -2
  98. data/lib/active_record/locking/pessimistic.rb +3 -3
  99. data/lib/active_record/log_subscriber.rb +7 -26
  100. data/lib/active_record/migration.rb +38 -37
  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/model_schema.rb +30 -9
  104. data/lib/active_record/nested_attributes.rb +2 -2
  105. data/lib/active_record/no_touching.rb +7 -0
  106. data/lib/active_record/persistence.rb +18 -7
  107. data/lib/active_record/query_cache.rb +11 -4
  108. data/lib/active_record/querying.rb +19 -11
  109. data/lib/active_record/railtie.rb +71 -42
  110. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  111. data/lib/active_record/railties/controller_runtime.rb +30 -35
  112. data/lib/active_record/railties/databases.rake +94 -43
  113. data/lib/active_record/reflection.rb +46 -34
  114. data/lib/active_record/relation.rb +150 -69
  115. data/lib/active_record/relation/batches.rb +13 -10
  116. data/lib/active_record/relation/calculations.rb +34 -23
  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.rb +4 -6
  121. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  122. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  123. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  124. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  125. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  126. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  127. data/lib/active_record/relation/query_attribute.rb +15 -12
  128. data/lib/active_record/relation/query_methods.rb +26 -47
  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/result.rb +30 -11
  132. data/lib/active_record/sanitization.rb +2 -39
  133. data/lib/active_record/schema.rb +1 -10
  134. data/lib/active_record/schema_dumper.rb +12 -6
  135. data/lib/active_record/schema_migration.rb +4 -0
  136. data/lib/active_record/scoping.rb +9 -8
  137. data/lib/active_record/scoping/default.rb +8 -1
  138. data/lib/active_record/scoping/named.rb +10 -14
  139. data/lib/active_record/statement_cache.rb +30 -3
  140. data/lib/active_record/store.rb +39 -8
  141. data/lib/active_record/table_metadata.rb +1 -4
  142. data/lib/active_record/tasks/database_tasks.rb +89 -23
  143. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  146. data/lib/active_record/test_databases.rb +38 -0
  147. data/lib/active_record/test_fixtures.rb +224 -0
  148. data/lib/active_record/timestamp.rb +4 -6
  149. data/lib/active_record/transactions.rb +2 -21
  150. data/lib/active_record/translation.rb +1 -1
  151. data/lib/active_record/type.rb +3 -4
  152. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  153. data/lib/active_record/type_caster/connection.rb +1 -6
  154. data/lib/active_record/type_caster/map.rb +1 -4
  155. data/lib/active_record/validations/uniqueness.rb +13 -25
  156. data/lib/arel.rb +44 -0
  157. data/lib/arel/alias_predication.rb +9 -0
  158. data/lib/arel/attributes.rb +22 -0
  159. data/lib/arel/attributes/attribute.rb +37 -0
  160. data/lib/arel/collectors/bind.rb +24 -0
  161. data/lib/arel/collectors/composite.rb +31 -0
  162. data/lib/arel/collectors/plain_string.rb +20 -0
  163. data/lib/arel/collectors/sql_string.rb +20 -0
  164. data/lib/arel/collectors/substitute_binds.rb +28 -0
  165. data/lib/arel/crud.rb +42 -0
  166. data/lib/arel/delete_manager.rb +18 -0
  167. data/lib/arel/errors.rb +9 -0
  168. data/lib/arel/expressions.rb +29 -0
  169. data/lib/arel/factory_methods.rb +49 -0
  170. data/lib/arel/insert_manager.rb +49 -0
  171. data/lib/arel/math.rb +45 -0
  172. data/lib/arel/nodes.rb +67 -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/order_predications.rb +13 -0
  217. data/lib/arel/predications.rb +257 -0
  218. data/lib/arel/select_manager.rb +271 -0
  219. data/lib/arel/table.rb +110 -0
  220. data/lib/arel/tree_manager.rb +72 -0
  221. data/lib/arel/update_manager.rb +34 -0
  222. data/lib/arel/visitors.rb +20 -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/window_predications.rb +9 -0
  237. data/lib/rails/generators/active_record/migration.rb +14 -1
  238. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  239. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  240. metadata +101 -23
@@ -54,6 +54,12 @@ module ActiveRecord
54
54
  @tables = tables
55
55
  @table = tables.first
56
56
  end
57
+
58
+ def readonly?
59
+ return @readonly if defined?(@readonly)
60
+
61
+ @readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
62
+ end
57
63
  end
58
64
  end
59
65
  end
@@ -54,8 +54,8 @@ module ActiveRecord
54
54
  length = column_names_with_alias.length
55
55
 
56
56
  while index < length
57
- column_name, alias_name = column_names_with_alias[index]
58
- hash[column_name] = row[alias_name]
57
+ column = column_names_with_alias[index]
58
+ hash[column.name] = row[column.alias]
59
59
  index += 1
60
60
  end
61
61
 
@@ -88,7 +88,6 @@ module ActiveRecord
88
88
  if records.empty?
89
89
  []
90
90
  else
91
- records.uniq!
92
91
  Array.wrap(associations).flat_map { |association|
93
92
  preloaders_on association, records, preload_scope
94
93
  }
@@ -98,34 +97,34 @@ module ActiveRecord
98
97
  private
99
98
 
100
99
  # Loads all the given data into +records+ for the +association+.
101
- def preloaders_on(association, records, scope)
100
+ def preloaders_on(association, records, scope, polymorphic_parent = false)
102
101
  case association
103
102
  when Hash
104
- preloaders_for_hash(association, records, scope)
105
- when Symbol
106
- preloaders_for_one(association, records, scope)
107
- when String
108
- preloaders_for_one(association.to_sym, records, scope)
103
+ preloaders_for_hash(association, records, scope, polymorphic_parent)
104
+ when Symbol, String
105
+ preloaders_for_one(association, records, scope, polymorphic_parent)
109
106
  else
110
107
  raise ArgumentError, "#{association.inspect} was not recognized for preload"
111
108
  end
112
109
  end
113
110
 
114
- def preloaders_for_hash(association, records, scope)
111
+ def preloaders_for_hash(association, records, scope, polymorphic_parent)
115
112
  association.flat_map { |parent, child|
116
- loaders = preloaders_for_one parent, records, scope
117
-
118
- recs = loaders.flat_map(&:preloaded_records).uniq
119
- loaders.concat Array.wrap(child).flat_map { |assoc|
120
- preloaders_on assoc, recs, scope
121
- }
122
- loaders
113
+ grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
114
+ loaders = preloaders_for_reflection(reflection, reflection_records, scope)
115
+ recs = loaders.flat_map(&:preloaded_records)
116
+ child_polymorphic_parent = reflection && reflection.options[:polymorphic]
117
+ loaders.concat Array.wrap(child).flat_map { |assoc|
118
+ preloaders_on assoc, recs, scope, child_polymorphic_parent
119
+ }
120
+ loaders
121
+ end
123
122
  }
124
123
  end
125
124
 
126
125
  # Loads all the given data into +records+ for a singular +association+.
127
126
  #
128
- # Functions by instantiating a preloader class such as Preloader::HasManyThrough and
127
+ # Functions by instantiating a preloader class such as Preloader::Association and
129
128
  # call the +run+ method for each passed in class in the +records+ argument.
130
129
  #
131
130
  # Not all records have the same class, so group then preload group on the reflection
@@ -135,24 +134,28 @@ module ActiveRecord
135
134
  # Additionally, polymorphic belongs_to associations can have multiple associated
136
135
  # classes, depending on the polymorphic_type field. So we group by the classes as
137
136
  # well.
138
- def preloaders_for_one(association, records, scope)
139
- grouped_records(association, records).flat_map do |reflection, klasses|
140
- klasses.map do |rhs_klass, rs|
141
- loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
142
- loader.run self
143
- loader
137
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
138
+ grouped_records(association, records, polymorphic_parent)
139
+ .flat_map do |reflection, reflection_records|
140
+ preloaders_for_reflection reflection, reflection_records, scope
144
141
  end
142
+ end
143
+
144
+ def preloaders_for_reflection(reflection, records, scope)
145
+ records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
146
+ loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
147
+ loader.run self
148
+ loader
145
149
  end
146
150
  end
147
151
 
148
- def grouped_records(association, records)
152
+ def grouped_records(association, records, polymorphic_parent)
149
153
  h = {}
150
154
  records.each do |record|
151
155
  next unless record
152
- assoc = record.association(association)
153
- next unless assoc.klass
154
- klasses = h[assoc.reflection] ||= {}
155
- (klasses[assoc.klass] ||= []) << record
156
+ reflection = record.class._reflect_on_association(association)
157
+ next if polymorphic_parent && !reflection || !record.association(association).klass
158
+ (h[reflection] ||= []) << record
156
159
  end
157
160
  h
158
161
  end
@@ -169,7 +172,7 @@ module ActiveRecord
169
172
  owners.flat_map { |owner| owner.association(reflection.name).target }
170
173
  end
171
174
 
172
- protected
175
+ private
173
176
  attr_reader :owners, :reflection
174
177
  end
175
178
 
@@ -27,10 +27,9 @@ module ActiveRecord
27
27
  end
28
28
  end
29
29
 
30
- protected
30
+ private
31
31
  attr_reader :owners, :reflection, :preload_scope, :model, :klass
32
32
 
33
- private
34
33
  # The name of the key on the associated records
35
34
  def association_key_name
36
35
  reflection.join_primary_key(klass)
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  # Implements the reload reader method, e.g. foo.reload_bar for
27
27
  # Foo.has_one :bar
28
28
  def force_reload_reader
29
- klass.uncached { reload }
29
+ reload(true)
30
30
  target
31
31
  end
32
32
 
@@ -36,21 +36,7 @@ module ActiveRecord
36
36
  end
37
37
 
38
38
  def find_target
39
- scope = self.scope
40
- return scope.take if skip_statement_cache?(scope)
41
-
42
- conn = klass.connection
43
- sc = reflection.association_scope_cache(conn, owner) do |params|
44
- as = AssociationScope.create { params.bind }
45
- target_scope.merge!(as.scope(self)).limit(1)
46
- end
47
-
48
- binds = AssociationScope.get_bind_values(owner, reflection.chain)
49
- sc.execute(binds, conn) do |record|
50
- set_inverse_instance record
51
- end.first
52
- rescue ::RangeError
53
- nil
39
+ super.first
54
40
  end
55
41
 
56
42
  def replace(record)
@@ -4,7 +4,6 @@ require "active_model/forbidden_attributes_protection"
4
4
 
5
5
  module ActiveRecord
6
6
  module AttributeAssignment
7
- extend ActiveSupport::Concern
8
7
  include ActiveModel::AttributeAssignment
9
8
 
10
9
  private
@@ -46,16 +45,14 @@ module ActiveRecord
46
45
  def execute_callstack_for_multiparameter_attributes(callstack)
47
46
  errors = []
48
47
  callstack.each do |name, values_with_empty_parameters|
49
- begin
50
- if values_with_empty_parameters.each_value.all?(&:nil?)
51
- values = nil
52
- else
53
- values = values_with_empty_parameters
54
- end
55
- send("#{name}=", values)
56
- rescue => ex
57
- errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
48
+ if values_with_empty_parameters.each_value.all?(&:nil?)
49
+ values = nil
50
+ else
51
+ values = values_with_empty_parameters
58
52
  end
53
+ send("#{name}=", values)
54
+ rescue => ex
55
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
59
56
  end
60
57
  unless errors.empty?
61
58
  error_descriptions = errors.map(&:message).join(",")
@@ -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
@@ -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