activerecord 5.1.7 → 5.2.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (261) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +556 -685
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -5
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record.rb +11 -4
  8. data/lib/active_record/aggregations.rb +6 -5
  9. data/lib/active_record/association_relation.rb +7 -5
  10. data/lib/active_record/associations.rb +40 -63
  11. data/lib/active_record/associations/alias_tracker.rb +19 -27
  12. data/lib/active_record/associations/association.rb +41 -37
  13. data/lib/active_record/associations/association_scope.rb +38 -50
  14. data/lib/active_record/associations/belongs_to_association.rb +27 -8
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  16. data/lib/active_record/associations/builder/association.rb +4 -7
  17. data/lib/active_record/associations/builder/belongs_to.rb +12 -4
  18. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +2 -0
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +59 -47
  24. data/lib/active_record/associations/collection_proxy.rb +20 -49
  25. data/lib/active_record/associations/foreign_association.rb +2 -0
  26. data/lib/active_record/associations/has_many_association.rb +12 -1
  27. data/lib/active_record/associations/has_many_through_association.rb +36 -30
  28. data/lib/active_record/associations/has_one_association.rb +12 -1
  29. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  30. data/lib/active_record/associations/join_dependency.rb +48 -93
  31. data/lib/active_record/associations/join_dependency/join_association.rb +39 -63
  32. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  33. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  34. data/lib/active_record/associations/preloader.rb +18 -38
  35. data/lib/active_record/associations/preloader/association.rb +45 -61
  36. data/lib/active_record/associations/preloader/through_association.rb +71 -79
  37. data/lib/active_record/associations/singular_association.rb +14 -16
  38. data/lib/active_record/associations/through_association.rb +26 -11
  39. data/lib/active_record/attribute_assignment.rb +2 -5
  40. data/lib/active_record/attribute_decorators.rb +3 -2
  41. data/lib/active_record/attribute_methods.rb +65 -24
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  43. data/lib/active_record/attribute_methods/dirty.rb +30 -214
  44. data/lib/active_record/attribute_methods/primary_key.rb +7 -6
  45. data/lib/active_record/attribute_methods/query.rb +2 -0
  46. data/lib/active_record/attribute_methods/read.rb +9 -3
  47. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  48. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  49. data/lib/active_record/attribute_methods/write.rb +21 -9
  50. data/lib/active_record/attributes.rb +6 -5
  51. data/lib/active_record/autosave_association.rb +35 -19
  52. data/lib/active_record/base.rb +2 -0
  53. data/lib/active_record/callbacks.rb +8 -6
  54. data/lib/active_record/coders/json.rb +2 -0
  55. data/lib/active_record/coders/yaml_column.rb +2 -0
  56. data/lib/active_record/collection_cache_key.rb +12 -8
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +139 -41
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +174 -33
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +15 -5
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -31
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +64 -6
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +152 -81
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +84 -97
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
  70. data/lib/active_record/connection_adapters/column.rb +3 -1
  71. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  72. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
  73. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  74. data/lib/active_record/connection_adapters/mysql/database_statements.rb +47 -2
  75. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  76. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  77. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  78. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  79. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
  80. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  81. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  82. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  83. data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
  85. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
  99. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  101. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  106. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  109. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
  110. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  111. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  113. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  114. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +233 -111
  115. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
  117. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -73
  118. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  119. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  124. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  125. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
  126. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +81 -94
  127. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  128. data/lib/active_record/connection_handling.rb +4 -2
  129. data/lib/active_record/core.rb +41 -61
  130. data/lib/active_record/counter_cache.rb +10 -3
  131. data/lib/active_record/define_callbacks.rb +5 -3
  132. data/lib/active_record/dynamic_matchers.rb +9 -9
  133. data/lib/active_record/enum.rb +18 -13
  134. data/lib/active_record/errors.rb +42 -3
  135. data/lib/active_record/explain.rb +3 -1
  136. data/lib/active_record/explain_registry.rb +2 -0
  137. data/lib/active_record/explain_subscriber.rb +2 -0
  138. data/lib/active_record/fixture_set/file.rb +2 -0
  139. data/lib/active_record/fixtures.rb +67 -60
  140. data/lib/active_record/gem_version.rb +5 -3
  141. data/lib/active_record/inheritance.rb +49 -19
  142. data/lib/active_record/integration.rb +58 -19
  143. data/lib/active_record/internal_metadata.rb +2 -0
  144. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  145. data/lib/active_record/locking/optimistic.rb +14 -17
  146. data/lib/active_record/locking/pessimistic.rb +9 -6
  147. data/lib/active_record/log_subscriber.rb +43 -0
  148. data/lib/active_record/migration.rb +189 -139
  149. data/lib/active_record/migration/command_recorder.rb +11 -9
  150. data/lib/active_record/migration/compatibility.rb +47 -9
  151. data/lib/active_record/migration/join_table.rb +2 -0
  152. data/lib/active_record/model_schema.rb +16 -21
  153. data/lib/active_record/nested_attributes.rb +18 -6
  154. data/lib/active_record/no_touching.rb +3 -1
  155. data/lib/active_record/null_relation.rb +2 -0
  156. data/lib/active_record/persistence.rb +167 -16
  157. data/lib/active_record/query_cache.rb +6 -8
  158. data/lib/active_record/querying.rb +4 -2
  159. data/lib/active_record/railtie.rb +62 -6
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +2 -0
  162. data/lib/active_record/railties/databases.rake +46 -36
  163. data/lib/active_record/readonly_attributes.rb +3 -2
  164. data/lib/active_record/reflection.rb +108 -194
  165. data/lib/active_record/relation.rb +120 -214
  166. data/lib/active_record/relation/batches.rb +20 -5
  167. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  168. data/lib/active_record/relation/calculations.rb +45 -19
  169. data/lib/active_record/relation/delegation.rb +45 -27
  170. data/lib/active_record/relation/finder_methods.rb +75 -76
  171. data/lib/active_record/relation/from_clause.rb +2 -8
  172. data/lib/active_record/relation/merger.rb +53 -23
  173. data/lib/active_record/relation/predicate_builder.rb +60 -79
  174. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  175. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  176. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  177. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  178. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  179. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  180. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  181. data/lib/active_record/relation/query_attribute.rb +28 -2
  182. data/lib/active_record/relation/query_methods.rb +128 -99
  183. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  184. data/lib/active_record/relation/spawn_methods.rb +4 -2
  185. data/lib/active_record/relation/where_clause.rb +65 -68
  186. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  187. data/lib/active_record/result.rb +2 -0
  188. data/lib/active_record/runtime_registry.rb +2 -0
  189. data/lib/active_record/sanitization.rb +129 -121
  190. data/lib/active_record/schema.rb +4 -2
  191. data/lib/active_record/schema_dumper.rb +36 -26
  192. data/lib/active_record/schema_migration.rb +2 -0
  193. data/lib/active_record/scoping.rb +9 -8
  194. data/lib/active_record/scoping/default.rb +8 -9
  195. data/lib/active_record/scoping/named.rb +23 -7
  196. data/lib/active_record/secure_token.rb +2 -0
  197. data/lib/active_record/serialization.rb +2 -0
  198. data/lib/active_record/statement_cache.rb +23 -13
  199. data/lib/active_record/store.rb +3 -1
  200. data/lib/active_record/suppressor.rb +2 -0
  201. data/lib/active_record/table_metadata.rb +12 -3
  202. data/lib/active_record/tasks/database_tasks.rb +25 -14
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  206. data/lib/active_record/timestamp.rb +6 -6
  207. data/lib/active_record/touch_later.rb +2 -0
  208. data/lib/active_record/transactions.rb +33 -28
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type.rb +4 -1
  211. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  212. data/lib/active_record/type/date.rb +2 -0
  213. data/lib/active_record/type/date_time.rb +2 -0
  214. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  215. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  216. data/lib/active_record/type/internal/timezone.rb +2 -0
  217. data/lib/active_record/type/json.rb +30 -0
  218. data/lib/active_record/type/serialized.rb +2 -0
  219. data/lib/active_record/type/text.rb +2 -0
  220. data/lib/active_record/type/time.rb +2 -0
  221. data/lib/active_record/type/type_map.rb +2 -0
  222. data/lib/active_record/type/unsigned_integer.rb +2 -0
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/type_caster/connection.rb +2 -0
  225. data/lib/active_record/type_caster/map.rb +3 -1
  226. data/lib/active_record/validations.rb +2 -0
  227. data/lib/active_record/validations/absence.rb +2 -0
  228. data/lib/active_record/validations/associated.rb +2 -0
  229. data/lib/active_record/validations/length.rb +2 -0
  230. data/lib/active_record/validations/presence.rb +2 -0
  231. data/lib/active_record/validations/uniqueness.rb +35 -5
  232. data/lib/active_record/version.rb +2 -0
  233. data/lib/rails/generators/active_record.rb +3 -1
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration.rb +2 -0
  237. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  238. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  239. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  243. metadata +23 -36
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -122
  254. data/lib/active_record/attribute_set.rb +0 -113
  255. data/lib/active_record/attribute_set/builder.rb +0 -126
  256. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  257. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  258. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  259. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  260. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  261. data/lib/active_record/type/internal/abstract_json.rb +0 -37
@@ -1,19 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_record/associations/join_dependency/join_part"
2
4
 
3
5
  module ActiveRecord
4
6
  module Associations
5
7
  class JoinDependency # :nodoc:
6
8
  class JoinAssociation < JoinPart # :nodoc:
7
- # The reflection of the association represented
8
- attr_reader :reflection
9
-
10
- attr_accessor :tables
9
+ attr_reader :reflection, :tables
10
+ attr_accessor :table
11
11
 
12
12
  def initialize(reflection, children)
13
13
  super(reflection.klass, children)
14
14
 
15
- @reflection = reflection
16
- @tables = nil
15
+ @reflection = reflection
16
+ @tables = nil
17
17
  end
18
18
 
19
19
  def match?(other)
@@ -21,84 +21,60 @@ module ActiveRecord
21
21
  super && reflection == other.reflection
22
22
  end
23
23
 
24
- JoinInformation = Struct.new :joins, :binds
25
-
26
- def join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
27
- joins = []
28
- binds = []
29
- tables = tables.reverse
24
+ def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
25
+ joins = []
30
26
 
31
27
  # The chain starts with the target table, but we want to end with it here (makes
32
28
  # more sense in this context), so we reverse
33
- chain.reverse_each do |reflection|
34
- table = tables.shift
29
+ reflection.chain.reverse_each.with_index(1) do |reflection, i|
30
+ table = tables[-i]
35
31
  klass = reflection.klass
36
32
 
37
- join_keys = reflection.join_keys
38
- key = join_keys.key
39
- foreign_key = join_keys.foreign_key
40
-
41
- constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
33
+ join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
42
34
 
43
- rel = reflection.join_scope(table)
35
+ arel = join_scope.arel(alias_tracker.aliases)
36
+ nodes = arel.constraints.first
44
37
 
45
- if rel && !rel.arel.constraints.empty?
46
- binds += rel.bound_attributes
47
- constraint = constraint.and rel.arel.constraints
38
+ others, children = nodes.children.partition do |node|
39
+ !fetch_arel_attribute(node) { |attr| attr.relation.name == table.name }
48
40
  end
41
+ nodes = table.create_and(children)
49
42
 
50
- if reflection.type
51
- value = foreign_klass.base_class.name
52
- column = klass.columns_hash[reflection.type.to_s]
43
+ joins << table.create_join(table, table.create_on(nodes), join_type)
53
44
 
54
- binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
55
- constraint = constraint.and klass.arel_attribute(reflection.type, table).eq(Arel::Nodes::BindParam.new)
45
+ unless others.empty?
46
+ joins.concat arel.join_sources
47
+ append_constraints(joins.last, others)
56
48
  end
57
49
 
58
- joins << table.create_join(table, table.create_on(constraint), join_type)
59
-
60
50
  # The current table in this iteration becomes the foreign table in the next
61
51
  foreign_table, foreign_klass = table, klass
62
52
  end
63
53
 
64
- JoinInformation.new joins, binds
54
+ joins
65
55
  end
66
56
 
67
- # Builds equality condition.
68
- #
69
- # Example:
70
- #
71
- # class Physician < ActiveRecord::Base
72
- # has_many :appointments
73
- # end
74
- #
75
- # If I execute `Physician.joins(:appointments).to_a` then
76
- # klass # => Physician
77
- # table # => #<Arel::Table @name="appointments" ...>
78
- # key # => physician_id
79
- # foreign_table # => #<Arel::Table @name="physicians" ...>
80
- # foreign_key # => id
81
- #
82
- def build_constraint(klass, table, key, foreign_table, foreign_key)
83
- constraint = table[key].eq(foreign_table[foreign_key])
84
-
85
- if klass.finder_needs_type_condition?
86
- constraint = table.create_and([
87
- constraint,
88
- klass.send(:type_condition, table)
89
- ])
90
- end
91
-
92
- constraint
57
+ def tables=(tables)
58
+ @tables = tables
59
+ @table = tables.first
93
60
  end
94
61
 
95
- def table
96
- tables.first
97
- end
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
98
69
 
99
- def aliased_table_name
100
- table.table_alias || table.name
101
- end
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
102
78
  end
103
79
  end
104
80
  end
@@ -1,20 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_record/associations/join_dependency/join_part"
2
4
 
3
5
  module ActiveRecord
4
6
  module Associations
5
7
  class JoinDependency # :nodoc:
6
8
  class JoinBase < JoinPart # :nodoc:
7
- def match?(other)
8
- return true if self == other
9
- super && base_klass == other.base_klass
10
- end
9
+ attr_reader :table
11
10
 
12
- def table
13
- base_klass.arel_table
11
+ def initialize(base_klass, table, children)
12
+ super(base_klass, children)
13
+ @table = table
14
14
  end
15
15
 
16
- def aliased_table_name
17
- base_klass.table_name
16
+ def match?(other)
17
+ return true if self == other
18
+ super && base_klass == other.base_klass
18
19
  end
19
20
  end
20
21
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  class JoinDependency # :nodoc:
@@ -22,10 +24,6 @@ module ActiveRecord
22
24
  @children = children
23
25
  end
24
26
 
25
- def name
26
- reflection.name
27
- end
28
-
29
27
  def match?(other)
30
28
  self.class == other.class
31
29
  end
@@ -35,13 +33,15 @@ module ActiveRecord
35
33
  children.each { |child| child.each(&block) }
36
34
  end
37
35
 
38
- # An Arel::Table for the active_record
39
- def table
40
- raise NotImplementedError
36
+ def each_children(&block)
37
+ children.each do |child|
38
+ yield self, child
39
+ child.each_children(&block)
40
+ end
41
41
  end
42
42
 
43
- # The alias for the active_record's table
44
- def aliased_table_name
43
+ # An Arel::Table for the active_record
44
+ def table
45
45
  raise NotImplementedError
46
46
  end
47
47
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  # Implements the details of eager loading of Active Record associations.
@@ -42,20 +44,10 @@ module ActiveRecord
42
44
  extend ActiveSupport::Autoload
43
45
 
44
46
  eager_autoload do
45
- autoload :Association, "active_record/associations/preloader/association"
46
- autoload :SingularAssociation, "active_record/associations/preloader/singular_association"
47
- autoload :CollectionAssociation, "active_record/associations/preloader/collection_association"
48
- autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
49
-
50
- autoload :HasMany, "active_record/associations/preloader/has_many"
51
- autoload :HasManyThrough, "active_record/associations/preloader/has_many_through"
52
- autoload :HasOne, "active_record/associations/preloader/has_one"
53
- autoload :HasOneThrough, "active_record/associations/preloader/has_one_through"
54
- autoload :BelongsTo, "active_record/associations/preloader/belongs_to"
47
+ autoload :Association, "active_record/associations/preloader/association"
48
+ autoload :ThroughAssociation, "active_record/associations/preloader/through_association"
55
49
  end
56
50
 
57
- NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
58
-
59
51
  # Eager loads the named associations for the given Active Record record(s).
60
52
  #
61
53
  # In this description, 'association name' shall refer to the name passed
@@ -91,14 +83,13 @@ module ActiveRecord
91
83
  # { author: :avatar }
92
84
  # [ :books, { author: :avatar } ]
93
85
  def preload(records, associations, preload_scope = nil)
94
- records = Array.wrap(records).compact.uniq
95
- associations = Array.wrap(associations)
96
- preload_scope = preload_scope || NULL_RELATION
86
+ records = Array.wrap(records).compact
97
87
 
98
88
  if records.empty?
99
89
  []
100
90
  else
101
- associations.flat_map { |association|
91
+ records.uniq!
92
+ Array.wrap(associations).flat_map { |association|
102
93
  preloaders_on association, records, preload_scope
103
94
  }
104
95
  end
@@ -147,7 +138,7 @@ module ActiveRecord
147
138
  def preloaders_for_one(association, records, scope)
148
139
  grouped_records(association, records).flat_map do |reflection, klasses|
149
140
  klasses.map do |rhs_klass, rs|
150
- loader = preloader_for(reflection, rs, rhs_klass).new(rhs_klass, rs, reflection, scope)
141
+ loader = preloader_for(reflection, rs).new(rhs_klass, rs, reflection, scope)
151
142
  loader.run self
152
143
  loader
153
144
  end
@@ -159,6 +150,7 @@ module ActiveRecord
159
150
  records.each do |record|
160
151
  next unless record
161
152
  assoc = record.association(association)
153
+ next unless assoc.klass
162
154
  klasses = h[assoc.reflection] ||= {}
163
155
  (klasses[assoc.klass] ||= []) << record
164
156
  end
@@ -166,8 +158,6 @@ module ActiveRecord
166
158
  end
167
159
 
168
160
  class AlreadyLoaded # :nodoc:
169
- attr_reader :owners, :reflection
170
-
171
161
  def initialize(klass, owners, reflection, preload_scope)
172
162
  @owners = owners
173
163
  @reflection = reflection
@@ -178,34 +168,24 @@ module ActiveRecord
178
168
  def preloaded_records
179
169
  owners.flat_map { |owner| owner.association(reflection.name).target }
180
170
  end
181
- end
182
171
 
183
- class NullPreloader # :nodoc:
184
- def self.new(klass, owners, reflection, preload_scope); self; end
185
- def self.run(preloader); end
186
- def self.preloaded_records; []; end
187
- def self.owners; []; end
172
+ protected
173
+ attr_reader :owners, :reflection
188
174
  end
189
175
 
190
176
  # Returns a class containing the logic needed to load preload the data
191
- # and attach it to a relation. For example +Preloader::Association+ or
192
- # +Preloader::HasManyThrough+. The class returned implements a `run` method
177
+ # and attach it to a relation. The class returned implements a `run` method
193
178
  # that accepts a preloader.
194
- def preloader_for(reflection, owners, rhs_klass)
195
- return NullPreloader unless rhs_klass
196
-
197
- if owners.first.association(reflection.name).loaded?
179
+ def preloader_for(reflection, owners)
180
+ if owners.all? { |o| o.association(reflection.name).loaded? }
198
181
  return AlreadyLoaded
199
182
  end
200
183
  reflection.check_preloadable!
201
184
 
202
- case reflection.macro
203
- when :has_many
204
- reflection.options[:through] ? HasManyThrough : HasMany
205
- when :has_one
206
- reflection.options[:through] ? HasOneThrough : HasOne
207
- when :belongs_to
208
- BelongsTo
185
+ if reflection.options[:through]
186
+ ThroughAssociation
187
+ else
188
+ Association
209
189
  end
210
190
  end
211
191
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  class Preloader
4
6
  class Association #:nodoc:
5
- attr_reader :owners, :reflection, :preload_scope, :model, :klass
6
7
  attr_reader :preloaded_records
7
8
 
8
9
  def initialize(klass, owners, reflection, preload_scope)
@@ -11,79 +12,54 @@ module ActiveRecord
11
12
  @reflection = reflection
12
13
  @preload_scope = preload_scope
13
14
  @model = owners.first && owners.first.class
14
- @scope = nil
15
15
  @preloaded_records = []
16
16
  end
17
17
 
18
18
  def run(preloader)
19
- preload(preloader)
20
- end
21
-
22
- def preload(preloader)
23
- raise NotImplementedError
24
- end
25
-
26
- def scope
27
- @scope ||= build_scope
28
- end
29
-
30
- def records_for(ids)
31
- scope.where(association_key_name => ids)
32
- end
33
-
34
- def table
35
- klass.arel_table
36
- end
37
-
38
- # The name of the key on the associated records
39
- def association_key_name
40
- raise NotImplementedError
41
- end
42
-
43
- # This is overridden by HABTM as the condition should be on the foreign_key column in
44
- # the join table
45
- def association_key
46
- klass.arel_attribute(association_key_name, table)
47
- end
19
+ records = load_records do |record|
20
+ owner = owners_by_key[convert_key(record[association_key_name])]
21
+ association = owner.association(reflection.name)
22
+ association.set_inverse_instance(record)
23
+ end
48
24
 
49
- # The name of the key on the model which declares the association
50
- def owner_key_name
51
- raise NotImplementedError
25
+ owners.each do |owner|
26
+ associate_records_to_owner(owner, records[convert_key(owner[owner_key_name])] || [])
27
+ end
52
28
  end
53
29
 
54
- def options
55
- reflection.options
56
- end
30
+ protected
31
+ attr_reader :owners, :reflection, :preload_scope, :model, :klass
57
32
 
58
33
  private
34
+ # The name of the key on the associated records
35
+ def association_key_name
36
+ reflection.join_primary_key(klass)
37
+ end
59
38
 
60
- def associated_records_by_owner(preloader)
61
- records = load_records do |record|
62
- owner = owners_by_key[convert_key(record[association_key_name])]
63
- association = owner.association(reflection.name)
64
- association.set_inverse_instance(record)
65
- end
39
+ # The name of the key on the model which declares the association
40
+ def owner_key_name
41
+ reflection.join_foreign_key
42
+ end
66
43
 
67
- owners.each_with_object({}) do |owner, result|
68
- result[owner] = records[convert_key(owner[owner_key_name])] || []
44
+ def associate_records_to_owner(owner, records)
45
+ association = owner.association(reflection.name)
46
+ association.loaded!
47
+ if reflection.collection?
48
+ association.target.concat(records)
49
+ else
50
+ association.target = records.first unless records.empty?
69
51
  end
70
52
  end
71
53
 
72
54
  def owner_keys
73
- unless defined?(@owner_keys)
74
- @owner_keys = owners.map do |owner|
75
- owner[owner_key_name]
76
- end
77
- @owner_keys.uniq!
78
- @owner_keys.compact!
79
- end
80
- @owner_keys
55
+ @owner_keys ||= owners_by_key.keys
81
56
  end
82
57
 
83
58
  def owners_by_key
84
59
  unless defined?(@owners_by_key)
85
60
  @owners_by_key = owners.each_with_object({}) do |owner, h|
86
- h[convert_key(owner[owner_key_name])] = owner
61
+ key = convert_key(owner[owner_key_name])
62
+ h[key] = owner if key
87
63
  end
88
64
  end
89
65
  @owners_by_key
@@ -106,11 +82,11 @@ module ActiveRecord
106
82
  end
107
83
 
108
84
  def association_key_type
109
- @klass.type_for_attribute(association_key_name.to_s).type
85
+ @klass.type_for_attribute(association_key_name).type
110
86
  end
111
87
 
112
88
  def owner_key_type
113
- @model.type_for_attribute(owner_key_name.to_s).type
89
+ @model.type_for_attribute(owner_key_name).type
114
90
  end
115
91
 
116
92
  def load_records(&block)
@@ -119,26 +95,34 @@ module ActiveRecord
119
95
  # Make several smaller queries if necessary or make one query if the adapter supports it
120
96
  slices = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
121
97
  @preloaded_records = slices.flat_map do |slice|
122
- records_for(slice).load(&block)
98
+ records_for(slice, &block)
123
99
  end
124
100
  @preloaded_records.group_by do |record|
125
101
  convert_key(record[association_key_name])
126
102
  end
127
103
  end
128
104
 
105
+ def records_for(ids, &block)
106
+ scope.where(association_key_name => ids).load(&block)
107
+ end
108
+
109
+ def scope
110
+ @scope ||= build_scope
111
+ end
112
+
129
113
  def reflection_scope
130
- @reflection_scope ||= reflection.scope_for(klass)
114
+ @reflection_scope ||= reflection.scope ? reflection.scope_for(klass.unscoped) : klass.unscoped
131
115
  end
132
116
 
133
117
  def build_scope
134
118
  scope = klass.scope_for_association
135
119
 
136
120
  if reflection.type
137
- scope.where!(reflection.type => model.base_class.sti_name)
121
+ scope.where!(reflection.type => model.polymorphic_name)
138
122
  end
139
123
 
140
- scope.merge!(reflection_scope)
141
- scope.merge!(preload_scope) if preload_scope != NULL_RELATION
124
+ scope.merge!(reflection_scope) if reflection.scope
125
+ scope.merge!(preload_scope) if preload_scope
142
126
  scope
143
127
  end
144
128
  end