activerecord 5.1.0 → 5.2.0.rc1

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 (260) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +410 -530
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/examples/performance.rb +2 -0
  6. data/examples/simple.rb +2 -0
  7. data/lib/active_record/aggregations.rb +6 -5
  8. data/lib/active_record/association_relation.rb +4 -2
  9. data/lib/active_record/associations/alias_tracker.rb +23 -32
  10. data/lib/active_record/associations/association.rb +20 -21
  11. data/lib/active_record/associations/association_scope.rb +49 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +12 -10
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -6
  16. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +2 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +50 -41
  22. data/lib/active_record/associations/collection_proxy.rb +22 -39
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +4 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +12 -18
  26. data/lib/active_record/associations/has_one_association.rb +5 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +8 -7
  28. data/lib/active_record/associations/join_dependency/join_association.rb +17 -64
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
  31. data/lib/active_record/associations/join_dependency.rb +27 -44
  32. data/lib/active_record/associations/preloader/association.rb +53 -92
  33. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  34. data/lib/active_record/associations/preloader.rb +17 -37
  35. data/lib/active_record/associations/singular_association.rb +14 -10
  36. data/lib/active_record/associations/through_association.rb +26 -11
  37. data/lib/active_record/associations.rb +68 -76
  38. data/lib/active_record/attribute_assignment.rb +2 -0
  39. data/lib/active_record/attribute_decorators.rb +3 -2
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
  41. data/lib/active_record/attribute_methods/dirty.rb +24 -214
  42. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  43. data/lib/active_record/attribute_methods/query.rb +2 -0
  44. data/lib/active_record/attribute_methods/read.rb +8 -2
  45. data/lib/active_record/attribute_methods/serialization.rb +23 -0
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
  47. data/lib/active_record/attribute_methods/write.rb +22 -19
  48. data/lib/active_record/attribute_methods.rb +48 -12
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +8 -11
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +8 -6
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +2 -0
  55. data/lib/active_record/collection_cache_key.rb +14 -10
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +175 -33
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -24
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +58 -3
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +165 -85
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -97
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +118 -180
  69. data/lib/active_record/connection_adapters/column.rb +4 -2
  70. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -17
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -23
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  96. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -2
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  103. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -1
  107. data/lib/active_record/connection_adapters/postgresql/quoting.rb +22 -1
  108. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  109. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  111. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  112. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +269 -126
  113. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  114. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql_adapter.rb +64 -85
  116. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  117. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  118. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +18 -0
  120. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  122. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  123. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
  124. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +92 -95
  125. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  126. data/lib/active_record/connection_handling.rb +4 -2
  127. data/lib/active_record/core.rb +39 -60
  128. data/lib/active_record/counter_cache.rb +3 -2
  129. data/lib/active_record/define_callbacks.rb +5 -3
  130. data/lib/active_record/dynamic_matchers.rb +9 -9
  131. data/lib/active_record/enum.rb +17 -13
  132. data/lib/active_record/errors.rb +42 -3
  133. data/lib/active_record/explain.rb +3 -1
  134. data/lib/active_record/explain_registry.rb +2 -0
  135. data/lib/active_record/explain_subscriber.rb +2 -0
  136. data/lib/active_record/fixture_set/file.rb +2 -0
  137. data/lib/active_record/fixtures.rb +67 -60
  138. data/lib/active_record/gem_version.rb +4 -2
  139. data/lib/active_record/inheritance.rb +9 -9
  140. data/lib/active_record/integration.rb +58 -19
  141. data/lib/active_record/internal_metadata.rb +2 -0
  142. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  143. data/lib/active_record/locking/optimistic.rb +8 -6
  144. data/lib/active_record/locking/pessimistic.rb +9 -6
  145. data/lib/active_record/log_subscriber.rb +46 -4
  146. data/lib/active_record/migration/command_recorder.rb +11 -9
  147. data/lib/active_record/migration/compatibility.rb +74 -22
  148. data/lib/active_record/migration/join_table.rb +2 -0
  149. data/lib/active_record/migration.rb +181 -137
  150. data/lib/active_record/model_schema.rb +73 -58
  151. data/lib/active_record/nested_attributes.rb +18 -6
  152. data/lib/active_record/no_touching.rb +3 -1
  153. data/lib/active_record/null_relation.rb +2 -0
  154. data/lib/active_record/persistence.rb +153 -18
  155. data/lib/active_record/query_cache.rb +17 -12
  156. data/lib/active_record/querying.rb +4 -2
  157. data/lib/active_record/railtie.rb +61 -3
  158. data/lib/active_record/railties/console_sandbox.rb +2 -0
  159. data/lib/active_record/railties/controller_runtime.rb +2 -0
  160. data/lib/active_record/railties/databases.rake +47 -37
  161. data/lib/active_record/readonly_attributes.rb +3 -2
  162. data/lib/active_record/reflection.rb +131 -204
  163. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  164. data/lib/active_record/relation/batches.rb +32 -17
  165. data/lib/active_record/relation/calculations.rb +58 -20
  166. data/lib/active_record/relation/delegation.rb +10 -29
  167. data/lib/active_record/relation/finder_methods.rb +74 -85
  168. data/lib/active_record/relation/from_clause.rb +2 -8
  169. data/lib/active_record/relation/merger.rb +51 -20
  170. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  171. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  172. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  173. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +54 -0
  175. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -6
  176. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  177. data/lib/active_record/relation/predicate_builder.rb +53 -78
  178. data/lib/active_record/relation/query_attribute.rb +9 -2
  179. data/lib/active_record/relation/query_methods.rb +101 -95
  180. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  181. data/lib/active_record/relation/spawn_methods.rb +3 -1
  182. data/lib/active_record/relation/where_clause.rb +65 -67
  183. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  184. data/lib/active_record/relation.rb +99 -202
  185. data/lib/active_record/result.rb +2 -0
  186. data/lib/active_record/runtime_registry.rb +2 -0
  187. data/lib/active_record/sanitization.rb +129 -121
  188. data/lib/active_record/schema.rb +4 -2
  189. data/lib/active_record/schema_dumper.rb +36 -26
  190. data/lib/active_record/schema_migration.rb +2 -0
  191. data/lib/active_record/scoping/default.rb +10 -7
  192. data/lib/active_record/scoping/named.rb +38 -12
  193. data/lib/active_record/scoping.rb +12 -10
  194. data/lib/active_record/secure_token.rb +2 -0
  195. data/lib/active_record/serialization.rb +2 -0
  196. data/lib/active_record/statement_cache.rb +22 -12
  197. data/lib/active_record/store.rb +3 -1
  198. data/lib/active_record/suppressor.rb +2 -0
  199. data/lib/active_record/table_metadata.rb +12 -3
  200. data/lib/active_record/tasks/database_tasks.rb +37 -25
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  204. data/lib/active_record/timestamp.rb +5 -5
  205. data/lib/active_record/touch_later.rb +2 -0
  206. data/lib/active_record/transactions.rb +9 -7
  207. data/lib/active_record/translation.rb +2 -0
  208. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  209. data/lib/active_record/type/date.rb +2 -0
  210. data/lib/active_record/type/date_time.rb +2 -0
  211. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  212. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  213. data/lib/active_record/type/internal/timezone.rb +2 -0
  214. data/lib/active_record/type/json.rb +30 -0
  215. data/lib/active_record/type/serialized.rb +2 -0
  216. data/lib/active_record/type/text.rb +2 -0
  217. data/lib/active_record/type/time.rb +2 -0
  218. data/lib/active_record/type/type_map.rb +2 -0
  219. data/lib/active_record/type/unsigned_integer.rb +2 -0
  220. data/lib/active_record/type.rb +4 -1
  221. data/lib/active_record/type_caster/connection.rb +2 -0
  222. data/lib/active_record/type_caster/map.rb +3 -1
  223. data/lib/active_record/type_caster.rb +2 -0
  224. data/lib/active_record/validations/absence.rb +2 -0
  225. data/lib/active_record/validations/associated.rb +2 -0
  226. data/lib/active_record/validations/length.rb +2 -0
  227. data/lib/active_record/validations/presence.rb +2 -0
  228. data/lib/active_record/validations/uniqueness.rb +35 -5
  229. data/lib/active_record/validations.rb +2 -0
  230. data/lib/active_record/version.rb +2 -0
  231. data/lib/active_record.rb +11 -4
  232. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  233. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  235. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  236. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration.rb +2 -0
  238. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  239. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  240. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  241. data/lib/rails/generators/active_record.rb +3 -1
  242. metadata +25 -37
  243. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  244. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  245. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  246. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  247. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  248. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  249. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  250. data/lib/active_record/attribute/user_provided_default.rb +0 -30
  251. data/lib/active_record/attribute.rb +0 -240
  252. data/lib/active_record/attribute_mutation_tracker.rb +0 -113
  253. data/lib/active_record/attribute_set/builder.rb +0 -124
  254. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  255. data/lib/active_record/attribute_set.rb +0 -113
  256. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  257. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  258. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  259. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  260. data/lib/active_record/type/internal/abstract_json.rb +0 -33
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  # = Active Record Association Collection
@@ -30,7 +32,8 @@ module ActiveRecord
30
32
  reload
31
33
  end
32
34
 
33
- CollectionProxy.create(klass, self)
35
+ @proxy ||= CollectionProxy.create(klass, self)
36
+ @proxy.reset_scope
34
37
  end
35
38
 
36
39
  # Implements the writer method, e.g. foo.items= for Foo.has_many :items
@@ -43,23 +46,25 @@ module ActiveRecord
43
46
  if loaded?
44
47
  target.pluck(reflection.association_primary_key)
45
48
  else
46
- @association_ids ||= (
47
- column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
48
- scope.pluck(column)
49
- )
49
+ @association_ids ||= scope.pluck(reflection.association_primary_key)
50
50
  end
51
51
  end
52
52
 
53
53
  # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
54
54
  def ids_writer(ids)
55
- pk_type = reflection.association_primary_key_type
55
+ primary_key = reflection.association_primary_key
56
+ pk_type = klass.type_for_attribute(primary_key)
56
57
  ids = Array(ids).reject(&:blank?)
57
58
  ids.map! { |i| pk_type.cast(i) }
58
- records = klass.where(reflection.association_primary_key => ids).index_by do |r|
59
- r.send(reflection.association_primary_key)
59
+
60
+ records = klass.where(primary_key => ids).index_by do |r|
61
+ r.public_send(primary_key)
60
62
  end.values_at(*ids).compact
63
+
61
64
  if records.size != ids.size
62
- klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, reflection.association_primary_key)
65
+ found_ids = records.map { |record| record.public_send(primary_key) }
66
+ not_found_ids = ids - found_ids
67
+ klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
63
68
  else
64
69
  replace(records)
65
70
  end
@@ -68,26 +73,29 @@ module ActiveRecord
68
73
  def reset
69
74
  super
70
75
  @target = []
76
+ @association_ids = nil
71
77
  end
72
78
 
73
79
  def find(*args)
74
- if block_given?
75
- load_target.find(*args) { |*block_args| yield(*block_args) }
76
- else
77
- if options[:inverse_of] && loaded?
78
- args_flatten = args.flatten
79
- raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
80
- result = find_by_scan(*args)
81
-
82
- result_size = Array(result).size
83
- if !result || result_size != args_flatten.size
84
- scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
85
- else
86
- result
87
- end
80
+ if options[:inverse_of] && loaded?
81
+ args_flatten = args.flatten
82
+ model = scope.klass
83
+
84
+ if args_flatten.blank?
85
+ error_message = "Couldn't find #{model.name} without an ID"
86
+ raise RecordNotFound.new(error_message, model.name, model.primary_key, args)
87
+ end
88
+
89
+ result = find_by_scan(*args)
90
+
91
+ result_size = Array(result).size
92
+ if !result || result_size != args_flatten.size
93
+ scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
88
94
  else
89
- scope.find(*args)
95
+ result
90
96
  end
97
+ else
98
+ scope.find(*args)
91
99
  end
92
100
  end
93
101
 
@@ -179,8 +187,6 @@ module ActiveRecord
179
187
  # are actually removed from the database, that depends precisely on
180
188
  # +delete_records+. They are in any case removed from the collection.
181
189
  def delete(*records)
182
- return if records.empty?
183
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
184
190
  delete_or_destroy(records, options[:dependent])
185
191
  end
186
192
 
@@ -190,8 +196,6 @@ module ActiveRecord
190
196
  # Note that this method removes records from the database ignoring the
191
197
  # +:dependent+ option.
192
198
  def destroy(*records)
193
- return if records.empty?
194
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
195
199
  delete_or_destroy(records, :destroy)
196
200
  end
197
201
 
@@ -299,18 +303,17 @@ module ActiveRecord
299
303
  private
300
304
 
301
305
  def find_target
302
- return scope.to_a if skip_statement_cache?
306
+ scope = self.scope
307
+ return scope.to_a if skip_statement_cache?(scope)
303
308
 
304
309
  conn = klass.connection
305
- sc = reflection.association_scope_cache(conn, owner) do
306
- StatementCache.create(conn) { |params|
307
- as = AssociationScope.create { params.bind }
308
- target_scope.merge as.scope(self, conn)
309
- }
310
+ sc = reflection.association_scope_cache(conn, owner) do |params|
311
+ as = AssociationScope.create { params.bind }
312
+ target_scope.merge!(as.scope(self))
310
313
  end
311
314
 
312
315
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
313
- sc.execute(binds, klass, conn) do |record|
316
+ sc.execute(binds, conn) do |record|
314
317
  set_inverse_instance(record)
315
318
  end
316
319
  end
@@ -356,7 +359,10 @@ module ActiveRecord
356
359
  transaction do
357
360
  add_to_target(build_record(attributes)) do |record|
358
361
  yield(record) if block_given?
359
- insert_record(record, true, raise) { @_was_loaded = loaded? }
362
+ insert_record(record, true, raise) {
363
+ @_was_loaded = loaded?
364
+ @association_ids = nil
365
+ }
360
366
  end
361
367
  end
362
368
  end
@@ -371,11 +377,9 @@ module ActiveRecord
371
377
  end
372
378
  end
373
379
 
374
- def create_scope
375
- scope.scope_for_create.stringify_keys
376
- end
377
-
378
380
  def delete_or_destroy(records, method)
381
+ return if records.empty?
382
+ records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
379
383
  records = records.flatten
380
384
  records.each { |record| raise_on_type_mismatch!(record) }
381
385
  existing_records = records.reject(&:new_record?)
@@ -429,7 +433,12 @@ module ActiveRecord
429
433
  records.each do |record|
430
434
  raise_on_type_mismatch!(record)
431
435
  add_to_target(record) do
432
- result &&= insert_record(record, true, raise) { @_was_loaded = loaded? } unless owner.new_record?
436
+ unless owner.new_record?
437
+ result &&= insert_record(record, true, raise) {
438
+ @_was_loaded = loaded?
439
+ @association_ids = nil
440
+ }
441
+ end
433
442
  end
434
443
  end
435
444
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Associations
3
5
  # Association proxies in Active Record are middlemen between the object that
@@ -30,7 +32,10 @@ module ActiveRecord
30
32
  class CollectionProxy < Relation
31
33
  def initialize(klass, association) #:nodoc:
32
34
  @association = association
33
- super klass, klass.arel_table, klass.predicate_builder
35
+ super klass
36
+
37
+ extensions = association.extensions
38
+ extend(*extensions) if extensions.any?
34
39
  end
35
40
 
36
41
  def target
@@ -130,8 +135,9 @@ module ActiveRecord
130
135
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
131
136
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
132
137
  # # ]
133
- def find(*args, &block)
134
- @association.find(*args, &block)
138
+ def find(*args)
139
+ return super if block_given?
140
+ @association.find(*args)
135
141
  end
136
142
 
137
143
  ##
@@ -743,10 +749,6 @@ module ActiveRecord
743
749
  # # ]
744
750
 
745
751
  #--
746
- def uniq
747
- load_target.uniq
748
- end
749
-
750
752
  def calculate(operation, column_name)
751
753
  null_scope? ? scope.calculate(operation, column_name) : super
752
754
  end
@@ -986,6 +988,12 @@ module ActiveRecord
986
988
  load_target == other
987
989
  end
988
990
 
991
+ ##
992
+ # :method: to_ary
993
+ #
994
+ # :call-seq:
995
+ # to_ary()
996
+ #
989
997
  # Returns a new array of objects from the collection. If the collection
990
998
  # hasn't been loaded, it fetches the records from the database.
991
999
  #
@@ -1019,10 +1027,6 @@ module ActiveRecord
1019
1027
  # # #<Pet id: 5, name: "Brain", person_id: 1>,
1020
1028
  # # #<Pet id: 6, name: "Boss", person_id: 1>
1021
1029
  # # ]
1022
- def to_ary
1023
- load_target.dup
1024
- end
1025
- alias_method :to_a, :to_ary
1026
1030
 
1027
1031
  def records # :nodoc:
1028
1032
  load_target
@@ -1070,7 +1074,6 @@ module ActiveRecord
1070
1074
  end
1071
1075
 
1072
1076
  # Reloads the collection from the database. Returns +self+.
1073
- # Equivalent to <tt>collection(true)</tt>.
1074
1077
  #
1075
1078
  # class Person < ActiveRecord::Base
1076
1079
  # has_many :pets
@@ -1084,13 +1087,9 @@ module ActiveRecord
1084
1087
  #
1085
1088
  # person.pets.reload # fetches pets from the database
1086
1089
  # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1087
- #
1088
- # person.pets(true) # fetches pets from the database
1089
- # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1090
1090
  def reload
1091
- @scope = nil
1092
1091
  proxy_association.reload
1093
- self
1092
+ reset_scope
1094
1093
  end
1095
1094
 
1096
1095
  # Unloads the association. Returns +self+.
@@ -1110,9 +1109,14 @@ module ActiveRecord
1110
1109
  # person.pets # fetches pets from the database
1111
1110
  # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1112
1111
  def reset
1113
- @scope = nil
1114
1112
  proxy_association.reset
1115
1113
  proxy_association.reset_scope
1114
+ reset_scope
1115
+ end
1116
+
1117
+ def reset_scope # :nodoc:
1118
+ @offsets = {}
1119
+ @scope = nil
1116
1120
  self
1117
1121
  end
1118
1122
 
@@ -1125,19 +1129,6 @@ module ActiveRecord
1125
1129
 
1126
1130
  delegate(*delegate_methods, to: :scope)
1127
1131
 
1128
- module DelegateExtending # :nodoc:
1129
- private
1130
- def method_missing(method, *args, &block)
1131
- extending_values = association_scope.extending_values
1132
- if extending_values.any? && (extending_values - self.class.included_modules).any?
1133
- self.class.include(*extending_values)
1134
- public_send(method, *args, &block)
1135
- else
1136
- super
1137
- end
1138
- end
1139
- end
1140
-
1141
1132
  private
1142
1133
 
1143
1134
  def find_nth_with_limit(index, limit)
@@ -1158,17 +1149,9 @@ module ActiveRecord
1158
1149
  @association.find_from_target?
1159
1150
  end
1160
1151
 
1161
- def association_scope
1162
- @association.association_scope
1163
- end
1164
-
1165
1152
  def exec_queries
1166
1153
  load_target
1167
1154
  end
1168
-
1169
- def respond_to_missing?(method, _)
1170
- association_scope.respond_to?(method) || super
1171
- end
1172
1155
  end
1173
1156
  end
1174
1157
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Associations
2
4
  module ForeignAssociation # :nodoc:
3
5
  def foreign_key_present?
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Has Many Association
3
4
  module Associations
5
+ # = Active Record Has Many Association
4
6
  # This is the proxy that handles a has many association.
5
7
  #
6
8
  # If the association has a <tt>:through</tt> option further specialization
@@ -61,7 +63,7 @@ module ActiveRecord
61
63
  count = if reflection.has_cached_counter?
62
64
  owner._read_attribute(reflection.counter_cache_column).to_i
63
65
  else
64
- scope.count
66
+ scope.count(:all)
65
67
  end
66
68
 
67
69
  # If there's nothing in the database and @target has no new records
@@ -1,14 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Has Many Through Association
3
4
  module Associations
5
+ # = Active Record Has Many Through Association
4
6
  class HasManyThroughAssociation < HasManyAssociation #:nodoc:
5
7
  include ThroughAssociation
6
8
 
7
9
  def initialize(owner, reflection)
8
10
  super
9
-
10
- @through_records = {}
11
- @through_association = nil
11
+ @through_records = {}
12
12
  end
13
13
 
14
14
  def concat(*records)
@@ -48,11 +48,6 @@ module ActiveRecord
48
48
  end
49
49
 
50
50
  private
51
-
52
- def through_association
53
- @through_association ||= owner.association(through_reflection.name)
54
- end
55
-
56
51
  # The through record (built with build_record) is temporarily cached
57
52
  # so that it may be reused if insert_record is subsequently called.
58
53
  #
@@ -109,6 +104,11 @@ module ActiveRecord
109
104
  record
110
105
  end
111
106
 
107
+ def remove_records(existing_records, records, method)
108
+ super
109
+ delete_through_records(records)
110
+ end
111
+
112
112
  def target_reflection_has_associated_record?
113
113
  !(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
114
114
  end
@@ -133,21 +133,15 @@ module ActiveRecord
133
133
 
134
134
  scope = through_association.scope
135
135
  scope.where! construct_join_attributes(*records)
136
+ scope = scope.where(through_scope_attributes)
136
137
 
137
138
  case method
138
139
  when :destroy
139
140
  if scope.klass.primary_key
140
- count = scope.destroy_all.length
141
+ count = scope.destroy_all.count(&:destroyed?)
141
142
  else
142
143
  scope.each(&:_run_destroy_callbacks)
143
-
144
- arel = scope.arel
145
-
146
- stmt = Arel::DeleteManager.new
147
- stmt.from scope.klass.arel_table
148
- stmt.wheres = arel.constraints
149
-
150
- count = scope.klass.connection.delete(stmt, "SQL", scope.bound_attributes)
144
+ count = scope.delete_all
151
145
  end
152
146
  when :nullify
153
147
  count = scope.update_all(source_reflection.foreign_key => nil)
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Has One Association
3
4
  module Associations
5
+ # = Active Record Has One Association
4
6
  class HasOneAssociation < SingularAssociation #:nodoc:
5
7
  include ForeignAssociation
6
8
 
@@ -56,6 +58,7 @@ module ActiveRecord
56
58
  when :delete
57
59
  target.delete
58
60
  when :destroy
61
+ target.destroyed_by_association = reflection
59
62
  target.destroy
60
63
  when :nullify
61
64
  target.update_columns(reflection.foreign_key => nil) if target.persisted?
@@ -78,6 +81,7 @@ module ActiveRecord
78
81
  when :delete
79
82
  target.delete
80
83
  when :destroy
84
+ target.destroyed_by_association = reflection
81
85
  target.destroy
82
86
  else
83
87
  nullify_owner_attributes(target)
@@ -1,20 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Has One Through Association
3
4
  module Associations
5
+ # = Active Record Has One Through Association
4
6
  class HasOneThroughAssociation < HasOneAssociation #:nodoc:
5
7
  include ThroughAssociation
6
8
 
7
- def replace(record)
8
- create_through_record(record)
9
+ def replace(record, save = true)
10
+ create_through_record(record, save)
9
11
  self.target = record
10
12
  end
11
13
 
12
14
  private
13
-
14
- def create_through_record(record)
15
+ def create_through_record(record, save)
15
16
  ensure_not_nested
16
17
 
17
- through_proxy = owner.association(through_reflection.name)
18
+ through_proxy = through_association
18
19
  through_record = through_proxy.load_target
19
20
 
20
21
  if through_record && !record
@@ -28,7 +29,7 @@ module ActiveRecord
28
29
 
29
30
  if through_record
30
31
  through_record.update(attributes)
31
- elsif owner.new_record?
32
+ elsif owner.new_record? || !save
32
33
  through_proxy.build(attributes)
33
34
  else
34
35
  through_proxy.create(attributes)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_record/associations/join_dependency/join_part"
2
4
 
3
5
  module ActiveRecord
@@ -9,11 +11,12 @@ module ActiveRecord
9
11
 
10
12
  attr_accessor :tables
11
13
 
12
- def initialize(reflection, children)
14
+ def initialize(reflection, children, alias_tracker)
13
15
  super(reflection.klass, children)
14
16
 
15
- @reflection = reflection
16
- @tables = nil
17
+ @alias_tracker = alias_tracker
18
+ @reflection = reflection
19
+ @tables = nil
17
20
  end
18
21
 
19
22
  def match?(other)
@@ -21,11 +24,8 @@ module ActiveRecord
21
24
  super && reflection == other.reflection
22
25
  end
23
26
 
24
- JoinInformation = Struct.new :joins, :binds
25
-
26
27
  def join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
27
28
  joins = []
28
- binds = []
29
29
  tables = tables.reverse
30
30
 
31
31
  # The chain starts with the target table, but we want to end with it here (makes
@@ -34,79 +34,32 @@ module ActiveRecord
34
34
  table = tables.shift
35
35
  klass = reflection.klass
36
36
 
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)
42
-
43
- predicate_builder = PredicateBuilder.new(TableMetadata.new(klass, table))
44
- scope_chain_items = reflection.join_scopes(table, predicate_builder)
45
- klass_scope = reflection.klass_join_scope(table, predicate_builder)
37
+ constraint = reflection.build_join_constraint(table, foreign_table)
46
38
 
47
- scope_chain_items.concat [klass_scope].compact
48
-
49
- rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
50
- left.merge right
51
- end
52
-
53
- if rel && !rel.arel.constraints.empty?
54
- binds += rel.bound_attributes
55
- constraint = constraint.and rel.arel.constraints
56
- end
39
+ joins << table.create_join(table, table.create_on(constraint), join_type)
57
40
 
58
- if reflection.type
59
- value = foreign_klass.base_class.name
60
- column = klass.columns_hash[reflection.type.to_s]
41
+ join_scope = reflection.join_scope(table, foreign_klass)
42
+ arel = join_scope.arel(alias_tracker.aliases)
61
43
 
62
- binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
63
- constraint = constraint.and klass.arel_attribute(reflection.type, table).eq(Arel::Nodes::BindParam.new)
44
+ if arel.constraints.any?
45
+ joins.concat arel.join_sources
46
+ right = joins.last.right
47
+ right.expr = right.expr.and(arel.constraints)
64
48
  end
65
49
 
66
- joins << table.create_join(table, table.create_on(constraint), join_type)
67
-
68
50
  # The current table in this iteration becomes the foreign table in the next
69
51
  foreign_table, foreign_klass = table, klass
70
52
  end
71
53
 
72
- JoinInformation.new joins, binds
73
- end
74
-
75
- # Builds equality condition.
76
- #
77
- # Example:
78
- #
79
- # class Physician < ActiveRecord::Base
80
- # has_many :appointments
81
- # end
82
- #
83
- # If I execute `Physician.joins(:appointments).to_a` then
84
- # klass # => Physician
85
- # table # => #<Arel::Table @name="appointments" ...>
86
- # key # => physician_id
87
- # foreign_table # => #<Arel::Table @name="physicians" ...>
88
- # foreign_key # => id
89
- #
90
- def build_constraint(klass, table, key, foreign_table, foreign_key)
91
- constraint = table[key].eq(foreign_table[foreign_key])
92
-
93
- if klass.finder_needs_type_condition?
94
- constraint = table.create_and([
95
- constraint,
96
- klass.send(:type_condition, table)
97
- ])
98
- end
99
-
100
- constraint
54
+ joins
101
55
  end
102
56
 
103
57
  def table
104
58
  tables.first
105
59
  end
106
60
 
107
- def aliased_table_name
108
- table.table_alias || table.name
109
- end
61
+ protected
62
+ attr_reader :alias_tracker
110
63
  end
111
64
  end
112
65
  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
@@ -40,11 +38,6 @@ module ActiveRecord
40
38
  raise NotImplementedError
41
39
  end
42
40
 
43
- # The alias for the active_record's table
44
- def aliased_table_name
45
- raise NotImplementedError
46
- end
47
-
48
41
  def extract_record(row, column_names_with_alias)
49
42
  # This code is performance critical as it is called per row.
50
43
  # see: https://github.com/rails/rails/pull/12185