activerecord 5.1.0 → 5.2.3

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 (261) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +596 -450
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -5
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record.rb +11 -4
  8. data/lib/active_record/aggregations.rb +6 -5
  9. data/lib/active_record/association_relation.rb +7 -5
  10. data/lib/active_record/associations.rb +77 -85
  11. data/lib/active_record/associations/alias_tracker.rb +23 -32
  12. data/lib/active_record/associations/association.rb +49 -35
  13. data/lib/active_record/associations/association_scope.rb +55 -55
  14. data/lib/active_record/associations/belongs_to_association.rb +30 -11
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  16. data/lib/active_record/associations/builder/association.rb +4 -7
  17. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  18. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +2 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +66 -53
  24. data/lib/active_record/associations/collection_proxy.rb +30 -73
  25. data/lib/active_record/associations/foreign_association.rb +2 -0
  26. data/lib/active_record/associations/has_many_association.rb +13 -2
  27. data/lib/active_record/associations/has_many_through_association.rb +37 -19
  28. data/lib/active_record/associations/has_one_association.rb +14 -1
  29. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  30. data/lib/active_record/associations/join_dependency.rb +52 -96
  31. data/lib/active_record/associations/join_dependency/join_association.rb +22 -75
  32. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  33. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/preloader/association.rb +53 -92
  36. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  37. data/lib/active_record/associations/singular_association.rb +14 -16
  38. data/lib/active_record/associations/through_association.rb +27 -12
  39. data/lib/active_record/attribute_assignment.rb +2 -5
  40. data/lib/active_record/attribute_decorators.rb +3 -2
  41. data/lib/active_record/attribute_methods.rb +65 -24
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +33 -216
  44. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  45. data/lib/active_record/attribute_methods/query.rb +2 -0
  46. data/lib/active_record/attribute_methods/read.rb +9 -3
  47. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  49. data/lib/active_record/attribute_methods/write.rb +22 -19
  50. data/lib/active_record/attributes.rb +7 -6
  51. data/lib/active_record/autosave_association.rb +15 -13
  52. data/lib/active_record/base.rb +2 -0
  53. data/lib/active_record/callbacks.rb +12 -6
  54. data/lib/active_record/coders/json.rb +2 -0
  55. data/lib/active_record/coders/yaml_column.rb +2 -0
  56. data/lib/active_record/collection_cache_key.rb +15 -11
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +120 -39
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +192 -37
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -2
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -25
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +65 -7
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -87
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -98
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +126 -189
  70. data/lib/active_record/connection_adapters/column.rb +4 -2
  71. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
  73. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -15
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +13 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +258 -129
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -87
  118. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +24 -1
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +90 -96
  127. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  128. data/lib/active_record/connection_handling.rb +4 -2
  129. data/lib/active_record/core.rb +41 -61
  130. data/lib/active_record/counter_cache.rb +20 -15
  131. data/lib/active_record/define_callbacks.rb +5 -3
  132. data/lib/active_record/dynamic_matchers.rb +9 -9
  133. data/lib/active_record/enum.rb +18 -13
  134. data/lib/active_record/errors.rb +60 -15
  135. data/lib/active_record/explain.rb +3 -1
  136. data/lib/active_record/explain_registry.rb +2 -0
  137. data/lib/active_record/explain_subscriber.rb +2 -0
  138. data/lib/active_record/fixture_set/file.rb +2 -0
  139. data/lib/active_record/fixtures.rb +67 -60
  140. data/lib/active_record/gem_version.rb +4 -2
  141. data/lib/active_record/inheritance.rb +49 -19
  142. data/lib/active_record/integration.rb +58 -19
  143. data/lib/active_record/internal_metadata.rb +2 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  145. data/lib/active_record/locking/optimistic.rb +30 -42
  146. data/lib/active_record/locking/pessimistic.rb +10 -7
  147. data/lib/active_record/log_subscriber.rb +46 -4
  148. data/lib/active_record/migration.rb +189 -139
  149. data/lib/active_record/migration/command_recorder.rb +11 -9
  150. data/lib/active_record/migration/compatibility.rb +81 -29
  151. data/lib/active_record/migration/join_table.rb +2 -0
  152. data/lib/active_record/model_schema.rb +74 -58
  153. data/lib/active_record/nested_attributes.rb +18 -6
  154. data/lib/active_record/no_touching.rb +3 -1
  155. data/lib/active_record/null_relation.rb +2 -0
  156. data/lib/active_record/persistence.rb +199 -54
  157. data/lib/active_record/query_cache.rb +8 -10
  158. data/lib/active_record/querying.rb +5 -3
  159. data/lib/active_record/railtie.rb +62 -6
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +2 -0
  162. data/lib/active_record/railties/databases.rake +48 -38
  163. data/lib/active_record/readonly_attributes.rb +3 -2
  164. data/lib/active_record/reflection.rb +137 -207
  165. data/lib/active_record/relation.rb +132 -207
  166. data/lib/active_record/relation/batches.rb +32 -17
  167. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  168. data/lib/active_record/relation/calculations.rb +66 -25
  169. data/lib/active_record/relation/delegation.rb +45 -29
  170. data/lib/active_record/relation/finder_methods.rb +76 -85
  171. data/lib/active_record/relation/from_clause.rb +2 -8
  172. data/lib/active_record/relation/merger.rb +53 -23
  173. data/lib/active_record/relation/predicate_builder.rb +60 -79
  174. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  175. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  176. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  177. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  178. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  179. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  180. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  181. data/lib/active_record/relation/query_attribute.rb +28 -2
  182. data/lib/active_record/relation/query_methods.rb +135 -103
  183. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  184. data/lib/active_record/relation/spawn_methods.rb +4 -2
  185. data/lib/active_record/relation/where_clause.rb +65 -67
  186. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  187. data/lib/active_record/result.rb +2 -0
  188. data/lib/active_record/runtime_registry.rb +2 -0
  189. data/lib/active_record/sanitization.rb +129 -121
  190. data/lib/active_record/schema.rb +4 -2
  191. data/lib/active_record/schema_dumper.rb +36 -26
  192. data/lib/active_record/schema_migration.rb +2 -0
  193. data/lib/active_record/scoping.rb +12 -10
  194. data/lib/active_record/scoping/default.rb +10 -7
  195. data/lib/active_record/scoping/named.rb +40 -12
  196. data/lib/active_record/secure_token.rb +2 -0
  197. data/lib/active_record/serialization.rb +2 -0
  198. data/lib/active_record/statement_cache.rb +22 -12
  199. data/lib/active_record/store.rb +3 -1
  200. data/lib/active_record/suppressor.rb +2 -0
  201. data/lib/active_record/table_metadata.rb +12 -3
  202. data/lib/active_record/tasks/database_tasks.rb +38 -26
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  206. data/lib/active_record/timestamp.rb +13 -6
  207. data/lib/active_record/touch_later.rb +2 -0
  208. data/lib/active_record/transactions.rb +32 -27
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type.rb +4 -1
  211. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  212. data/lib/active_record/type/date.rb +2 -0
  213. data/lib/active_record/type/date_time.rb +2 -0
  214. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  215. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  216. data/lib/active_record/type/internal/timezone.rb +2 -0
  217. data/lib/active_record/type/json.rb +30 -0
  218. data/lib/active_record/type/serialized.rb +6 -0
  219. data/lib/active_record/type/text.rb +2 -0
  220. data/lib/active_record/type/time.rb +2 -0
  221. data/lib/active_record/type/type_map.rb +2 -0
  222. data/lib/active_record/type/unsigned_integer.rb +2 -0
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/type_caster/connection.rb +2 -0
  225. data/lib/active_record/type_caster/map.rb +3 -1
  226. data/lib/active_record/validations.rb +2 -0
  227. data/lib/active_record/validations/absence.rb +2 -0
  228. data/lib/active_record/validations/associated.rb +2 -0
  229. data/lib/active_record/validations/length.rb +2 -0
  230. data/lib/active_record/validations/presence.rb +2 -0
  231. data/lib/active_record/validations/uniqueness.rb +36 -6
  232. data/lib/active_record/version.rb +2 -0
  233. data/lib/rails/generators/active_record.rb +3 -1
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration.rb +2 -0
  237. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  238. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  239. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. metadata +24 -36
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -113
  254. data/lib/active_record/attribute_set.rb +0 -113
  255. data/lib/active_record/attribute_set/builder.rb +0 -124
  256. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  257. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  258. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  259. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  260. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  261. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  class SingularAssociation < Association #:nodoc:
@@ -15,9 +17,8 @@ module ActiveRecord
15
17
  replace(record)
16
18
  end
17
19
 
18
- def build(attributes = {})
19
- record = build_record(attributes)
20
- yield(record) if block_given?
20
+ def build(attributes = {}, &block)
21
+ record = build_record(attributes, &block)
21
22
  set_new_record(record)
22
23
  record
23
24
  end
@@ -30,24 +31,22 @@ module ActiveRecord
30
31
  end
31
32
 
32
33
  private
33
-
34
- def create_scope
35
- scope.scope_for_create.stringify_keys.except(klass.primary_key)
34
+ def scope_for_create
35
+ super.except!(klass.primary_key)
36
36
  end
37
37
 
38
38
  def find_target
39
- return scope.take if skip_statement_cache?
39
+ scope = self.scope
40
+ return scope.take if skip_statement_cache?(scope)
40
41
 
41
42
  conn = klass.connection
42
- sc = reflection.association_scope_cache(conn, owner) do
43
- StatementCache.create(conn) { |params|
44
- as = AssociationScope.create { params.bind }
45
- target_scope.merge(as.scope(self, conn)).limit(1)
46
- }
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)
47
46
  end
48
47
 
49
48
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
50
- sc.execute(binds, klass, conn) do |record|
49
+ sc.execute(binds, conn) do |record|
51
50
  set_inverse_instance record
52
51
  end.first
53
52
  rescue ::RangeError
@@ -62,9 +61,8 @@ module ActiveRecord
62
61
  replace(record)
63
62
  end
64
63
 
65
- def _create_record(attributes, raise_error = false)
66
- record = build_record(attributes)
67
- yield(record) if block_given?
64
+ def _create_record(attributes, raise_error = false, &block)
65
+ record = build_record(attributes, &block)
68
66
  saved = record.save
69
67
  set_new_record(record)
70
68
  raise RecordInvalid.new(record) if !saved && raise_error
@@ -1,10 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Through Association
3
4
  module Associations
5
+ # = Active Record Through Association
4
6
  module ThroughAssociation #:nodoc:
5
- delegate :source_reflection, :through_reflection, to: :reflection
7
+ delegate :source_reflection, to: :reflection
6
8
 
7
9
  private
10
+ def through_reflection
11
+ @through_reflection ||= begin
12
+ refl = reflection.through_reflection
13
+
14
+ while refl.through_reflection?
15
+ refl = refl.through_reflection
16
+ end
17
+
18
+ refl
19
+ end
20
+ end
21
+
22
+ def through_association
23
+ @through_association ||= owner.association(through_reflection.name)
24
+ end
8
25
 
9
26
  # We merge in these scopes for two reasons:
10
27
  #
@@ -13,7 +30,7 @@ module ActiveRecord
13
30
  def target_scope
14
31
  scope = super
15
32
  reflection.chain.drop(1).each do |reflection|
16
- relation = reflection.klass.all
33
+ relation = reflection.klass.scope_for_association
17
34
  scope.merge!(
18
35
  relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
19
36
  )
@@ -36,24 +53,22 @@ module ActiveRecord
36
53
  def construct_join_attributes(*records)
37
54
  ensure_mutable
38
55
 
39
- if source_reflection.association_primary_key(reflection.klass) == reflection.klass.primary_key
56
+ association_primary_key = source_reflection.association_primary_key(reflection.klass)
57
+
58
+ if association_primary_key == reflection.klass.primary_key && !options[:source_type]
40
59
  join_attributes = { source_reflection.name => records }
41
60
  else
42
61
  join_attributes = {
43
- source_reflection.foreign_key =>
44
- records.map { |record|
45
- record.send(source_reflection.association_primary_key(reflection.klass))
46
- }
62
+ source_reflection.foreign_key => records.map(&association_primary_key.to_sym)
47
63
  }
48
64
  end
49
65
 
50
66
  if options[:source_type]
51
- join_attributes[source_reflection.foreign_type] =
52
- records.map { |record| record.class.base_class.name }
67
+ join_attributes[source_reflection.foreign_type] = [ options[:source_type] ]
53
68
  end
54
69
 
55
70
  if records.count == 1
56
- Hash[join_attributes.map { |k, v| [k, v.first] }]
71
+ join_attributes.transform_values!(&:first)
57
72
  else
58
73
  join_attributes
59
74
  end
@@ -99,7 +114,7 @@ module ActiveRecord
99
114
  attributes[inverse.foreign_key] = target.id
100
115
  end
101
116
 
102
- super(attributes)
117
+ super
103
118
  end
104
119
  end
105
120
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_model/forbidden_attributes_protection"
2
4
 
3
5
  module ActiveRecord
@@ -5,11 +7,6 @@ module ActiveRecord
5
7
  extend ActiveSupport::Concern
6
8
  include ActiveModel::AttributeAssignment
7
9
 
8
- # Alias for ActiveModel::AttributeAssignment#assign_attributes. See ActiveModel::AttributeAssignment.
9
- def attributes=(attributes)
10
- assign_attributes(attributes)
11
- end
12
-
13
10
  private
14
11
 
15
12
  def _assign_attributes(attributes)
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeDecorators # :nodoc:
3
5
  extend ActiveSupport::Concern
4
6
 
5
7
  included do
6
- class_attribute :attribute_type_decorations, instance_accessor: false # :internal:
7
- self.attribute_type_decorations = TypeDecorator.new
8
+ class_attribute :attribute_type_decorations, instance_accessor: false, default: TypeDecorator.new # :internal:
8
9
  end
9
10
 
10
11
  module ClassMethods # :nodoc:
@@ -1,7 +1,6 @@
1
- require "active_support/core_ext/enumerable"
2
- require "active_support/core_ext/string/filters"
1
+ # frozen_string_literal: true
2
+
3
3
  require "mutex_m"
4
- require "concurrent/map"
5
4
 
6
5
  module ActiveRecord
7
6
  # = Active Record Attribute Methods
@@ -34,7 +33,9 @@ module ActiveRecord
34
33
 
35
34
  BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
36
35
 
37
- class GeneratedAttributeMethods < Module; end # :nodoc:
36
+ class GeneratedAttributeMethods < Module #:nodoc:
37
+ include Mutex_m
38
+ end
38
39
 
39
40
  module ClassMethods
40
41
  def inherited(child_class) #:nodoc:
@@ -43,7 +44,7 @@ module ActiveRecord
43
44
  end
44
45
 
45
46
  def initialize_generated_modules # :nodoc:
46
- @generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
47
+ @generated_attribute_methods = GeneratedAttributeMethods.new
47
48
  @attribute_methods_generated = false
48
49
  include @generated_attribute_methods
49
50
 
@@ -62,7 +63,6 @@ module ActiveRecord
62
63
  super(attribute_names)
63
64
  @attribute_methods_generated = true
64
65
  end
65
- true
66
66
  end
67
67
 
68
68
  def undefine_attribute_methods # :nodoc:
@@ -167,6 +167,57 @@ module ActiveRecord
167
167
  end
168
168
  end
169
169
 
170
+ # Regexp whitelist. Matches the following:
171
+ # "#{table_name}.#{column_name}"
172
+ # "#{column_name}"
173
+ COLUMN_NAME_WHITELIST = /\A(?:\w+\.)?\w+\z/i
174
+
175
+ # Regexp whitelist. Matches the following:
176
+ # "#{table_name}.#{column_name}"
177
+ # "#{table_name}.#{column_name} #{direction}"
178
+ # "#{table_name}.#{column_name} #{direction} NULLS FIRST"
179
+ # "#{table_name}.#{column_name} NULLS LAST"
180
+ # "#{column_name}"
181
+ # "#{column_name} #{direction}"
182
+ # "#{column_name} #{direction} NULLS FIRST"
183
+ # "#{column_name} NULLS LAST"
184
+ COLUMN_NAME_ORDER_WHITELIST = /
185
+ \A
186
+ (?:\w+\.)?
187
+ \w+
188
+ (?:\s+asc|\s+desc)?
189
+ (?:\s+nulls\s+(?:first|last))?
190
+ \z
191
+ /ix
192
+
193
+ def enforce_raw_sql_whitelist(args, whitelist: COLUMN_NAME_WHITELIST) # :nodoc:
194
+ 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) }
199
+ end
200
+
201
+ return if unexpected.none?
202
+
203
+ if allow_unsafe_raw_sql == :deprecated
204
+ ActiveSupport::Deprecation.warn(
205
+ "Dangerous query method (method whose arguments are used as raw " \
206
+ "SQL) called with non-attribute argument(s): " \
207
+ "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
208
+ "arguments will be disallowed in Rails 6.0. This method should " \
209
+ "not be called with user-provided values, such as request " \
210
+ "parameters or model attributes. Known-safe values can be passed " \
211
+ "by wrapping them in Arel.sql()."
212
+ )
213
+ else
214
+ raise(ActiveRecord::UnknownAttributeReference,
215
+ "Query method called with non-attribute argument(s): " +
216
+ unexpected.map(&:inspect).join(", ")
217
+ )
218
+ end
219
+ end
220
+
170
221
  # Returns true if the given attribute exists, otherwise false.
171
222
  #
172
223
  # class Person < ActiveRecord::Base
@@ -236,7 +287,7 @@ module ActiveRecord
236
287
  return has_attribute?(name)
237
288
  end
238
289
 
239
- return true
290
+ true
240
291
  end
241
292
 
242
293
  # Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
@@ -401,24 +452,18 @@ module ActiveRecord
401
452
 
402
453
  private
403
454
 
404
- def arel_attributes_with_values_for_create(attribute_names)
405
- arel_attributes_with_values(attributes_for_create(attribute_names))
455
+ def attributes_with_values_for_create(attribute_names)
456
+ attributes_with_values(attributes_for_create(attribute_names))
406
457
  end
407
458
 
408
- def arel_attributes_with_values_for_update(attribute_names)
409
- arel_attributes_with_values(attributes_for_update(attribute_names))
459
+ def attributes_with_values_for_update(attribute_names)
460
+ attributes_with_values(attributes_for_update(attribute_names))
410
461
  end
411
462
 
412
- # Returns a Hash of the Arel::Attributes and attribute values that have been
413
- # typecasted for use in an Arel insert/update method.
414
- def arel_attributes_with_values(attribute_names)
415
- attrs = {}
416
- arel_table = self.class.arel_table
417
-
418
- attribute_names.each do |name|
419
- attrs[arel_table[name]] = typecasted_attribute_value(name)
463
+ def attributes_with_values(attribute_names)
464
+ attribute_names.each_with_object({}) do |name, attrs|
465
+ attrs[name] = _read_attribute(name)
420
466
  end
421
- attrs
422
467
  end
423
468
 
424
469
  # Filters the primary keys and readonly attributes from the attribute names.
@@ -443,9 +488,5 @@ module ActiveRecord
443
488
  def pk_attribute?(name)
444
489
  name == self.class.primary_key
445
490
  end
446
-
447
- def typecasted_attribute_value(name)
448
- _read_attribute(name)
449
- end
450
491
  end
451
492
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  # = Active Record Attribute Methods Before Type Cast
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "active_support/core_ext/module/attribute_accessors"
3
- require "active_record/attribute_mutation_tracker"
4
4
 
5
5
  module ActiveRecord
6
6
  module AttributeMethods
7
- module Dirty # :nodoc:
7
+ module Dirty
8
8
  extend ActiveSupport::Concern
9
9
 
10
10
  include ActiveModel::Dirty
@@ -14,11 +14,7 @@ module ActiveRecord
14
14
  raise "You cannot include Dirty after Timestamp"
15
15
  end
16
16
 
17
- class_attribute :partial_writes, instance_writer: false
18
- self.partial_writes = true
19
-
20
- after_create { changes_internally_applied }
21
- after_update { changes_internally_applied }
17
+ class_attribute :partial_writes, instance_writer: false, default: true
22
18
 
23
19
  # Attribute methods for "changed in last call to save?"
24
20
  attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
@@ -30,106 +26,18 @@ module ActiveRecord
30
26
  attribute_method_suffix("_change_to_be_saved", "_in_database")
31
27
  end
32
28
 
33
- # Attempts to +save+ the record and clears changed attributes if successful.
34
- def save(*)
35
- if status = super
36
- changes_applied
37
- end
38
- status
39
- end
40
-
41
- # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
42
- def save!(*)
43
- super.tap do
44
- changes_applied
45
- end
46
- end
47
-
48
29
  # <tt>reload</tt> the record and clears changed attributes.
49
30
  def reload(*)
50
31
  super.tap do
51
- @previous_mutation_tracker = nil
52
- clear_mutation_trackers
53
- @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
54
- end
55
- end
56
-
57
- def initialize_dup(other) # :nodoc:
58
- super
59
- @attributes = self.class._default_attributes.map do |attr|
60
- attr.with_value_from_user(@attributes.fetch_value(attr.name))
61
- end
62
- clear_mutation_trackers
63
- end
64
-
65
- def changes_internally_applied # :nodoc:
66
- @mutations_before_last_save = mutation_tracker
67
- forget_attribute_assignments
68
- @mutations_from_database = AttributeMutationTracker.new(@attributes)
69
- end
70
-
71
- def changes_applied
72
- @previous_mutation_tracker = mutation_tracker
73
- @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
74
- clear_mutation_trackers
75
- end
76
-
77
- def clear_changes_information
78
- @previous_mutation_tracker = nil
79
- @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
80
- forget_attribute_assignments
81
- clear_mutation_trackers
82
- end
83
-
84
- def raw_write_attribute(attr_name, *)
85
- result = super
86
- clear_attribute_change(attr_name)
87
- result
88
- end
89
-
90
- def clear_attribute_changes(attr_names)
91
- super
92
- attr_names.each do |attr_name|
93
- clear_attribute_change(attr_name)
94
- end
95
- end
96
-
97
- def changed_attributes
98
- # This should only be set by methods which will call changed_attributes
99
- # multiple times when it is known that the computed value cannot change.
100
- if defined?(@cached_changed_attributes)
101
- @cached_changed_attributes
102
- else
103
- emit_warning_if_needed("changed_attributes", "saved_changes.transform_values(&:first)")
104
- super.reverse_merge(mutation_tracker.changed_values).freeze
105
- end
106
- end
107
-
108
- def changes
109
- cache_changed_attributes do
110
- emit_warning_if_needed("changes", "saved_changes")
111
- super
112
- end
113
- end
114
-
115
- def previous_changes
116
- unless previous_mutation_tracker.equal?(mutations_before_last_save)
117
- ActiveSupport::Deprecation.warn(<<-EOW.strip_heredoc)
118
- The behavior of `previous_changes` inside of after callbacks is
119
- deprecated without replacement. In the next release of Rails,
120
- this method inside of `after_save` will return the changes that
121
- were just saved.
122
- EOW
32
+ @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
33
+ @mutations_before_last_save = nil
34
+ @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
35
+ @mutations_from_database = nil
123
36
  end
124
- previous_mutation_tracker.changes
125
- end
126
-
127
- def attribute_changed_in_place?(attr_name)
128
- mutation_tracker.changed_in_place?(attr_name)
129
37
  end
130
38
 
131
39
  # Did this attribute change when we last saved? This method can be invoked
132
- # as `saved_change_to_name?` instead of `saved_change_to_attribute?("name")`.
40
+ # as +saved_change_to_name?+ instead of <tt>saved_change_to_attribute?("name")</tt>.
133
41
  # Behaves similarly to +attribute_changed?+. This method is useful in
134
42
  # after callbacks to determine if the call to save changed a certain
135
43
  # attribute.
@@ -152,8 +60,8 @@ module ActiveRecord
152
60
  # Behaves similarly to +attribute_change+. This method is useful in after
153
61
  # callbacks, to see the change in an attribute that just occurred
154
62
  #
155
- # This method can be invoked as `saved_change_to_name` in instead of
156
- # `saved_change_to_attribute("name")`
63
+ # This method can be invoked as +saved_change_to_name+ in instead of
64
+ # <tt>saved_change_to_attribute("name")</tt>
157
65
  def saved_change_to_attribute(attr_name)
158
66
  mutations_before_last_save.change_to_attribute(attr_name)
159
67
  end
@@ -166,7 +74,7 @@ module ActiveRecord
166
74
  mutations_before_last_save.original_value(attr_name)
167
75
  end
168
76
 
169
- # Did the last call to `save` have any changes to change?
77
+ # Did the last call to +save+ have any changes to change?
170
78
  def saved_changes?
171
79
  mutations_before_last_save.any_changes?
172
80
  end
@@ -176,158 +84,67 @@ module ActiveRecord
176
84
  mutations_before_last_save.changes
177
85
  end
178
86
 
179
- # Alias for `attribute_changed?`
87
+ # Alias for +attribute_changed?+
180
88
  def will_save_change_to_attribute?(attr_name, **options)
181
89
  mutations_from_database.changed?(attr_name, **options)
182
90
  end
183
91
 
184
- # Alias for `attribute_change`
92
+ # Alias for +attribute_change+
185
93
  def attribute_change_to_be_saved(attr_name)
186
94
  mutations_from_database.change_to_attribute(attr_name)
187
95
  end
188
96
 
189
- # Alias for `attribute_was`
97
+ # Alias for +attribute_was+
190
98
  def attribute_in_database(attr_name)
191
99
  mutations_from_database.original_value(attr_name)
192
100
  end
193
101
 
194
- # Alias for `changed?`
102
+ # Alias for +changed?+
195
103
  def has_changes_to_save?
196
104
  mutations_from_database.any_changes?
197
105
  end
198
106
 
199
- # Alias for `changes`
107
+ # Alias for +changes+
200
108
  def changes_to_save
201
109
  mutations_from_database.changes
202
110
  end
203
111
 
204
- # Alias for `changed`
112
+ # Alias for +changed+
205
113
  def changed_attribute_names_to_save
206
- changes_to_save.keys
114
+ mutations_from_database.changed_attribute_names
207
115
  end
208
116
 
209
- # Alias for `changed_attributes`
117
+ # Alias for +changed_attributes+
210
118
  def attributes_in_database
211
- changes_to_save.transform_values(&:first)
212
- end
213
-
214
- def attribute_was(*)
215
- emit_warning_if_needed("attribute_was", "attribute_before_last_save")
216
- super
217
- end
218
-
219
- def attribute_change(*)
220
- emit_warning_if_needed("attribute_change", "saved_change_to_attribute")
221
- super
222
- end
223
-
224
- def attribute_changed?(*)
225
- emit_warning_if_needed("attribute_changed?", "saved_change_to_attribute?")
226
- super
227
- end
228
-
229
- def changed?(*)
230
- emit_warning_if_needed("changed?", "saved_changes?")
231
- super
232
- end
233
-
234
- def changed(*)
235
- emit_warning_if_needed("changed", "saved_changes.keys")
236
- super
119
+ mutations_from_database.changed_values
237
120
  end
238
121
 
239
122
  private
240
-
241
- def mutation_tracker
242
- unless defined?(@mutation_tracker)
243
- @mutation_tracker = nil
244
- end
245
- @mutation_tracker ||= AttributeMutationTracker.new(@attributes)
246
- end
247
-
248
- def emit_warning_if_needed(method_name, new_method_name)
249
- unless mutation_tracker.equal?(mutations_from_database)
250
- ActiveSupport::Deprecation.warn(<<-EOW.squish)
251
- The behavior of `#{method_name}` inside of after callbacks will
252
- be changing in the next version of Rails. The new return value will reflect the
253
- behavior of calling the method after `save` returned (e.g. the opposite of what
254
- it returns now). To maintain the current behavior, use `#{new_method_name}`
255
- instead.
256
- EOW
257
- end
258
- end
259
-
260
- def mutations_from_database
261
- unless defined?(@mutations_from_database)
262
- @mutations_from_database = nil
263
- end
264
- @mutations_from_database ||= mutation_tracker
265
- end
266
-
267
- def changes_include?(attr_name)
268
- super || mutation_tracker.changed?(attr_name)
269
- end
270
-
271
- def clear_attribute_change(attr_name)
272
- mutation_tracker.forget_change(attr_name)
273
- mutations_from_database.forget_change(attr_name)
274
- end
275
-
276
- def attribute_will_change!(attr_name)
277
- super
278
- if self.class.has_attribute?(attr_name)
279
- mutations_from_database.force_change(attr_name)
280
- else
281
- ActiveSupport::Deprecation.warn(<<-EOW.squish)
282
- #{attr_name} is not an attribute known to Active Record.
283
- This behavior is deprecated and will be removed in the next
284
- version of Rails. If you'd like #{attr_name} to be managed
285
- by Active Record, add `attribute :#{attr_name} to your class.
286
- EOW
287
- mutations_from_database.deprecated_force_change(attr_name)
123
+ def write_attribute_without_type_cast(attr_name, value)
124
+ name = attr_name.to_s
125
+ if self.class.attribute_alias?(name)
126
+ name = self.class.attribute_alias(name)
288
127
  end
128
+ result = super(name, value)
129
+ clear_attribute_change(name)
130
+ result
289
131
  end
290
132
 
291
133
  def _update_record(*)
292
- partial_writes? ? super(keys_for_partial_write) : super
134
+ affected_rows = partial_writes? ? super(keys_for_partial_write) : super
135
+ changes_applied
136
+ affected_rows
293
137
  end
294
138
 
295
139
  def _create_record(*)
296
- partial_writes? ? super(keys_for_partial_write) : super
140
+ id = partial_writes? ? super(keys_for_partial_write) : super
141
+ changes_applied
142
+ id
297
143
  end
298
144
 
299
145
  def keys_for_partial_write
300
146
  changed_attribute_names_to_save & self.class.column_names
301
147
  end
302
-
303
- def forget_attribute_assignments
304
- @attributes = @attributes.map(&:forgetting_assignment)
305
- end
306
-
307
- def clear_mutation_trackers
308
- @mutation_tracker = nil
309
- @mutations_from_database = nil
310
- @mutations_before_last_save = nil
311
- end
312
-
313
- def previous_mutation_tracker
314
- @previous_mutation_tracker ||= NullMutationTracker.instance
315
- end
316
-
317
- def mutations_before_last_save
318
- @mutations_before_last_save ||= previous_mutation_tracker
319
- end
320
-
321
- def cache_changed_attributes
322
- @cached_changed_attributes = changed_attributes
323
- yield
324
- ensure
325
- clear_changed_attributes_cache
326
- end
327
-
328
- def clear_changed_attributes_cache
329
- remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
330
- end
331
148
  end
332
149
  end
333
150
  end