activerecord 5.1.0 → 5.2.3

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 (261) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +596 -450
  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 +77 -85
  11. data/lib/active_record/associations/alias_tracker.rb +23 -32
  12. data/lib/active_record/associations/association.rb +49 -35
  13. data/lib/active_record/associations/association_scope.rb +55 -55
  14. data/lib/active_record/associations/belongs_to_association.rb +30 -11
  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 +21 -8
  18. data/lib/active_record/associations/builder/collection_association.rb +1 -1
  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 +66 -53
  24. data/lib/active_record/associations/collection_proxy.rb +30 -73
  25. data/lib/active_record/associations/foreign_association.rb +2 -0
  26. data/lib/active_record/associations/has_many_association.rb +13 -2
  27. data/lib/active_record/associations/has_many_through_association.rb +37 -19
  28. data/lib/active_record/associations/has_one_association.rb +14 -1
  29. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  30. data/lib/active_record/associations/join_dependency.rb +52 -96
  31. data/lib/active_record/associations/join_dependency/join_association.rb +22 -75
  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 +17 -37
  35. data/lib/active_record/associations/preloader/association.rb +53 -92
  36. data/lib/active_record/associations/preloader/through_association.rb +72 -73
  37. data/lib/active_record/associations/singular_association.rb +14 -16
  38. data/lib/active_record/associations/through_association.rb +27 -12
  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 +33 -216
  44. data/lib/active_record/attribute_methods/primary_key.rb +10 -13
  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 +22 -19
  50. data/lib/active_record/attributes.rb +7 -6
  51. data/lib/active_record/autosave_association.rb +15 -13
  52. data/lib/active_record/base.rb +2 -0
  53. data/lib/active_record/callbacks.rb +12 -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 +15 -11
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +120 -39
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +192 -37
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -2
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -25
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -6
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +65 -7
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -87
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -98
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +126 -189
  70. data/lib/active_record/connection_adapters/column.rb +4 -2
  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 +45 -15
  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 -23
  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 +30 -1
  84. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -32
  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 +13 -1
  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 +3 -1
  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 -11
  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 +8 -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 +22 -1
  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 +258 -129
  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 +75 -87
  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 +24 -1
  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 +90 -96
  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 +20 -15
  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 +60 -15
  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 +4 -2
  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 +30 -42
  146. data/lib/active_record/locking/pessimistic.rb +10 -7
  147. data/lib/active_record/log_subscriber.rb +46 -4
  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 +81 -29
  151. data/lib/active_record/migration/join_table.rb +2 -0
  152. data/lib/active_record/model_schema.rb +74 -58
  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 +199 -54
  157. data/lib/active_record/query_cache.rb +8 -10
  158. data/lib/active_record/querying.rb +5 -3
  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 +48 -38
  163. data/lib/active_record/readonly_attributes.rb +3 -2
  164. data/lib/active_record/reflection.rb +137 -207
  165. data/lib/active_record/relation.rb +132 -207
  166. data/lib/active_record/relation/batches.rb +32 -17
  167. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  168. data/lib/active_record/relation/calculations.rb +66 -25
  169. data/lib/active_record/relation/delegation.rb +45 -29
  170. data/lib/active_record/relation/finder_methods.rb +76 -85
  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 +135 -103
  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 -67
  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 +12 -10
  194. data/lib/active_record/scoping/default.rb +10 -7
  195. data/lib/active_record/scoping/named.rb +40 -12
  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 +22 -12
  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 +38 -26
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +11 -50
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -3
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  206. data/lib/active_record/timestamp.rb +13 -6
  207. data/lib/active_record/touch_later.rb +2 -0
  208. data/lib/active_record/transactions.rb +32 -27
  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 +6 -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 +36 -6
  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 +24 -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 -113
  254. data/lib/active_record/attribute_set.rb +0 -113
  255. data/lib/active_record/attribute_set/builder.rb +0 -124
  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 -33
@@ -1,26 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Belongs To Association
3
4
  module Associations
5
+ # = Active Record Belongs To Association
4
6
  class BelongsToAssociation < SingularAssociation #:nodoc:
5
7
  def handle_dependency
6
- target.send(options[:dependent]) if load_target
8
+ return unless load_target
9
+
10
+ case options[:dependent]
11
+ when :destroy
12
+ target.destroy
13
+ raise ActiveRecord::Rollback unless target.destroyed?
14
+ else
15
+ target.send(options[:dependent])
16
+ end
7
17
  end
8
18
 
9
19
  def replace(record)
10
20
  if record
11
21
  raise_on_type_mismatch!(record)
12
22
  update_counters_on_replace(record)
13
- replace_keys(record)
14
23
  set_inverse_instance(record)
15
24
  @updated = true
16
25
  else
17
26
  decrement_counters
18
- remove_keys
19
27
  end
20
28
 
29
+ replace_keys(record)
30
+
21
31
  self.target = record
22
32
  end
23
33
 
34
+ def inversed_from(record)
35
+ replace_keys(record)
36
+ super
37
+ end
38
+
24
39
  def default(&block)
25
40
  writer(owner.instance_exec(&block)) if reader.nil?
26
41
  end
@@ -42,14 +57,18 @@ module ActiveRecord
42
57
  update_counters(1)
43
58
  end
44
59
 
60
+ def target_changed?
61
+ owner.saved_change_to_attribute?(reflection.foreign_key)
62
+ end
63
+
45
64
  private
46
65
 
47
66
  def update_counters(by)
48
67
  if require_counter_update? && foreign_key_present?
49
68
  if target && !stale_target?
50
- target.increment!(reflection.counter_cache_column, by)
69
+ target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
51
70
  else
52
- klass.update_counters(target_id, reflection.counter_cache_column => by)
71
+ klass.update_counters(target_id, reflection.counter_cache_column => by, touch: reflection.options[:touch])
53
72
  end
54
73
  end
55
74
  end
@@ -65,22 +84,22 @@ module ActiveRecord
65
84
  def update_counters_on_replace(record)
66
85
  if require_counter_update? && different_target?(record)
67
86
  owner.instance_variable_set :@_after_replace_counter_called, true
68
- record.increment!(reflection.counter_cache_column)
87
+ record.increment!(reflection.counter_cache_column, touch: reflection.options[:touch])
69
88
  decrement_counters
70
89
  end
71
90
  end
72
91
 
73
92
  # Checks whether record is different to the current target, without loading it
74
93
  def different_target?(record)
75
- record.id != owner._read_attribute(reflection.foreign_key)
94
+ record._read_attribute(primary_key(record)) != owner._read_attribute(reflection.foreign_key)
76
95
  end
77
96
 
78
97
  def replace_keys(record)
79
- owner[reflection.foreign_key] = record._read_attribute(reflection.association_primary_key(record.class))
98
+ owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record)) : nil
80
99
  end
81
100
 
82
- def remove_keys
83
- owner[reflection.foreign_key] = nil
101
+ def primary_key(record)
102
+ reflection.association_primary_key(record.class)
84
103
  end
85
104
 
86
105
  def foreign_key_present?
@@ -1,22 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Belongs To Polymorphic Association
3
4
  module Associations
5
+ # = Active Record Belongs To Polymorphic Association
4
6
  class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
5
7
  def klass
6
8
  type = owner[reflection.foreign_type]
7
9
  type.presence && type.constantize
8
10
  end
9
11
 
10
- private
12
+ def target_changed?
13
+ super || owner.saved_change_to_attribute?(reflection.foreign_type)
14
+ end
11
15
 
16
+ private
12
17
  def replace_keys(record)
13
18
  super
14
- owner[reflection.foreign_type] = record.class.base_class.name
15
- end
16
-
17
- def remove_keys
18
- super
19
- owner[reflection.foreign_type] = nil
19
+ owner[reflection.foreign_type] = record ? record.class.polymorphic_name : nil
20
20
  end
21
21
 
22
22
  def different_target?(record)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This is the parent Association class which defines the variables
2
4
  # used by all associations.
3
5
  #
@@ -36,11 +38,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
36
38
  def self.create_reflection(model, name, scope, options, extension = nil)
37
39
  raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
38
40
 
39
- if scope.is_a?(Hash)
40
- options = scope
41
- scope = nil
42
- end
43
-
44
41
  validate_options(options)
45
42
 
46
43
  scope = build_scope(scope, extension)
@@ -107,8 +104,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
107
104
 
108
105
  def self.define_readers(mixin, name)
109
106
  mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
110
- def #{name}(*args)
111
- association(:#{name}).reader(*args)
107
+ def #{name}
108
+ association(:#{name}).reader
112
109
  end
113
110
  CODE
114
111
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class BelongsTo < SingularAssociation #:nodoc:
3
5
  def self.macro
@@ -32,11 +34,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
32
34
  foreign_key = reflection.foreign_key
33
35
  cache_column = reflection.counter_cache_column
34
36
 
35
- if (@_after_create_counter_called ||= false)
36
- @_after_create_counter_called = false
37
- elsif (@_after_replace_counter_called ||= false)
37
+ if (@_after_replace_counter_called ||= false)
38
38
  @_after_replace_counter_called = false
39
- elsif saved_change_to_attribute?(foreign_key) && !new_record?
39
+ elsif association(reflection.name).target_changed?
40
40
  if reflection.polymorphic?
41
41
  model = attribute_in_database(reflection.foreign_type).try(:constantize)
42
42
  model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
@@ -49,14 +49,22 @@ module ActiveRecord::Associations::Builder # :nodoc:
49
49
  foreign_key = attribute_in_database foreign_key
50
50
 
51
51
  if foreign_key && model.respond_to?(:increment_counter)
52
+ foreign_key = counter_cache_target(reflection, model, foreign_key)
52
53
  model.increment_counter(cache_column, foreign_key)
53
54
  end
54
55
 
55
56
  if foreign_key_was && model_was.respond_to?(:decrement_counter)
57
+ foreign_key_was = counter_cache_target(reflection, model_was, foreign_key_was)
56
58
  model_was.decrement_counter(cache_column, foreign_key_was)
57
59
  end
58
60
  end
59
61
  end
62
+
63
+ private
64
+ def counter_cache_target(reflection, model, foreign_key)
65
+ primary_key = reflection.association_primary_key(model)
66
+ model.unscoped.where!(primary_key => foreign_key)
67
+ end
60
68
  end
61
69
  end
62
70
 
@@ -84,7 +92,8 @@ module ActiveRecord::Associations::Builder # :nodoc:
84
92
  else
85
93
  klass = association.klass
86
94
  end
87
- old_record = klass.find_by(klass.primary_key => old_foreign_id)
95
+ primary_key = reflection.association_primary_key(klass)
96
+ old_record = klass.find_by(primary_key => old_foreign_id)
88
97
 
89
98
  if old_record
90
99
  if touch != true
@@ -114,9 +123,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
114
123
  BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
115
124
  }}
116
125
 
117
- model.after_save callback.(:saved_changes), if: :saved_changes?
118
- model.after_touch callback.(:changes_to_save)
119
- model.after_destroy callback.(:changes_to_save)
126
+ unless reflection.counter_cache_column
127
+ model.after_create callback.(:saved_changes), if: :saved_changes?
128
+ model.after_destroy callback.(:changes_to_save)
129
+ end
130
+
131
+ model.after_update callback.(:saved_changes), if: :saved_changes?
132
+ model.after_touch callback.(:changes_to_save)
120
133
  end
121
134
 
122
135
  def self.add_default_callbacks(model, reflection)
@@ -1,4 +1,4 @@
1
- # This class is inherited by the has_many and has_many_and_belongs_to_many association classes
1
+ # frozen_string_literal: true
2
2
 
3
3
  require "active_record/associations"
4
4
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class HasAndBelongsToMany # :nodoc:
3
5
  class JoinTableResolver # :nodoc:
@@ -45,7 +47,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
45
47
  habtm = JoinTableResolver.build lhs_model, association_name, options
46
48
 
47
49
  join_model = Class.new(ActiveRecord::Base) {
48
- class << self;
50
+ class << self
49
51
  attr_accessor :left_model
50
52
  attr_accessor :name
51
53
  attr_accessor :table_name_resolver
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class HasMany < CollectionAssociation #:nodoc:
3
5
  def self.macro
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class HasOne < SingularAssociation #:nodoc:
3
5
  def self.macro
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class is inherited by the has_one and belongs_to association classes
2
4
 
3
5
  module ActiveRecord::Associations::Builder # :nodoc:
@@ -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
@@ -42,24 +45,28 @@ module ActiveRecord
42
45
  def ids_reader
43
46
  if loaded?
44
47
  target.pluck(reflection.association_primary_key)
48
+ elsif !target.empty?
49
+ load_target.pluck(reflection.association_primary_key)
45
50
  else
46
- @association_ids ||= (
47
- column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
48
- scope.pluck(column)
49
- )
51
+ @association_ids ||= scope.pluck(reflection.association_primary_key)
50
52
  end
51
53
  end
52
54
 
53
55
  # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
54
56
  def ids_writer(ids)
55
- pk_type = reflection.association_primary_key_type
57
+ primary_key = reflection.association_primary_key
58
+ pk_type = klass.type_for_attribute(primary_key)
56
59
  ids = Array(ids).reject(&:blank?)
57
60
  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)
61
+
62
+ records = klass.where(primary_key => ids).index_by do |r|
63
+ r.public_send(primary_key)
60
64
  end.values_at(*ids).compact
65
+
61
66
  if records.size != ids.size
62
- klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, reflection.association_primary_key)
67
+ found_ids = records.map { |record| record.public_send(primary_key) }
68
+ not_found_ids = ids - found_ids
69
+ klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
63
70
  else
64
71
  replace(records)
65
72
  end
@@ -68,26 +75,29 @@ module ActiveRecord
68
75
  def reset
69
76
  super
70
77
  @target = []
78
+ @association_ids = nil
71
79
  end
72
80
 
73
81
  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
82
+ if options[:inverse_of] && loaded?
83
+ args_flatten = args.flatten
84
+ model = scope.klass
85
+
86
+ if args_flatten.blank?
87
+ error_message = "Couldn't find #{model.name} without an ID"
88
+ raise RecordNotFound.new(error_message, model.name, model.primary_key, args)
89
+ end
90
+
91
+ result = find_by_scan(*args)
92
+
93
+ result_size = Array(result).size
94
+ if !result || result_size != args_flatten.size
95
+ scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
88
96
  else
89
- scope.find(*args)
97
+ result
90
98
  end
99
+ else
100
+ scope.find(*args)
91
101
  end
92
102
  end
93
103
 
@@ -95,15 +105,12 @@ module ActiveRecord
95
105
  if attributes.is_a?(Array)
96
106
  attributes.collect { |attr| build(attr, &block) }
97
107
  else
98
- add_to_target(build_record(attributes)) do |record|
99
- yield(record) if block_given?
100
- end
108
+ add_to_target(build_record(attributes, &block))
101
109
  end
102
110
  end
103
111
 
104
- # Add +records+ to this association. Returns +self+ so method calls may
105
- # be chained. Since << flattens its argument list and inserts each record,
106
- # +push+ and +concat+ behave identically.
112
+ # Add +records+ to this association. Since +<<+ flattens its argument list
113
+ # and inserts each record, +push+ and +concat+ behave identically.
107
114
  def concat(*records)
108
115
  records = records.flatten
109
116
  if owner.new_record?
@@ -179,8 +186,6 @@ module ActiveRecord
179
186
  # are actually removed from the database, that depends precisely on
180
187
  # +delete_records+. They are in any case removed from the collection.
181
188
  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
189
  delete_or_destroy(records, options[:dependent])
185
190
  end
186
191
 
@@ -190,8 +195,6 @@ module ActiveRecord
190
195
  # Note that this method removes records from the database ignoring the
191
196
  # +:dependent+ option.
192
197
  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
198
  delete_or_destroy(records, :destroy)
196
199
  end
197
200
 
@@ -299,18 +302,17 @@ module ActiveRecord
299
302
  private
300
303
 
301
304
  def find_target
302
- return scope.to_a if skip_statement_cache?
305
+ scope = self.scope
306
+ return scope.to_a if skip_statement_cache?(scope)
303
307
 
304
308
  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
- }
309
+ sc = reflection.association_scope_cache(conn, owner) do |params|
310
+ as = AssociationScope.create { params.bind }
311
+ target_scope.merge!(as.scope(self))
310
312
  end
311
313
 
312
314
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
313
- sc.execute(binds, klass, conn) do |record|
315
+ sc.execute(binds, conn) do |record|
314
316
  set_inverse_instance(record)
315
317
  end
316
318
  end
@@ -353,12 +355,17 @@ module ActiveRecord
353
355
  if attributes.is_a?(Array)
354
356
  attributes.collect { |attr| _create_record(attr, raise, &block) }
355
357
  else
358
+ record = build_record(attributes, &block)
356
359
  transaction do
357
- add_to_target(build_record(attributes)) do |record|
358
- yield(record) if block_given?
359
- insert_record(record, true, raise) { @_was_loaded = loaded? }
360
+ result = nil
361
+ add_to_target(record) do
362
+ result = insert_record(record, true, raise) {
363
+ @_was_loaded = loaded?
364
+ }
360
365
  end
366
+ raise ActiveRecord::Rollback unless result
361
367
  end
368
+ record
362
369
  end
363
370
  end
364
371
 
@@ -371,11 +378,9 @@ module ActiveRecord
371
378
  end
372
379
  end
373
380
 
374
- def create_scope
375
- scope.scope_for_create.stringify_keys
376
- end
377
-
378
381
  def delete_or_destroy(records, method)
382
+ return if records.empty?
383
+ records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
379
384
  records = records.flatten
380
385
  records.each { |record| raise_on_type_mismatch!(record) }
381
386
  existing_records = records.reject(&:new_record?)
@@ -392,6 +397,7 @@ module ActiveRecord
392
397
 
393
398
  delete_records(existing_records, method) if existing_records.any?
394
399
  records.each { |record| target.delete(record) }
400
+ @association_ids = nil
395
401
 
396
402
  records.each { |record| callback(:after_remove, record) }
397
403
  end
@@ -404,9 +410,9 @@ module ActiveRecord
404
410
  end
405
411
 
406
412
  def replace_records(new_target, original_target)
407
- delete(target - new_target)
413
+ delete(difference(target, new_target))
408
414
 
409
- unless concat(new_target - target)
415
+ unless concat(difference(new_target, target))
410
416
  @target = original_target
411
417
  raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
412
418
  "new records could not be saved."
@@ -416,7 +422,7 @@ module ActiveRecord
416
422
  end
417
423
 
418
424
  def replace_common_records_in_memory(new_target, original_target)
419
- common_records = new_target & original_target
425
+ common_records = intersection(new_target, original_target)
420
426
  common_records.each do |record|
421
427
  skip_callbacks = true
422
428
  replace_on_target(record, @target.index(record), skip_callbacks)
@@ -429,11 +435,17 @@ module ActiveRecord
429
435
  records.each do |record|
430
436
  raise_on_type_mismatch!(record)
431
437
  add_to_target(record) do
432
- result &&= insert_record(record, true, raise) { @_was_loaded = loaded? } unless owner.new_record?
438
+ unless owner.new_record?
439
+ result &&= insert_record(record, true, raise) {
440
+ @_was_loaded = loaded?
441
+ }
442
+ end
433
443
  end
434
444
  end
435
445
 
436
- result && records
446
+ raise ActiveRecord::Rollback unless result
447
+
448
+ records
437
449
  end
438
450
 
439
451
  def replace_on_target(record, index, skip_callbacks)
@@ -448,6 +460,7 @@ module ActiveRecord
448
460
  if index
449
461
  target[index] = record
450
462
  elsif @_was_loaded || !loaded?
463
+ @association_ids = nil
451
464
  target << record
452
465
  end
453
466