activerecord 4.2.0 → 5.2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +640 -928
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +466 -280
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +439 -330
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -324
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +136 -90
  133. data/lib/active_record/errors.rb +180 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +117 -35
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -339
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +208 -123
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +30 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  # = Active Record Counter Cache
3
5
  module CounterCache
@@ -12,13 +14,21 @@ module ActiveRecord
12
14
  #
13
15
  # * +id+ - The id of the object you wish to reset a counter on.
14
16
  # * +counters+ - One or more association counters to reset. Association name or counter name can be given.
17
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
18
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
19
+ # touch that column or an array of symbols to touch just those ones.
15
20
  #
16
21
  # ==== Examples
17
22
  #
18
- # # For Post with id #1 records reset the comments_count
23
+ # # For the Post with id #1, reset the comments_count
19
24
  # Post.reset_counters(1, :comments)
20
- def reset_counters(id, *counters)
25
+ #
26
+ # # Like above, but also touch the +updated_at+ and/or +updated_on+
27
+ # # attributes.
28
+ # Post.reset_counters(1, :comments, touch: true)
29
+ def reset_counters(id, *counters, touch: nil)
21
30
  object = find(id)
31
+
22
32
  counters.each do |counter_association|
23
33
  has_many_association = _reflect_on_association(counter_association)
24
34
  unless has_many_association
@@ -26,7 +36,7 @@ module ActiveRecord
26
36
  has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
27
37
  counter_association = has_many_association.plural_name if has_many_association
28
38
  end
29
- raise ArgumentError, "'#{self.name}' has no association called '#{counter_association}'" unless has_many_association
39
+ raise ArgumentError, "'#{name}' has no association called '#{counter_association}'" unless has_many_association
30
40
 
31
41
  if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
32
42
  has_many_association = has_many_association.through_reflection
@@ -37,25 +47,33 @@ module ActiveRecord
37
47
  reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
38
48
  counter_name = reflection.counter_cache_column
39
49
 
40
- stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
41
- arel_table[counter_name] => object.send(counter_association).count(:all)
42
- }, primary_key)
43
- connection.update stmt
50
+ updates = { counter_name => object.send(counter_association).count(:all) }
51
+
52
+ if touch
53
+ names = touch if touch != true
54
+ updates.merge!(touch_attributes_with_time(*names))
55
+ end
56
+
57
+ unscoped.where(primary_key => object.id).update_all(updates)
44
58
  end
45
- return true
59
+
60
+ true
46
61
  end
47
62
 
48
63
  # A generic "counter updater" implementation, intended primarily to be
49
- # used by increment_counter and decrement_counter, but which may also
64
+ # used by #increment_counter and #decrement_counter, but which may also
50
65
  # be useful on its own. It simply does a direct SQL update for the record
51
66
  # with the given ID, altering the given hash of counters by the amount
52
67
  # given by the corresponding value:
53
68
  #
54
69
  # ==== Parameters
55
70
  #
56
- # * +id+ - The id of the object you wish to update a counter on or an Array of ids.
71
+ # * +id+ - The id of the object you wish to update a counter on or an array of ids.
57
72
  # * +counters+ - A Hash containing the names of the fields
58
73
  # to update as keys and the amount to update the field by as values.
74
+ # * <tt>:touch</tt> option - Touch timestamp columns when updating.
75
+ # If attribute names are passed, they are updated along with updated_at/on
76
+ # attributes.
59
77
  #
60
78
  # ==== Examples
61
79
  #
@@ -74,65 +92,92 @@ module ActiveRecord
74
92
  # # UPDATE posts
75
93
  # # SET comment_count = COALESCE(comment_count, 0) + 1
76
94
  # # WHERE id IN (10, 15)
95
+ #
96
+ # # For the Posts with id of 10 and 15, increment the comment_count by 1
97
+ # # and update the updated_at value for each counter.
98
+ # Post.update_counters [10, 15], comment_count: 1, touch: true
99
+ # # Executes the following SQL:
100
+ # # UPDATE posts
101
+ # # SET comment_count = COALESCE(comment_count, 0) + 1,
102
+ # # `updated_at` = '2016-10-13T09:59:23-05:00'
103
+ # # WHERE id IN (10, 15)
77
104
  def update_counters(id, counters)
105
+ touch = counters.delete(:touch)
106
+
78
107
  updates = counters.map do |counter_name, value|
79
- operator = value < 0 ? '-' : '+'
108
+ operator = value < 0 ? "-" : "+"
80
109
  quoted_column = connection.quote_column_name(counter_name)
81
110
  "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
82
111
  end
83
112
 
84
- unscoped.where(primary_key => id).update_all updates.join(', ')
113
+ if touch
114
+ names = touch if touch != true
115
+ touch_updates = touch_attributes_with_time(*names)
116
+ updates << sanitize_sql_for_assignment(touch_updates) unless touch_updates.empty?
117
+ end
118
+
119
+ if id.is_a?(Relation) && self == id.klass
120
+ relation = id
121
+ else
122
+ relation = unscoped.where!(primary_key => id)
123
+ end
124
+
125
+ relation.update_all updates.join(", ")
85
126
  end
86
127
 
87
128
  # Increment a numeric field by one, via a direct SQL update.
88
129
  #
89
130
  # This method is used primarily for maintaining counter_cache columns that are
90
- # used to store aggregate values. For example, a DiscussionBoard may cache
131
+ # used to store aggregate values. For example, a +DiscussionBoard+ may cache
91
132
  # posts_count and comments_count to avoid running an SQL query to calculate the
92
133
  # number of posts and comments there are, each time it is displayed.
93
134
  #
94
135
  # ==== Parameters
95
136
  #
96
137
  # * +counter_name+ - The name of the field that should be incremented.
97
- # * +id+ - The id of the object that should be incremented or an Array of ids.
138
+ # * +id+ - The id of the object that should be incremented or an array of ids.
139
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
140
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
141
+ # touch that column or an array of symbols to touch just those ones.
98
142
  #
99
143
  # ==== Examples
100
144
  #
101
- # # Increment the post_count column for the record with an id of 5
102
- # DiscussionBoard.increment_counter(:post_count, 5)
103
- def increment_counter(counter_name, id)
104
- update_counters(id, counter_name => 1)
145
+ # # Increment the posts_count column for the record with an id of 5
146
+ # DiscussionBoard.increment_counter(:posts_count, 5)
147
+ #
148
+ # # Increment the posts_count column for the record with an id of 5
149
+ # # and update the updated_at value.
150
+ # DiscussionBoard.increment_counter(:posts_count, 5, touch: true)
151
+ def increment_counter(counter_name, id, touch: nil)
152
+ update_counters(id, counter_name => 1, touch: touch)
105
153
  end
106
154
 
107
155
  # Decrement a numeric field by one, via a direct SQL update.
108
156
  #
109
- # This works the same as increment_counter but reduces the column value by
157
+ # This works the same as #increment_counter but reduces the column value by
110
158
  # 1 instead of increasing it.
111
159
  #
112
160
  # ==== Parameters
113
161
  #
114
162
  # * +counter_name+ - The name of the field that should be decremented.
115
- # * +id+ - The id of the object that should be decremented or an Array of ids.
163
+ # * +id+ - The id of the object that should be decremented or an array of ids.
164
+ # * <tt>:touch</tt> - Touch timestamp columns when updating.
165
+ # Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
166
+ # touch that column or an array of symbols to touch just those ones.
116
167
  #
117
168
  # ==== Examples
118
169
  #
119
- # # Decrement the post_count column for the record with an id of 5
120
- # DiscussionBoard.decrement_counter(:post_count, 5)
121
- def decrement_counter(counter_name, id)
122
- update_counters(id, counter_name => -1)
170
+ # # Decrement the posts_count column for the record with an id of 5
171
+ # DiscussionBoard.decrement_counter(:posts_count, 5)
172
+ #
173
+ # # Decrement the posts_count column for the record with an id of 5
174
+ # # and update the updated_at value.
175
+ # DiscussionBoard.decrement_counter(:posts_count, 5, touch: true)
176
+ def decrement_counter(counter_name, id, touch: nil)
177
+ update_counters(id, counter_name => -1, touch: touch)
123
178
  end
124
179
  end
125
180
 
126
- protected
127
-
128
- def actually_destroyed?
129
- @_actually_destroyed
130
- end
131
-
132
- def clear_destroy_state
133
- @_actually_destroyed = nil
134
- end
135
-
136
181
  private
137
182
 
138
183
  def _create_record(*)
@@ -141,7 +186,6 @@ module ActiveRecord
141
186
  each_counter_cached_associations do |association|
142
187
  if send(association.reflection.name)
143
188
  association.increment_counters
144
- @_after_create_counter_called = true
145
189
  end
146
190
  end
147
191
 
@@ -167,9 +211,8 @@ module ActiveRecord
167
211
 
168
212
  def each_counter_cached_associations
169
213
  _reflections.each do |name, reflection|
170
- yield association(name) if reflection.belongs_to? && reflection.counter_cache_column
214
+ yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
171
215
  end
172
216
  end
173
-
174
217
  end
175
218
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # This module exists because ActiveRecord::AttributeMethods::Dirty needs to
5
+ # define callbacks, but continue to have its version of +save+ be the super
6
+ # method of ActiveRecord::Callbacks. This will be removed when the removal
7
+ # of deprecated code removes this need.
8
+ module DefineCallbacks
9
+ extend ActiveSupport::Concern
10
+
11
+ module ClassMethods # :nodoc:
12
+ include ActiveModel::Callbacks
13
+ end
14
+
15
+ included do
16
+ include ActiveModel::Validations::Callbacks
17
+
18
+ define_model_callbacks :initialize, :find, :touch, only: :after
19
+ define_model_callbacks :save, :create, :update, :destroy
20
+ end
21
+ end
22
+ end
@@ -1,140 +1,122 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module DynamicMatchers #:nodoc:
3
- # This code in this file seems to have a lot of indirection, but the indirection
4
- # is there to provide extension points for the activerecord-deprecated_finders
5
- # gem. When we stop supporting activerecord-deprecated_finders (from Rails 5),
6
- # then we can remove the indirection.
7
-
8
- def respond_to?(name, include_private = false)
9
- if self == Base
10
- super
11
- else
5
+ private
6
+ def respond_to_missing?(name, _)
7
+ if self == Base
8
+ super
9
+ else
10
+ match = Method.match(self, name)
11
+ match && match.valid? || super
12
+ end
13
+ end
14
+
15
+ def method_missing(name, *arguments, &block)
12
16
  match = Method.match(self, name)
13
- match && match.valid? || super
17
+
18
+ if match && match.valid?
19
+ match.define
20
+ send(name, *arguments, &block)
21
+ else
22
+ super
23
+ end
14
24
  end
15
- end
16
25
 
17
- private
26
+ class Method
27
+ @matchers = []
18
28
 
19
- def method_missing(name, *arguments, &block)
20
- match = Method.match(self, name)
29
+ class << self
30
+ attr_reader :matchers
21
31
 
22
- if match && match.valid?
23
- match.define
24
- send(name, *arguments, &block)
25
- else
26
- super
27
- end
28
- end
32
+ def match(model, name)
33
+ klass = matchers.find { |k| k.pattern.match?(name) }
34
+ klass.new(model, name) if klass
35
+ end
29
36
 
30
- class Method
31
- @matchers = []
37
+ def pattern
38
+ @pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
39
+ end
32
40
 
33
- class << self
34
- attr_reader :matchers
41
+ def prefix
42
+ raise NotImplementedError
43
+ end
35
44
 
36
- def match(model, name)
37
- klass = matchers.find { |k| name =~ k.pattern }
38
- klass.new(model, name) if klass
45
+ def suffix
46
+ ""
47
+ end
39
48
  end
40
49
 
41
- def pattern
42
- @pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
43
- end
50
+ attr_reader :model, :name, :attribute_names
44
51
 
45
- def prefix
46
- raise NotImplementedError
52
+ def initialize(model, name)
53
+ @model = model
54
+ @name = name.to_s
55
+ @attribute_names = @name.match(self.class.pattern)[1].split("_and_")
56
+ @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
47
57
  end
48
58
 
49
- def suffix
50
- ''
59
+ def valid?
60
+ attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
51
61
  end
52
- end
53
62
 
54
- attr_reader :model, :name, :attribute_names
63
+ def define
64
+ model.class_eval <<-CODE, __FILE__, __LINE__ + 1
65
+ def self.#{name}(#{signature})
66
+ #{body}
67
+ end
68
+ CODE
69
+ end
55
70
 
56
- def initialize(model, name)
57
- @model = model
58
- @name = name.to_s
59
- @attribute_names = @name.match(self.class.pattern)[1].split('_and_')
60
- @attribute_names.map! { |n| @model.attribute_aliases[n] || n }
61
- end
71
+ private
62
72
 
63
- def valid?
64
- attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
65
- end
66
-
67
- def define
68
- model.class_eval <<-CODE, __FILE__, __LINE__ + 1
69
- def self.#{name}(#{signature})
70
- #{body}
73
+ def body
74
+ "#{finder}(#{attributes_hash})"
71
75
  end
72
- CODE
73
- end
74
76
 
75
- def body
76
- raise NotImplementedError
77
- end
78
- end
79
-
80
- module Finder
81
- # Extended in activerecord-deprecated_finders
82
- def body
83
- result
84
- end
85
-
86
- # Extended in activerecord-deprecated_finders
87
- def result
88
- "#{finder}(#{attributes_hash})"
89
- end
90
-
91
- # The parameters in the signature may have reserved Ruby words, in order
92
- # to prevent errors, we start each param name with `_`.
93
- #
94
- # Extended in activerecord-deprecated_finders
95
- def signature
96
- attribute_names.map { |name| "_#{name}" }.join(', ')
97
- end
77
+ # The parameters in the signature may have reserved Ruby words, in order
78
+ # to prevent errors, we start each param name with `_`.
79
+ def signature
80
+ attribute_names.map { |name| "_#{name}" }.join(", ")
81
+ end
98
82
 
99
- # Given that the parameters starts with `_`, the finder needs to use the
100
- # same parameter name.
101
- def attributes_hash
102
- "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(',') + "}"
103
- end
83
+ # Given that the parameters starts with `_`, the finder needs to use the
84
+ # same parameter name.
85
+ def attributes_hash
86
+ "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(",") + "}"
87
+ end
104
88
 
105
- def finder
106
- raise NotImplementedError
89
+ def finder
90
+ raise NotImplementedError
91
+ end
107
92
  end
108
- end
109
93
 
110
- class FindBy < Method
111
- Method.matchers << self
112
- include Finder
94
+ class FindBy < Method
95
+ Method.matchers << self
113
96
 
114
- def self.prefix
115
- "find_by"
116
- end
97
+ def self.prefix
98
+ "find_by"
99
+ end
117
100
 
118
- def finder
119
- "find_by"
101
+ def finder
102
+ "find_by"
103
+ end
120
104
  end
121
- end
122
105
 
123
- class FindByBang < Method
124
- Method.matchers << self
125
- include Finder
106
+ class FindByBang < Method
107
+ Method.matchers << self
126
108
 
127
- def self.prefix
128
- "find_by"
129
- end
109
+ def self.prefix
110
+ "find_by"
111
+ end
130
112
 
131
- def self.suffix
132
- "!"
133
- end
113
+ def self.suffix
114
+ "!"
115
+ end
134
116
 
135
- def finder
136
- "find_by!"
117
+ def finder
118
+ "find_by!"
119
+ end
137
120
  end
138
- end
139
121
  end
140
122
  end