activerecord 5.1.5 → 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 (261) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +655 -608
  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/aggregations.rb +6 -5
  8. data/lib/active_record/association_relation.rb +7 -5
  9. data/lib/active_record/associations/alias_tracker.rb +19 -27
  10. data/lib/active_record/associations/association.rb +41 -37
  11. data/lib/active_record/associations/association_scope.rb +38 -50
  12. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +4 -7
  15. data/lib/active_record/associations/builder/belongs_to.rb +14 -5
  16. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  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 +59 -47
  22. data/lib/active_record/associations/collection_proxy.rb +20 -49
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +12 -1
  25. data/lib/active_record/associations/has_many_through_association.rb +36 -30
  26. data/lib/active_record/associations/has_one_association.rb +12 -1
  27. data/lib/active_record/associations/has_one_through_association.rb +13 -8
  28. data/lib/active_record/associations/join_dependency/join_association.rb +39 -63
  29. data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +9 -9
  31. data/lib/active_record/associations/join_dependency.rb +48 -93
  32. data/lib/active_record/associations/preloader/association.rb +45 -61
  33. data/lib/active_record/associations/preloader/through_association.rb +71 -79
  34. data/lib/active_record/associations/preloader.rb +18 -38
  35. data/lib/active_record/associations/singular_association.rb +14 -16
  36. data/lib/active_record/associations/through_association.rb +26 -11
  37. data/lib/active_record/associations.rb +40 -63
  38. data/lib/active_record/attribute_assignment.rb +2 -5
  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 +32 -216
  42. data/lib/active_record/attribute_methods/primary_key.rb +7 -6
  43. data/lib/active_record/attribute_methods/query.rb +2 -0
  44. data/lib/active_record/attribute_methods/read.rb +9 -3
  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 +21 -9
  48. data/lib/active_record/attribute_methods.rb +65 -24
  49. data/lib/active_record/attributes.rb +7 -6
  50. data/lib/active_record/autosave_association.rb +35 -19
  51. data/lib/active_record/base.rb +2 -0
  52. data/lib/active_record/callbacks.rb +12 -6
  53. data/lib/active_record/coders/json.rb +2 -0
  54. data/lib/active_record/coders/yaml_column.rb +15 -1
  55. data/lib/active_record/collection_cache_key.rb +12 -8
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +142 -42
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +174 -33
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +15 -5
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -32
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +64 -6
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +152 -81
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +84 -97
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +110 -173
  69. data/lib/active_record/connection_adapters/column.rb +3 -1
  70. data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +13 -2
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +47 -2
  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 -30
  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 +2 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
  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 +13 -1
  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.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
  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 -1
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -3
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +8 -2
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +50 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +234 -112
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +3 -1
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +66 -74
  117. data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +24 -1
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +75 -1
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +82 -95
  126. data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
  127. data/lib/active_record/connection_handling.rb +4 -2
  128. data/lib/active_record/core.rb +51 -61
  129. data/lib/active_record/counter_cache.rb +20 -15
  130. data/lib/active_record/define_callbacks.rb +5 -3
  131. data/lib/active_record/dynamic_matchers.rb +9 -9
  132. data/lib/active_record/enum.rb +18 -13
  133. data/lib/active_record/errors.rb +60 -15
  134. data/lib/active_record/explain.rb +3 -1
  135. data/lib/active_record/explain_registry.rb +2 -0
  136. data/lib/active_record/explain_subscriber.rb +2 -0
  137. data/lib/active_record/fixture_set/file.rb +2 -0
  138. data/lib/active_record/fixtures.rb +67 -60
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +49 -19
  141. data/lib/active_record/integration.rb +58 -19
  142. data/lib/active_record/internal_metadata.rb +2 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +3 -1
  144. data/lib/active_record/locking/optimistic.rb +30 -42
  145. data/lib/active_record/locking/pessimistic.rb +9 -6
  146. data/lib/active_record/log_subscriber.rb +43 -0
  147. data/lib/active_record/migration/command_recorder.rb +11 -9
  148. data/lib/active_record/migration/compatibility.rb +47 -9
  149. data/lib/active_record/migration/join_table.rb +2 -0
  150. data/lib/active_record/migration.rb +189 -139
  151. data/lib/active_record/model_schema.rb +19 -24
  152. data/lib/active_record/nested_attributes.rb +18 -6
  153. data/lib/active_record/no_touching.rb +3 -1
  154. data/lib/active_record/null_relation.rb +2 -0
  155. data/lib/active_record/persistence.rb +198 -49
  156. data/lib/active_record/query_cache.rb +12 -14
  157. data/lib/active_record/querying.rb +4 -2
  158. data/lib/active_record/railtie.rb +80 -6
  159. data/lib/active_record/railties/console_sandbox.rb +2 -0
  160. data/lib/active_record/railties/controller_runtime.rb +2 -0
  161. data/lib/active_record/railties/databases.rake +46 -36
  162. data/lib/active_record/readonly_attributes.rb +3 -2
  163. data/lib/active_record/reflection.rb +108 -194
  164. data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
  165. data/lib/active_record/relation/batches.rb +20 -5
  166. data/lib/active_record/relation/calculations.rb +46 -20
  167. data/lib/active_record/relation/delegation.rb +45 -27
  168. data/lib/active_record/relation/finder_methods.rb +77 -78
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +53 -23
  171. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
  172. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  173. data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
  174. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
  175. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  176. data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
  177. data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
  178. data/lib/active_record/relation/predicate_builder.rb +60 -79
  179. data/lib/active_record/relation/query_attribute.rb +28 -2
  180. data/lib/active_record/relation/query_methods.rb +129 -100
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +4 -2
  183. data/lib/active_record/relation/where_clause.rb +65 -68
  184. data/lib/active_record/relation/where_clause_factory.rb +5 -48
  185. data/lib/active_record/relation.rb +120 -214
  186. data/lib/active_record/result.rb +2 -0
  187. data/lib/active_record/runtime_registry.rb +2 -0
  188. data/lib/active_record/sanitization.rb +129 -121
  189. data/lib/active_record/schema.rb +4 -2
  190. data/lib/active_record/schema_dumper.rb +36 -26
  191. data/lib/active_record/schema_migration.rb +2 -0
  192. data/lib/active_record/scoping/default.rb +8 -9
  193. data/lib/active_record/scoping/named.rb +23 -7
  194. data/lib/active_record/scoping.rb +9 -8
  195. data/lib/active_record/secure_token.rb +2 -0
  196. data/lib/active_record/serialization.rb +2 -0
  197. data/lib/active_record/statement_cache.rb +23 -13
  198. data/lib/active_record/store.rb +3 -1
  199. data/lib/active_record/suppressor.rb +2 -0
  200. data/lib/active_record/table_metadata.rb +12 -3
  201. data/lib/active_record/tasks/database_tasks.rb +26 -15
  202. data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
  203. data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
  204. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
  205. data/lib/active_record/timestamp.rb +13 -6
  206. data/lib/active_record/touch_later.rb +2 -0
  207. data/lib/active_record/transactions.rb +33 -28
  208. data/lib/active_record/translation.rb +2 -0
  209. data/lib/active_record/type/adapter_specific_registry.rb +2 -0
  210. data/lib/active_record/type/date.rb +2 -0
  211. data/lib/active_record/type/date_time.rb +2 -0
  212. data/lib/active_record/type/decimal_without_scale.rb +2 -0
  213. data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
  214. data/lib/active_record/type/internal/timezone.rb +2 -0
  215. data/lib/active_record/type/json.rb +30 -0
  216. data/lib/active_record/type/serialized.rb +6 -0
  217. data/lib/active_record/type/text.rb +2 -0
  218. data/lib/active_record/type/time.rb +2 -0
  219. data/lib/active_record/type/type_map.rb +2 -0
  220. data/lib/active_record/type/unsigned_integer.rb +2 -0
  221. data/lib/active_record/type.rb +4 -1
  222. data/lib/active_record/type_caster/connection.rb +2 -0
  223. data/lib/active_record/type_caster/map.rb +3 -1
  224. data/lib/active_record/type_caster.rb +2 -0
  225. data/lib/active_record/validations/absence.rb +2 -0
  226. data/lib/active_record/validations/associated.rb +2 -0
  227. data/lib/active_record/validations/length.rb +2 -0
  228. data/lib/active_record/validations/presence.rb +2 -0
  229. data/lib/active_record/validations/uniqueness.rb +36 -6
  230. data/lib/active_record/validations.rb +2 -0
  231. data/lib/active_record/version.rb +2 -0
  232. data/lib/active_record.rb +11 -4
  233. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  234. data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
  235. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
  236. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
  237. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
  238. data/lib/rails/generators/active_record/migration.rb +2 -0
  239. data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
  240. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
  241. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
  242. data/lib/rails/generators/active_record.rb +3 -1
  243. metadata +26 -40
  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/user_provided_default.rb +0 -30
  252. data/lib/active_record/attribute.rb +0 -240
  253. data/lib/active_record/attribute_mutation_tracker.rb +0 -114
  254. data/lib/active_record/attribute_set/builder.rb +0 -124
  255. data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
  256. data/lib/active_record/attribute_set.rb +0 -113
  257. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
  258. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  259. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
  260. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
  261. data/lib/active_record/type/internal/abstract_json.rb +0 -37
@@ -1,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
@@ -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
 
@@ -20,10 +20,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
20
20
  }
21
21
  end
22
22
 
23
- def self.define_extensions(model, name)
23
+ def self.define_extensions(model, name, &block)
24
24
  if block_given?
25
25
  extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
26
- extension = Module.new(&Proc.new)
26
+ extension = Module.new(&block)
27
27
  model.parent.const_set(extension_module_name, extension)
28
28
  end
29
29
  end
@@ -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
@@ -43,6 +45,8 @@ module ActiveRecord
43
45
  def ids_reader
44
46
  if loaded?
45
47
  target.pluck(reflection.association_primary_key)
48
+ elsif !target.empty?
49
+ load_target.pluck(reflection.association_primary_key)
46
50
  else
47
51
  @association_ids ||= scope.pluck(reflection.association_primary_key)
48
52
  end
@@ -50,17 +54,19 @@ module ActiveRecord
50
54
 
51
55
  # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
52
56
  def ids_writer(ids)
53
- pk_type = reflection.association_primary_key_type
57
+ primary_key = reflection.association_primary_key
58
+ pk_type = klass.type_for_attribute(primary_key)
54
59
  ids = Array(ids).reject(&:blank?)
55
60
  ids.map! { |i| pk_type.cast(i) }
56
61
 
57
- primary_key = reflection.association_primary_key
58
62
  records = klass.where(primary_key => ids).index_by do |r|
59
63
  r.public_send(primary_key)
60
64
  end.values_at(*ids).compact
61
65
 
62
66
  if records.size != ids.size
63
- klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, 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)
64
70
  else
65
71
  replace(records)
66
72
  end
@@ -69,26 +75,29 @@ module ActiveRecord
69
75
  def reset
70
76
  super
71
77
  @target = []
78
+ @association_ids = nil
72
79
  end
73
80
 
74
81
  def find(*args)
75
- if block_given?
76
- load_target.find(*args) { |*block_args| yield(*block_args) }
77
- else
78
- if options[:inverse_of] && loaded?
79
- args_flatten = args.flatten
80
- raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
81
- result = find_by_scan(*args)
82
-
83
- result_size = Array(result).size
84
- if !result || result_size != args_flatten.size
85
- scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
86
- else
87
- result
88
- 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)
89
96
  else
90
- scope.find(*args)
97
+ result
91
98
  end
99
+ else
100
+ scope.find(*args)
92
101
  end
93
102
  end
94
103
 
@@ -96,15 +105,12 @@ module ActiveRecord
96
105
  if attributes.is_a?(Array)
97
106
  attributes.collect { |attr| build(attr, &block) }
98
107
  else
99
- add_to_target(build_record(attributes)) do |record|
100
- yield(record) if block_given?
101
- end
108
+ add_to_target(build_record(attributes, &block))
102
109
  end
103
110
  end
104
111
 
105
- # Add +records+ to this association. Returns +self+ so method calls may
106
- # be chained. Since << flattens its argument list and inserts each record,
107
- # +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.
108
114
  def concat(*records)
109
115
  records = records.flatten
110
116
  if owner.new_record?
@@ -180,8 +186,6 @@ module ActiveRecord
180
186
  # are actually removed from the database, that depends precisely on
181
187
  # +delete_records+. They are in any case removed from the collection.
182
188
  def delete(*records)
183
- return if records.empty?
184
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
185
189
  delete_or_destroy(records, options[:dependent])
186
190
  end
187
191
 
@@ -191,8 +195,6 @@ module ActiveRecord
191
195
  # Note that this method removes records from the database ignoring the
192
196
  # +:dependent+ option.
193
197
  def destroy(*records)
194
- return if records.empty?
195
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
196
198
  delete_or_destroy(records, :destroy)
197
199
  end
198
200
 
@@ -300,18 +302,17 @@ module ActiveRecord
300
302
  private
301
303
 
302
304
  def find_target
303
- return scope.to_a if skip_statement_cache?
305
+ scope = self.scope
306
+ return scope.to_a if skip_statement_cache?(scope)
304
307
 
305
308
  conn = klass.connection
306
- sc = reflection.association_scope_cache(conn, owner) do
307
- StatementCache.create(conn) { |params|
308
- as = AssociationScope.create { params.bind }
309
- target_scope.merge as.scope(self, conn)
310
- }
309
+ sc = reflection.association_scope_cache(conn, owner) do |params|
310
+ as = AssociationScope.create { params.bind }
311
+ target_scope.merge!(as.scope(self))
311
312
  end
312
313
 
313
314
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
314
- sc.execute(binds, klass, conn) do |record|
315
+ sc.execute(binds, conn) do |record|
315
316
  set_inverse_instance(record)
316
317
  end
317
318
  end
@@ -354,12 +355,17 @@ module ActiveRecord
354
355
  if attributes.is_a?(Array)
355
356
  attributes.collect { |attr| _create_record(attr, raise, &block) }
356
357
  else
358
+ record = build_record(attributes, &block)
357
359
  transaction do
358
- add_to_target(build_record(attributes)) do |record|
359
- yield(record) if block_given?
360
- 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
+ }
361
365
  end
366
+ raise ActiveRecord::Rollback unless result
362
367
  end
368
+ record
363
369
  end
364
370
  end
365
371
 
@@ -372,11 +378,9 @@ module ActiveRecord
372
378
  end
373
379
  end
374
380
 
375
- def create_scope
376
- scope.scope_for_create.stringify_keys
377
- end
378
-
379
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) }
380
384
  records = records.flatten
381
385
  records.each { |record| raise_on_type_mismatch!(record) }
382
386
  existing_records = records.reject(&:new_record?)
@@ -393,6 +397,7 @@ module ActiveRecord
393
397
 
394
398
  delete_records(existing_records, method) if existing_records.any?
395
399
  records.each { |record| target.delete(record) }
400
+ @association_ids = nil
396
401
 
397
402
  records.each { |record| callback(:after_remove, record) }
398
403
  end
@@ -405,9 +410,9 @@ module ActiveRecord
405
410
  end
406
411
 
407
412
  def replace_records(new_target, original_target)
408
- delete(target - new_target)
413
+ delete(difference(target, new_target))
409
414
 
410
- unless concat(new_target - target)
415
+ unless concat(difference(new_target, target))
411
416
  @target = original_target
412
417
  raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
413
418
  "new records could not be saved."
@@ -417,7 +422,7 @@ module ActiveRecord
417
422
  end
418
423
 
419
424
  def replace_common_records_in_memory(new_target, original_target)
420
- common_records = new_target & original_target
425
+ common_records = intersection(new_target, original_target)
421
426
  common_records.each do |record|
422
427
  skip_callbacks = true
423
428
  replace_on_target(record, @target.index(record), skip_callbacks)
@@ -430,11 +435,17 @@ module ActiveRecord
430
435
  records.each do |record|
431
436
  raise_on_type_mismatch!(record)
432
437
  add_to_target(record) do
433
- 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
434
443
  end
435
444
  end
436
445
 
437
- result && records
446
+ raise ActiveRecord::Rollback unless result
447
+
448
+ records
438
449
  end
439
450
 
440
451
  def replace_on_target(record, index, skip_callbacks)
@@ -449,6 +460,7 @@ module ActiveRecord
449
460
  if index
450
461
  target[index] = record
451
462
  elsif @_was_loaded || !loaded?
463
+ @association_ids = nil
452
464
  target << record
453
465
  end
454
466
 
@@ -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,7 @@ 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
34
36
 
35
37
  extensions = association.extensions
36
38
  extend(*extensions) if extensions.any?
@@ -133,8 +135,9 @@ module ActiveRecord
133
135
  # # #<Pet id: 2, name: "Spook", person_id: 1>,
134
136
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
135
137
  # # ]
136
- def find(*args, &block)
137
- @association.find(*args, &block)
138
+ def find(*args)
139
+ return super if block_given?
140
+ @association.find(*args)
138
141
  end
139
142
 
140
143
  ##
@@ -363,34 +366,6 @@ module ActiveRecord
363
366
  @association.create!(attributes, &block)
364
367
  end
365
368
 
366
- # Add one or more records to the collection by setting their foreign keys
367
- # to the association's primary key. Since #<< flattens its argument list and
368
- # inserts each record, +push+ and #concat behave identically. Returns +self+
369
- # so method calls may be chained.
370
- #
371
- # class Person < ActiveRecord::Base
372
- # has_many :pets
373
- # end
374
- #
375
- # person.pets.size # => 0
376
- # person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
377
- # person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
378
- # person.pets.size # => 3
379
- #
380
- # person.id # => 1
381
- # person.pets
382
- # # => [
383
- # # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
384
- # # #<Pet id: 2, name: "Spook", person_id: 1>,
385
- # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
386
- # # ]
387
- #
388
- # person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
389
- # person.pets.size # => 5
390
- def concat(*records)
391
- @association.concat(*records)
392
- end
393
-
394
369
  # Replaces this collection with +other_array+. This will perform a diff
395
370
  # and delete/add only records that have changed.
396
371
  #
@@ -497,7 +472,7 @@ module ActiveRecord
497
472
  # Pet.find(1, 2, 3)
498
473
  # # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (1, 2, 3)
499
474
  def delete_all(dependent = nil)
500
- @association.delete_all(dependent)
475
+ @association.delete_all(dependent).tap { reset_scope }
501
476
  end
502
477
 
503
478
  # Deletes the records of the collection directly from the database
@@ -524,7 +499,7 @@ module ActiveRecord
524
499
  #
525
500
  # Pet.find(1) # => Couldn't find Pet with id=1
526
501
  def destroy_all
527
- @association.destroy_all
502
+ @association.destroy_all.tap { reset_scope }
528
503
  end
529
504
 
530
505
  # Deletes the +records+ supplied from the collection according to the strategy
@@ -643,7 +618,7 @@ module ActiveRecord
643
618
  # # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
644
619
  # # ]
645
620
  def delete(*records)
646
- @association.delete(*records)
621
+ @association.delete(*records).tap { reset_scope }
647
622
  end
648
623
 
649
624
  # Destroys the +records+ supplied and removes them from the collection.
@@ -715,7 +690,7 @@ module ActiveRecord
715
690
  #
716
691
  # Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with 'id': (4, 5, 6)
717
692
  def destroy(*records)
718
- @association.destroy(*records)
693
+ @association.destroy(*records).tap { reset_scope }
719
694
  end
720
695
 
721
696
  ##
@@ -746,10 +721,6 @@ module ActiveRecord
746
721
  # # ]
747
722
 
748
723
  #--
749
- def uniq
750
- load_target.uniq
751
- end
752
-
753
724
  def calculate(operation, column_name)
754
725
  null_scope? ? scope.calculate(operation, column_name) : super
755
726
  end
@@ -989,6 +960,12 @@ module ActiveRecord
989
960
  load_target == other
990
961
  end
991
962
 
963
+ ##
964
+ # :method: to_ary
965
+ #
966
+ # :call-seq:
967
+ # to_ary()
968
+ #
992
969
  # Returns a new array of objects from the collection. If the collection
993
970
  # hasn't been loaded, it fetches the records from the database.
994
971
  #
@@ -1022,18 +999,15 @@ module ActiveRecord
1022
999
  # # #<Pet id: 5, name: "Brain", person_id: 1>,
1023
1000
  # # #<Pet id: 6, name: "Boss", person_id: 1>
1024
1001
  # # ]
1025
- def to_ary
1026
- load_target.dup
1027
- end
1028
- alias_method :to_a, :to_ary
1029
1002
 
1030
1003
  def records # :nodoc:
1031
1004
  load_target
1032
1005
  end
1033
1006
 
1034
1007
  # Adds one or more +records+ to the collection by setting their foreign keys
1035
- # to the association's primary key. Returns +self+, so several appends may be
1036
- # chained together.
1008
+ # to the association's primary key. Since +<<+ flattens its argument list and
1009
+ # inserts each record, +push+ and +concat+ behave identically. Returns +self+
1010
+ # so several appends may be chained together.
1037
1011
  #
1038
1012
  # class Person < ActiveRecord::Base
1039
1013
  # has_many :pets
@@ -1056,6 +1030,7 @@ module ActiveRecord
1056
1030
  end
1057
1031
  alias_method :push, :<<
1058
1032
  alias_method :append, :<<
1033
+ alias_method :concat, :<<
1059
1034
 
1060
1035
  def prepend(*args)
1061
1036
  raise NoMethodError, "prepend on association is not defined. Please use <<, push or append"
@@ -1073,7 +1048,6 @@ module ActiveRecord
1073
1048
  end
1074
1049
 
1075
1050
  # Reloads the collection from the database. Returns +self+.
1076
- # Equivalent to <tt>collection(true)</tt>.
1077
1051
  #
1078
1052
  # class Person < ActiveRecord::Base
1079
1053
  # has_many :pets
@@ -1087,9 +1061,6 @@ module ActiveRecord
1087
1061
  #
1088
1062
  # person.pets.reload # fetches pets from the database
1089
1063
  # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1090
- #
1091
- # person.pets(true) # fetches pets from the database
1092
- # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
1093
1064
  def reload
1094
1065
  proxy_association.reload
1095
1066
  reset_scope
@@ -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
@@ -97,6 +99,7 @@ module ActiveRecord
97
99
  def delete_or_nullify_all_records(method)
98
100
  count = delete_count(method, scope)
99
101
  update_counter(-count)
102
+ count
100
103
  end
101
104
 
102
105
  # Deletes the records according to the <tt>:dependent</tt> option.
@@ -128,6 +131,14 @@ module ActiveRecord
128
131
  end
129
132
  saved_successfully
130
133
  end
134
+
135
+ def difference(a, b)
136
+ a - b
137
+ end
138
+
139
+ def intersection(a, b)
140
+ a & b
141
+ end
131
142
  end
132
143
  end
133
144
  end