activerecord 5.1.5 → 5.2.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 +450 -699
  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 +4 -2
  9. data/lib/active_record/associations/alias_tracker.rb +19 -27
  10. data/lib/active_record/associations/association.rb +33 -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 +1 -1
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +2 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +52 -41
  22. data/lib/active_record/associations/collection_proxy.rb +12 -15
  23. data/lib/active_record/associations/foreign_association.rb +2 -0
  24. data/lib/active_record/associations/has_many_association.rb +3 -1
  25. data/lib/active_record/associations/has_many_through_association.rb +8 -19
  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 +22 -67
  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 +17 -37
  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 +25 -214
  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 +16 -14
  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 +2 -0
  55. data/lib/active_record/collection_cache_key.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +110 -35
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +157 -29
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
  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 +149 -78
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -21
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +81 -96
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +92 -165
  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 +2 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +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 +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.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 +3 -1
  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 +14 -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 +248 -112
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +57 -73
  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 +20 -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 +79 -92
  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 +39 -60
  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 +17 -13
  133. data/lib/active_record/errors.rb +42 -3
  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 +4 -2
  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 +40 -2
  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 +196 -48
  156. data/lib/active_record/query_cache.rb +12 -14
  157. data/lib/active_record/querying.rb +3 -1
  158. data/lib/active_record/railtie.rb +61 -3
  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 +110 -192
  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 +31 -9
  167. data/lib/active_record/relation/delegation.rb +15 -27
  168. data/lib/active_record/relation/finder_methods.rb +71 -76
  169. data/lib/active_record/relation/from_clause.rb +2 -8
  170. data/lib/active_record/relation/merger.rb +47 -20
  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 +55 -79
  179. data/lib/active_record/relation/query_attribute.rb +26 -2
  180. data/lib/active_record/relation/query_methods.rb +95 -91
  181. data/lib/active_record/relation/record_fetch_warning.rb +2 -0
  182. data/lib/active_record/relation/spawn_methods.rb +3 -1
  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 +106 -219
  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 +6 -7
  193. data/lib/active_record/scoping/named.rb +21 -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 +22 -12
  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 +25 -14
  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 +32 -27
  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 +23 -36
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -17
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -15
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -15
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -18
  251. data/lib/active_record/attribute/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,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
 
@@ -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
@@ -50,17 +52,19 @@ module ActiveRecord
50
52
 
51
53
  # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
52
54
  def ids_writer(ids)
53
- pk_type = reflection.association_primary_key_type
55
+ primary_key = reflection.association_primary_key
56
+ pk_type = klass.type_for_attribute(primary_key)
54
57
  ids = Array(ids).reject(&:blank?)
55
58
  ids.map! { |i| pk_type.cast(i) }
56
59
 
57
- primary_key = reflection.association_primary_key
58
60
  records = klass.where(primary_key => ids).index_by do |r|
59
61
  r.public_send(primary_key)
60
62
  end.values_at(*ids).compact
61
63
 
62
64
  if records.size != ids.size
63
- klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key)
65
+ found_ids = records.map { |record| record.public_send(primary_key) }
66
+ not_found_ids = ids - found_ids
67
+ klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
64
68
  else
65
69
  replace(records)
66
70
  end
@@ -69,26 +73,29 @@ module ActiveRecord
69
73
  def reset
70
74
  super
71
75
  @target = []
76
+ @association_ids = nil
72
77
  end
73
78
 
74
79
  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
80
+ if options[:inverse_of] && loaded?
81
+ args_flatten = args.flatten
82
+ model = scope.klass
83
+
84
+ if args_flatten.blank?
85
+ error_message = "Couldn't find #{model.name} without an ID"
86
+ raise RecordNotFound.new(error_message, model.name, model.primary_key, args)
87
+ end
88
+
89
+ result = find_by_scan(*args)
90
+
91
+ result_size = Array(result).size
92
+ if !result || result_size != args_flatten.size
93
+ scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
89
94
  else
90
- scope.find(*args)
95
+ result
91
96
  end
97
+ else
98
+ scope.find(*args)
92
99
  end
93
100
  end
94
101
 
@@ -96,9 +103,7 @@ module ActiveRecord
96
103
  if attributes.is_a?(Array)
97
104
  attributes.collect { |attr| build(attr, &block) }
98
105
  else
99
- add_to_target(build_record(attributes)) do |record|
100
- yield(record) if block_given?
101
- end
106
+ add_to_target(build_record(attributes, &block))
102
107
  end
103
108
  end
104
109
 
@@ -180,8 +185,6 @@ module ActiveRecord
180
185
  # are actually removed from the database, that depends precisely on
181
186
  # +delete_records+. They are in any case removed from the collection.
182
187
  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
188
  delete_or_destroy(records, options[:dependent])
186
189
  end
187
190
 
@@ -191,8 +194,6 @@ module ActiveRecord
191
194
  # Note that this method removes records from the database ignoring the
192
195
  # +:dependent+ option.
193
196
  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
197
  delete_or_destroy(records, :destroy)
197
198
  end
198
199
 
@@ -300,18 +301,17 @@ module ActiveRecord
300
301
  private
301
302
 
302
303
  def find_target
303
- return scope.to_a if skip_statement_cache?
304
+ scope = self.scope
305
+ return scope.to_a if skip_statement_cache?(scope)
304
306
 
305
307
  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
- }
308
+ sc = reflection.association_scope_cache(conn, owner) do |params|
309
+ as = AssociationScope.create { params.bind }
310
+ target_scope.merge!(as.scope(self))
311
311
  end
312
312
 
313
313
  binds = AssociationScope.get_bind_values(owner, reflection.chain)
314
- sc.execute(binds, klass, conn) do |record|
314
+ sc.execute(binds, conn) do |record|
315
315
  set_inverse_instance(record)
316
316
  end
317
317
  end
@@ -354,12 +354,18 @@ module ActiveRecord
354
354
  if attributes.is_a?(Array)
355
355
  attributes.collect { |attr| _create_record(attr, raise, &block) }
356
356
  else
357
+ record = build_record(attributes, &block)
357
358
  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? }
359
+ result = nil
360
+ add_to_target(record) do
361
+ result = insert_record(record, true, raise) {
362
+ @_was_loaded = loaded?
363
+ @association_ids = nil
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?)
@@ -430,11 +434,18 @@ module ActiveRecord
430
434
  records.each do |record|
431
435
  raise_on_type_mismatch!(record)
432
436
  add_to_target(record) do
433
- result &&= insert_record(record, true, raise) { @_was_loaded = loaded? } unless owner.new_record?
437
+ unless owner.new_record?
438
+ result &&= insert_record(record, true, raise) {
439
+ @_was_loaded = loaded?
440
+ @association_ids = nil
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)
@@ -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
  ##
@@ -746,10 +749,6 @@ module ActiveRecord
746
749
  # # ]
747
750
 
748
751
  #--
749
- def uniq
750
- load_target.uniq
751
- end
752
-
753
752
  def calculate(operation, column_name)
754
753
  null_scope? ? scope.calculate(operation, column_name) : super
755
754
  end
@@ -989,6 +988,12 @@ module ActiveRecord
989
988
  load_target == other
990
989
  end
991
990
 
991
+ ##
992
+ # :method: to_ary
993
+ #
994
+ # :call-seq:
995
+ # to_ary()
996
+ #
992
997
  # Returns a new array of objects from the collection. If the collection
993
998
  # hasn't been loaded, it fetches the records from the database.
994
999
  #
@@ -1022,10 +1027,6 @@ module ActiveRecord
1022
1027
  # # #<Pet id: 5, name: "Brain", person_id: 1>,
1023
1028
  # # #<Pet id: 6, name: "Boss", person_id: 1>
1024
1029
  # # ]
1025
- def to_ary
1026
- load_target.dup
1027
- end
1028
- alias_method :to_a, :to_ary
1029
1030
 
1030
1031
  def records # :nodoc:
1031
1032
  load_target
@@ -1073,7 +1074,6 @@ module ActiveRecord
1073
1074
  end
1074
1075
 
1075
1076
  # Reloads the collection from the database. Returns +self+.
1076
- # Equivalent to <tt>collection(true)</tt>.
1077
1077
  #
1078
1078
  # class Person < ActiveRecord::Base
1079
1079
  # has_many :pets
@@ -1087,9 +1087,6 @@ module ActiveRecord
1087
1087
  #
1088
1088
  # person.pets.reload # fetches pets from the database
1089
1089
  # # => [#<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
1090
  def reload
1094
1091
  proxy_association.reload
1095
1092
  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
@@ -1,14 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Has Many Through Association
3
4
  module Associations
5
+ # = Active Record Has Many Through Association
4
6
  class HasManyThroughAssociation < HasManyAssociation #:nodoc:
5
7
  include ThroughAssociation
6
8
 
7
9
  def initialize(owner, reflection)
8
10
  super
9
-
10
- @through_records = {}
11
- @through_association = nil
11
+ @through_records = {}
12
12
  end
13
13
 
14
14
  def concat(*records)
@@ -48,11 +48,6 @@ module ActiveRecord
48
48
  end
49
49
 
50
50
  private
51
-
52
- def through_association
53
- @through_association ||= owner.association(through_reflection.name)
54
- end
55
-
56
51
  # The through record (built with build_record) is temporarily cached
57
52
  # so that it may be reused if insert_record is subsequently called.
58
53
  #
@@ -95,7 +90,7 @@ module ActiveRecord
95
90
  def build_record(attributes)
96
91
  ensure_not_nested
97
92
 
98
- record = super(attributes)
93
+ record = super
99
94
 
100
95
  inverse = source_reflection.inverse_of
101
96
  if inverse
@@ -138,21 +133,15 @@ module ActiveRecord
138
133
 
139
134
  scope = through_association.scope
140
135
  scope.where! construct_join_attributes(*records)
136
+ scope = scope.where(through_scope_attributes)
141
137
 
142
138
  case method
143
139
  when :destroy
144
140
  if scope.klass.primary_key
145
- count = scope.destroy_all.length
141
+ count = scope.destroy_all.count(&:destroyed?)
146
142
  else
147
143
  scope.each(&:_run_destroy_callbacks)
148
-
149
- arel = scope.arel
150
-
151
- stmt = Arel::DeleteManager.new
152
- stmt.from scope.klass.arel_table
153
- stmt.wheres = arel.constraints
154
-
155
- count = scope.klass.connection.delete(stmt, "SQL", scope.bound_attributes)
144
+ count = scope.delete_all
156
145
  end
157
146
  when :nullify
158
147
  count = scope.update_all(source_reflection.foreign_key => nil)
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Has One Association
3
4
  module Associations
5
+ # = Active Record Has One Association
4
6
  class HasOneAssociation < SingularAssociation #:nodoc:
5
7
  include ForeignAssociation
6
8
 
@@ -58,6 +60,7 @@ module ActiveRecord
58
60
  when :destroy
59
61
  target.destroyed_by_association = reflection
60
62
  target.destroy
63
+ throw(:abort) unless target.destroyed?
61
64
  when :nullify
62
65
  target.update_columns(reflection.foreign_key => nil) if target.persisted?
63
66
  end
@@ -104,6 +107,14 @@ module ActiveRecord
104
107
  yield
105
108
  end
106
109
  end
110
+
111
+ def _create_record(attributes, raise_error = false, &block)
112
+ unless owner.persisted?
113
+ raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
114
+ end
115
+
116
+ super
117
+ end
107
118
  end
108
119
  end
109
120
  end
@@ -1,20 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
- # = Active Record Has One Through Association
3
4
  module Associations
5
+ # = Active Record Has One Through Association
4
6
  class HasOneThroughAssociation < HasOneAssociation #:nodoc:
5
7
  include ThroughAssociation
6
8
 
7
- def replace(record)
8
- create_through_record(record)
9
+ def replace(record, save = true)
10
+ create_through_record(record, save)
9
11
  self.target = record
10
12
  end
11
13
 
12
14
  private
13
-
14
- def create_through_record(record)
15
+ def create_through_record(record, save)
15
16
  ensure_not_nested
16
17
 
17
- through_proxy = owner.association(through_reflection.name)
18
+ through_proxy = through_association
18
19
  through_record = through_proxy.load_target
19
20
 
20
21
  if through_record && !record
@@ -27,8 +28,12 @@ module ActiveRecord
27
28
  end
28
29
 
29
30
  if through_record
30
- through_record.update(attributes)
31
- elsif owner.new_record?
31
+ if through_record.new_record?
32
+ through_record.assign_attributes(attributes)
33
+ else
34
+ through_record.update(attributes)
35
+ end
36
+ elsif owner.new_record? || !save
32
37
  through_proxy.build(attributes)
33
38
  else
34
39
  through_proxy.create(attributes)
@@ -1,19 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_record/associations/join_dependency/join_part"
2
4
 
3
5
  module ActiveRecord
4
6
  module Associations
5
7
  class JoinDependency # :nodoc:
6
8
  class JoinAssociation < JoinPart # :nodoc:
7
- # The reflection of the association represented
8
- attr_reader :reflection
9
-
10
- attr_accessor :tables
9
+ attr_reader :reflection, :tables
10
+ attr_accessor :table
11
11
 
12
12
  def initialize(reflection, children)
13
13
  super(reflection.klass, children)
14
14
 
15
- @reflection = reflection
16
- @tables = nil
15
+ @reflection = reflection
16
+ @tables = nil
17
17
  end
18
18
 
19
19
  def match?(other)
@@ -21,83 +21,38 @@ module ActiveRecord
21
21
  super && reflection == other.reflection
22
22
  end
23
23
 
24
- JoinInformation = Struct.new :joins, :binds
25
-
26
- def join_constraints(foreign_table, foreign_klass, join_type, tables, chain)
27
- joins = []
28
- binds = []
29
- tables = tables.reverse
24
+ def join_constraints(foreign_table, foreign_klass, join_type, alias_tracker)
25
+ joins = []
30
26
 
31
27
  # The chain starts with the target table, but we want to end with it here (makes
32
28
  # more sense in this context), so we reverse
33
- chain.reverse_each do |reflection|
34
- table = tables.shift
29
+ reflection.chain.reverse_each.with_index(1) do |reflection, i|
30
+ table = tables[-i]
35
31
  klass = reflection.klass
36
32
 
37
- join_keys = reflection.join_keys
38
- key = join_keys.key
39
- foreign_key = join_keys.foreign_key
40
-
41
- constraint = build_constraint(klass, table, key, foreign_table, foreign_key)
33
+ constraint = reflection.build_join_constraint(table, foreign_table)
42
34
 
43
- rel = reflection.join_scope(table)
44
-
45
- if rel && !rel.arel.constraints.empty?
46
- binds += rel.bound_attributes
47
- constraint = constraint.and rel.arel.constraints
48
- end
35
+ joins << table.create_join(table, table.create_on(constraint), join_type)
49
36
 
50
- if reflection.type
51
- value = foreign_klass.base_class.name
52
- column = klass.columns_hash[reflection.type.to_s]
37
+ join_scope = reflection.join_scope(table, foreign_klass)
38
+ arel = join_scope.arel(alias_tracker.aliases)
53
39
 
54
- binds << Relation::QueryAttribute.new(column.name, value, klass.type_for_attribute(column.name))
55
- constraint = constraint.and klass.arel_attribute(reflection.type, table).eq(Arel::Nodes::BindParam.new)
40
+ if arel.constraints.any?
41
+ joins.concat arel.join_sources
42
+ right = joins.last.right
43
+ right.expr = right.expr.and(arel.constraints)
56
44
  end
57
45
 
58
- joins << table.create_join(table, table.create_on(constraint), join_type)
59
-
60
46
  # The current table in this iteration becomes the foreign table in the next
61
47
  foreign_table, foreign_klass = table, klass
62
48
  end
63
49
 
64
- JoinInformation.new joins, binds
65
- end
66
-
67
- # Builds equality condition.
68
- #
69
- # Example:
70
- #
71
- # class Physician < ActiveRecord::Base
72
- # has_many :appointments
73
- # end
74
- #
75
- # If I execute `Physician.joins(:appointments).to_a` then
76
- # klass # => Physician
77
- # table # => #<Arel::Table @name="appointments" ...>
78
- # key # => physician_id
79
- # foreign_table # => #<Arel::Table @name="physicians" ...>
80
- # foreign_key # => id
81
- #
82
- def build_constraint(klass, table, key, foreign_table, foreign_key)
83
- constraint = table[key].eq(foreign_table[foreign_key])
84
-
85
- if klass.finder_needs_type_condition?
86
- constraint = table.create_and([
87
- constraint,
88
- klass.send(:type_condition, table)
89
- ])
90
- end
91
-
92
- constraint
93
- end
94
-
95
- def table
96
- tables.first
50
+ joins
97
51
  end
98
52
 
99
- def aliased_table_name
100
- table.table_alias || table.name
53
+ def tables=(tables)
54
+ @tables = tables
55
+ @table = tables.first
101
56
  end
102
57
  end
103
58
  end