activerecord 5.2.4.4 → 6.0.3.4

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 (292) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +777 -552
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +5 -3
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +10 -2
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +4 -3
  9. data/lib/active_record/association_relation.rb +10 -8
  10. data/lib/active_record/associations.rb +21 -16
  11. data/lib/active_record/associations/alias_tracker.rb +0 -1
  12. data/lib/active_record/associations/association.rb +56 -19
  13. data/lib/active_record/associations/association_scope.rb +4 -6
  14. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  16. data/lib/active_record/associations/builder/association.rb +14 -18
  17. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  18. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +35 -1
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +12 -23
  24. data/lib/active_record/associations/collection_proxy.rb +13 -17
  25. data/lib/active_record/associations/foreign_association.rb +7 -0
  26. data/lib/active_record/associations/has_many_association.rb +2 -11
  27. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  28. data/lib/active_record/associations/has_one_association.rb +28 -30
  29. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +37 -28
  31. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  32. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  33. data/lib/active_record/associations/preloader.rb +39 -32
  34. data/lib/active_record/associations/preloader/association.rb +38 -36
  35. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  36. data/lib/active_record/associations/singular_association.rb +2 -16
  37. data/lib/active_record/attribute_assignment.rb +7 -11
  38. data/lib/active_record/attribute_decorators.rb +0 -2
  39. data/lib/active_record/attribute_methods.rb +28 -100
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
  41. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  42. data/lib/active_record/attribute_methods/primary_key.rb +15 -24
  43. data/lib/active_record/attribute_methods/query.rb +2 -3
  44. data/lib/active_record/attribute_methods/read.rb +15 -54
  45. data/lib/active_record/attribute_methods/serialization.rb +1 -2
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
  47. data/lib/active_record/attribute_methods/write.rb +17 -25
  48. data/lib/active_record/attributes.rb +13 -1
  49. data/lib/active_record/autosave_association.rb +3 -5
  50. data/lib/active_record/base.rb +2 -3
  51. data/lib/active_record/callbacks.rb +6 -21
  52. data/lib/active_record/coders/yaml_column.rb +0 -1
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +103 -18
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +100 -72
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +191 -43
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +142 -215
  65. data/lib/active_record/connection_adapters/column.rb +17 -13
  66. data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
  67. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  68. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
  70. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
  71. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
  73. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  74. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  75. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +132 -16
  76. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  82. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
  98. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
  101. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  102. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +135 -146
  107. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  108. data/lib/active_record/connection_handling.rb +139 -26
  109. data/lib/active_record/core.rb +103 -61
  110. data/lib/active_record/counter_cache.rb +8 -30
  111. data/lib/active_record/database_configurations.rb +233 -0
  112. data/lib/active_record/database_configurations/database_config.rb +37 -0
  113. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  114. data/lib/active_record/database_configurations/url_config.rb +78 -0
  115. data/lib/active_record/dynamic_matchers.rb +3 -4
  116. data/lib/active_record/enum.rb +37 -7
  117. data/lib/active_record/errors.rb +15 -7
  118. data/lib/active_record/explain.rb +1 -2
  119. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  120. data/lib/active_record/fixture_set/render_context.rb +17 -0
  121. data/lib/active_record/fixture_set/table_row.rb +152 -0
  122. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  123. data/lib/active_record/fixtures.rb +144 -474
  124. data/lib/active_record/gem_version.rb +3 -3
  125. data/lib/active_record/inheritance.rb +13 -6
  126. data/lib/active_record/insert_all.rb +179 -0
  127. data/lib/active_record/integration.rb +68 -16
  128. data/lib/active_record/internal_metadata.rb +11 -3
  129. data/lib/active_record/locking/optimistic.rb +5 -7
  130. data/lib/active_record/locking/pessimistic.rb +3 -3
  131. data/lib/active_record/log_subscriber.rb +8 -27
  132. data/lib/active_record/middleware/database_selector.rb +74 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  134. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  135. data/lib/active_record/migration.rb +104 -85
  136. data/lib/active_record/migration/command_recorder.rb +54 -22
  137. data/lib/active_record/migration/compatibility.rb +79 -52
  138. data/lib/active_record/migration/join_table.rb +0 -1
  139. data/lib/active_record/model_schema.rb +33 -11
  140. data/lib/active_record/nested_attributes.rb +2 -4
  141. data/lib/active_record/no_touching.rb +9 -2
  142. data/lib/active_record/null_relation.rb +0 -1
  143. data/lib/active_record/persistence.rb +232 -29
  144. data/lib/active_record/query_cache.rb +11 -4
  145. data/lib/active_record/querying.rb +33 -21
  146. data/lib/active_record/railtie.rb +80 -43
  147. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  148. data/lib/active_record/railties/controller_runtime.rb +30 -35
  149. data/lib/active_record/railties/databases.rake +199 -46
  150. data/lib/active_record/reflection.rb +40 -38
  151. data/lib/active_record/relation.rb +322 -80
  152. data/lib/active_record/relation/batches.rb +13 -11
  153. data/lib/active_record/relation/calculations.rb +54 -48
  154. data/lib/active_record/relation/delegation.rb +33 -49
  155. data/lib/active_record/relation/finder_methods.rb +23 -28
  156. data/lib/active_record/relation/from_clause.rb +4 -0
  157. data/lib/active_record/relation/merger.rb +11 -21
  158. data/lib/active_record/relation/predicate_builder.rb +5 -11
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  160. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  163. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  164. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  165. data/lib/active_record/relation/query_attribute.rb +13 -8
  166. data/lib/active_record/relation/query_methods.rb +221 -70
  167. data/lib/active_record/relation/spawn_methods.rb +1 -2
  168. data/lib/active_record/relation/where_clause.rb +14 -11
  169. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  170. data/lib/active_record/result.rb +30 -12
  171. data/lib/active_record/sanitization.rb +32 -40
  172. data/lib/active_record/schema.rb +2 -11
  173. data/lib/active_record/schema_dumper.rb +22 -7
  174. data/lib/active_record/schema_migration.rb +6 -2
  175. data/lib/active_record/scoping.rb +8 -9
  176. data/lib/active_record/scoping/default.rb +4 -6
  177. data/lib/active_record/scoping/named.rb +21 -17
  178. data/lib/active_record/statement_cache.rb +30 -3
  179. data/lib/active_record/store.rb +87 -8
  180. data/lib/active_record/suppressor.rb +2 -2
  181. data/lib/active_record/table_metadata.rb +23 -15
  182. data/lib/active_record/tasks/database_tasks.rb +194 -25
  183. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
  184. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
  185. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
  186. data/lib/active_record/test_databases.rb +23 -0
  187. data/lib/active_record/test_fixtures.rb +225 -0
  188. data/lib/active_record/timestamp.rb +39 -26
  189. data/lib/active_record/touch_later.rb +5 -4
  190. data/lib/active_record/transactions.rb +64 -73
  191. data/lib/active_record/translation.rb +1 -1
  192. data/lib/active_record/type.rb +3 -5
  193. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  194. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  195. data/lib/active_record/type/serialized.rb +0 -1
  196. data/lib/active_record/type/type_map.rb +0 -1
  197. data/lib/active_record/type/unsigned_integer.rb +0 -1
  198. data/lib/active_record/type_caster/connection.rb +15 -14
  199. data/lib/active_record/type_caster/map.rb +1 -4
  200. data/lib/active_record/validations.rb +3 -3
  201. data/lib/active_record/validations/associated.rb +1 -2
  202. data/lib/active_record/validations/uniqueness.rb +15 -27
  203. data/lib/arel.rb +62 -0
  204. data/lib/arel/alias_predication.rb +9 -0
  205. data/lib/arel/attributes.rb +22 -0
  206. data/lib/arel/attributes/attribute.rb +37 -0
  207. data/lib/arel/collectors/bind.rb +24 -0
  208. data/lib/arel/collectors/composite.rb +31 -0
  209. data/lib/arel/collectors/plain_string.rb +20 -0
  210. data/lib/arel/collectors/sql_string.rb +20 -0
  211. data/lib/arel/collectors/substitute_binds.rb +28 -0
  212. data/lib/arel/crud.rb +42 -0
  213. data/lib/arel/delete_manager.rb +18 -0
  214. data/lib/arel/errors.rb +9 -0
  215. data/lib/arel/expressions.rb +29 -0
  216. data/lib/arel/factory_methods.rb +49 -0
  217. data/lib/arel/insert_manager.rb +49 -0
  218. data/lib/arel/math.rb +45 -0
  219. data/lib/arel/nodes.rb +68 -0
  220. data/lib/arel/nodes/and.rb +32 -0
  221. data/lib/arel/nodes/ascending.rb +23 -0
  222. data/lib/arel/nodes/binary.rb +52 -0
  223. data/lib/arel/nodes/bind_param.rb +36 -0
  224. data/lib/arel/nodes/case.rb +55 -0
  225. data/lib/arel/nodes/casted.rb +50 -0
  226. data/lib/arel/nodes/comment.rb +29 -0
  227. data/lib/arel/nodes/count.rb +12 -0
  228. data/lib/arel/nodes/delete_statement.rb +45 -0
  229. data/lib/arel/nodes/descending.rb +23 -0
  230. data/lib/arel/nodes/equality.rb +18 -0
  231. data/lib/arel/nodes/extract.rb +24 -0
  232. data/lib/arel/nodes/false.rb +16 -0
  233. data/lib/arel/nodes/full_outer_join.rb +8 -0
  234. data/lib/arel/nodes/function.rb +44 -0
  235. data/lib/arel/nodes/grouping.rb +8 -0
  236. data/lib/arel/nodes/in.rb +8 -0
  237. data/lib/arel/nodes/infix_operation.rb +80 -0
  238. data/lib/arel/nodes/inner_join.rb +8 -0
  239. data/lib/arel/nodes/insert_statement.rb +37 -0
  240. data/lib/arel/nodes/join_source.rb +20 -0
  241. data/lib/arel/nodes/matches.rb +18 -0
  242. data/lib/arel/nodes/named_function.rb +23 -0
  243. data/lib/arel/nodes/node.rb +50 -0
  244. data/lib/arel/nodes/node_expression.rb +13 -0
  245. data/lib/arel/nodes/outer_join.rb +8 -0
  246. data/lib/arel/nodes/over.rb +15 -0
  247. data/lib/arel/nodes/regexp.rb +16 -0
  248. data/lib/arel/nodes/right_outer_join.rb +8 -0
  249. data/lib/arel/nodes/select_core.rb +67 -0
  250. data/lib/arel/nodes/select_statement.rb +41 -0
  251. data/lib/arel/nodes/sql_literal.rb +16 -0
  252. data/lib/arel/nodes/string_join.rb +11 -0
  253. data/lib/arel/nodes/table_alias.rb +27 -0
  254. data/lib/arel/nodes/terminal.rb +16 -0
  255. data/lib/arel/nodes/true.rb +16 -0
  256. data/lib/arel/nodes/unary.rb +45 -0
  257. data/lib/arel/nodes/unary_operation.rb +20 -0
  258. data/lib/arel/nodes/unqualified_column.rb +22 -0
  259. data/lib/arel/nodes/update_statement.rb +41 -0
  260. data/lib/arel/nodes/values_list.rb +9 -0
  261. data/lib/arel/nodes/window.rb +126 -0
  262. data/lib/arel/nodes/with.rb +11 -0
  263. data/lib/arel/order_predications.rb +13 -0
  264. data/lib/arel/predications.rb +256 -0
  265. data/lib/arel/select_manager.rb +271 -0
  266. data/lib/arel/table.rb +110 -0
  267. data/lib/arel/tree_manager.rb +72 -0
  268. data/lib/arel/update_manager.rb +34 -0
  269. data/lib/arel/visitors.rb +20 -0
  270. data/lib/arel/visitors/depth_first.rb +203 -0
  271. data/lib/arel/visitors/dot.rb +296 -0
  272. data/lib/arel/visitors/ibm_db.rb +34 -0
  273. data/lib/arel/visitors/informix.rb +62 -0
  274. data/lib/arel/visitors/mssql.rb +156 -0
  275. data/lib/arel/visitors/mysql.rb +83 -0
  276. data/lib/arel/visitors/oracle.rb +158 -0
  277. data/lib/arel/visitors/oracle12.rb +65 -0
  278. data/lib/arel/visitors/postgresql.rb +109 -0
  279. data/lib/arel/visitors/sqlite.rb +38 -0
  280. data/lib/arel/visitors/to_sql.rb +888 -0
  281. data/lib/arel/visitors/visitor.rb +45 -0
  282. data/lib/arel/visitors/where_sql.rb +22 -0
  283. data/lib/arel/window_predications.rb +9 -0
  284. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  285. data/lib/rails/generators/active_record/migration.rb +14 -2
  286. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  287. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  288. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  289. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  290. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  291. metadata +115 -29
  292. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -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
 
@@ -1005,7 +1002,7 @@ module ActiveRecord
1005
1002
  end
1006
1003
 
1007
1004
  # 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
1005
+ # to the association's primary key. Since <tt><<</tt> flattens its argument list and
1009
1006
  # inserts each record, +push+ and +concat+ behave identically. Returns +self+
1010
1007
  # so several appends may be chained together.
1011
1008
  #
@@ -1032,7 +1029,7 @@ module ActiveRecord
1032
1029
  alias_method :append, :<<
1033
1030
  alias_method :concat, :<<
1034
1031
 
1035
- def prepend(*args)
1032
+ def prepend(*args) # :nodoc:
1036
1033
  raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
1037
1034
  end
1038
1035
 
@@ -1062,7 +1059,7 @@ module ActiveRecord
1062
1059
  # person.pets.reload # fetches pets from the database
1063
1060
  # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1064
1061
  def reload
1065
- proxy_association.reload
1062
+ proxy_association.reload(true)
1066
1063
  reset_scope
1067
1064
  end
1068
1065
 
@@ -1099,12 +1096,11 @@ module ActiveRecord
1099
1096
  SpawnMethods,
1100
1097
  ].flat_map { |klass|
1101
1098
  klass.public_instance_methods(false)
1102
- } - self.public_instance_methods(false) - [:select] + [:scoping]
1099
+ } - self.public_instance_methods(false) - [:select] + [:scoping, :values]
1103
1100
 
1104
1101
  delegate(*delegate_methods, to: :scope)
1105
1102
 
1106
1103
  private
1107
-
1108
1104
  def find_nth_with_limit(index, limit)
1109
1105
  load_target if find_from_target?
1110
1106
  super
@@ -9,5 +9,12 @@ 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
12
19
  end
13
20
  end
@@ -36,16 +36,7 @@ module ActiveRecord
36
36
  super
37
37
  end
38
38
 
39
- def empty?
40
- if reflection.has_cached_counter?
41
- size.zero?
42
- else
43
- super
44
- end
45
- end
46
-
47
39
  private
48
-
49
40
  # Returns the number of records in this collection.
50
41
  #
51
42
  # If the association has a counter cache it gets that value. Otherwise
@@ -69,7 +60,7 @@ module ActiveRecord
69
60
  # If there's nothing in the database and @target has no new records
70
61
  # we are certain the current target is an empty array. This is a
71
62
  # documented side-effect of the method that may avoid an extra SELECT.
72
- (@target ||= []) && loaded! if count == 0
63
+ loaded! if count == 0
73
64
 
74
65
  [association_scope.limit_value, count].compact.min
75
66
  end
@@ -92,7 +83,7 @@ module ActiveRecord
92
83
  if method == :delete_all
93
84
  scope.delete_all
94
85
  else
95
- scope.update_all(reflection.foreign_key => nil)
86
+ scope.update_all(nullified_owner_attributes)
96
87
  end
97
88
  end
98
89
 
@@ -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,6 +34,20 @@ 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
  #
@@ -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
@@ -62,12 +33,39 @@ module ActiveRecord
62
33
  target.destroy
63
34
  throw(:abort) unless target.destroyed?
64
35
  when :nullify
65
- target.update_columns(reflection.foreign_key => nil) if target.persisted?
36
+ target.update_columns(nullified_owner_attributes) if target.persisted?
66
37
  end
67
38
  end
68
39
  end
69
40
 
70
41
  private
42
+ def replace(record, save = true)
43
+ raise_on_type_mismatch!(record) if record
44
+
45
+ return target unless load_target || record
46
+
47
+ assigning_another_record = target != record
48
+ if assigning_another_record || record.has_changes_to_save?
49
+ save &&= owner.persisted?
50
+
51
+ transaction_if(save) do
52
+ remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
53
+
54
+ if record
55
+ set_owner_attributes(record)
56
+ set_inverse_instance(record)
57
+
58
+ if save && !record.save
59
+ nullify_owner_attributes(record)
60
+ set_owner_attributes(target) if target
61
+ raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ self.target = record
68
+ end
71
69
 
72
70
  # The reason that the save param for replace is false, if for create (not just build),
73
71
  # is because the setting of the foreign keys is actually handled by the scoping when
@@ -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
 
@@ -14,10 +14,8 @@ module ActiveRecord
14
14
  i[column.name] = column.alias
15
15
  }
16
16
  }
17
- @name_and_alias_cache = tables.each_with_object({}) { |table, h|
18
- h[table.node] = table.columns.map { |column|
19
- [column.name, column.alias]
20
- }
17
+ @columns_cache = tables.each_with_object({}) { |table, h|
18
+ h[table.node] = table.columns
21
19
  }
22
20
  end
23
21
 
@@ -25,9 +23,8 @@ module ActiveRecord
25
23
  @tables.flat_map(&:column_aliases)
26
24
  end
27
25
 
28
- # An array of [column_name, alias] pairs for the table
29
26
  def column_aliases(node)
30
- @name_and_alias_cache[node]
27
+ @columns_cache[node]
31
28
  end
32
29
 
33
30
  def column_alias(node, column)
@@ -67,16 +64,21 @@ module ActiveRecord
67
64
  end
68
65
  end
69
66
 
70
- def initialize(base, table, associations)
67
+ def initialize(base, table, associations, join_type)
71
68
  tree = self.class.make_tree associations
72
69
  @join_root = JoinBase.new(base, table, build(tree, base))
70
+ @join_type = join_type
71
+ end
72
+
73
+ def base_klass
74
+ join_root.base_klass
73
75
  end
74
76
 
75
77
  def reflections
76
78
  join_root.drop(1).map!(&:reflection)
77
79
  end
78
80
 
79
- def join_constraints(joins_to_add, join_type, alias_tracker)
81
+ def join_constraints(joins_to_add, alias_tracker)
80
82
  @alias_tracker = alias_tracker
81
83
 
82
84
  construct_tables!(join_root)
@@ -85,9 +87,9 @@ module ActiveRecord
85
87
  joins.concat joins_to_add.flat_map { |oj|
86
88
  construct_tables!(oj.join_root)
87
89
  if join_root.match? oj.join_root
88
- walk join_root, oj.join_root
90
+ walk(join_root, oj.join_root, oj.join_type)
89
91
  else
90
- make_join_constraints(oj.join_root, join_type)
92
+ make_join_constraints(oj.join_root, oj.join_type)
91
93
  end
92
94
  }
93
95
  end
@@ -103,7 +105,9 @@ module ActiveRecord
103
105
 
104
106
  model_cache = Hash.new { |h, klass| h[klass] = {} }
105
107
  parents = model_cache[join_root]
108
+
106
109
  column_aliases = aliases.column_aliases join_root
110
+ column_aliases += explicit_selections(column_aliases, result_set)
107
111
 
108
112
  message_bus = ActiveSupport::Notifications.instrumenter
109
113
 
@@ -116,7 +120,7 @@ module ActiveRecord
116
120
  result_set.each { |row_hash|
117
121
  parent_key = primary_key ? row_hash[primary_key] : row_hash
118
122
  parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, &block)
119
- construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
123
+ construct(parent, join_root, row_hash, seen, model_cache)
120
124
  }
121
125
  end
122
126
 
@@ -128,9 +132,18 @@ module ActiveRecord
128
132
  end
129
133
 
130
134
  protected
131
- attr_reader :alias_tracker, :join_root
135
+ attr_reader :join_root, :join_type
132
136
 
133
137
  private
138
+ attr_reader :alias_tracker
139
+
140
+ def explicit_selections(root_column_aliases, result_set)
141
+ root_names = root_column_aliases.map(&:name).to_set
142
+ result_set.columns
143
+ .reject { |n| root_names.include?(n) || n =~ /\At\d+_r\d+\z/ }
144
+ .map { |n| Aliases::Column.new(n, n) }
145
+ end
146
+
134
147
  def aliases
135
148
  @aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
136
149
  columns = join_part.column_names.each_with_index.map { |column_name, j|
@@ -152,7 +165,7 @@ module ActiveRecord
152
165
  end
153
166
  end
154
167
 
155
- def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin)
168
+ def make_constraints(parent, child, join_type)
156
169
  foreign_table = parent.table
157
170
  foreign_klass = parent.base_klass
158
171
  joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
@@ -170,17 +183,17 @@ module ActiveRecord
170
183
  end
171
184
 
172
185
  def table_alias_for(reflection, parent, join)
173
- name = "#{reflection.plural_name}_#{parent.table_name}"
186
+ name = reflection.alias_candidate(parent.table_name)
174
187
  join ? "#{name}_join" : name
175
188
  end
176
189
 
177
- def walk(left, right)
190
+ def walk(left, right, join_type)
178
191
  intersection, missing = right.children.map { |node1|
179
192
  [left.children.find { |node2| node1.match? node2 }, node1]
180
193
  }.partition(&:first)
181
194
 
182
- joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r) }
183
- joins.concat missing.flat_map { |_, n| make_constraints(left, n) }
195
+ joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r, join_type) }
196
+ joins.concat missing.flat_map { |_, n| make_constraints(left, n, join_type) }
184
197
  end
185
198
 
186
199
  def find_reflection(klass, name)
@@ -202,7 +215,7 @@ module ActiveRecord
202
215
  end
203
216
  end
204
217
 
205
- def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
218
+ def construct(ar_parent, parent, row, seen, model_cache)
206
219
  return if ar_parent.nil?
207
220
 
208
221
  parent.children.each do |node|
@@ -211,7 +224,7 @@ module ActiveRecord
211
224
  other.loaded!
212
225
  elsif ar_parent.association_cached?(node.reflection.name)
213
226
  model = ar_parent.association(node.reflection.name).target
214
- construct(model, node, row, rs, seen, model_cache, aliases)
227
+ construct(model, node, row, seen, model_cache)
215
228
  next
216
229
  end
217
230
 
@@ -226,22 +239,17 @@ module ActiveRecord
226
239
  model = seen[ar_parent.object_id][node][id]
227
240
 
228
241
  if model
229
- construct(model, node, row, rs, seen, model_cache, aliases)
242
+ construct(model, node, row, seen, model_cache)
230
243
  else
231
- model = construct_model(ar_parent, node, row, model_cache, id, aliases)
232
-
233
- if node.reflection.scope &&
234
- node.reflection.scope_for(node.base_klass.unscoped).readonly_value
235
- model.readonly!
236
- end
244
+ model = construct_model(ar_parent, node, row, model_cache, id)
237
245
 
238
246
  seen[ar_parent.object_id][node][id] = model
239
- construct(model, node, row, rs, seen, model_cache, aliases)
247
+ construct(model, node, row, seen, model_cache)
240
248
  end
241
249
  end
242
250
  end
243
251
 
244
- def construct_model(record, node, row, model_cache, id, aliases)
252
+ def construct_model(record, node, row, model_cache, id)
245
253
  other = record.association(node.reflection.name)
246
254
 
247
255
  model = model_cache[node][id] ||=
@@ -255,6 +263,7 @@ module ActiveRecord
255
263
  other.target = model
256
264
  end
257
265
 
266
+ model.readonly! if node.readonly?
258
267
  model
259
268
  end
260
269
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_record/associations/join_dependency/join_part"
4
+ require "active_support/core_ext/array/extract"
4
5
 
5
6
  module ActiveRecord
6
7
  module Associations
@@ -35,10 +36,9 @@ module ActiveRecord
35
36
  arel = join_scope.arel(alias_tracker.aliases)
36
37
  nodes = arel.constraints.first
37
38
 
38
- others, children = nodes.children.partition do |node|
39
- !fetch_arel_attribute(node) { |attr| attr.relation.name == table.name }
39
+ others = nodes.children.extract! do |node|
40
+ !Arel.fetch_attribute(node) { |attr| attr.relation.name == table.name }
40
41
  end
41
- nodes = table.create_and(children)
42
42
 
43
43
  joins << table.create_join(table, table.create_on(nodes), join_type)
44
44
 
@@ -59,14 +59,13 @@ module ActiveRecord
59
59
  @table = tables.first
60
60
  end
61
61
 
62
- private
63
- def fetch_arel_attribute(value)
64
- case value
65
- when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThan, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThan, Arel::Nodes::GreaterThanOrEqual
66
- yield value.left.is_a?(Arel::Attributes::Attribute) ? value.left : value.right
67
- end
68
- end
62
+ def readonly?
63
+ return @readonly if defined?(@readonly)
69
64
 
65
+ @readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
66
+ end
67
+
68
+ private
70
69
  def append_constraints(join, constraints)
71
70
  if join.is_a?(Arel::Nodes::StringJoin)
72
71
  join_string = table.create_and(constraints.unshift(join.left))