activerecord 5.2.8.1 → 6.1.6.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1255 -596
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +9 -8
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +100 -41
  10. data/lib/active_record/associations/association_scope.rb +23 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +55 -48
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  13. data/lib/active_record/associations/builder/association.rb +45 -22
  14. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  15. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -2
  18. data/lib/active_record/associations/builder/has_one.rb +33 -2
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  20. data/lib/active_record/associations/collection_association.rb +44 -34
  21. data/lib/active_record/associations/collection_proxy.rb +25 -21
  22. data/lib/active_record/associations/foreign_association.rb +20 -0
  23. data/lib/active_record/associations/has_many_association.rb +26 -13
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  25. data/lib/active_record/associations/has_one_association.rb +43 -31
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  28. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/preloader/association.rb +69 -43
  31. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/singular_association.rb +3 -17
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/associations.rb +137 -25
  36. data/lib/active_record/attribute_assignment.rb +17 -19
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  38. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  40. data/lib/active_record/attribute_methods/query.rb +4 -8
  41. data/lib/active_record/attribute_methods/read.rb +14 -56
  42. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +18 -34
  45. data/lib/active_record/attribute_methods.rb +81 -143
  46. data/lib/active_record/attributes.rb +46 -9
  47. data/lib/active_record/autosave_association.rb +57 -42
  48. data/lib/active_record/base.rb +4 -17
  49. data/lib/active_record/callbacks.rb +158 -43
  50. data/lib/active_record/coders/yaml_column.rb +1 -2
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +211 -90
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  64. data/lib/active_record/connection_adapters/column.rb +30 -12
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +88 -32
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +142 -19
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -54
  81. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  99. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  101. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  102. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  104. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  105. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  106. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  107. data/lib/active_record/connection_adapters/schema_cache.rb +159 -21
  108. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  109. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
  110. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  111. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  112. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  113. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  114. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  115. data/lib/active_record/connection_adapters.rb +52 -0
  116. data/lib/active_record/connection_handling.rb +293 -33
  117. data/lib/active_record/core.rb +333 -98
  118. data/lib/active_record/counter_cache.rb +8 -30
  119. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  120. data/lib/active_record/database_configurations/database_config.rb +80 -0
  121. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  122. data/lib/active_record/database_configurations/url_config.rb +53 -0
  123. data/lib/active_record/database_configurations.rb +273 -0
  124. data/lib/active_record/delegated_type.rb +209 -0
  125. data/lib/active_record/destroy_association_async_job.rb +36 -0
  126. data/lib/active_record/dynamic_matchers.rb +3 -4
  127. data/lib/active_record/enum.rb +108 -36
  128. data/lib/active_record/errors.rb +62 -19
  129. data/lib/active_record/explain.rb +10 -6
  130. data/lib/active_record/explain_subscriber.rb +1 -1
  131. data/lib/active_record/fixture_set/file.rb +10 -17
  132. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  133. data/lib/active_record/fixture_set/render_context.rb +17 -0
  134. data/lib/active_record/fixture_set/table_row.rb +152 -0
  135. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  136. data/lib/active_record/fixtures.rb +200 -481
  137. data/lib/active_record/gem_version.rb +3 -3
  138. data/lib/active_record/inheritance.rb +53 -24
  139. data/lib/active_record/insert_all.rb +212 -0
  140. data/lib/active_record/integration.rb +67 -17
  141. data/lib/active_record/internal_metadata.rb +28 -9
  142. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  143. data/lib/active_record/locking/optimistic.rb +37 -23
  144. data/lib/active_record/locking/pessimistic.rb +9 -5
  145. data/lib/active_record/log_subscriber.rb +35 -35
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  148. data/lib/active_record/middleware/database_selector.rb +77 -0
  149. data/lib/active_record/migration/command_recorder.rb +96 -44
  150. data/lib/active_record/migration/compatibility.rb +145 -64
  151. data/lib/active_record/migration/join_table.rb +0 -1
  152. data/lib/active_record/migration.rb +206 -157
  153. data/lib/active_record/model_schema.rb +148 -22
  154. data/lib/active_record/nested_attributes.rb +4 -7
  155. data/lib/active_record/no_touching.rb +8 -1
  156. data/lib/active_record/null_relation.rb +0 -1
  157. data/lib/active_record/persistence.rb +267 -59
  158. data/lib/active_record/query_cache.rb +21 -4
  159. data/lib/active_record/querying.rb +40 -23
  160. data/lib/active_record/railtie.rb +116 -59
  161. data/lib/active_record/railties/console_sandbox.rb +2 -4
  162. data/lib/active_record/railties/controller_runtime.rb +30 -35
  163. data/lib/active_record/railties/databases.rake +411 -80
  164. data/lib/active_record/readonly_attributes.rb +4 -0
  165. data/lib/active_record/reflection.rb +109 -93
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  167. data/lib/active_record/relation/batches.rb +44 -35
  168. data/lib/active_record/relation/calculations.rb +157 -90
  169. data/lib/active_record/relation/delegation.rb +35 -50
  170. data/lib/active_record/relation/finder_methods.rb +64 -39
  171. data/lib/active_record/relation/from_clause.rb +5 -1
  172. data/lib/active_record/relation/merger.rb +32 -40
  173. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  174. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  179. data/lib/active_record/relation/predicate_builder.rb +62 -45
  180. data/lib/active_record/relation/query_attribute.rb +13 -8
  181. data/lib/active_record/relation/query_methods.rb +476 -187
  182. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  183. data/lib/active_record/relation/spawn_methods.rb +9 -9
  184. data/lib/active_record/relation/where_clause.rb +115 -62
  185. data/lib/active_record/relation.rb +379 -115
  186. data/lib/active_record/result.rb +64 -38
  187. data/lib/active_record/runtime_registry.rb +2 -2
  188. data/lib/active_record/sanitization.rb +22 -41
  189. data/lib/active_record/schema.rb +2 -11
  190. data/lib/active_record/schema_dumper.rb +54 -9
  191. data/lib/active_record/schema_migration.rb +7 -9
  192. data/lib/active_record/scoping/default.rb +4 -8
  193. data/lib/active_record/scoping/named.rb +17 -24
  194. data/lib/active_record/scoping.rb +8 -9
  195. data/lib/active_record/secure_token.rb +16 -8
  196. data/lib/active_record/serialization.rb +5 -3
  197. data/lib/active_record/signed_id.rb +116 -0
  198. data/lib/active_record/statement_cache.rb +49 -6
  199. data/lib/active_record/store.rb +88 -9
  200. data/lib/active_record/suppressor.rb +2 -2
  201. data/lib/active_record/table_metadata.rb +42 -43
  202. data/lib/active_record/tasks/database_tasks.rb +277 -81
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  206. data/lib/active_record/test_databases.rb +24 -0
  207. data/lib/active_record/test_fixtures.rb +287 -0
  208. data/lib/active_record/timestamp.rb +43 -32
  209. data/lib/active_record/touch_later.rb +23 -22
  210. data/lib/active_record/transactions.rb +62 -118
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  213. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  214. data/lib/active_record/type/serialized.rb +6 -3
  215. data/lib/active_record/type/time.rb +10 -0
  216. data/lib/active_record/type/type_map.rb +0 -1
  217. data/lib/active_record/type/unsigned_integer.rb +0 -1
  218. data/lib/active_record/type.rb +10 -5
  219. data/lib/active_record/type_caster/connection.rb +15 -15
  220. data/lib/active_record/type_caster/map.rb +8 -8
  221. data/lib/active_record/validations/associated.rb +1 -2
  222. data/lib/active_record/validations/numericality.rb +35 -0
  223. data/lib/active_record/validations/uniqueness.rb +38 -30
  224. data/lib/active_record/validations.rb +4 -3
  225. data/lib/active_record.rb +13 -12
  226. data/lib/arel/alias_predication.rb +9 -0
  227. data/lib/arel/attributes/attribute.rb +41 -0
  228. data/lib/arel/collectors/bind.rb +29 -0
  229. data/lib/arel/collectors/composite.rb +39 -0
  230. data/lib/arel/collectors/plain_string.rb +20 -0
  231. data/lib/arel/collectors/sql_string.rb +27 -0
  232. data/lib/arel/collectors/substitute_binds.rb +35 -0
  233. data/lib/arel/crud.rb +42 -0
  234. data/lib/arel/delete_manager.rb +18 -0
  235. data/lib/arel/errors.rb +9 -0
  236. data/lib/arel/expressions.rb +29 -0
  237. data/lib/arel/factory_methods.rb +49 -0
  238. data/lib/arel/insert_manager.rb +49 -0
  239. data/lib/arel/math.rb +45 -0
  240. data/lib/arel/nodes/and.rb +32 -0
  241. data/lib/arel/nodes/ascending.rb +23 -0
  242. data/lib/arel/nodes/binary.rb +126 -0
  243. data/lib/arel/nodes/bind_param.rb +44 -0
  244. data/lib/arel/nodes/case.rb +55 -0
  245. data/lib/arel/nodes/casted.rb +62 -0
  246. data/lib/arel/nodes/comment.rb +29 -0
  247. data/lib/arel/nodes/count.rb +12 -0
  248. data/lib/arel/nodes/delete_statement.rb +45 -0
  249. data/lib/arel/nodes/descending.rb +23 -0
  250. data/lib/arel/nodes/equality.rb +15 -0
  251. data/lib/arel/nodes/extract.rb +24 -0
  252. data/lib/arel/nodes/false.rb +16 -0
  253. data/lib/arel/nodes/full_outer_join.rb +8 -0
  254. data/lib/arel/nodes/function.rb +44 -0
  255. data/lib/arel/nodes/grouping.rb +11 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  257. data/lib/arel/nodes/in.rb +15 -0
  258. data/lib/arel/nodes/infix_operation.rb +92 -0
  259. data/lib/arel/nodes/inner_join.rb +8 -0
  260. data/lib/arel/nodes/insert_statement.rb +37 -0
  261. data/lib/arel/nodes/join_source.rb +20 -0
  262. data/lib/arel/nodes/matches.rb +18 -0
  263. data/lib/arel/nodes/named_function.rb +23 -0
  264. data/lib/arel/nodes/node.rb +51 -0
  265. data/lib/arel/nodes/node_expression.rb +13 -0
  266. data/lib/arel/nodes/ordering.rb +27 -0
  267. data/lib/arel/nodes/outer_join.rb +8 -0
  268. data/lib/arel/nodes/over.rb +15 -0
  269. data/lib/arel/nodes/regexp.rb +16 -0
  270. data/lib/arel/nodes/right_outer_join.rb +8 -0
  271. data/lib/arel/nodes/select_core.rb +67 -0
  272. data/lib/arel/nodes/select_statement.rb +41 -0
  273. data/lib/arel/nodes/sql_literal.rb +19 -0
  274. data/lib/arel/nodes/string_join.rb +11 -0
  275. data/lib/arel/nodes/table_alias.rb +31 -0
  276. data/lib/arel/nodes/terminal.rb +16 -0
  277. data/lib/arel/nodes/true.rb +16 -0
  278. data/lib/arel/nodes/unary.rb +44 -0
  279. data/lib/arel/nodes/unary_operation.rb +20 -0
  280. data/lib/arel/nodes/unqualified_column.rb +22 -0
  281. data/lib/arel/nodes/update_statement.rb +41 -0
  282. data/lib/arel/nodes/values_list.rb +9 -0
  283. data/lib/arel/nodes/window.rb +126 -0
  284. data/lib/arel/nodes/with.rb +11 -0
  285. data/lib/arel/nodes.rb +70 -0
  286. data/lib/arel/order_predications.rb +13 -0
  287. data/lib/arel/predications.rb +250 -0
  288. data/lib/arel/select_manager.rb +270 -0
  289. data/lib/arel/table.rb +118 -0
  290. data/lib/arel/tree_manager.rb +72 -0
  291. data/lib/arel/update_manager.rb +34 -0
  292. data/lib/arel/visitors/dot.rb +308 -0
  293. data/lib/arel/visitors/mysql.rb +93 -0
  294. data/lib/arel/visitors/postgresql.rb +120 -0
  295. data/lib/arel/visitors/sqlite.rb +38 -0
  296. data/lib/arel/visitors/to_sql.rb +899 -0
  297. data/lib/arel/visitors/visitor.rb +45 -0
  298. data/lib/arel/visitors.rb +13 -0
  299. data/lib/arel/window_predications.rb +9 -0
  300. data/lib/arel.rb +54 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  303. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  304. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  305. data/lib/rails/generators/active_record/migration.rb +19 -2
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +116 -30
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -5,7 +5,7 @@
5
5
  module ActiveRecord::Associations::Builder # :nodoc:
6
6
  class SingularAssociation < Association #:nodoc:
7
7
  def self.valid_options(options)
8
- super + [:foreign_type, :dependent, :primary_key, :inverse_of, :required]
8
+ super + [:required, :touch]
9
9
  end
10
10
 
11
11
  def self.define_accessors(model, reflection)
@@ -38,5 +38,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
38
38
  end
39
39
  CODE
40
40
  end
41
+
42
+ private_class_method :valid_options, :define_accessors, :define_constructors
41
43
  end
42
44
  end
@@ -56,7 +56,7 @@ module ActiveRecord
56
56
  def ids_writer(ids)
57
57
  primary_key = reflection.association_primary_key
58
58
  pk_type = klass.type_for_attribute(primary_key)
59
- ids = Array(ids).reject(&:blank?)
59
+ ids = Array(ids).compact_blank
60
60
  ids.map! { |i| pk_type.cast(i) }
61
61
 
62
62
  records = klass.where(primary_key => ids).index_by do |r|
@@ -75,6 +75,7 @@ module ActiveRecord
75
75
  def reset
76
76
  super
77
77
  @target = []
78
+ @replaced_or_added_targets = Set.new
78
79
  @association_ids = nil
79
80
  end
80
81
 
@@ -101,11 +102,11 @@ module ActiveRecord
101
102
  end
102
103
  end
103
104
 
104
- def build(attributes = {}, &block)
105
+ def build(attributes = nil, &block)
105
106
  if attributes.is_a?(Array)
106
107
  attributes.collect { |attr| build(attr, &block) }
107
108
  else
108
- add_to_target(build_record(attributes, &block))
109
+ add_to_target(build_record(attributes, &block), replace: true)
109
110
  end
110
111
  end
111
112
 
@@ -211,9 +212,11 @@ module ActiveRecord
211
212
  def size
212
213
  if !find_target? || loaded?
213
214
  target.size
215
+ elsif @association_ids
216
+ @association_ids.size
214
217
  elsif !association_scope.group_values.empty?
215
218
  load_target.size
216
- elsif !association_scope.distinct_value && target.is_a?(Array)
219
+ elsif !association_scope.distinct_value && !target.empty?
217
220
  unsaved_records = target.select(&:new_record?)
218
221
  unsaved_records.size + count_records
219
222
  else
@@ -226,14 +229,14 @@ module ActiveRecord
226
229
  # If the collection has been loaded
227
230
  # it is equivalent to <tt>collection.size.zero?</tt>. If the
228
231
  # collection has not been loaded, it is equivalent to
229
- # <tt>collection.exists?</tt>. If the collection has not already been
232
+ # <tt>!collection.exists?</tt>. If the collection has not already been
230
233
  # loaded and you are going to fetch the records anyway it is better to
231
234
  # check <tt>collection.length.zero?</tt>.
232
235
  def empty?
233
- if loaded?
236
+ if loaded? || @association_ids || reflection.has_cached_counter?
234
237
  size.zero?
235
238
  else
236
- @target.blank? && !scope.exists?
239
+ target.empty? && !scope.exists?
237
240
  end
238
241
  end
239
242
 
@@ -276,11 +279,19 @@ module ActiveRecord
276
279
  target
277
280
  end
278
281
 
279
- def add_to_target(record, skip_callbacks = false, &block)
280
- if association_scope.distinct_value
281
- index = @target.index(record)
282
+ def add_to_target(record, skip_callbacks: false, replace: false, &block)
283
+ replace_on_target(record, skip_callbacks, replace: replace || association_scope.distinct_value, &block)
284
+ end
285
+
286
+ def target=(record)
287
+ return super unless ActiveRecord::Base.has_many_inversing
288
+
289
+ case record
290
+ when Array
291
+ super
292
+ else
293
+ replace_on_target(record, true, replace: true, inversing: true)
282
294
  end
283
- replace_on_target(record, index, skip_callbacks, &block)
284
295
  end
285
296
 
286
297
  def scope
@@ -295,28 +306,13 @@ module ActiveRecord
295
306
 
296
307
  def find_from_target?
297
308
  loaded? ||
309
+ owner.strict_loading? ||
310
+ reflection.strict_loading? ||
298
311
  owner.new_record? ||
299
312
  target.any? { |record| record.new_record? || record.changed? }
300
313
  end
301
314
 
302
315
  private
303
-
304
- def find_target
305
- scope = self.scope
306
- return scope.to_a if skip_statement_cache?(scope)
307
-
308
- conn = klass.connection
309
- sc = reflection.association_scope_cache(conn, owner) do |params|
310
- as = AssociationScope.create { params.bind }
311
- target_scope.merge!(as.scope(self))
312
- end
313
-
314
- binds = AssociationScope.get_bind_values(owner, reflection.chain)
315
- sc.execute(binds, conn) do |record|
316
- set_inverse_instance(record)
317
- end
318
- end
319
-
320
316
  # We have some records loaded from the database (persisted) and some that are
321
317
  # in-memory (memory). The same record may be represented in the persisted array
322
318
  # and in the memory array.
@@ -344,7 +340,7 @@ module ActiveRecord
344
340
  end
345
341
  end
346
342
 
347
- persisted + memory
343
+ persisted + memory.reject(&:persisted?)
348
344
  end
349
345
 
350
346
  def _create_record(attributes, raise = false, &block)
@@ -393,10 +389,12 @@ module ActiveRecord
393
389
  end
394
390
 
395
391
  def remove_records(existing_records, records, method)
396
- records.each { |record| callback(:before_remove, record) }
392
+ catch(:abort) do
393
+ records.each { |record| callback(:before_remove, record) }
394
+ end || return
397
395
 
398
396
  delete_records(existing_records, method) if existing_records.any?
399
- records.each { |record| target.delete(record) }
397
+ @target -= records
400
398
  @association_ids = nil
401
399
 
402
400
  records.each { |record| callback(:after_remove, record) }
@@ -425,7 +423,7 @@ module ActiveRecord
425
423
  common_records = intersection(new_target, original_target)
426
424
  common_records.each do |record|
427
425
  skip_callbacks = true
428
- replace_on_target(record, @target.index(record), skip_callbacks)
426
+ replace_on_target(record, skip_callbacks, replace: true)
429
427
  end
430
428
  end
431
429
 
@@ -448,8 +446,14 @@ module ActiveRecord
448
446
  records
449
447
  end
450
448
 
451
- def replace_on_target(record, index, skip_callbacks)
452
- callback(:before_add, record) unless skip_callbacks
449
+ def replace_on_target(record, skip_callbacks, replace:, inversing: false)
450
+ if replace && (!record.new_record? || @replaced_or_added_targets.include?(record))
451
+ index = @target.index(record)
452
+ end
453
+
454
+ catch(:abort) do
455
+ callback(:before_add, record)
456
+ end || return unless skip_callbacks
453
457
 
454
458
  set_inverse_instance(record)
455
459
 
@@ -457,6 +461,12 @@ module ActiveRecord
457
461
 
458
462
  yield(record) if block_given?
459
463
 
464
+ if !index && @replaced_or_added_targets.include?(record)
465
+ index = @target.index(record)
466
+ end
467
+
468
+ @replaced_or_added_targets << record if inversing || index || record.new_record?
469
+
460
470
  if index
461
471
  target[index] = record
462
472
  elsif @_was_loaded || !loaded?
@@ -2,11 +2,8 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Associations
5
- # Association proxies in Active Record are middlemen between the object that
6
- # holds the association, known as the <tt>@owner</tt>, and the actual associated
7
- # object, known as the <tt>@target</tt>. The kind of association any proxy is
8
- # about is available in <tt>@reflection</tt>. That's an instance of the class
9
- # ActiveRecord::Reflection::AssociationReflection.
5
+ # Collection proxies in Active Record are middlemen between an
6
+ # <tt>association</tt>, and its <tt>target</tt> result set.
10
7
  #
11
8
  # For example, given
12
9
  #
@@ -16,21 +13,21 @@ module ActiveRecord
16
13
  #
17
14
  # blog = Blog.first
18
15
  #
19
- # the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
20
- # <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
21
- # the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
16
+ # The collection proxy returned by <tt>blog.posts</tt> is built from a
17
+ # <tt>:has_many</tt> <tt>association</tt>, and delegates to a collection
18
+ # of posts as the <tt>target</tt>.
22
19
  #
23
- # This class delegates unknown methods to <tt>@target</tt> via
24
- # <tt>method_missing</tt>.
20
+ # This class delegates unknown methods to the <tt>association</tt>'s
21
+ # relation class via a delegate cache.
25
22
  #
26
- # The <tt>@target</tt> object is not \loaded until needed. For example,
23
+ # The <tt>target</tt> result set is not loaded until needed. For example,
27
24
  #
28
25
  # blog.posts.count
29
26
  #
30
27
  # is computed directly through SQL and does not trigger by itself the
31
28
  # instantiation of the actual post records.
32
29
  class CollectionProxy < Relation
33
- def initialize(klass, association) #:nodoc:
30
+ def initialize(klass, association, **) #:nodoc:
34
31
  @association = association
35
32
  super klass
36
33
 
@@ -54,6 +51,7 @@ module ActiveRecord
54
51
  def loaded?
55
52
  @association.loaded?
56
53
  end
54
+ alias :loaded :loaded?
57
55
 
58
56
  ##
59
57
  # :method: select
@@ -103,7 +101,7 @@ module ActiveRecord
103
101
  # converting them into an array and iterating through them using
104
102
  # Array#select.
105
103
  #
106
- # person.pets.select { |pet| pet.name =~ /oo/ }
104
+ # person.pets.select { |pet| /oo/.match?(pet.name) }
107
105
  # # => [
108
106
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
109
107
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
@@ -376,7 +374,7 @@ module ActiveRecord
376
374
  # person.pets
377
375
  # # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
378
376
  #
379
- # other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
377
+ # other_pets = [Pet.new(name: 'Puff', group: 'celebrities')]
380
378
  #
381
379
  # person.pets.replace(other_pets)
382
380
  #
@@ -923,7 +921,7 @@ module ActiveRecord
923
921
  !!@association.include?(record)
924
922
  end
925
923
 
926
- def proxy_association
924
+ def proxy_association # :nodoc:
927
925
  @association
928
926
  end
929
927
 
@@ -1005,7 +1003,7 @@ module ActiveRecord
1005
1003
  end
1006
1004
 
1007
1005
  # Adds one or more +records+ to the collection by setting their foreign keys
1008
- # to the association's primary key. Since +<<+ flattens its argument list and
1006
+ # to the association's primary key. Since <tt><<</tt> flattens its argument list and
1009
1007
  # inserts each record, +push+ and +concat+ behave identically. Returns +self+
1010
1008
  # so several appends may be chained together.
1011
1009
  #
@@ -1032,7 +1030,7 @@ module ActiveRecord
1032
1030
  alias_method :append, :<<
1033
1031
  alias_method :concat, :<<
1034
1032
 
1035
- def prepend(*args)
1033
+ def prepend(*args) # :nodoc:
1036
1034
  raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
1037
1035
  end
1038
1036
 
@@ -1062,7 +1060,7 @@ module ActiveRecord
1062
1060
  # person.pets.reload # fetches pets from the database
1063
1061
  # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1064
1062
  def reload
1065
- proxy_association.reload
1063
+ proxy_association.reload(true)
1066
1064
  reset_scope
1067
1065
  end
1068
1066
 
@@ -1089,22 +1087,28 @@ module ActiveRecord
1089
1087
  end
1090
1088
 
1091
1089
  def reset_scope # :nodoc:
1092
- @offsets = {}
1090
+ @offsets = @take = nil
1093
1091
  @scope = nil
1094
1092
  self
1095
1093
  end
1096
1094
 
1095
+ def inspect # :nodoc:
1096
+ load_target if find_from_target?
1097
+ super
1098
+ end
1099
+
1097
1100
  delegate_methods = [
1098
1101
  QueryMethods,
1099
1102
  SpawnMethods,
1100
1103
  ].flat_map { |klass|
1101
1104
  klass.public_instance_methods(false)
1102
- } - self.public_instance_methods(false) - [:select] + [:scoping]
1105
+ } - self.public_instance_methods(false) - [:select] + [
1106
+ :scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
1107
+ ]
1103
1108
 
1104
1109
  delegate(*delegate_methods, to: :scope)
1105
1110
 
1106
1111
  private
1107
-
1108
1112
  def find_nth_with_limit(index, limit)
1109
1113
  load_target if find_from_target?
1110
1114
  super
@@ -9,5 +9,25 @@ module ActiveRecord::Associations
9
9
  false
10
10
  end
11
11
  end
12
+
13
+ def nullified_owner_attributes
14
+ Hash.new.tap do |attrs|
15
+ attrs[reflection.foreign_key] = nil
16
+ attrs[reflection.type] = nil if reflection.type.present?
17
+ end
18
+ end
19
+
20
+ private
21
+ # Sets the owner attributes on the given record
22
+ def set_owner_attributes(record)
23
+ return if options[:through]
24
+
25
+ key = owner._read_attribute(reflection.join_foreign_key)
26
+ record._write_attribute(reflection.join_primary_key, key)
27
+
28
+ if reflection.type
29
+ record._write_attribute(reflection.type, owner.class.polymorphic_name)
30
+ end
31
+ end
12
32
  end
13
33
  end
@@ -26,6 +26,28 @@ module ActiveRecord
26
26
  # No point in executing the counter update since we're going to destroy the parent anyway
27
27
  load_target.each { |t| t.destroyed_by_association = reflection }
28
28
  destroy_all
29
+ when :destroy_async
30
+ load_target.each do |t|
31
+ t.destroyed_by_association = reflection
32
+ end
33
+
34
+ unless target.empty?
35
+ association_class = target.first.class
36
+ primary_key_column = association_class.primary_key.to_sym
37
+
38
+ ids = target.collect do |assoc|
39
+ assoc.public_send(primary_key_column)
40
+ end
41
+
42
+ enqueue_destroy_association(
43
+ owner_model_name: owner.class.to_s,
44
+ owner_id: owner.id,
45
+ association_class: reflection.klass.to_s,
46
+ association_ids: ids,
47
+ association_primary_key_column: primary_key_column,
48
+ ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
49
+ )
50
+ end
29
51
  else
30
52
  delete_all
31
53
  end
@@ -36,16 +58,7 @@ module ActiveRecord
36
58
  super
37
59
  end
38
60
 
39
- def empty?
40
- if reflection.has_cached_counter?
41
- size.zero?
42
- else
43
- super
44
- end
45
- end
46
-
47
61
  private
48
-
49
62
  # Returns the number of records in this collection.
50
63
  #
51
64
  # If the association has a counter cache it gets that value. Otherwise
@@ -61,7 +74,7 @@ module ActiveRecord
61
74
  # the loaded flag is set to true as well.
62
75
  def count_records
63
76
  count = if reflection.has_cached_counter?
64
- owner._read_attribute(reflection.counter_cache_column).to_i
77
+ owner.read_attribute(reflection.counter_cache_column).to_i
65
78
  else
66
79
  scope.count(:all)
67
80
  end
@@ -69,7 +82,7 @@ module ActiveRecord
69
82
  # If there's nothing in the database and @target has no new records
70
83
  # we are certain the current target is an empty array. This is a
71
84
  # documented side-effect of the method that may avoid an extra SELECT.
72
- (@target ||= []) && loaded! if count == 0
85
+ loaded! if count == 0
73
86
 
74
87
  [association_scope.limit_value, count].compact.min
75
88
  end
@@ -84,7 +97,7 @@ module ActiveRecord
84
97
  if reflection.counter_must_be_updated_by_has_many?
85
98
  counter = reflection.counter_cache_column
86
99
  owner.increment(counter, difference)
87
- owner.send(:clear_attribute_change, counter) # eww
100
+ owner.send(:"clear_#{counter}_change")
88
101
  end
89
102
  end
90
103
 
@@ -92,7 +105,7 @@ module ActiveRecord
92
105
  if method == :delete_all
93
106
  scope.delete_all
94
107
  else
95
- scope.update_all(reflection.foreign_key => nil)
108
+ scope.update_all(nullified_owner_attributes)
96
109
  end
97
110
  end
98
111
 
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
 
9
9
  def initialize(owner, reflection)
10
10
  super
11
- @through_records = {}
11
+ @through_records = {}.compare_by_identity
12
12
  end
13
13
 
14
14
  def concat(*records)
@@ -21,20 +21,6 @@ module ActiveRecord
21
21
  super
22
22
  end
23
23
 
24
- def concat_records(records)
25
- ensure_not_nested
26
-
27
- records = super(records, true)
28
-
29
- if owner.new_record? && records
30
- records.flatten.each do |record|
31
- build_through_record(record)
32
- end
33
- end
34
-
35
- records
36
- end
37
-
38
24
  def insert_record(record, validate = true, raise = false)
39
25
  ensure_not_nested
40
26
 
@@ -48,13 +34,27 @@ module ActiveRecord
48
34
  end
49
35
 
50
36
  private
37
+ def concat_records(records)
38
+ ensure_not_nested
39
+
40
+ records = super(records, true)
41
+
42
+ if owner.new_record? && records
43
+ records.flatten.each do |record|
44
+ build_through_record(record)
45
+ end
46
+ end
47
+
48
+ records
49
+ end
50
+
51
51
  # The through record (built with build_record) is temporarily cached
52
52
  # so that it may be reused if insert_record is subsequently called.
53
53
  #
54
54
  # However, after insert_record has been called, the cache is cleared in
55
55
  # order to allow multiple instances of the same record in an association.
56
56
  def build_through_record(record)
57
- @through_records[record.object_id] ||= begin
57
+ @through_records[record] ||= begin
58
58
  ensure_mutable
59
59
 
60
60
  attributes = through_scope_attributes
@@ -65,7 +65,10 @@ module ActiveRecord
65
65
  end
66
66
  end
67
67
 
68
+ attr_reader :through_scope
69
+
68
70
  def through_scope_attributes
71
+ scope = through_scope || self.scope
69
72
  scope.where_values_hash(through_association.reflection.name.to_s).
70
73
  except!(through_association.reflection.foreign_key,
71
74
  through_association.reflection.klass.inheritance_column)
@@ -77,12 +80,13 @@ module ActiveRecord
77
80
  association.save!
78
81
  end
79
82
  ensure
80
- @through_records.delete(record.object_id)
83
+ @through_records.delete(record)
81
84
  end
82
85
 
83
86
  def build_record(attributes)
84
87
  ensure_not_nested
85
88
 
89
+ @through_scope = scope
86
90
  record = super
87
91
 
88
92
  inverse = source_reflection.inverse_of
@@ -95,6 +99,8 @@ module ActiveRecord
95
99
  end
96
100
 
97
101
  record
102
+ ensure
103
+ @through_scope = nil
98
104
  end
99
105
 
100
106
  def remove_records(existing_records, records, method)
@@ -202,7 +208,7 @@ module ActiveRecord
202
208
  end
203
209
  end
204
210
 
205
- @through_records.delete(record.object_id)
211
+ @through_records.delete(record)
206
212
  end
207
213
  end
208
214
 
@@ -23,35 +23,6 @@ module ActiveRecord
23
23
  end
24
24
  end
25
25
 
26
- def replace(record, save = true)
27
- raise_on_type_mismatch!(record) if record
28
- load_target
29
-
30
- return target unless target || record
31
-
32
- assigning_another_record = target != record
33
- if assigning_another_record || record.has_changes_to_save?
34
- save &&= owner.persisted?
35
-
36
- transaction_if(save) do
37
- remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
38
-
39
- if record
40
- set_owner_attributes(record)
41
- set_inverse_instance(record)
42
-
43
- if save && !record.save
44
- nullify_owner_attributes(record)
45
- set_owner_attributes(target) if target
46
- raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
47
- end
48
- end
49
- end
50
- end
51
-
52
- self.target = record
53
- end
54
-
55
26
  def delete(method = options[:dependent])
56
27
  if load_target
57
28
  case method
@@ -61,13 +32,52 @@ module ActiveRecord
61
32
  target.destroyed_by_association = reflection
62
33
  target.destroy
63
34
  throw(:abort) unless target.destroyed?
35
+ when :destroy_async
36
+ primary_key_column = target.class.primary_key.to_sym
37
+ id = target.public_send(primary_key_column)
38
+
39
+ enqueue_destroy_association(
40
+ owner_model_name: owner.class.to_s,
41
+ owner_id: owner.id,
42
+ association_class: reflection.klass.to_s,
43
+ association_ids: [id],
44
+ association_primary_key_column: primary_key_column,
45
+ ensuring_owner_was_method: options.fetch(:ensuring_owner_was, nil)
46
+ )
64
47
  when :nullify
65
- target.update_columns(reflection.foreign_key => nil) if target.persisted?
48
+ target.update_columns(nullified_owner_attributes) if target.persisted?
66
49
  end
67
50
  end
68
51
  end
69
52
 
70
53
  private
54
+ def replace(record, save = true)
55
+ raise_on_type_mismatch!(record) if record
56
+
57
+ return target unless load_target || record
58
+
59
+ assigning_another_record = target != record
60
+ if assigning_another_record || record.has_changes_to_save?
61
+ save &&= owner.persisted?
62
+
63
+ transaction_if(save) do
64
+ remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
65
+
66
+ if record
67
+ set_owner_attributes(record)
68
+ set_inverse_instance(record)
69
+
70
+ if save && !record.save
71
+ nullify_owner_attributes(record)
72
+ set_owner_attributes(target) if target
73
+ raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ self.target = record
80
+ end
71
81
 
72
82
  # The reason that the save param for replace is false, if for create (not just build),
73
83
  # is because the setting of the foreign keys is actually handled by the scoping when
@@ -83,7 +93,9 @@ module ActiveRecord
83
93
  target.delete
84
94
  when :destroy
85
95
  target.destroyed_by_association = reflection
86
- target.destroy
96
+ if target.persisted?
97
+ target.destroy
98
+ end
87
99
  else
88
100
  nullify_owner_attributes(target)
89
101
  remove_inverse_instance(target)
@@ -6,12 +6,12 @@ module ActiveRecord
6
6
  class HasOneThroughAssociation < HasOneAssociation #:nodoc:
7
7
  include ThroughAssociation
8
8
 
9
- def replace(record, save = true)
10
- create_through_record(record, save)
11
- self.target = record
12
- end
13
-
14
9
  private
10
+ def replace(record, save = true)
11
+ create_through_record(record, save)
12
+ self.target = record
13
+ end
14
+
15
15
  def create_through_record(record, save)
16
16
  ensure_not_nested
17
17