activerecord 4.2.0 → 5.2.8.1

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 (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +640 -928
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  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 +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +466 -280
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +439 -330
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -324
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +136 -90
  133. data/lib/active_record/errors.rb +180 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +117 -35
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -339
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +208 -123
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +30 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,27 +1,45 @@
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
-
6
7
  def handle_dependency
7
- 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
8
17
  end
9
18
 
10
19
  def replace(record)
11
20
  if record
12
21
  raise_on_type_mismatch!(record)
13
- update_counters(record)
14
- replace_keys(record)
22
+ update_counters_on_replace(record)
15
23
  set_inverse_instance(record)
16
24
  @updated = true
17
25
  else
18
26
  decrement_counters
19
- remove_keys
20
27
  end
21
28
 
29
+ replace_keys(record)
30
+
22
31
  self.target = record
23
32
  end
24
33
 
34
+ def inversed_from(record)
35
+ replace_keys(record)
36
+ super
37
+ end
38
+
39
+ def default(&block)
40
+ writer(owner.instance_exec(&block)) if reader.nil?
41
+ end
42
+
25
43
  def reset
26
44
  super
27
45
  @updated = false
@@ -32,60 +50,60 @@ module ActiveRecord
32
50
  end
33
51
 
34
52
  def decrement_counters # :nodoc:
35
- with_cache_name { |name| decrement_counter name }
53
+ update_counters(-1)
36
54
  end
37
55
 
38
56
  def increment_counters # :nodoc:
39
- with_cache_name { |name| increment_counter name }
57
+ update_counters(1)
40
58
  end
41
59
 
42
- private
60
+ def target_changed?
61
+ owner.saved_change_to_attribute?(reflection.foreign_key)
62
+ end
43
63
 
44
- def find_target?
45
- !loaded? && foreign_key_present? && klass
46
- end
64
+ private
47
65
 
48
- def with_cache_name
49
- counter_cache_name = reflection.counter_cache_column
50
- return unless counter_cache_name && owner.persisted?
51
- yield counter_cache_name
66
+ def update_counters(by)
67
+ if require_counter_update? && foreign_key_present?
68
+ if target && !stale_target?
69
+ target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
70
+ else
71
+ klass.update_counters(target_id, reflection.counter_cache_column => by, touch: reflection.options[:touch])
72
+ end
73
+ end
52
74
  end
53
75
 
54
- def update_counters(record)
55
- with_cache_name do |name|
56
- return unless different_target? record
57
- record.class.increment_counter(name, record.id)
58
- decrement_counter name
59
- end
76
+ def find_target?
77
+ !loaded? && foreign_key_present? && klass
60
78
  end
61
79
 
62
- def decrement_counter(counter_cache_name)
63
- if foreign_key_present?
64
- klass.decrement_counter(counter_cache_name, target_id)
65
- end
80
+ def require_counter_update?
81
+ reflection.counter_cache_column && owner.persisted?
66
82
  end
67
83
 
68
- def increment_counter(counter_cache_name)
69
- if foreign_key_present?
70
- klass.increment_counter(counter_cache_name, target_id)
84
+ def update_counters_on_replace(record)
85
+ if require_counter_update? && different_target?(record)
86
+ owner.instance_variable_set :@_after_replace_counter_called, true
87
+ record.increment!(reflection.counter_cache_column, touch: reflection.options[:touch])
88
+ decrement_counters
71
89
  end
72
90
  end
73
91
 
74
92
  # Checks whether record is different to the current target, without loading it
75
93
  def different_target?(record)
76
- record.id != owner[reflection.foreign_key]
94
+ record._read_attribute(primary_key(record)) != owner._read_attribute(reflection.foreign_key)
77
95
  end
78
96
 
79
97
  def replace_keys(record)
80
- owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)]
98
+ owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record)) : nil
81
99
  end
82
100
 
83
- def remove_keys
84
- owner[reflection.foreign_key] = nil
101
+ def primary_key(record)
102
+ reflection.association_primary_key(record.class)
85
103
  end
86
104
 
87
105
  def foreign_key_present?
88
- owner[reflection.foreign_key]
106
+ owner._read_attribute(reflection.foreign_key)
89
107
  end
90
108
 
91
109
  # NOTE - for now, we're only supporting inverse setting from belongs_to back onto
@@ -99,12 +117,13 @@ module ActiveRecord
99
117
  if options[:primary_key]
100
118
  owner.send(reflection.name).try(:id)
101
119
  else
102
- owner[reflection.foreign_key]
120
+ owner._read_attribute(reflection.foreign_key)
103
121
  end
104
122
  end
105
123
 
106
124
  def stale_state
107
- owner[reflection.foreign_key] && owner[reflection.foreign_key].to_s
125
+ result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
126
+ result && result.to_s
108
127
  end
109
128
  end
110
129
  end
@@ -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,4 +1,4 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
1
+ # frozen_string_literal: true
2
2
 
3
3
  # This is the parent Association class which defines the variables
4
4
  # used by all associations.
@@ -11,19 +11,14 @@ require 'active_support/core_ext/module/attribute_accessors'
11
11
  # - CollectionAssociation
12
12
  # - HasManyAssociation
13
13
 
14
- module ActiveRecord::Associations::Builder
14
+ module ActiveRecord::Associations::Builder # :nodoc:
15
15
  class Association #:nodoc:
16
16
  class << self
17
17
  attr_accessor :extensions
18
- # TODO: This class accessor is needed to make activerecord-deprecated_finders work.
19
- # We can move it to a constant in 5.0.
20
- attr_accessor :valid_options
21
18
  end
22
19
  self.extensions = []
23
20
 
24
- self.valid_options = [:class_name, :class, :foreign_key, :validate]
25
-
26
- attr_reader :name, :scope, :options
21
+ VALID_OPTIONS = [:class_name, :anonymous_class, :foreign_key, :validate] # :nodoc:
27
22
 
28
23
  def self.build(model, name, scope, options, &block)
29
24
  if model.dangerous_attribute_method?(name)
@@ -32,57 +27,55 @@ module ActiveRecord::Associations::Builder
32
27
  "Please choose a different association name."
33
28
  end
34
29
 
35
- builder = create_builder model, name, scope, options, &block
36
- reflection = builder.build(model)
30
+ extension = define_extensions model, name, &block
31
+ reflection = create_reflection model, name, scope, options, extension
37
32
  define_accessors model, reflection
38
33
  define_callbacks model, reflection
39
34
  define_validations model, reflection
40
- builder.define_extensions model
41
35
  reflection
42
36
  end
43
37
 
44
- def self.create_builder(model, name, scope, options, &block)
38
+ def self.create_reflection(model, name, scope, options, extension = nil)
45
39
  raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
46
40
 
47
- new(model, name, scope, options, &block)
48
- end
41
+ validate_options(options)
49
42
 
50
- def initialize(model, name, scope, options)
51
- # TODO: Move this to create_builder as soon we drop support to activerecord-deprecated_finders.
52
- if scope.is_a?(Hash)
53
- options = scope
54
- scope = nil
55
- end
43
+ scope = build_scope(scope, extension)
56
44
 
57
- # TODO: Remove this model argument as soon we drop support to activerecord-deprecated_finders.
58
- @name = name
59
- @scope = scope
60
- @options = options
45
+ ActiveRecord::Reflection.create(macro, name, scope, options, model)
46
+ end
61
47
 
62
- validate_options
48
+ def self.build_scope(scope, extension)
49
+ new_scope = scope
63
50
 
64
51
  if scope && scope.arity == 0
65
- @scope = proc { instance_exec(&scope) }
52
+ new_scope = proc { instance_exec(&scope) }
66
53
  end
54
+
55
+ if extension
56
+ new_scope = wrap_scope new_scope, extension
57
+ end
58
+
59
+ new_scope
67
60
  end
68
61
 
69
- def build(model)
70
- ActiveRecord::Reflection.create(macro, name, scope, options, model)
62
+ def self.wrap_scope(scope, extension)
63
+ scope
71
64
  end
72
65
 
73
- def macro
66
+ def self.macro
74
67
  raise NotImplementedError
75
68
  end
76
69
 
77
- def valid_options
78
- Association.valid_options + Association.extensions.flat_map(&:valid_options)
70
+ def self.valid_options(options)
71
+ VALID_OPTIONS + Association.extensions.flat_map(&:valid_options)
79
72
  end
80
73
 
81
- def validate_options
82
- options.assert_valid_keys(valid_options)
74
+ def self.validate_options(options)
75
+ options.assert_valid_keys(valid_options(options))
83
76
  end
84
77
 
85
- def define_extensions(model)
78
+ def self.define_extensions(model, name)
86
79
  end
87
80
 
88
81
  def self.define_callbacks(model, reflection)
@@ -111,8 +104,8 @@ module ActiveRecord::Associations::Builder
111
104
 
112
105
  def self.define_readers(mixin, name)
113
106
  mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
114
- def #{name}(*args)
115
- association(:#{name}).reader(*args)
107
+ def #{name}
108
+ association(:#{name}).reader
116
109
  end
117
110
  CODE
118
111
  end
@@ -133,8 +126,6 @@ module ActiveRecord::Associations::Builder
133
126
  raise NotImplementedError
134
127
  end
135
128
 
136
- private
137
-
138
129
  def self.check_dependent_options(dependent)
139
130
  unless valid_dependent_options.include? dependent
140
131
  raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
@@ -1,11 +1,13 @@
1
- module ActiveRecord::Associations::Builder
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord::Associations::Builder # :nodoc:
2
4
  class BelongsTo < SingularAssociation #:nodoc:
3
- def macro
5
+ def self.macro
4
6
  :belongs_to
5
7
  end
6
8
 
7
- def valid_options
8
- super + [:foreign_type, :polymorphic, :touch, :counter_cache]
9
+ def self.valid_options(options)
10
+ super + [:polymorphic, :touch, :counter_cache, :optional, :default]
9
11
  end
10
12
 
11
13
  def self.valid_dependent_options
@@ -16,6 +18,7 @@ module ActiveRecord::Associations::Builder
16
18
  super
17
19
  add_counter_cache_callbacks(model, reflection) if reflection.options[:counter_cache]
18
20
  add_touch_callbacks(model, reflection) if reflection.options[:touch]
21
+ add_default_callbacks(model, reflection) if reflection.options[:default]
19
22
  end
20
23
 
21
24
  def self.define_accessors(mixin, reflection)
@@ -23,8 +26,6 @@ module ActiveRecord::Associations::Builder
23
26
  add_counter_cache_methods mixin
24
27
  end
25
28
 
26
- private
27
-
28
29
  def self.add_counter_cache_methods(mixin)
29
30
  return if mixin.method_defined? :belongs_to_counter_cache_after_update
30
31
 
@@ -33,21 +34,37 @@ module ActiveRecord::Associations::Builder
33
34
  foreign_key = reflection.foreign_key
34
35
  cache_column = reflection.counter_cache_column
35
36
 
36
- if (@_after_create_counter_called ||= false)
37
- @_after_create_counter_called = false
38
- elsif attribute_changed?(foreign_key) && !new_record? && reflection.constructable?
39
- model = reflection.klass
40
- foreign_key_was = attribute_was foreign_key
41
- foreign_key = attribute foreign_key
37
+ if (@_after_replace_counter_called ||= false)
38
+ @_after_replace_counter_called = false
39
+ elsif association(reflection.name).target_changed?
40
+ if reflection.polymorphic?
41
+ model = attribute_in_database(reflection.foreign_type).try(:constantize)
42
+ model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
43
+ else
44
+ model = reflection.klass
45
+ model_was = reflection.klass
46
+ end
47
+
48
+ foreign_key_was = attribute_before_last_save foreign_key
49
+ foreign_key = attribute_in_database foreign_key
42
50
 
43
51
  if foreign_key && model.respond_to?(:increment_counter)
52
+ foreign_key = counter_cache_target(reflection, model, foreign_key)
44
53
  model.increment_counter(cache_column, foreign_key)
45
54
  end
46
- if foreign_key_was && model.respond_to?(:decrement_counter)
47
- model.decrement_counter(cache_column, foreign_key_was)
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)
58
+ model_was.decrement_counter(cache_column, foreign_key_was)
48
59
  end
49
60
  end
50
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
51
68
  end
52
69
  end
53
70
 
@@ -62,24 +79,27 @@ module ActiveRecord::Associations::Builder
62
79
  klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
63
80
  end
64
81
 
65
- def self.touch_record(o, foreign_key, name, touch) # :nodoc:
66
- old_foreign_id = o.changed_attributes[foreign_key]
82
+ def self.touch_record(o, changes, foreign_key, name, touch, touch_method) # :nodoc:
83
+ old_foreign_id = changes[foreign_key] && changes[foreign_key].first
67
84
 
68
85
  if old_foreign_id
69
86
  association = o.association(name)
70
87
  reflection = association.reflection
71
88
  if reflection.polymorphic?
72
- klass = o.public_send("#{reflection.foreign_type}_was").constantize
89
+ foreign_type = reflection.foreign_type
90
+ klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
91
+ klass = klass.constantize
73
92
  else
74
93
  klass = association.klass
75
94
  end
76
- 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)
77
97
 
78
98
  if old_record
79
99
  if touch != true
80
- old_record.touch touch
100
+ old_record.send(touch_method, touch)
81
101
  else
82
- old_record.touch
102
+ old_record.send(touch_method)
83
103
  end
84
104
  end
85
105
  end
@@ -87,9 +107,9 @@ module ActiveRecord::Associations::Builder
87
107
  record = o.send name
88
108
  if record && record.persisted?
89
109
  if touch != true
90
- record.touch touch
110
+ record.send(touch_method, touch)
91
111
  else
92
- record.touch
112
+ record.send(touch_method)
93
113
  end
94
114
  end
95
115
  end
@@ -99,18 +119,45 @@ module ActiveRecord::Associations::Builder
99
119
  n = reflection.name
100
120
  touch = reflection.options[:touch]
101
121
 
102
- callback = lambda { |record|
103
- BelongsTo.touch_record(record, foreign_key, n, touch)
104
- }
122
+ callback = lambda { |changes_method| lambda { |record|
123
+ BelongsTo.touch_record(record, record.send(changes_method), foreign_key, n, touch, belongs_to_touch_method)
124
+ }}
125
+
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)
133
+ end
105
134
 
106
- model.after_save callback, if: :changed?
107
- model.after_touch callback
108
- model.after_destroy callback
135
+ def self.add_default_callbacks(model, reflection)
136
+ model.before_validation lambda { |o|
137
+ o.association(reflection.name).default(&reflection.options[:default])
138
+ }
109
139
  end
110
140
 
111
141
  def self.add_destroy_callbacks(model, reflection)
112
- name = reflection.name
113
- model.after_destroy lambda { |o| o.association(name).handle_dependency }
142
+ model.after_destroy lambda { |o| o.association(reflection.name).handle_dependency }
143
+ end
144
+
145
+ def self.define_validations(model, reflection)
146
+ if reflection.options.key?(:required)
147
+ reflection.options[:optional] = !reflection.options.delete(:required)
148
+ end
149
+
150
+ if reflection.options[:optional].nil?
151
+ required = model.belongs_to_required_by_default
152
+ else
153
+ required = !reflection.options[:optional]
154
+ end
155
+
156
+ super
157
+
158
+ if required
159
+ model.validates_presence_of reflection.name, message: :required
160
+ end
114
161
  end
115
162
  end
116
163
  end
@@ -1,28 +1,16 @@
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
- require 'active_record/associations'
3
+ require "active_record/associations"
4
4
 
5
- module ActiveRecord::Associations::Builder
5
+ module ActiveRecord::Associations::Builder # :nodoc:
6
6
  class CollectionAssociation < Association #:nodoc:
7
-
8
7
  CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
9
8
 
10
- def valid_options
9
+ def self.valid_options(options)
11
10
  super + [:table_name, :before_add,
12
11
  :after_add, :before_remove, :after_remove, :extend]
13
12
  end
14
13
 
15
- attr_reader :block_extension
16
-
17
- def initialize(model, name, scope, options)
18
- super
19
- @mod = nil
20
- if block_given?
21
- @mod = Module.new(&Proc.new)
22
- @scope = wrap_scope @scope, @mod
23
- end
24
- end
25
-
26
14
  def self.define_callbacks(model, reflection)
27
15
  super
28
16
  name = reflection.name
@@ -32,10 +20,11 @@ module ActiveRecord::Associations::Builder
32
20
  }
33
21
  end
34
22
 
35
- def define_extensions(model)
36
- if @mod
23
+ def self.define_extensions(model, name, &block)
24
+ if block_given?
37
25
  extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
38
- model.parent.const_set(extension_module_name, @mod)
26
+ extension = Module.new(&block)
27
+ model.parent.const_set(extension_module_name, extension)
39
28
  end
40
29
  end
41
30
 
@@ -78,11 +67,13 @@ module ActiveRecord::Associations::Builder
78
67
  CODE
79
68
  end
80
69
 
81
- private
82
-
83
- def wrap_scope(scope, mod)
70
+ def self.wrap_scope(scope, mod)
84
71
  if scope
85
- proc { |owner| instance_exec(owner, &scope).extending(mod) }
72
+ if scope.arity > 0
73
+ proc { |owner| instance_exec(owner, &scope).extending(mod) }
74
+ else
75
+ proc { instance_exec(&scope).extending(mod) }
76
+ end
86
77
  else
87
78
  proc { extending(mod) }
88
79
  end