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
@@ -1,215 +1,209 @@
1
- require 'active_support/core_ext/object/blank'
2
- require 'active_support/core_ext/object/try'
3
-
4
1
  module ActiveRecord
5
2
  module Calculations
6
- # Count operates using three different approaches.
7
- #
8
- # * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
9
- # * Count using column: By passing a column name to count, it will return a count of all the
10
- # rows for the model with supplied column present.
11
- # * Count using options will find the row count matched by the options used.
12
- #
13
- # The third approach, count using options, accepts an option hash as the only parameter. The options are:
14
- #
15
- # * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
16
- # See conditions in the intro to ActiveRecord::Base.
17
- # * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id"
18
- # (rarely needed) or named associations in the same form used for the <tt>:include</tt> option, which will
19
- # perform an INNER JOIN on the associated table(s). If the value is a string, then the records
20
- # will be returned read-only since they will have attributes that do not correspond to the table's columns.
21
- # Pass <tt>:readonly => false</tt> to override.
22
- # * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs.
23
- # The symbols named refer to already defined associations. When using named associations, count
24
- # returns the number of DISTINCT items for the model you're counting.
25
- # See eager loading under Associations.
26
- # * <tt>:order</tt>: An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
27
- # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
28
- # * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example,
29
- # want to do a join but not include the joined columns.
30
- # * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as
31
- # SELECT COUNT(DISTINCT posts.id) ...
32
- # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an
33
- # alternate table name (or even the name of a database view).
34
- #
35
- # Examples for counting all:
36
- # Person.count # returns the total count of all people
37
- #
38
- # Examples for counting by column:
39
- # Person.count(:age) # returns the total count of all people whose age is present in database
40
- #
41
- # Examples for count with options:
42
- # Person.count(:conditions => "age > 26")
43
- #
44
- # # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN.
45
- # Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job)
46
- #
47
- # # finds the number of rows matching the conditions and joins.
48
- # Person.count(:conditions => "age > 26 AND job.salary > 60000",
49
- # :joins => "LEFT JOIN jobs on jobs.person_id = person.id")
50
- #
51
- # Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
52
- # Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
53
- #
54
- # Note: <tt>Person.count(:all)</tt> will not work because it will use <tt>:all</tt> as the condition.
55
- # Use Person.count instead.
56
- def count(column_name = nil, options = {})
57
- column_name, options = nil, column_name if column_name.is_a?(Hash)
58
- calculate(:count, column_name, options)
3
+ # Count the records.
4
+ #
5
+ # Person.count
6
+ # # => the total count of all people
7
+ #
8
+ # Person.count(:age)
9
+ # # => returns the total count of all people whose age is present in database
10
+ #
11
+ # Person.count(:all)
12
+ # # => performs a COUNT(*) (:all is an alias for '*')
13
+ #
14
+ # Person.distinct.count(:age)
15
+ # # => counts the number of different age values
16
+ #
17
+ # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group],
18
+ # it returns a Hash whose keys represent the aggregated column,
19
+ # and the values are the respective amounts:
20
+ #
21
+ # Person.group(:city).count
22
+ # # => { 'Rome' => 5, 'Paris' => 3 }
23
+ #
24
+ # If #count is used with {Relation#group}[rdoc-ref:QueryMethods#group] for multiple columns, it returns a Hash whose
25
+ # keys are an array containing the individual values of each column and the value
26
+ # of each key would be the #count.
27
+ #
28
+ # Article.group(:status, :category).count
29
+ # # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
30
+ # ["published", "business"]=>0, ["published", "technology"]=>2}
31
+ #
32
+ # If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
33
+ #
34
+ # Person.select(:age).count
35
+ # # => counts the number of different age values
36
+ #
37
+ # Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
38
+ # between databases. In invalid cases, an error from the database is thrown.
39
+ def count(column_name = nil)
40
+ calculate(:count, column_name)
59
41
  end
60
42
 
61
43
  # Calculates the average value on a given column. Returns +nil+ if there's
62
- # no row. See +calculate+ for examples with options.
44
+ # no row. See #calculate for examples with options.
63
45
  #
64
- # Person.average('age') # => 35.8
65
- def average(column_name, options = {})
66
- calculate(:average, column_name, options)
46
+ # Person.average(:age) # => 35.8
47
+ def average(column_name)
48
+ calculate(:average, column_name)
67
49
  end
68
50
 
69
51
  # Calculates the minimum value on a given column. The value is returned
70
52
  # with the same data type of the column, or +nil+ if there's no row. See
71
- # +calculate+ for examples with options.
53
+ # #calculate for examples with options.
72
54
  #
73
- # Person.minimum('age') # => 7
74
- def minimum(column_name, options = {})
75
- calculate(:minimum, column_name, options)
55
+ # Person.minimum(:age) # => 7
56
+ def minimum(column_name)
57
+ calculate(:minimum, column_name)
76
58
  end
77
59
 
78
60
  # Calculates the maximum value on a given column. The value is returned
79
61
  # with the same data type of the column, or +nil+ if there's no row. See
80
- # +calculate+ for examples with options.
62
+ # #calculate for examples with options.
81
63
  #
82
- # Person.maximum('age') # => 93
83
- def maximum(column_name, options = {})
84
- calculate(:maximum, column_name, options)
64
+ # Person.maximum(:age) # => 93
65
+ def maximum(column_name)
66
+ calculate(:maximum, column_name)
85
67
  end
86
68
 
87
69
  # Calculates the sum of values on a given column. The value is returned
88
- # with the same data type of the column, 0 if there's no row. See
89
- # +calculate+ for examples with options.
70
+ # with the same data type of the column, +0+ if there's no row. See
71
+ # #calculate for examples with options.
90
72
  #
91
- # Person.sum('age') # => 4562
92
- def sum(*args)
93
- if block_given?
94
- self.to_a.sum(*args) {|*block_args| yield(*block_args)}
95
- else
96
- calculate(:sum, *args)
97
- end
73
+ # Person.sum(:age) # => 4562
74
+ def sum(column_name = nil, &block)
75
+ return super(&block) if block_given?
76
+ calculate(:sum, column_name)
98
77
  end
99
78
 
100
- # This calculates aggregate values in the given column. Methods for count, sum, average,
101
- # minimum, and maximum have been added as shortcuts. Options such as <tt>:conditions</tt>,
102
- # <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
79
+ # This calculates aggregate values in the given column. Methods for #count, #sum, #average,
80
+ # #minimum, and #maximum have been added as shortcuts.
103
81
  #
104
- # There are two basic forms of output:
105
- # * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
106
- # for AVG, and the given column's type for everything else.
107
- # * Grouped values: This returns an ordered hash of the values and groups them by the
108
- # <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
109
- #
110
- # values = Person.maximum(:age, :group => 'last_name')
111
- # puts values["Drake"]
112
- # => 43
113
- #
114
- # drake = Family.find_by_last_name('Drake')
115
- # values = Person.maximum(:age, :group => :family) # Person belongs_to :family
116
- # puts values[drake]
117
- # => 43
118
- #
119
- # values.each do |family, max_age|
120
- # ...
121
- # end
122
- #
123
- # Options:
124
- # * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
125
- # See conditions in the intro to ActiveRecord::Base.
126
- # * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
127
- # the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
128
- # * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
129
- # (Rarely needed).
130
- # The records will be returned read-only since they will have attributes that do not correspond to the
131
- # table's columns.
132
- # * <tt>:order</tt> - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
133
- # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
134
- # * <tt>:select</tt> - By default, this is * as in SELECT * FROM, but can be changed if you for example
135
- # want to do a join, but not include the joined columns.
136
- # * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as
137
- # SELECT COUNT(DISTINCT posts.id) ...
138
- #
139
- # Examples:
140
82
  # Person.calculate(:count, :all) # The same as Person.count
141
83
  # Person.average(:age) # SELECT AVG(age) FROM people...
142
- # Person.minimum(:age, :conditions => ['last_name != ?', 'Drake']) # Selects the minimum age for
143
- # # everyone with a last name other than 'Drake'
144
84
  #
145
85
  # # Selects the minimum age for any family without any minors
146
- # Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name)
86
+ # Person.group(:last_name).having("min(age) > 17").minimum(:age)
147
87
  #
148
88
  # Person.sum("2 * age")
149
- def calculate(operation, column_name, options = {})
150
- if options.except(:distinct).present?
151
- apply_finder_options(options.except(:distinct)).calculate(operation, column_name, :distinct => options[:distinct])
152
- else
153
- relation = with_default_scope
89
+ #
90
+ # There are two basic forms of output:
91
+ #
92
+ # * Single aggregate value: The single value is type cast to Integer for COUNT, Float
93
+ # for AVG, and the given column's type for everything else.
94
+ #
95
+ # * Grouped values: This returns an ordered hash of the values and groups them. It
96
+ # takes either a column name, or the name of a belongs_to association.
97
+ #
98
+ # values = Person.group('last_name').maximum(:age)
99
+ # puts values["Drake"]
100
+ # # => 43
101
+ #
102
+ # drake = Family.find_by(last_name: 'Drake')
103
+ # values = Person.group(:family).maximum(:age) # Person belongs_to :family
104
+ # puts values[drake]
105
+ # # => 43
106
+ #
107
+ # values.each do |family, max_age|
108
+ # ...
109
+ # end
110
+ def calculate(operation, column_name)
111
+ if column_name.is_a?(Symbol) && attribute_alias?(column_name)
112
+ column_name = attribute_alias(column_name)
113
+ end
154
114
 
155
- if relation.equal?(self)
156
- if eager_loading? || (includes_values.present? && references_eager_loaded_tables?)
157
- construct_relation_for_association_calculations.calculate(operation, column_name, options)
158
- else
159
- perform_calculation(operation, column_name, options)
160
- end
161
- else
162
- relation.calculate(operation, column_name, options)
163
- end
115
+ if has_include?(column_name)
116
+ construct_relation_for_association_calculations.calculate(operation, column_name)
117
+ else
118
+ perform_calculation(operation, column_name)
164
119
  end
165
- rescue ThrowResult
166
- 0
167
120
  end
168
121
 
169
- # This method is designed to perform select by a single column as direct SQL query
170
- # Returns <tt>Array</tt> with values of the specified column name
171
- # The values has same data type as column.
122
+ # Use #pluck as a shortcut to select one or more attributes without
123
+ # loading a bunch of records just to grab the attributes you want.
124
+ #
125
+ # Person.pluck(:name)
172
126
  #
173
- # Examples:
127
+ # instead of
174
128
  #
175
- # Person.pluck(:id) # SELECT people.id FROM people
176
- # Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
177
- # Person.where(:confirmed => true).limit(5).pluck(:id)
129
+ # Person.all.map(&:name)
178
130
  #
179
- def pluck(column_name)
180
- if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
181
- column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
131
+ # Pluck returns an Array of attribute values type-casted to match
132
+ # the plucked column names, if they can be deduced. Plucking an SQL fragment
133
+ # returns String values by default.
134
+ #
135
+ # Person.pluck(:name)
136
+ # # SELECT people.name FROM people
137
+ # # => ['David', 'Jeremy', 'Jose']
138
+ #
139
+ # Person.pluck(:id, :name)
140
+ # # SELECT people.id, people.name FROM people
141
+ # # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
142
+ #
143
+ # Person.distinct.pluck(:role)
144
+ # # SELECT DISTINCT role FROM people
145
+ # # => ['admin', 'member', 'guest']
146
+ #
147
+ # Person.where(age: 21).limit(5).pluck(:id)
148
+ # # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
149
+ # # => [2, 3]
150
+ #
151
+ # Person.pluck('DATEDIFF(updated_at, created_at)')
152
+ # # SELECT DATEDIFF(updated_at, created_at) FROM people
153
+ # # => ['0', '27761', '173']
154
+ #
155
+ # See also #ids.
156
+ #
157
+ def pluck(*column_names)
158
+ if loaded? && (column_names.map(&:to_s) - @klass.attribute_names - @klass.attribute_aliases.keys).empty?
159
+ return @records.pluck(*column_names)
182
160
  end
183
161
 
184
- result = klass.connection.exec_query(select(column_name).to_sql)
185
- last_column = result.columns.last
186
-
187
- result.map do |attributes|
188
- klass.type_cast_attribute(last_column, klass.initialize_attributes(attributes))
162
+ if has_include?(column_names.first)
163
+ construct_relation_for_association_calculations.pluck(*column_names)
164
+ else
165
+ relation = spawn
166
+ relation.select_values = column_names.map { |cn|
167
+ @klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
168
+ }
169
+ result = klass.connection.select_all(relation.arel, nil, bound_attributes)
170
+ result.cast_values(klass.attribute_types)
189
171
  end
190
172
  end
191
173
 
174
+ # Pluck all the ID's for the relation using the table's primary key
175
+ #
176
+ # Person.ids # SELECT people.id FROM people
177
+ # Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id
178
+ def ids
179
+ pluck primary_key
180
+ end
181
+
192
182
  private
193
183
 
194
- def perform_calculation(operation, column_name, options = {})
184
+ def has_include?(column_name)
185
+ eager_loading? || (includes_values.present? && column_name && column_name != :all)
186
+ end
187
+
188
+ def perform_calculation(operation, column_name)
195
189
  operation = operation.to_s.downcase
196
190
 
197
- # If #count is used in conjuction with #uniq it is considered distinct. (eg. relation.uniq.count)
198
- distinct = options[:distinct] || self.uniq_value
191
+ # If #count is used with #distinct (i.e. `relation.distinct.count`) it is
192
+ # considered distinct.
193
+ distinct = self.distinct_value
199
194
 
200
195
  if operation == "count"
201
- column_name ||= (select_for_count || :all)
196
+ column_name ||= select_for_count
202
197
 
203
198
  unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
204
199
  distinct = true
205
200
  end
206
201
 
207
202
  column_name = primary_key if column_name == :all && distinct
208
-
209
- distinct = nil if column_name =~ /\s*DISTINCT\s+/i
203
+ distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
210
204
  end
211
205
 
212
- if @group_values.any?
206
+ if group_values.any?
213
207
  execute_grouped_calculation(operation, column_name, distinct)
214
208
  else
215
209
  execute_simple_calculation(operation, column_name, distinct)
@@ -217,6 +211,8 @@ module ActiveRecord
217
211
  end
218
212
 
219
213
  def aggregate_column(column_name)
214
+ return column_name if Arel::Expressions === column_name
215
+
220
216
  if @klass.column_names.include?(column_name.to_s)
221
217
  Arel::Attribute.new(@klass.unscoped.table, column_name)
222
218
  else
@@ -229,8 +225,10 @@ module ActiveRecord
229
225
  end
230
226
 
231
227
  def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
232
- # Postgresql doesn't like ORDER BY when there are no GROUP BY
233
- relation = reorder(nil)
228
+ # PostgreSQL doesn't like ORDER BY when there are no GROUP BY
229
+ relation = unscope(:order)
230
+
231
+ column_alias = column_name
234
232
 
235
233
  if operation == "count" && (relation.limit_value || relation.offset_value)
236
234
  # Shortcut when limit is zero.
@@ -242,36 +240,42 @@ module ActiveRecord
242
240
 
243
241
  select_value = operation_over_aggregate_column(column, operation, distinct)
244
242
 
243
+ column_alias = select_value.alias
244
+ column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
245
245
  relation.select_values = [select_value]
246
246
 
247
247
  query_builder = relation.arel
248
248
  end
249
249
 
250
- type_cast_calculated_value(@klass.connection.select_value(query_builder), column_for(column_name), operation)
250
+ result = @klass.connection.select_all(query_builder, nil, bound_attributes)
251
+ row = result.first
252
+ value = row && row.values.first
253
+ column = result.column_types.fetch(column_alias) do
254
+ type_for(column_name)
255
+ end
256
+
257
+ type_cast_calculated_value(value, column, operation)
251
258
  end
252
259
 
253
260
  def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
254
- group_attrs = @group_values
261
+ group_attrs = group_values
255
262
 
256
263
  if group_attrs.first.respond_to?(:to_sym)
257
- association = @klass.reflect_on_association(group_attrs.first.to_sym)
258
- associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
264
+ association = @klass._reflect_on_association(group_attrs.first)
265
+ associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
259
266
  group_fields = Array(associated ? association.foreign_key : group_attrs)
260
267
  else
261
268
  group_fields = group_attrs
262
269
  end
270
+ group_fields = arel_columns(group_fields)
263
271
 
264
272
  group_aliases = group_fields.map { |field| column_alias_for(field) }
265
- group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
266
- [aliaz, column_for(field)]
267
- }
268
-
269
- group = @klass.connection.adapter_name == 'FrontBase' ? group_aliases : group_fields
273
+ group_columns = group_aliases.zip(group_fields)
270
274
 
271
275
  if operation == 'count' && column_name == :all
272
276
  aggregate_alias = 'count_all'
273
277
  else
274
- aggregate_alias = column_alias_for(operation, column_name)
278
+ aggregate_alias = column_alias_for([operation, column_name].join(' '))
275
279
  end
276
280
 
277
281
  select_values = [
@@ -280,9 +284,9 @@ module ActiveRecord
280
284
  operation,
281
285
  distinct).as(aggregate_alias)
282
286
  ]
283
- select_values += @select_values unless @having_values.empty?
287
+ select_values += select_values unless having_clause.empty?
284
288
 
285
- select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
289
+ select_values.concat group_columns.map { |aliaz, field|
286
290
  if field.respond_to?(:as)
287
291
  field.as(aliaz)
288
292
  else
@@ -290,24 +294,30 @@ module ActiveRecord
290
294
  end
291
295
  }
292
296
 
293
- relation = except(:group).group(group)
297
+ relation = except(:group)
298
+ relation.group_values = group_fields
294
299
  relation.select_values = select_values
295
300
 
296
- calculated_data = @klass.connection.select_all(relation)
301
+ calculated_data = @klass.connection.select_all(relation, nil, relation.bound_attributes)
297
302
 
298
303
  if association
299
304
  key_ids = calculated_data.collect { |row| row[group_aliases.first] }
300
- key_records = association.klass.base_class.find(key_ids)
305
+ key_records = association.klass.base_class.where(association.klass.base_class.primary_key => key_ids)
301
306
  key_records = Hash[key_records.map { |r| [r.id, r] }]
302
307
  end
303
308
 
304
- ActiveSupport::OrderedHash[calculated_data.map do |row|
305
- key = group_columns.map { |aliaz, column|
309
+ Hash[calculated_data.map do |row|
310
+ key = group_columns.map { |aliaz, col_name|
311
+ column = calculated_data.column_types.fetch(aliaz) do
312
+ type_for(col_name)
313
+ end
306
314
  type_cast_calculated_value(row[aliaz], column)
307
315
  }
308
316
  key = key.first if key.size == 1
309
317
  key = key_records[key] if associated
310
- [key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
318
+
319
+ column_type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
320
+ [key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
311
321
  end]
312
322
  end
313
323
 
@@ -318,11 +328,12 @@ module ActiveRecord
318
328
  # column_alias_for("sum(id)") # => "sum_id"
319
329
  # column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
320
330
  # column_alias_for("count(*)") # => "count_all"
321
- # column_alias_for("count", "id") # => "count_id"
322
- def column_alias_for(*keys)
323
- keys.map! {|k| k.respond_to?(:to_sql) ? k.to_sql : k}
324
- table_name = keys.join(' ')
325
- table_name.downcase!
331
+ def column_alias_for(keys)
332
+ if keys.respond_to? :name
333
+ keys = "#{keys.relation.name}.#{keys.name}"
334
+ end
335
+
336
+ table_name = keys.to_s.downcase
326
337
  table_name.gsub!(/\*/, 'all')
327
338
  table_name.gsub!(/\W+/, ' ')
328
339
  table_name.strip!
@@ -331,28 +342,26 @@ module ActiveRecord
331
342
  @klass.connection.table_alias_for(table_name)
332
343
  end
333
344
 
334
- def column_for(field)
345
+ def type_for(field)
335
346
  field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
336
- @klass.columns.detect { |c| c.name.to_s == field_name }
347
+ @klass.type_for_attribute(field_name)
337
348
  end
338
349
 
339
- def type_cast_calculated_value(value, column, operation = nil)
350
+ def type_cast_calculated_value(value, type, operation = nil)
340
351
  case operation
341
352
  when 'count' then value.to_i
342
- when 'sum' then type_cast_using_column(value || '0', column)
353
+ when 'sum' then type.deserialize(value || 0)
343
354
  when 'average' then value.respond_to?(:to_d) ? value.to_d : value
344
- else type_cast_using_column(value, column)
355
+ else type.deserialize(value)
345
356
  end
346
357
  end
347
358
 
348
- def type_cast_using_column(value, column)
349
- column ? column.type_cast(value) : value
350
- end
351
-
352
359
  def select_for_count
353
- if @select_values.present?
354
- select = @select_values.join(", ")
355
- select if select !~ /(,|\*)/
360
+ if select_values.present?
361
+ return select_values.first if select_values.one?
362
+ select_values.join(", ")
363
+ else
364
+ :all
356
365
  end
357
366
  end
358
367