activerecord 7.0.8.7 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +781 -1777
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +30 -30
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +31 -23
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +40 -9
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +35 -21
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +4 -3
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +153 -33
  47. data/lib/active_record/attributes.rb +96 -71
  48. data/lib/active_record/autosave_association.rb +81 -39
  49. data/lib/active_record/base.rb +11 -7
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +343 -91
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +229 -64
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +142 -12
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +60 -55
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +108 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -203
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -45
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +51 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +101 -105
  110. data/lib/active_record/core.rb +273 -178
  111. data/lib/active_record/counter_cache.rb +69 -35
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +56 -27
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +46 -22
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
  130. data/lib/active_record/encryption/encryptor.rb +35 -19
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +130 -28
  143. data/lib/active_record/errors.rb +154 -34
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +48 -10
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +236 -118
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +96 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +35 -10
  178. data/lib/active_record/railtie.rb +131 -87
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +147 -155
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +270 -108
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +97 -21
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +3 -2
  196. data/lib/active_record/relation/query_methods.rb +585 -109
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +15 -21
  200. data/lib/active_record/relation.rb +592 -92
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +90 -23
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +33 -11
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +23 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +108 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +3 -1
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/crud.rb +2 -0
  248. data/lib/arel/delete_manager.rb +5 -0
  249. data/lib/arel/errors.rb +10 -0
  250. data/lib/arel/factory_methods.rb +4 -0
  251. data/lib/arel/nodes/binary.rb +6 -7
  252. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  253. data/lib/arel/nodes/cte.rb +36 -0
  254. data/lib/arel/nodes/delete_statement.rb +4 -2
  255. data/lib/arel/nodes/fragments.rb +35 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  257. data/lib/arel/nodes/leading_join.rb +8 -0
  258. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  259. data/lib/arel/nodes/node.rb +115 -5
  260. data/lib/arel/nodes/sql_literal.rb +13 -0
  261. data/lib/arel/nodes/table_alias.rb +4 -0
  262. data/lib/arel/nodes/update_statement.rb +4 -2
  263. data/lib/arel/nodes.rb +6 -2
  264. data/lib/arel/predications.rb +3 -1
  265. data/lib/arel/select_manager.rb +7 -3
  266. data/lib/arel/table.rb +9 -5
  267. data/lib/arel/tree_manager.rb +8 -3
  268. data/lib/arel/update_manager.rb +7 -1
  269. data/lib/arel/visitors/dot.rb +3 -0
  270. data/lib/arel/visitors/mysql.rb +17 -5
  271. data/lib/arel/visitors/postgresql.rb +1 -12
  272. data/lib/arel/visitors/sqlite.rb +25 -0
  273. data/lib/arel/visitors/to_sql.rb +114 -34
  274. data/lib/arel/visitors/visitor.rb +2 -2
  275. data/lib/arel.rb +21 -3
  276. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  277. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  278. data/lib/rails/generators/active_record/migration.rb +3 -1
  279. data/lib/rails/generators/active_record/model/USAGE +113 -0
  280. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  281. metadata +56 -17
  282. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  283. data/lib/active_record/null_relation.rb +0 -63
@@ -3,6 +3,7 @@
3
3
  require "active_support/core_ext/enumerable"
4
4
 
5
5
  module ActiveRecord
6
+ # = Active Record \Calculations
6
7
  module Calculations
7
8
  class ColumnAliasTracker # :nodoc:
8
9
  def initialize(connection)
@@ -59,28 +60,37 @@ module ActiveRecord
59
60
  # Person.distinct.count(:age)
60
61
  # # => counts the number of different age values
61
62
  #
62
- # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
63
+ # If +count+ is used with {Relation#group}[rdoc-ref:QueryMethods#group],
63
64
  # it returns a Hash whose keys represent the aggregated column,
64
65
  # and the values are the respective amounts:
65
66
  #
66
67
  # Person.group(:city).count
67
68
  # # => { 'Rome' => 5, 'Paris' => 3 }
68
69
  #
69
- # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
70
+ # If +count+ is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
70
71
  # keys are an array containing the individual values of each column and the value
71
- # of each key would be the #count.
72
+ # of each key would be the count.
72
73
  #
73
74
  # Article.group(:status, :category).count
74
- # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
75
- # # ["published", "business"]=>0, ["published", "technology"]=>2}
75
+ # # => {["draft", "business"]=>10, ["draft", "technology"]=>4, ["published", "technology"]=>2}
76
76
  #
77
- # If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
77
+ # If +count+ is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
78
78
  #
79
79
  # Person.select(:age).count
80
80
  # # => counts the number of different age values
81
81
  #
82
- # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
82
+ # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid +count+ expressions. The specifics differ
83
83
  # between databases. In invalid cases, an error from the database is thrown.
84
+ #
85
+ # When given a block, calls the block with each record in the relation and
86
+ # returns the number of records for which the block returns a truthy value.
87
+ #
88
+ # Person.count { |person| person.age > 21 }
89
+ # # => counts the number of people older that 21
90
+ #
91
+ # If the relation hasn't been loaded yet, calling +count+ with a block will
92
+ # load all records in the relation. If there are a lot of records in the
93
+ # relation, loading all records could result in performance issues.
84
94
  def count(column_name = nil)
85
95
  if block_given?
86
96
  unless column_name.nil?
@@ -93,6 +103,12 @@ module ActiveRecord
93
103
  end
94
104
  end
95
105
 
106
+ # Same as #count, but performs the query asynchronously and returns an
107
+ # ActiveRecord::Promise.
108
+ def async_count(column_name = nil)
109
+ async.count(column_name)
110
+ end
111
+
96
112
  # Calculates the average value on a given column. Returns +nil+ if there's
97
113
  # no row. See #calculate for examples with options.
98
114
  #
@@ -101,6 +117,12 @@ module ActiveRecord
101
117
  calculate(:average, column_name)
102
118
  end
103
119
 
120
+ # Same as #average, but performs the query asynchronously and returns an
121
+ # ActiveRecord::Promise.
122
+ def async_average(column_name)
123
+ async.average(column_name)
124
+ end
125
+
104
126
  # Calculates the minimum value on a given column. The value is returned
105
127
  # with the same data type of the column, or +nil+ if there's no row. See
106
128
  # #calculate for examples with options.
@@ -110,6 +132,12 @@ module ActiveRecord
110
132
  calculate(:minimum, column_name)
111
133
  end
112
134
 
135
+ # Same as #minimum, but performs the query asynchronously and returns an
136
+ # ActiveRecord::Promise.
137
+ def async_minimum(column_name)
138
+ async.minimum(column_name)
139
+ end
140
+
113
141
  # Calculates the maximum value on a given column. The value is returned
114
142
  # with the same data type of the column, or +nil+ if there's no row. See
115
143
  # #calculate for examples with options.
@@ -119,32 +147,41 @@ module ActiveRecord
119
147
  calculate(:maximum, column_name)
120
148
  end
121
149
 
150
+ # Same as #maximum, but performs the query asynchronously and returns an
151
+ # ActiveRecord::Promise.
152
+ def async_maximum(column_name)
153
+ async.maximum(column_name)
154
+ end
155
+
122
156
  # Calculates the sum of values on a given column. The value is returned
123
157
  # with the same data type of the column, +0+ if there's no row. See
124
158
  # #calculate for examples with options.
125
159
  #
126
160
  # Person.sum(:age) # => 4562
127
- def sum(identity_or_column = nil, &block)
161
+ #
162
+ # When given a block, calls the block with each record in the relation and
163
+ # returns the sum of +initial_value_or_column+ plus the block return values:
164
+ #
165
+ # Person.sum { |person| person.age } # => 4562
166
+ # Person.sum(1000) { |person| person.age } # => 5562
167
+ #
168
+ # If the relation hasn't been loaded yet, calling +sum+ with a block will
169
+ # load all records in the relation. If there are a lot of records in the
170
+ # relation, loading all records could result in performance issues.
171
+ def sum(initial_value_or_column = 0, &block)
128
172
  if block_given?
129
- values = map(&block)
130
- if identity_or_column.nil? && (values.first.is_a?(Numeric) || values.first(1) == [] || values.first.respond_to?(:coerce))
131
- identity_or_column = 0
132
- end
133
-
134
- if identity_or_column.nil?
135
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
136
- Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
137
- Sum of non-numeric elements requires an initial argument.
138
- MSG
139
- values.inject(:+) || 0
140
- else
141
- values.sum(identity_or_column)
142
- end
173
+ map(&block).sum(initial_value_or_column)
143
174
  else
144
- calculate(:sum, identity_or_column)
175
+ calculate(:sum, initial_value_or_column)
145
176
  end
146
177
  end
147
178
 
179
+ # Same as #sum, but performs the query asynchronously and returns an
180
+ # ActiveRecord::Promise.
181
+ def async_sum(identity_or_column = nil)
182
+ async.sum(identity_or_column)
183
+ end
184
+
148
185
  # This calculates aggregate values in the given column. Methods for #count, #sum, #average,
149
186
  # #minimum, and #maximum have been added as shortcuts.
150
187
  #
@@ -177,13 +214,26 @@ module ActiveRecord
177
214
  # ...
178
215
  # end
179
216
  def calculate(operation, column_name)
217
+ operation = operation.to_s.downcase
218
+
219
+ if @none
220
+ case operation
221
+ when "count", "sum"
222
+ result = group_values.any? ? Hash.new : 0
223
+ return @async ? Promise::Complete.new(result) : result
224
+ when "average", "minimum", "maximum"
225
+ result = group_values.any? ? Hash.new : nil
226
+ return @async ? Promise::Complete.new(result) : result
227
+ end
228
+ end
229
+
180
230
  if has_include?(column_name)
181
231
  relation = apply_join_dependency
182
232
 
183
- if operation.to_s.downcase == "count"
233
+ if operation == "count"
184
234
  unless distinct_value || distinct_select?(column_name || select_for_count)
185
235
  relation.distinct!
186
- relation.select_values = [ klass.primary_key || table[Arel.star] ]
236
+ relation.select_values = Array(klass.primary_key || table[Arel.star])
187
237
  end
188
238
  # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT
189
239
  relation.order_values = [] if group_values.empty?
@@ -224,36 +274,62 @@ module ActiveRecord
224
274
  # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
225
275
  # # => [2, 3]
226
276
  #
277
+ # Comment.joins(:person).pluck(:id, person: [:id])
278
+ # # SELECT comments.id, people.id FROM comments INNER JOIN people on comments.person_id = people.id
279
+ # # => [[1, 2], [2, 2]]
280
+ #
227
281
  # Person.pluck(Arel.sql('DATEDIFF(updated_at, created_at)'))
228
282
  # # SELECT DATEDIFF(updated_at, created_at) FROM people
229
283
  # # => ['0', '27761', '173']
230
284
  #
231
285
  # See also #ids.
232
- #
233
286
  def pluck(*column_names)
287
+ if @none
288
+ if @async
289
+ return Promise::Complete.new([])
290
+ else
291
+ return []
292
+ end
293
+ end
294
+
234
295
  if loaded? && all_attributes?(column_names)
235
- return records.pluck(*column_names)
296
+ result = records.pluck(*column_names)
297
+ if @async
298
+ return Promise::Complete.new(result)
299
+ else
300
+ return result
301
+ end
236
302
  end
237
303
 
238
304
  if has_include?(column_names.first)
239
305
  relation = apply_join_dependency
240
306
  relation.pluck(*column_names)
241
307
  else
242
- klass.disallow_raw_sql!(column_names)
243
- columns = arel_columns(column_names)
308
+ klass.disallow_raw_sql!(flattened_args(column_names))
244
309
  relation = spawn
310
+ columns = relation.arel_columns(column_names)
245
311
  relation.select_values = columns
246
312
  result = skip_query_cache_if_necessary do
247
313
  if where_clause.contradiction?
248
- ActiveRecord::Result.empty
314
+ ActiveRecord::Result.empty(async: @async)
249
315
  else
250
- klass.connection.select_all(relation.arel, "#{klass.name} Pluck")
316
+ klass.with_connection do |c|
317
+ c.select_all(relation.arel, "#{klass.name} Pluck", async: @async)
318
+ end
251
319
  end
252
320
  end
253
- type_cast_pluck_values(result, columns)
321
+ result.then do |result|
322
+ type_cast_pluck_values(result, columns)
323
+ end
254
324
  end
255
325
  end
256
326
 
327
+ # Same as #pluck, but performs the query asynchronously and returns an
328
+ # ActiveRecord::Promise.
329
+ def async_pluck(*column_names)
330
+ async.pluck(*column_names)
331
+ end
332
+
257
333
  # Pick the value(s) from the named column(s) in the current relation.
258
334
  # This is short-hand for <tt>relation.limit(1).pluck(*column_names).first</tt>, and is primarily useful
259
335
  # when you have a relation that's already narrowed down to a single row.
@@ -270,20 +346,74 @@ module ActiveRecord
270
346
  # # => [ 'David', 'david@loudthinking.com' ]
271
347
  def pick(*column_names)
272
348
  if loaded? && all_attributes?(column_names)
273
- return records.pick(*column_names)
349
+ result = records.pick(*column_names)
350
+ return @async ? Promise::Complete.new(result) : result
274
351
  end
275
352
 
276
- limit(1).pluck(*column_names).first
353
+ limit(1).pluck(*column_names).then(&:first)
354
+ end
355
+
356
+ # Same as #pick, but performs the query asynchronously and returns an
357
+ # ActiveRecord::Promise.
358
+ def async_pick(*column_names)
359
+ async.pick(*column_names)
277
360
  end
278
361
 
279
- # Pluck all the ID's for the relation using the table's primary key
362
+ # Returns the base model's ID's for the relation using the table's primary key
280
363
  #
281
364
  # Person.ids # SELECT people.id FROM people
282
- # Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id
365
+ # Person.joins(:company).ids # SELECT people.id FROM people INNER JOIN companies ON companies.id = people.company_id
283
366
  def ids
284
- pluck primary_key
367
+ primary_key_array = Array(primary_key)
368
+
369
+ if loaded?
370
+ result = records.map do |record|
371
+ if primary_key_array.one?
372
+ record._read_attribute(primary_key_array.first)
373
+ else
374
+ primary_key_array.map { |column| record._read_attribute(column) }
375
+ end
376
+ end
377
+ return @async ? Promise::Complete.new(result) : result
378
+ end
379
+
380
+ if has_include?(primary_key)
381
+ relation = apply_join_dependency.group(*primary_key_array)
382
+ return relation.ids
383
+ end
384
+
385
+ columns = arel_columns(primary_key_array)
386
+ relation = spawn
387
+ relation.select_values = columns
388
+
389
+ result = if relation.where_clause.contradiction?
390
+ ActiveRecord::Result.empty
391
+ else
392
+ skip_query_cache_if_necessary do
393
+ klass.with_connection do |c|
394
+ c.select_all(relation, "#{klass.name} Ids", async: @async)
395
+ end
396
+ end
397
+ end
398
+
399
+ result.then { |result| type_cast_pluck_values(result, columns) }
400
+ end
401
+
402
+ # Same as #ids, but performs the query asynchronously and returns an
403
+ # ActiveRecord::Promise.
404
+ def async_ids
405
+ async.ids
285
406
  end
286
407
 
408
+ protected
409
+ def aggregate_column(column_name)
410
+ return column_name if Arel::Expressions === column_name
411
+
412
+ arel_column(column_name.to_s) do |name|
413
+ column_name == :all ? Arel.sql("*", retryable: true) : Arel.sql(name)
414
+ end
415
+ end
416
+
287
417
  private
288
418
  def all_attributes?(column_names)
289
419
  (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
@@ -324,29 +454,22 @@ module ActiveRecord
324
454
  column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
325
455
  end
326
456
 
327
- def aggregate_column(column_name)
328
- return column_name if Arel::Expressions === column_name
329
-
330
- arel_column(column_name.to_s) do |name|
331
- Arel.sql(column_name == :all ? "*" : name)
332
- end
333
- end
334
-
335
457
  def operation_over_aggregate_column(column, operation, distinct)
336
458
  operation == "count" ? column.count(distinct) : column.public_send(operation)
337
459
  end
338
460
 
339
461
  def execute_simple_calculation(operation, column_name, distinct) # :nodoc:
340
- if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
462
+ if build_count_subquery?(operation, column_name, distinct)
341
463
  # Shortcut when limit is zero.
342
464
  return 0 if limit_value == 0
343
465
 
466
+ relation = self
344
467
  query_builder = build_count_subquery(spawn, column_name, distinct)
345
468
  else
346
469
  # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
347
470
  relation = unscope(:order).distinct!(false)
348
471
 
349
- column = aggregate_column(column_name)
472
+ column = relation.aggregate_column(column_name)
350
473
  select_value = operation_over_aggregate_column(column, operation, distinct)
351
474
  select_value.distinct = true if operation == "sum" && distinct
352
475
 
@@ -355,15 +478,29 @@ module ActiveRecord
355
478
  query_builder = relation.arel
356
479
  end
357
480
 
358
- result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, "#{@klass.name} #{operation.capitalize}") }
359
-
360
- if operation != "count"
361
- type = column.try(:type_caster) ||
362
- lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
363
- type = type.subtype if Enum::EnumType === type
481
+ query_result = if relation.where_clause.contradiction?
482
+ if @async
483
+ FutureResult.wrap(ActiveRecord::Result.empty)
484
+ else
485
+ ActiveRecord::Result.empty
486
+ end
487
+ else
488
+ skip_query_cache_if_necessary do
489
+ @klass.with_connection do |c|
490
+ c.select_all(query_builder, "#{@klass.name} #{operation.capitalize}", async: @async)
491
+ end
492
+ end
364
493
  end
365
494
 
366
- type_cast_calculated_value(result.cast_values.first, operation, type)
495
+ query_result.then do |result|
496
+ if operation != "count"
497
+ type = column.try(:type_caster) ||
498
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
499
+ type = type.subtype if Enum::EnumType === type
500
+ end
501
+
502
+ type_cast_calculated_value(result.cast_values.first, operation, type)
503
+ end
367
504
  end
368
505
 
369
506
  def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
@@ -375,70 +512,77 @@ module ActiveRecord
375
512
  associated = association && association.belongs_to? # only count belongs_to associations
376
513
  group_fields = Array(association.foreign_key) if associated
377
514
  end
378
- group_fields = arel_columns(group_fields)
379
515
 
380
- column_alias_tracker = ColumnAliasTracker.new(connection)
516
+ relation = except(:group).distinct!(false)
517
+ group_fields = relation.arel_columns(group_fields)
381
518
 
382
- group_aliases = group_fields.map { |field|
383
- field = connection.visitor.compile(field) if Arel.arel_node?(field)
384
- column_alias_tracker.alias_for(field.to_s.downcase)
385
- }
386
- group_columns = group_aliases.zip(group_fields)
519
+ @klass.with_connection do |connection|
520
+ column_alias_tracker = ColumnAliasTracker.new(connection)
387
521
 
388
- column = aggregate_column(column_name)
389
- column_alias = column_alias_tracker.alias_for("#{operation} #{column_name.to_s.downcase}")
390
- select_value = operation_over_aggregate_column(column, operation, distinct)
391
- select_value.as(connection.quote_column_name(column_alias))
522
+ group_aliases = group_fields.map { |field|
523
+ field = connection.visitor.compile(field) if Arel.arel_node?(field)
524
+ column_alias_tracker.alias_for(field.to_s.downcase)
525
+ }
526
+ group_columns = group_aliases.zip(group_fields)
392
527
 
393
- select_values = [select_value]
394
- select_values += self.select_values unless having_clause.empty?
528
+ column = relation.aggregate_column(column_name)
529
+ column_alias = column_alias_tracker.alias_for("#{operation} #{column_name.to_s.downcase}")
530
+ select_value = operation_over_aggregate_column(column, operation, distinct)
531
+ select_value.as(adapter_class.quote_column_name(column_alias))
395
532
 
396
- select_values.concat group_columns.map { |aliaz, field|
397
- aliaz = connection.quote_column_name(aliaz)
398
- if field.respond_to?(:as)
399
- field.as(aliaz)
400
- else
401
- "#{field} AS #{aliaz}"
402
- end
403
- }
533
+ select_values = [select_value]
534
+ select_values += self.select_values unless having_clause.empty?
404
535
 
405
- relation = except(:group).distinct!(false)
406
- relation.group_values = group_fields
407
- relation.select_values = select_values
536
+ select_values.concat group_columns.map { |aliaz, field|
537
+ aliaz = adapter_class.quote_column_name(aliaz)
538
+ if field.respond_to?(:as)
539
+ field.as(aliaz)
540
+ else
541
+ "#{field} AS #{aliaz}"
542
+ end
543
+ }
408
544
 
409
- calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}") }
545
+ relation.group_values = group_fields
546
+ relation.select_values = select_values
410
547
 
411
- if association
412
- key_ids = calculated_data.collect { |row| row[group_aliases.first] }
413
- key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
414
- key_records = key_records.index_by(&:id)
415
- end
548
+ result = skip_query_cache_if_necessary do
549
+ connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}", async: @async)
550
+ end
416
551
 
417
- key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
418
- types[aliaz] = col_name.try(:type_caster) ||
419
- type_for(col_name) do
420
- calculated_data.column_types.fetch(aliaz, Type.default_value)
552
+ result.then do |calculated_data|
553
+ if association
554
+ key_ids = calculated_data.collect { |row| row[group_aliases.first] }
555
+ key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
556
+ key_records = key_records.index_by(&:id)
421
557
  end
422
- end
423
558
 
424
- hash_rows = calculated_data.cast_values(key_types).map! do |row|
425
- calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
426
- hash[col_name] = row[i]
427
- end
428
- end
559
+ key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
560
+ types[aliaz] = col_name.try(:type_caster) ||
561
+ type_for(col_name) do
562
+ calculated_data.column_types.fetch(aliaz, Type.default_value)
563
+ end
564
+ end
429
565
 
430
- if operation != "count"
431
- type = column.try(:type_caster) ||
432
- lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
433
- type = type.subtype if Enum::EnumType === type
434
- end
566
+ hash_rows = calculated_data.cast_values(key_types).map! do |row|
567
+ calculated_data.columns.each_with_object({}).with_index do |(col_name, hash), i|
568
+ hash[col_name] = row[i]
569
+ end
570
+ end
435
571
 
436
- hash_rows.each_with_object({}) do |row, result|
437
- key = group_aliases.map { |aliaz| row[aliaz] }
438
- key = key.first if key.size == 1
439
- key = key_records[key] if associated
572
+ if operation != "count"
573
+ type = column.try(:type_caster) ||
574
+ lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
575
+ type = type.subtype if Enum::EnumType === type
576
+ end
440
577
 
441
- result[key] = type_cast_calculated_value(row[column_alias], operation, type)
578
+ hash_rows.each_with_object({}) do |row, result|
579
+ key = group_aliases.map { |aliaz| row[aliaz] }
580
+ key = key.first if key.size == 1
581
+ key = key_records[key] if associated
582
+
583
+ result[key] = type_cast_calculated_value(row[column_alias], operation, type)
584
+ end
585
+ end
442
586
  end
443
587
  end
444
588
 
@@ -465,7 +609,7 @@ module ActiveRecord
465
609
  klass.attribute_types.fetch(name = result.columns[i]) do
466
610
  join_dependencies ||= build_join_dependencies
467
611
  lookup_cast_type_from_join_dependencies(name, join_dependencies) ||
468
- result.column_types[name] || Type.default_value
612
+ result.column_types[i] || Type.default_value
469
613
  end
470
614
  end
471
615
  end
@@ -493,22 +637,40 @@ module ActiveRecord
493
637
  def select_for_count
494
638
  if select_values.present?
495
639
  return select_values.first if select_values.one?
496
- select_values.join(", ")
640
+
641
+ select_values.map do |field|
642
+ column = arel_column(field.to_s) do |attr_name|
643
+ Arel.sql(attr_name)
644
+ end
645
+
646
+ if column.is_a?(Arel::Nodes::SqlLiteral)
647
+ column
648
+ else
649
+ "#{adapter_class.quote_table_name(column.relation.name)}.#{adapter_class.quote_column_name(column.name)}"
650
+ end
651
+ end.join(", ")
497
652
  else
498
653
  :all
499
654
  end
500
655
  end
501
656
 
657
+ def build_count_subquery?(operation, column_name, distinct)
658
+ # SQLite and older MySQL does not support `COUNT DISTINCT` with `*` or
659
+ # multiple columns, so we need to use subquery for this.
660
+ operation == "count" &&
661
+ (((column_name == :all || select_values.many?) && distinct) || has_limit_or_offset?)
662
+ end
663
+
502
664
  def build_count_subquery(relation, column_name, distinct)
503
665
  if column_name == :all
504
666
  column_alias = Arel.star
505
667
  relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
506
668
  else
507
669
  column_alias = Arel.sql("count_column")
508
- relation.select_values = [ aggregate_column(column_name).as(column_alias) ]
670
+ relation.select_values = [ relation.aggregate_column(column_name).as(column_alias) ]
509
671
  end
510
672
 
511
- subquery_alias = Arel.sql("subquery_for_count")
673
+ subquery_alias = Arel.sql("subquery_for_count", retryable: true)
512
674
  select_value = operation_over_aggregate_column(column_alias, "count", false)
513
675
 
514
676
  relation.build_subquery(subquery_alias, select_value)
@@ -1,10 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "mutex_m"
4
3
  require "active_support/core_ext/module/delegation"
5
4
 
6
5
  module ActiveRecord
7
6
  module Delegation # :nodoc:
7
+ class << self
8
+ def delegated_classes
9
+ [
10
+ ActiveRecord::Relation,
11
+ ActiveRecord::Associations::CollectionProxy,
12
+ ActiveRecord::AssociationRelation,
13
+ ActiveRecord::DisableJoinsAssociationRelation,
14
+ ]
15
+ end
16
+
17
+ def uncacheable_methods
18
+ @uncacheable_methods ||= (
19
+ delegated_classes.flat_map(&:public_instance_methods) - ActiveRecord::Relation.public_instance_methods
20
+ ).to_set.freeze
21
+ end
22
+ end
23
+
8
24
  module DelegateCache # :nodoc:
9
25
  def relation_delegate_class(klass)
10
26
  @relation_delegate_cache[klass]
@@ -12,12 +28,7 @@ module ActiveRecord
12
28
 
13
29
  def initialize_relation_delegate_cache
14
30
  @relation_delegate_cache = cache = {}
15
- [
16
- ActiveRecord::Relation,
17
- ActiveRecord::Associations::CollectionProxy,
18
- ActiveRecord::AssociationRelation,
19
- ActiveRecord::DisableJoinsAssociationRelation
20
- ].each do |klass|
31
+ Delegation.delegated_classes.each do |klass|
21
32
  delegate = Class.new(klass) {
22
33
  include ClassSpecificRelation
23
34
  }
@@ -55,23 +66,22 @@ module ActiveRecord
55
66
  end
56
67
 
57
68
  class GeneratedRelationMethods < Module # :nodoc:
58
- include Mutex_m
69
+ MUTEX = Mutex.new
59
70
 
60
71
  def generate_method(method)
61
- synchronize do
72
+ MUTEX.synchronize do
62
73
  return if method_defined?(method)
63
74
 
64
- if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method) && !DELEGATION_RESERVED_METHOD_NAMES.include?(method.to_s)
75
+ if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method) && !::ActiveSupport::Delegation::RESERVED_METHOD_NAMES.include?(method.to_s)
65
76
  module_eval <<-RUBY, __FILE__, __LINE__ + 1
66
77
  def #{method}(...)
67
78
  scoping { klass.#{method}(...) }
68
79
  end
69
80
  RUBY
70
81
  else
71
- define_method(method) do |*args, &block|
72
- scoping { klass.public_send(method, *args, &block) }
82
+ define_method(method) do |*args, **kwargs, &block|
83
+ scoping { klass.public_send(method, *args, **kwargs, &block) }
73
84
  end
74
- ruby2_keywords(method)
75
85
  end
76
86
  end
77
87
  end
@@ -85,12 +95,12 @@ module ActiveRecord
85
95
  # may vary depending on the klass of a relation, so we create a subclass of Relation
86
96
  # for each different klass, and the delegations are compiled into that subclass only.
87
97
 
88
- delegate :to_xml, :encode_with, :length, :each, :join,
98
+ delegate :to_xml, :encode_with, :length, :each, :join, :intersect?,
89
99
  :[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
90
100
  :to_sentence, :to_fs, :to_formatted_s, :as_json,
91
101
  :shuffle, :split, :slice, :index, :rindex, to: :records
92
102
 
93
- delegate :primary_key, :connection, to: :klass
103
+ delegate :primary_key, :lease_connection, :connection, :with_connection, :transaction, to: :klass
94
104
 
95
105
  module ClassSpecificRelation # :nodoc:
96
106
  extend ActiveSupport::Concern
@@ -102,15 +112,16 @@ module ActiveRecord
102
112
  end
103
113
 
104
114
  private
105
- def method_missing(method, *args, &block)
115
+ def method_missing(method, ...)
106
116
  if @klass.respond_to?(method)
107
- @klass.generate_relation_method(method)
108
- scoping { @klass.public_send(method, *args, &block) }
117
+ unless Delegation.uncacheable_methods.include?(method)
118
+ @klass.generate_relation_method(method)
119
+ end
120
+ scoping { @klass.public_send(method, ...) }
109
121
  else
110
122
  super
111
123
  end
112
124
  end
113
- ruby2_keywords(:method_missing)
114
125
  end
115
126
 
116
127
  module ClassMethods # :nodoc: