activerecord 3.2.19 → 5.0.0

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 (264) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1715 -604
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +40 -45
  5. data/examples/performance.rb +33 -22
  6. data/examples/simple.rb +3 -4
  7. data/lib/active_record/aggregations.rb +76 -51
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +54 -40
  10. data/lib/active_record/associations/association.rb +76 -56
  11. data/lib/active_record/associations/association_scope.rb +125 -93
  12. data/lib/active_record/associations/belongs_to_association.rb +57 -28
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +120 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +115 -62
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -53
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
  18. data/lib/active_record/associations/builder/has_many.rb +9 -65
  19. data/lib/active_record/associations/builder/has_one.rb +18 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +18 -19
  21. data/lib/active_record/associations/collection_association.rb +268 -186
  22. data/lib/active_record/associations/collection_proxy.rb +1003 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +81 -41
  25. data/lib/active_record/associations/has_many_through_association.rb +76 -55
  26. data/lib/active_record/associations/has_one_association.rb +51 -21
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +239 -155
  32. data/lib/active_record/associations/preloader/association.rb +97 -62
  33. data/lib/active_record/associations/preloader/collection_association.rb +2 -8
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +75 -33
  38. data/lib/active_record/associations/preloader.rb +111 -79
  39. data/lib/active_record/associations/singular_association.rb +35 -13
  40. data/lib/active_record/associations/through_association.rb +41 -19
  41. data/lib/active_record/associations.rb +727 -501
  42. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  43. data/lib/active_record/attribute.rb +213 -0
  44. data/lib/active_record/attribute_assignment.rb +32 -162
  45. data/lib/active_record/attribute_decorators.rb +67 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  47. data/lib/active_record/attribute_methods/dirty.rb +101 -61
  48. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  49. data/lib/active_record/attribute_methods/query.rb +7 -6
  50. data/lib/active_record/attribute_methods/read.rb +56 -117
  51. data/lib/active_record/attribute_methods/serialization.rb +43 -96
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
  53. data/lib/active_record/attribute_methods/write.rb +34 -45
  54. data/lib/active_record/attribute_methods.rb +333 -144
  55. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  56. data/lib/active_record/attribute_set/builder.rb +108 -0
  57. data/lib/active_record/attribute_set.rb +108 -0
  58. data/lib/active_record/attributes.rb +265 -0
  59. data/lib/active_record/autosave_association.rb +285 -223
  60. data/lib/active_record/base.rb +95 -490
  61. data/lib/active_record/callbacks.rb +95 -61
  62. data/lib/active_record/coders/json.rb +13 -0
  63. data/lib/active_record/coders/yaml_column.rb +28 -19
  64. data/lib/active_record/collection_cache_key.rb +40 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
  78. data/lib/active_record/connection_adapters/column.rb +30 -259
  79. data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
  80. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  81. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  82. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  83. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  84. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  89. data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
  90. data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
  91. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
  92. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
  115. data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
  116. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
  117. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
  118. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  119. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
  120. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  121. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  122. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
  123. data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
  124. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  125. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
  129. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  130. data/lib/active_record/connection_handling.rb +155 -0
  131. data/lib/active_record/core.rb +561 -0
  132. data/lib/active_record/counter_cache.rb +146 -105
  133. data/lib/active_record/dynamic_matchers.rb +101 -64
  134. data/lib/active_record/enum.rb +234 -0
  135. data/lib/active_record/errors.rb +153 -56
  136. data/lib/active_record/explain.rb +15 -63
  137. data/lib/active_record/explain_registry.rb +30 -0
  138. data/lib/active_record/explain_subscriber.rb +10 -6
  139. data/lib/active_record/fixture_set/file.rb +77 -0
  140. data/lib/active_record/fixtures.rb +355 -232
  141. data/lib/active_record/gem_version.rb +15 -0
  142. data/lib/active_record/inheritance.rb +144 -79
  143. data/lib/active_record/integration.rb +66 -13
  144. data/lib/active_record/internal_metadata.rb +56 -0
  145. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  146. data/lib/active_record/locale/en.yml +9 -1
  147. data/lib/active_record/locking/optimistic.rb +77 -56
  148. data/lib/active_record/locking/pessimistic.rb +6 -6
  149. data/lib/active_record/log_subscriber.rb +53 -28
  150. data/lib/active_record/migration/command_recorder.rb +166 -33
  151. data/lib/active_record/migration/compatibility.rb +126 -0
  152. data/lib/active_record/migration/join_table.rb +15 -0
  153. data/lib/active_record/migration.rb +792 -264
  154. data/lib/active_record/model_schema.rb +192 -130
  155. data/lib/active_record/nested_attributes.rb +238 -145
  156. data/lib/active_record/no_touching.rb +52 -0
  157. data/lib/active_record/null_relation.rb +89 -0
  158. data/lib/active_record/persistence.rb +357 -157
  159. data/lib/active_record/query_cache.rb +22 -43
  160. data/lib/active_record/querying.rb +34 -23
  161. data/lib/active_record/railtie.rb +88 -48
  162. data/lib/active_record/railties/console_sandbox.rb +3 -4
  163. data/lib/active_record/railties/controller_runtime.rb +5 -4
  164. data/lib/active_record/railties/databases.rake +170 -422
  165. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  166. data/lib/active_record/readonly_attributes.rb +2 -5
  167. data/lib/active_record/reflection.rb +715 -189
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  169. data/lib/active_record/relation/batches.rb +203 -50
  170. data/lib/active_record/relation/calculations.rb +203 -194
  171. data/lib/active_record/relation/delegation.rb +103 -25
  172. data/lib/active_record/relation/finder_methods.rb +457 -261
  173. data/lib/active_record/relation/from_clause.rb +32 -0
  174. data/lib/active_record/relation/merger.rb +167 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  179. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  180. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  181. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  182. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  183. data/lib/active_record/relation/predicate_builder.rb +153 -48
  184. data/lib/active_record/relation/query_attribute.rb +19 -0
  185. data/lib/active_record/relation/query_methods.rb +1019 -194
  186. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  187. data/lib/active_record/relation/spawn_methods.rb +46 -150
  188. data/lib/active_record/relation/where_clause.rb +174 -0
  189. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  190. data/lib/active_record/relation.rb +450 -245
  191. data/lib/active_record/result.rb +104 -12
  192. data/lib/active_record/runtime_registry.rb +22 -0
  193. data/lib/active_record/sanitization.rb +120 -94
  194. data/lib/active_record/schema.rb +28 -18
  195. data/lib/active_record/schema_dumper.rb +141 -74
  196. data/lib/active_record/schema_migration.rb +50 -0
  197. data/lib/active_record/scoping/default.rb +64 -57
  198. data/lib/active_record/scoping/named.rb +93 -108
  199. data/lib/active_record/scoping.rb +73 -121
  200. data/lib/active_record/secure_token.rb +38 -0
  201. data/lib/active_record/serialization.rb +7 -5
  202. data/lib/active_record/statement_cache.rb +113 -0
  203. data/lib/active_record/store.rb +173 -15
  204. data/lib/active_record/suppressor.rb +58 -0
  205. data/lib/active_record/table_metadata.rb +68 -0
  206. data/lib/active_record/tasks/database_tasks.rb +313 -0
  207. data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
  208. data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
  209. data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
  210. data/lib/active_record/timestamp.rb +42 -24
  211. data/lib/active_record/touch_later.rb +58 -0
  212. data/lib/active_record/transactions.rb +233 -105
  213. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  214. data/lib/active_record/type/date.rb +7 -0
  215. data/lib/active_record/type/date_time.rb +7 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  217. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  218. data/lib/active_record/type/internal/timezone.rb +15 -0
  219. data/lib/active_record/type/serialized.rb +63 -0
  220. data/lib/active_record/type/time.rb +20 -0
  221. data/lib/active_record/type/type_map.rb +64 -0
  222. data/lib/active_record/type.rb +72 -0
  223. data/lib/active_record/type_caster/connection.rb +29 -0
  224. data/lib/active_record/type_caster/map.rb +19 -0
  225. data/lib/active_record/type_caster.rb +7 -0
  226. data/lib/active_record/validations/absence.rb +23 -0
  227. data/lib/active_record/validations/associated.rb +33 -18
  228. data/lib/active_record/validations/length.rb +24 -0
  229. data/lib/active_record/validations/presence.rb +66 -0
  230. data/lib/active_record/validations/uniqueness.rb +128 -68
  231. data/lib/active_record/validations.rb +48 -40
  232. data/lib/active_record/version.rb +5 -7
  233. data/lib/active_record.rb +71 -47
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
  235. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
  236. data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
  237. data/lib/rails/generators/active_record/migration.rb +18 -8
  238. data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
  239. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  240. data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
  241. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  242. data/lib/rails/generators/active_record.rb +3 -11
  243. metadata +188 -134
  244. data/examples/associations.png +0 -0
  245. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  246. data/lib/active_record/associations/join_helper.rb +0 -55
  247. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  248. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  249. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  250. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  251. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  252. data/lib/active_record/dynamic_finder_match.rb +0 -68
  253. data/lib/active_record/dynamic_scope_match.rb +0 -23
  254. data/lib/active_record/fixtures/file.rb +0 -65
  255. data/lib/active_record/identity_map.rb +0 -162
  256. data/lib/active_record/observer.rb +0 -121
  257. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  258. data/lib/active_record/session_store.rb +0 -360
  259. data/lib/active_record/test_case.rb +0 -73
  260. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  261. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  262. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  263. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  264. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -0,0 +1,11 @@
1
+ module ActiveRecord::Associations
2
+ module ForeignAssociation # :nodoc:
3
+ def foreign_key_present?
4
+ if reflection.klass.primary_key
5
+ owner.attribute_present?(reflection.active_record_primary_key)
6
+ else
7
+ false
8
+ end
9
+ end
10
+ end
11
+ end
@@ -6,6 +6,37 @@ module ActiveRecord
6
6
  # If the association has a <tt>:through</tt> option further specialization
7
7
  # is provided by its child HasManyThroughAssociation.
8
8
  class HasManyAssociation < CollectionAssociation #:nodoc:
9
+ include ForeignAssociation
10
+
11
+ def handle_dependency
12
+ case options[:dependent]
13
+ when :restrict_with_exception
14
+ raise ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty?
15
+
16
+ when :restrict_with_error
17
+ unless empty?
18
+ record = owner.class.human_attribute_name(reflection.name).downcase
19
+ message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.many', record: record, raise: true) rescue nil
20
+ if message
21
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
22
+ The error key `:'restrict_dependent_destroy.many'` has been deprecated and will be removed in Rails 5.1.
23
+ Please use `:'restrict_dependent_destroy.has_many'` instead.
24
+ MESSAGE
25
+ end
26
+ owner.errors.add(:base, message || :'restrict_dependent_destroy.has_many', record: record)
27
+ throw(:abort)
28
+ end
29
+
30
+ else
31
+ if options[:dependent] == :destroy
32
+ # No point in executing the counter update since we're going to destroy the parent anyway
33
+ load_target.each { |t| t.destroyed_by_association = reflection }
34
+ destroy_all
35
+ else
36
+ delete_all
37
+ end
38
+ end
39
+ end
9
40
 
10
41
  def insert_record(record, validate = true, raise = false)
11
42
  set_owner_attributes(record)
@@ -18,6 +49,14 @@ module ActiveRecord
18
49
  end
19
50
  end
20
51
 
52
+ def empty?
53
+ if reflection.has_cached_counter?
54
+ size.zero?
55
+ else
56
+ super
57
+ end
58
+ end
59
+
21
60
  private
22
61
 
23
62
  # Returns the number of records in this collection.
@@ -34,12 +73,10 @@ module ActiveRecord
34
73
  # If the collection is empty the target is set to an empty array and
35
74
  # the loaded flag is set to true as well.
36
75
  def count_records
37
- count = if has_cached_counter?
38
- owner.send(:read_attribute, cached_counter_attribute_name)
39
- elsif options[:counter_sql] || options[:finder_sql]
40
- reflection.klass.count_by_sql(custom_counter_sql)
76
+ count = if reflection.has_cached_counter?
77
+ owner._read_attribute reflection.counter_cache_column
41
78
  else
42
- scoped.count
79
+ scope.count
43
80
  end
44
81
 
45
82
  # If there's nothing in the database and @target has no new records
@@ -47,61 +84,64 @@ module ActiveRecord
47
84
  # documented side-effect of the method that may avoid an extra SELECT.
48
85
  @target ||= [] and loaded! if count == 0
49
86
 
50
- [options[:limit], count].compact.min
87
+ [association_scope.limit_value, count].compact.min
51
88
  end
52
89
 
53
- def has_cached_counter?(reflection = reflection)
54
- owner.attribute_present?(cached_counter_attribute_name(reflection))
90
+ def update_counter(difference, reflection = reflection())
91
+ if reflection.has_cached_counter?
92
+ owner.increment!(reflection.counter_cache_column, difference)
93
+ end
55
94
  end
56
95
 
57
- def cached_counter_attribute_name(reflection = reflection)
58
- "#{reflection.name}_count"
96
+ def update_counter_in_memory(difference, reflection = reflection())
97
+ if reflection.counter_must_be_updated_by_has_many?
98
+ counter = reflection.counter_cache_column
99
+ owner.increment(counter, difference)
100
+ owner.send(:clear_attribute_change, counter) # eww
101
+ end
59
102
  end
60
103
 
61
- def update_counter(difference, reflection = reflection)
62
- if has_cached_counter?(reflection)
63
- counter = cached_counter_attribute_name(reflection)
64
- owner.class.update_counters(owner.id, counter => difference)
65
- owner[counter] += difference
66
- owner.changed_attributes.delete(counter) # eww
104
+ def delete_count(method, scope)
105
+ if method == :delete_all
106
+ scope.delete_all
107
+ else
108
+ scope.update_all(reflection.foreign_key => nil)
67
109
  end
68
110
  end
69
111
 
70
- # This shit is nasty. We need to avoid the following situation:
71
- #
72
- # * An associated record is deleted via record.destroy
73
- # * Hence the callbacks run, and they find a belongs_to on the record with a
74
- # :counter_cache options which points back at our owner. So they update the
75
- # counter cache.
76
- # * In which case, we must make sure to *not* update the counter cache, or else
77
- # it will be decremented twice.
78
- #
79
- # Hence this method.
80
- def inverse_updates_counter_cache?(reflection = reflection)
81
- counter_name = cached_counter_attribute_name(reflection)
82
- reflection.klass.reflect_on_all_associations(:belongs_to).any? { |inverse_reflection|
83
- inverse_reflection.counter_cache_column == counter_name
84
- }
112
+ def delete_or_nullify_all_records(method)
113
+ count = delete_count(method, self.scope)
114
+ update_counter(-count)
85
115
  end
86
116
 
87
117
  # Deletes the records according to the <tt>:dependent</tt> option.
88
118
  def delete_records(records, method)
89
119
  if method == :destroy
90
- records.each { |r| r.destroy }
91
- update_counter(-records.length) unless inverse_updates_counter_cache?
120
+ records.each(&:destroy!)
121
+ update_counter(-records.length) unless reflection.inverse_updates_counter_cache?
92
122
  else
93
- scope = self.scoped.where(reflection.klass.primary_key => records)
123
+ scope = self.scope.where(reflection.klass.primary_key => records)
124
+ update_counter(-delete_count(method, scope))
125
+ end
126
+ end
94
127
 
95
- if method == :delete_all
96
- update_counter(-scope.delete_all)
97
- else
98
- update_counter(-scope.update_all(reflection.foreign_key => nil))
99
- end
128
+ def concat_records(records, *)
129
+ update_counter_if_success(super, records.length)
130
+ end
131
+
132
+ def _create_record(attributes, *)
133
+ if attributes.is_a?(Array)
134
+ super
135
+ else
136
+ update_counter_if_success(super, 1)
100
137
  end
101
138
  end
102
139
 
103
- def foreign_key_present?
104
- owner.attribute_present?(reflection.association_primary_key)
140
+ def update_counter_if_success(saved_successfully, difference)
141
+ if saved_successfully
142
+ update_counter_in_memory(difference)
143
+ end
144
+ saved_successfully
105
145
  end
106
146
  end
107
147
  end
@@ -1,5 +1,3 @@
1
- require 'active_support/core_ext/object/blank'
2
-
3
1
  module ActiveRecord
4
2
  # = Active Record Has Many Through Association
5
3
  module Associations
@@ -13,90 +11,96 @@ module ActiveRecord
13
11
  @through_association = nil
14
12
  end
15
13
 
16
- # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been
17
- # loaded and calling collection.size if it has. If it's more likely than not that the collection does
18
- # have a size larger than zero, and you need to fetch that collection afterwards, it'll take one fewer
19
- # SELECT query if you use #length.
20
- def size
21
- if has_cached_counter?
22
- owner.send(:read_attribute, cached_counter_attribute_name)
23
- elsif loaded?
24
- target.size
25
- else
26
- count
27
- end
28
- end
29
-
30
14
  def concat(*records)
31
15
  unless owner.new_record?
32
16
  records.flatten.each do |record|
33
- raise_on_type_mismatch(record)
34
- record.save! if record.new_record?
17
+ raise_on_type_mismatch!(record)
35
18
  end
36
19
  end
37
20
 
38
21
  super
39
22
  end
40
23
 
41
- def insert_record(record, validate = true, raise = false)
24
+ def concat_records(records)
42
25
  ensure_not_nested
43
26
 
44
- if record.new_record?
45
- if raise
46
- record.save!(:validate => validate)
47
- else
48
- return unless record.save(:validate => validate)
27
+ records = super(records, true)
28
+
29
+ if owner.new_record? && records
30
+ records.flatten.each do |record|
31
+ build_through_record(record)
49
32
  end
50
33
  end
51
34
 
35
+ records
36
+ end
37
+
38
+ def insert_record(record, validate = true, raise = false)
39
+ ensure_not_nested
40
+
41
+ if raise
42
+ record.save!(:validate => validate)
43
+ else
44
+ return unless record.save(:validate => validate)
45
+ end
46
+
52
47
  save_through_record(record)
53
- update_counter(1)
48
+
54
49
  record
55
50
  end
56
51
 
57
- # ActiveRecord::Relation#delete_all needs to support joins before we can use a
58
- # SQL-only implementation.
59
- alias delete_all_on_destroy delete_all
60
-
61
52
  private
62
53
 
63
54
  def through_association
64
55
  @through_association ||= owner.association(through_reflection.name)
65
56
  end
66
57
 
67
- # We temporarily cache through record that has been build, because if we build a
68
- # through record in build_record and then subsequently call insert_record, then we
69
- # want to use the exact same object.
58
+ # The through record (built with build_record) is temporarily cached
59
+ # so that it may be reused if insert_record is subsequently called.
70
60
  #
71
- # However, after insert_record has been called, we clear the cache entry because
72
- # we want it to be possible to have multiple instances of the same record in an
73
- # association
61
+ # However, after insert_record has been called, the cache is cleared in
62
+ # order to allow multiple instances of the same record in an association.
74
63
  def build_through_record(record)
75
64
  @through_records[record.object_id] ||= begin
76
65
  ensure_mutable
77
66
 
78
- through_record = through_association.build
67
+ through_record = through_association.build(*options_for_through_record)
79
68
  through_record.send("#{source_reflection.name}=", record)
69
+
70
+ if options[:source_type]
71
+ through_record.send("#{source_reflection.foreign_type}=", options[:source_type])
72
+ end
73
+
80
74
  through_record
81
75
  end
82
76
  end
83
77
 
78
+ def options_for_through_record
79
+ [through_scope_attributes]
80
+ end
81
+
82
+ def through_scope_attributes
83
+ scope.where_values_hash(through_association.reflection.name.to_s).
84
+ except!(through_association.reflection.foreign_key,
85
+ through_association.reflection.klass.inheritance_column)
86
+ end
87
+
84
88
  def save_through_record(record)
85
89
  build_through_record(record).save!
86
90
  ensure
87
91
  @through_records.delete(record.object_id)
88
92
  end
89
93
 
90
- def build_record(attributes, options = {})
94
+ def build_record(attributes)
91
95
  ensure_not_nested
92
96
 
93
- record = super(attributes, options)
97
+ record = super(attributes)
94
98
 
95
99
  inverse = source_reflection.inverse_of
96
100
  if inverse
97
- if inverse.macro == :has_many
101
+ if inverse.collection?
98
102
  record.send(inverse.name) << build_through_record(record)
99
- elsif inverse.macro == :has_one
103
+ elsif inverse.has_one?
100
104
  record.send("#{inverse.name}=", build_through_record(record))
101
105
  end
102
106
  end
@@ -105,17 +109,13 @@ module ActiveRecord
105
109
  end
106
110
 
107
111
  def target_reflection_has_associated_record?
108
- if through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?
109
- false
110
- else
111
- true
112
- end
112
+ !(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?)
113
113
  end
114
114
 
115
115
  def update_through_counter?(method)
116
116
  case method
117
117
  when :destroy
118
- !inverse_updates_counter_cache?(through_reflection)
118
+ !through_reflection.inverse_updates_counter_cache?
119
119
  when :nullify
120
120
  false
121
121
  else
@@ -123,14 +123,31 @@ module ActiveRecord
123
123
  end
124
124
  end
125
125
 
126
+ def delete_or_nullify_all_records(method)
127
+ delete_records(load_target, method)
128
+ end
129
+
126
130
  def delete_records(records, method)
127
131
  ensure_not_nested
128
132
 
129
- scope = through_association.scoped.where(construct_join_attributes(*records))
133
+ scope = through_association.scope
134
+ scope.where! construct_join_attributes(*records)
130
135
 
131
136
  case method
132
137
  when :destroy
133
- count = scope.destroy_all.length
138
+ if scope.klass.primary_key
139
+ count = scope.destroy_all.length
140
+ else
141
+ scope.each(&:_run_destroy_callbacks)
142
+
143
+ arel = scope.arel
144
+
145
+ stmt = Arel::DeleteManager.new
146
+ stmt.from scope.klass.arel_table
147
+ stmt.wheres = arel.constraints
148
+
149
+ count = scope.klass.connection.delete(stmt, 'SQL', scope.bound_attributes)
150
+ end
134
151
  when :nullify
135
152
  count = scope.update_all(source_reflection.foreign_key => nil)
136
153
  else
@@ -139,29 +156,33 @@ module ActiveRecord
139
156
 
140
157
  delete_through_records(records)
141
158
 
142
- if source_reflection.options[:counter_cache]
159
+ if source_reflection.options[:counter_cache] && method != :destroy
143
160
  counter = source_reflection.counter_cache_column
144
161
  klass.decrement_counter counter, records.map(&:id)
145
162
  end
146
163
 
147
- if through_reflection.macro == :has_many && update_through_counter?(method)
164
+ if through_reflection.collection? && update_through_counter?(method)
148
165
  update_counter(-count, through_reflection)
166
+ else
167
+ update_counter(-count)
149
168
  end
150
-
151
- update_counter(-count)
152
169
  end
153
170
 
154
171
  def through_records_for(record)
155
172
  attributes = construct_join_attributes(record)
156
173
  candidates = Array.wrap(through_association.target)
157
- candidates.find_all { |c| c.attributes.slice(*attributes.keys) == attributes }
174
+ candidates.find_all do |c|
175
+ attributes.all? do |key, value|
176
+ c.public_send(key) == value
177
+ end
178
+ end
158
179
  end
159
180
 
160
181
  def delete_through_records(records)
161
182
  records.each do |record|
162
183
  through_records = through_records_for(record)
163
184
 
164
- if through_reflection.macro == :has_many
185
+ if through_reflection.collection?
165
186
  through_records.each { |r| through_association.target.delete(r) }
166
187
  else
167
188
  if through_records.include?(through_association.target)
@@ -175,7 +196,7 @@ module ActiveRecord
175
196
 
176
197
  def find_target
177
198
  return [] unless target_reflection_has_associated_record?
178
- scoped.all
199
+ get_records
179
200
  end
180
201
 
181
202
  # NOTE - not sure that we can actually cope with inverses here
@@ -1,24 +1,51 @@
1
- require 'active_support/core_ext/object/inclusion'
2
-
3
1
  module ActiveRecord
4
- # = Active Record Belongs To Has One Association
2
+ # = Active Record Has One Association
5
3
  module Associations
6
4
  class HasOneAssociation < SingularAssociation #:nodoc:
5
+ include ForeignAssociation
6
+
7
+ def handle_dependency
8
+ case options[:dependent]
9
+ when :restrict_with_exception
10
+ raise ActiveRecord::DeleteRestrictionError.new(reflection.name) if load_target
11
+
12
+ when :restrict_with_error
13
+ if load_target
14
+ record = owner.class.human_attribute_name(reflection.name).downcase
15
+ message = owner.errors.generate_message(:base, :'restrict_dependent_destroy.one', record: record, raise: true) rescue nil
16
+ if message
17
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
18
+ The error key `:'restrict_dependent_destroy.one'` has been deprecated and will be removed in Rails 5.1.
19
+ Please use `:'restrict_dependent_destroy.has_one'` instead.
20
+ MESSAGE
21
+ end
22
+ owner.errors.add(:base, message || :'restrict_dependent_destroy.has_one', record: record)
23
+ throw(:abort)
24
+ end
25
+
26
+ else
27
+ delete
28
+ end
29
+ end
30
+
7
31
  def replace(record, save = true)
8
- raise_on_type_mismatch(record) if record
32
+ raise_on_type_mismatch!(record) if record
9
33
  load_target
10
34
 
11
- # If target and record are nil, or target is equal to record,
12
- # we don't need to have transaction.
13
- if (target || record) && target != record
35
+ return self.target if !(target || record)
36
+
37
+ assigning_another_record = target != record
38
+ if assigning_another_record || record.changed?
39
+ save &&= owner.persisted?
40
+
14
41
  transaction_if(save) do
15
- remove_target!(options[:dependent]) if target && !target.destroyed?
16
-
42
+ remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
43
+
17
44
  if record
18
45
  set_owner_attributes(record)
19
46
  set_inverse_instance(record)
20
-
21
- if owner.persisted? && save && !record.save
47
+
48
+ if save && !record.save
22
49
  nullify_owner_attributes(record)
23
50
  set_owner_attributes(target) if target
24
51
  raise RecordNotSaved, "Failed to save the new associated #{reflection.name}."
@@ -38,7 +65,7 @@ module ActiveRecord
38
65
  when :destroy
39
66
  target.destroy
40
67
  when :nullify
41
- target.update_attribute(reflection.foreign_key, nil)
68
+ target.update_columns(reflection.foreign_key => nil) if target.persisted?
42
69
  end
43
70
  end
44
71
  end
@@ -54,16 +81,19 @@ module ActiveRecord
54
81
  end
55
82
 
56
83
  def remove_target!(method)
57
- if method.in?([:delete, :destroy])
58
- target.send(method)
59
- else
60
- nullify_owner_attributes(target)
84
+ case method
85
+ when :delete
86
+ target.delete
87
+ when :destroy
88
+ target.destroy
89
+ else
90
+ nullify_owner_attributes(target)
61
91
 
62
- if target.persisted? && owner.persisted? && !target.save
63
- set_owner_attributes(target)
64
- raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
65
- "The record failed to save when after its foreign key was set to nil."
66
- end
92
+ if target.persisted? && owner.persisted? && !target.save
93
+ set_owner_attributes(target)
94
+ raise RecordNotSaved, "Failed to remove the existing associated #{reflection.name}. " +
95
+ "The record failed to save after its foreign key was set to nil."
96
+ end
67
97
  end
68
98
  end
69
99
 
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
  attributes = construct_join_attributes(record)
24
24
 
25
25
  if through_record
26
- through_record.update_attributes(attributes)
26
+ through_record.update(attributes)
27
27
  elsif owner.new_record?
28
28
  through_proxy.build(attributes)
29
29
  else