activerecord 5.2.8.1 → 6.0.0.beta1

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

Potentially problematic release.


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

Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -816
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +35 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  12. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  14. data/lib/active_record/associations/collection_association.rb +11 -25
  15. data/lib/active_record/associations/collection_proxy.rb +32 -6
  16. data/lib/active_record/associations/foreign_association.rb +7 -0
  17. data/lib/active_record/associations/has_many_association.rb +1 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  19. data/lib/active_record/associations/has_one_association.rb +28 -30
  20. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  21. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  22. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/preloader/association.rb +1 -2
  25. data/lib/active_record/associations/preloader.rb +32 -29
  26. data/lib/active_record/associations/singular_association.rb +2 -16
  27. data/lib/active_record/associations.rb +16 -12
  28. data/lib/active_record/attribute_assignment.rb +7 -10
  29. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  31. data/lib/active_record/attribute_methods/read.rb +16 -48
  32. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  34. data/lib/active_record/attribute_methods/write.rb +15 -16
  35. data/lib/active_record/attribute_methods.rb +34 -56
  36. data/lib/active_record/autosave_association.rb +7 -21
  37. data/lib/active_record/base.rb +2 -2
  38. data/lib/active_record/callbacks.rb +3 -17
  39. data/lib/active_record/coders/yaml_column.rb +1 -13
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +75 -52
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations/database_config.rb +37 -0
  83. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  84. data/lib/active_record/database_configurations/url_config.rb +74 -0
  85. data/lib/active_record/database_configurations.rb +184 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration/command_recorder.rb +35 -5
  102. data/lib/active_record/migration/compatibility.rb +34 -16
  103. data/lib/active_record/migration.rb +38 -37
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -60
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation/batches.rb +13 -10
  116. data/lib/active_record/relation/calculations.rb +38 -28
  117. data/lib/active_record/relation/delegation.rb +4 -13
  118. data/lib/active_record/relation/finder_methods.rb +12 -25
  119. data/lib/active_record/relation/merger.rb +2 -6
  120. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  121. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  122. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  123. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  124. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  125. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  126. data/lib/active_record/relation/predicate_builder.rb +4 -6
  127. data/lib/active_record/relation/query_attribute.rb +15 -12
  128. data/lib/active_record/relation/query_methods.rb +29 -52
  129. data/lib/active_record/relation/where_clause.rb +4 -0
  130. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  131. data/lib/active_record/relation.rb +150 -69
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping/default.rb +10 -3
  138. data/lib/active_record/scoping/named.rb +10 -14
  139. data/lib/active_record/scoping.rb +9 -8
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  153. data/lib/active_record/type.rb +3 -4
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/active_record.rb +2 -1
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes/attribute.rb +37 -0
  160. data/lib/arel/attributes.rb +22 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes/and.rb +32 -0
  174. data/lib/arel/nodes/ascending.rb +23 -0
  175. data/lib/arel/nodes/binary.rb +52 -0
  176. data/lib/arel/nodes/bind_param.rb +36 -0
  177. data/lib/arel/nodes/case.rb +55 -0
  178. data/lib/arel/nodes/casted.rb +50 -0
  179. data/lib/arel/nodes/count.rb +12 -0
  180. data/lib/arel/nodes/delete_statement.rb +45 -0
  181. data/lib/arel/nodes/descending.rb +23 -0
  182. data/lib/arel/nodes/equality.rb +18 -0
  183. data/lib/arel/nodes/extract.rb +24 -0
  184. data/lib/arel/nodes/false.rb +16 -0
  185. data/lib/arel/nodes/full_outer_join.rb +8 -0
  186. data/lib/arel/nodes/function.rb +44 -0
  187. data/lib/arel/nodes/grouping.rb +8 -0
  188. data/lib/arel/nodes/in.rb +8 -0
  189. data/lib/arel/nodes/infix_operation.rb +80 -0
  190. data/lib/arel/nodes/inner_join.rb +8 -0
  191. data/lib/arel/nodes/insert_statement.rb +37 -0
  192. data/lib/arel/nodes/join_source.rb +20 -0
  193. data/lib/arel/nodes/matches.rb +18 -0
  194. data/lib/arel/nodes/named_function.rb +23 -0
  195. data/lib/arel/nodes/node.rb +50 -0
  196. data/lib/arel/nodes/node_expression.rb +13 -0
  197. data/lib/arel/nodes/outer_join.rb +8 -0
  198. data/lib/arel/nodes/over.rb +15 -0
  199. data/lib/arel/nodes/regexp.rb +16 -0
  200. data/lib/arel/nodes/right_outer_join.rb +8 -0
  201. data/lib/arel/nodes/select_core.rb +63 -0
  202. data/lib/arel/nodes/select_statement.rb +41 -0
  203. data/lib/arel/nodes/sql_literal.rb +16 -0
  204. data/lib/arel/nodes/string_join.rb +11 -0
  205. data/lib/arel/nodes/table_alias.rb +27 -0
  206. data/lib/arel/nodes/terminal.rb +16 -0
  207. data/lib/arel/nodes/true.rb +16 -0
  208. data/lib/arel/nodes/unary.rb +44 -0
  209. data/lib/arel/nodes/unary_operation.rb +20 -0
  210. data/lib/arel/nodes/unqualified_column.rb +22 -0
  211. data/lib/arel/nodes/update_statement.rb +41 -0
  212. data/lib/arel/nodes/values.rb +16 -0
  213. data/lib/arel/nodes/values_list.rb +24 -0
  214. data/lib/arel/nodes/window.rb +126 -0
  215. data/lib/arel/nodes/with.rb +11 -0
  216. data/lib/arel/nodes.rb +67 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors/depth_first.rb +199 -0
  224. data/lib/arel/visitors/dot.rb +292 -0
  225. data/lib/arel/visitors/ibm_db.rb +21 -0
  226. data/lib/arel/visitors/informix.rb +56 -0
  227. data/lib/arel/visitors/mssql.rb +143 -0
  228. data/lib/arel/visitors/mysql.rb +83 -0
  229. data/lib/arel/visitors/oracle.rb +159 -0
  230. data/lib/arel/visitors/oracle12.rb +67 -0
  231. data/lib/arel/visitors/postgresql.rb +116 -0
  232. data/lib/arel/visitors/sqlite.rb +39 -0
  233. data/lib/arel/visitors/to_sql.rb +913 -0
  234. data/lib/arel/visitors/visitor.rb +42 -0
  235. data/lib/arel/visitors/where_sql.rb +23 -0
  236. data/lib/arel/visitors.rb +20 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/arel.rb +44 -0
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/migration.rb +14 -1
  241. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  242. metadata +107 -29
@@ -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
  #
@@ -57,14 +57,21 @@ module ActiveRecord
57
57
  @through_records[record.object_id] ||= begin
58
58
  ensure_mutable
59
59
 
60
- attributes = through_scope_attributes
61
- attributes[source_reflection.name] = record
62
- attributes[source_reflection.foreign_type] = options[:source_type] if options[:source_type]
60
+ through_record = through_association.build(*options_for_through_record)
61
+ through_record.send("#{source_reflection.name}=", record)
62
+
63
+ if options[:source_type]
64
+ through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
65
+ end
63
66
 
64
- through_association.build(attributes)
67
+ through_record
65
68
  end
66
69
  end
67
70
 
71
+ def options_for_through_record
72
+ [through_scope_attributes]
73
+ end
74
+
68
75
  def through_scope_attributes
69
76
  scope.where_values_hash(through_association.reflection.name.to_s).
70
77
  except!(through_association.reflection.foreign_key,
@@ -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
 
@@ -30,21 +30,17 @@ module ActiveRecord
30
30
  table = tables[-i]
31
31
  klass = reflection.klass
32
32
 
33
- join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
33
+ constraint = reflection.build_join_constraint(table, foreign_table)
34
34
 
35
- arel = join_scope.arel(alias_tracker.aliases)
36
- nodes = arel.constraints.first
37
-
38
- others, children = nodes.children.partition do |node|
39
- !fetch_arel_attribute(node) { |attr| attr.relation.name == table.name }
40
- end
41
- nodes = table.create_and(children)
35
+ joins << table.create_join(table, table.create_on(constraint), join_type)
42
36
 
43
- joins << table.create_join(table, table.create_on(nodes), join_type)
37
+ join_scope = reflection.join_scope(table, foreign_klass)
38
+ arel = join_scope.arel(alias_tracker.aliases)
44
39
 
45
- unless others.empty?
40
+ if arel.constraints.any?
46
41
  joins.concat arel.join_sources
47
- append_constraints(joins.last, others)
42
+ right = joins.last.right
43
+ right.expr = right.expr.and(arel.constraints)
48
44
  end
49
45
 
50
46
  # The current table in this iteration becomes the foreign table in the next
@@ -59,22 +55,11 @@ module ActiveRecord
59
55
  @table = tables.first
60
56
  end
61
57
 
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
58
+ def readonly?
59
+ return @readonly if defined?(@readonly)
69
60
 
70
- def append_constraints(join, constraints)
71
- if join.is_a?(Arel::Nodes::StringJoin)
72
- join_string = table.create_and(constraints.unshift(join.left))
73
- join.left = Arel.sql(base_klass.connection.visitor.compile(join_string))
74
- else
75
- join.right.expr.children.concat(constraints)
76
- end
77
- end
61
+ @readonly = reflection.scope && reflection.scope_for(base_klass.unscoped).readonly_value
62
+ end
78
63
  end
79
64
  end
80
65
  end
@@ -54,8 +54,8 @@ module ActiveRecord
54
54
  length = column_names_with_alias.length
55
55
 
56
56
  while index < length
57
- column_name, alias_name = column_names_with_alias[index]
58
- hash[column_name] = row[alias_name]
57
+ column = column_names_with_alias[index]
58
+ hash[column.name] = row[column.alias]
59
59
  index += 1
60
60
  end
61
61
 
@@ -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)
@@ -116,7 +113,7 @@ module ActiveRecord
116
113
  result_set.each { |row_hash|
117
114
  parent_key = primary_key ? row_hash[primary_key] : row_hash
118
115
  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)
116
+ construct(parent, join_root, row_hash, seen, model_cache)
120
117
  }
121
118
  end
122
119
 
@@ -128,9 +125,11 @@ module ActiveRecord
128
125
  end
129
126
 
130
127
  protected
131
- attr_reader :alias_tracker, :join_root
128
+ attr_reader :join_root
132
129
 
133
130
  private
131
+ attr_reader :alias_tracker
132
+
134
133
  def aliases
135
134
  @aliases ||= Aliases.new join_root.each_with_index.map { |join_part, i|
136
135
  columns = join_part.column_names.each_with_index.map { |column_name, j|
@@ -170,7 +169,7 @@ module ActiveRecord
170
169
  end
171
170
 
172
171
  def table_alias_for(reflection, parent, join)
173
- name = "#{reflection.plural_name}_#{parent.table_name}"
172
+ name = reflection.alias_candidate(parent.table_name)
174
173
  join ? "#{name}_join" : name
175
174
  end
176
175
 
@@ -202,7 +201,7 @@ module ActiveRecord
202
201
  end
203
202
  end
204
203
 
205
- def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
204
+ def construct(ar_parent, parent, row, seen, model_cache)
206
205
  return if ar_parent.nil?
207
206
 
208
207
  parent.children.each do |node|
@@ -211,7 +210,7 @@ module ActiveRecord
211
210
  other.loaded!
212
211
  elsif ar_parent.association_cached?(node.reflection.name)
213
212
  model = ar_parent.association(node.reflection.name).target
214
- construct(model, node, row, rs, seen, model_cache, aliases)
213
+ construct(model, node, row, seen, model_cache)
215
214
  next
216
215
  end
217
216
 
@@ -226,22 +225,17 @@ module ActiveRecord
226
225
  model = seen[ar_parent.object_id][node][id]
227
226
 
228
227
  if model
229
- construct(model, node, row, rs, seen, model_cache, aliases)
228
+ construct(model, node, row, seen, model_cache)
230
229
  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
230
+ model = construct_model(ar_parent, node, row, model_cache, id)
237
231
 
238
232
  seen[ar_parent.object_id][node][id] = model
239
- construct(model, node, row, rs, seen, model_cache, aliases)
233
+ construct(model, node, row, seen, model_cache)
240
234
  end
241
235
  end
242
236
  end
243
237
 
244
- def construct_model(record, node, row, model_cache, id, aliases)
238
+ def construct_model(record, node, row, model_cache, id)
245
239
  other = record.association(node.reflection.name)
246
240
 
247
241
  model = model_cache[node][id] ||=
@@ -255,6 +249,7 @@ module ActiveRecord
255
249
  other.target = model
256
250
  end
257
251
 
252
+ model.readonly! if node.readonly?
258
253
  model
259
254
  end
260
255
  end
@@ -27,10 +27,9 @@ module ActiveRecord
27
27
  end
28
28
  end
29
29
 
30
- protected
30
+ private
31
31
  attr_reader :owners, :reflection, :preload_scope, :model, :klass
32
32
 
33
- private
34
33
  # The name of the key on the associated records
35
34
  def association_key_name
36
35
  reflection.join_primary_key(klass)
@@ -88,7 +88,6 @@ module ActiveRecord
88
88
  if records.empty?
89
89
  []
90
90
  else
91
- records.uniq!
92
91
  Array.wrap(associations).flat_map { |association|
93
92
  preloaders_on association, records, preload_scope
94
93
  }
@@ -98,34 +97,34 @@ module ActiveRecord
98
97
  private
99
98
 
100
99
  # Loads all the given data into +records+ for the +association+.
101
- def preloaders_on(association, records, scope)
100
+ def preloaders_on(association, records, scope, polymorphic_parent = false)
102
101
  case association
103
102
  when Hash
104
- preloaders_for_hash(association, records, scope)
105
- when Symbol
106
- preloaders_for_one(association, records, scope)
107
- when String
108
- preloaders_for_one(association.to_sym, records, scope)
103
+ preloaders_for_hash(association, records, scope, polymorphic_parent)
104
+ when Symbol, String
105
+ preloaders_for_one(association, records, scope, polymorphic_parent)
109
106
  else
110
107
  raise ArgumentError, "#{association.inspect} was not recognized for preload"
111
108
  end
112
109
  end
113
110
 
114
- def preloaders_for_hash(association, records, scope)
111
+ def preloaders_for_hash(association, records, scope, polymorphic_parent)
115
112
  association.flat_map { |parent, child|
116
- loaders = preloaders_for_one parent, records, scope
117
-
118
- recs = loaders.flat_map(&:preloaded_records).uniq
119
- loaders.concat Array.wrap(child).flat_map { |assoc|
120
- preloaders_on assoc, recs, scope
121
- }
122
- loaders
113
+ grouped_records(parent, records, polymorphic_parent).flat_map do |reflection, reflection_records|
114
+ loaders = preloaders_for_reflection(reflection, reflection_records, scope)
115
+ recs = loaders.flat_map(&:preloaded_records)
116
+ child_polymorphic_parent = reflection && reflection.options[:polymorphic]
117
+ loaders.concat Array.wrap(child).flat_map { |assoc|
118
+ preloaders_on assoc, recs, scope, child_polymorphic_parent
119
+ }
120
+ loaders
121
+ end
123
122
  }
124
123
  end
125
124
 
126
125
  # Loads all the given data into +records+ for a singular +association+.
127
126
  #
128
- # Functions by instantiating a preloader class such as Preloader::HasManyThrough and
127
+ # Functions by instantiating a preloader class such as Preloader::Association and
129
128
  # call the +run+ method for each passed in class in the +records+ argument.
130
129
  #
131
130
  # Not all records have the same class, so group then preload group on the reflection
@@ -135,24 +134,28 @@ module ActiveRecord
135
134
  # Additionally, polymorphic belongs_to associations can have multiple associated
136
135
  # classes, depending on the polymorphic_type field. So we group by the classes as
137
136
  # well.
138
- def preloaders_for_one(association, records, scope)
139
- grouped_records(association, records).flat_map do |reflection, klasses|
140
- klasses.map do |rhs_klass, rs|
141
- loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
142
- loader.run self
143
- loader
137
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
138
+ grouped_records(association, records, polymorphic_parent)
139
+ .flat_map do |reflection, reflection_records|
140
+ preloaders_for_reflection reflection, reflection_records, scope
144
141
  end
142
+ end
143
+
144
+ def preloaders_for_reflection(reflection, records, scope)
145
+ records.group_by { |record| record.association(reflection.name).klass }.map do |rhs_klass, rs|
146
+ loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
147
+ loader.run self
148
+ loader
145
149
  end
146
150
  end
147
151
 
148
- def grouped_records(association, records)
152
+ def grouped_records(association, records, polymorphic_parent)
149
153
  h = {}
150
154
  records.each do |record|
151
155
  next unless record
152
- assoc = record.association(association)
153
- next unless assoc.klass
154
- klasses = h[assoc.reflection] ||= {}
155
- (klasses[assoc.klass] ||= []) << record
156
+ reflection = record.class._reflect_on_association(association)
157
+ next if polymorphic_parent && !reflection || !record.association(association).klass
158
+ (h[reflection] ||= []) << record
156
159
  end
157
160
  h
158
161
  end
@@ -169,7 +172,7 @@ module ActiveRecord
169
172
  owners.flat_map { |owner| owner.association(reflection.name).target }
170
173
  end
171
174
 
172
- protected
175
+ private
173
176
  attr_reader :owners, :reflection
174
177
  end
175
178
 
@@ -177,7 +180,7 @@ module ActiveRecord
177
180
  # and attach it to a relation. The class returned implements a `run` method
178
181
  # that accepts a preloader.
179
182
  def preloader_for(reflection, owners)
180
- if owners.all? { |o| o.association(reflection.name).loaded? }
183
+ if owners.first.association(reflection.name).loaded?
181
184
  return AlreadyLoaded
182
185
  end
183
186
  reflection.check_preloadable!
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  # Implements the reload reader method, e.g. foo.reload_bar for
27
27
  # Foo.has_one :bar
28
28
  def force_reload_reader
29
- klass.uncached { reload }
29
+ reload(true)
30
30
  target
31
31
  end
32
32
 
@@ -36,21 +36,7 @@ module ActiveRecord
36
36
  end
37
37
 
38
38
  def find_target
39
- scope = self.scope
40
- return scope.take if skip_statement_cache?(scope)
41
-
42
- conn = klass.connection
43
- sc = reflection.association_scope_cache(conn, owner) do |params|
44
- as = AssociationScope.create { params.bind }
45
- target_scope.merge!(as.scope(self)).limit(1)
46
- end
47
-
48
- binds = AssociationScope.get_bind_values(owner, reflection.chain)
49
- sc.execute(binds, conn) do |record|
50
- set_inverse_instance record
51
- end.first
52
- rescue ::RangeError
53
- nil
39
+ super.first
54
40
  end
55
41
 
56
42
  def replace(record)
@@ -292,13 +292,13 @@ module ActiveRecord
292
292
  #
293
293
  # The project class now has the following methods (and more) to ease the traversal and
294
294
  # manipulation of its relationships:
295
- # * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
296
- # * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
297
- # * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
298
- # <tt>Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id),</tt>
299
- # <tt>Project#milestones.build, Project#milestones.create</tt>
300
- # * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
301
- # <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt>
295
+ # * <tt>Project#portfolio</tt>, <tt>Project#portfolio=(portfolio)</tt>, <tt>Project#reload_portfolio</tt>
296
+ # * <tt>Project#project_manager</tt>, <tt>Project#project_manager=(project_manager)</tt>, <tt>Project#reload_project_manager</tt>
297
+ # * <tt>Project#milestones.empty?</tt>, <tt>Project#milestones.size</tt>, <tt>Project#milestones</tt>, <tt>Project#milestones<<(milestone)</tt>,
298
+ # <tt>Project#milestones.delete(milestone)</tt>, <tt>Project#milestones.destroy(milestone)</tt>, <tt>Project#milestones.find(milestone_id)</tt>,
299
+ # <tt>Project#milestones.build</tt>, <tt>Project#milestones.create</tt>
300
+ # * <tt>Project#categories.empty?</tt>, <tt>Project#categories.size</tt>, <tt>Project#categories</tt>, <tt>Project#categories<<(category1)</tt>,
301
+ # <tt>Project#categories.delete(category1)</tt>, <tt>Project#categories.destroy(category1)</tt>
302
302
  #
303
303
  # === A word of warning
304
304
  #
@@ -1293,8 +1293,9 @@ module ActiveRecord
1293
1293
  #
1294
1294
  # * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
1295
1295
  # * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
1296
- # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed.
1297
- # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records.
1296
+ # * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Polymorphic type will also be nullified
1297
+ # on polymorphic associations. Callbacks are not executed.
1298
+ # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there are any associated records.
1298
1299
  # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects.
1299
1300
  #
1300
1301
  # If using with the <tt>:through</tt> option, the association on the join model must be
@@ -1436,8 +1437,9 @@ module ActiveRecord
1436
1437
  #
1437
1438
  # * <tt>:destroy</tt> causes the associated object to also be destroyed
1438
1439
  # * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
1439
- # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed.
1440
- # * <tt>:restrict_with_exception</tt> causes an exception to be raised if there is an associated record
1440
+ # * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Polymorphic type column is also nullified
1441
+ # on polymorphic associations. Callbacks are not executed.
1442
+ # * <tt>:restrict_with_exception</tt> causes an <tt>ActiveRecord::DeleteRestrictionError</tt> exception to be raised if there is an associated record
1441
1443
  # * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
1442
1444
  #
1443
1445
  # Note that <tt>:dependent</tt> option is ignored when using <tt>:through</tt> option.
@@ -1524,6 +1526,7 @@ module ActiveRecord
1524
1526
  # Returns the associated object. +nil+ is returned if none is found.
1525
1527
  # [association=(associate)]
1526
1528
  # Assigns the associate object, extracts the primary key, and sets it as the foreign key.
1529
+ # No modification or deletion of existing records takes place.
1527
1530
  # [build_association(attributes = {})]
1528
1531
  # Returns a new object of the associated type that has been instantiated
1529
1532
  # with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
@@ -1581,7 +1584,7 @@ module ActiveRecord
1581
1584
  # association will use "taggable_type" as the default <tt>:foreign_type</tt>.
1582
1585
  # [:primary_key]
1583
1586
  # Specify the method that returns the primary key of associated object used for the association.
1584
- # By default this is id.
1587
+ # By default this is +id+.
1585
1588
  # [:dependent]
1586
1589
  # If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
1587
1590
  # <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
@@ -1761,6 +1764,7 @@ module ActiveRecord
1761
1764
  # has_and_belongs_to_many :projects, -> { includes(:milestones, :manager) }
1762
1765
  # has_and_belongs_to_many :categories, ->(post) {
1763
1766
  # where("default_category = ?", post.default_category)
1767
+ # }
1764
1768
  #
1765
1769
  # === Extensions
1766
1770
  #
@@ -4,7 +4,6 @@ require "active_model/forbidden_attributes_protection"
4
4
 
5
5
  module ActiveRecord
6
6
  module AttributeAssignment
7
- extend ActiveSupport::Concern
8
7
  include ActiveModel::AttributeAssignment
9
8
 
10
9
  private
@@ -46,16 +45,14 @@ module ActiveRecord
46
45
  def execute_callstack_for_multiparameter_attributes(callstack)
47
46
  errors = []
48
47
  callstack.each do |name, values_with_empty_parameters|
49
- begin
50
- if values_with_empty_parameters.each_value.all?(&:nil?)
51
- values = nil
52
- else
53
- values = values_with_empty_parameters
54
- end
55
- send("#{name}=", values)
56
- rescue => ex
57
- errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
48
+ if values_with_empty_parameters.each_value.all?(&:nil?)
49
+ values = nil
50
+ else
51
+ values = values_with_empty_parameters
58
52
  end
53
+ send("#{name}=", values)
54
+ rescue => ex
55
+ errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
59
56
  end
60
57
  unless errors.empty?
61
58
  error_descriptions = errors.map(&:message).join(",")