activerecord 3.2.22.5 → 4.2.11.3
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1632 -609
- data/MIT-LICENSE +1 -1
- data/README.rdoc +37 -41
- data/examples/performance.rb +31 -19
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +56 -42
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -36
- data/lib/active_record/associations/association.rb +73 -55
- data/lib/active_record/associations/association_scope.rb +143 -82
- data/lib/active_record/associations/belongs_to_association.rb +65 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +125 -31
- data/lib/active_record/associations/builder/belongs_to.rb +89 -61
- data/lib/active_record/associations/builder/collection_association.rb +69 -49
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +12 -51
- data/lib/active_record/associations/builder/singular_association.rb +23 -17
- data/lib/active_record/associations/collection_association.rb +251 -177
- data/lib/active_record/associations/collection_proxy.rb +963 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +113 -22
- data/lib/active_record/associations/has_many_through_association.rb +99 -39
- data/lib/active_record/associations/has_one_association.rb +43 -20
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +76 -107
- data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
- data/lib/active_record/associations/join_dependency.rb +230 -156
- data/lib/active_record/associations/preloader/association.rb +96 -55
- data/lib/active_record/associations/preloader/collection_association.rb +3 -3
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +62 -33
- data/lib/active_record/associations/preloader.rb +101 -79
- data/lib/active_record/associations/singular_association.rb +29 -13
- data/lib/active_record/associations/through_association.rb +30 -16
- data/lib/active_record/associations.rb +463 -345
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +142 -151
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +137 -57
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +73 -106
- data/lib/active_record/attribute_methods/serialization.rb +44 -94
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
- data/lib/active_record/attribute_methods/write.rb +57 -44
- data/lib/active_record/attribute_methods.rb +301 -141
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +246 -217
- data/lib/active_record/base.rb +70 -474
- data/lib/active_record/callbacks.rb +66 -28
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +396 -219
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +261 -169
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
- data/lib/active_record/connection_adapters/column.rb +31 -245
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
- data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +430 -999
- data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +579 -0
- data/lib/active_record/counter_cache.rb +157 -105
- data/lib/active_record/dynamic_matchers.rb +119 -63
- data/lib/active_record/enum.rb +197 -0
- data/lib/active_record/errors.rb +94 -36
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +9 -5
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +302 -215
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +143 -70
- data/lib/active_record/integration.rb +65 -12
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +73 -52
- data/lib/active_record/locking/pessimistic.rb +5 -5
- data/lib/active_record/log_subscriber.rb +24 -21
- data/lib/active_record/migration/command_recorder.rb +124 -32
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +511 -213
- data/lib/active_record/model_schema.rb +91 -117
- data/lib/active_record/nested_attributes.rb +184 -130
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +276 -117
- data/lib/active_record/query_cache.rb +19 -37
- data/lib/active_record/querying.rb +28 -18
- data/lib/active_record/railtie.rb +73 -40
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +141 -416
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +1 -4
- data/lib/active_record/reflection.rb +513 -154
- data/lib/active_record/relation/batches.rb +91 -43
- data/lib/active_record/relation/calculations.rb +199 -161
- data/lib/active_record/relation/delegation.rb +116 -25
- data/lib/active_record/relation/finder_methods.rb +362 -248
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +135 -43
- data/lib/active_record/relation/query_methods.rb +928 -167
- data/lib/active_record/relation/spawn_methods.rb +48 -149
- data/lib/active_record/relation.rb +352 -207
- data/lib/active_record/result.rb +101 -10
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +56 -59
- data/lib/active_record/schema.rb +19 -13
- data/lib/active_record/schema_dumper.rb +106 -63
- data/lib/active_record/schema_migration.rb +53 -0
- data/lib/active_record/scoping/default.rb +50 -57
- data/lib/active_record/scoping/named.rb +73 -109
- data/lib/active_record/scoping.rb +58 -123
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +12 -22
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +168 -15
- data/lib/active_record/tasks/database_tasks.rb +299 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +23 -16
- data/lib/active_record/transactions.rb +125 -79
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +24 -16
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +123 -64
- data/lib/active_record/validations.rb +36 -29
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +66 -46
- data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/migration.rb +11 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +101 -45
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,68 +1,60 @@
|
|
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
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# Person.count
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
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.
|
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 +group+, it returns a Hash whose keys represent the aggregated column,
|
18
|
+
# and the values are the respective amounts:
|
19
|
+
#
|
20
|
+
# Person.group(:city).count
|
21
|
+
# # => { 'Rome' => 5, 'Paris' => 3 }
|
22
|
+
#
|
23
|
+
# If +count+ is used with +group+ for multiple columns, it returns a Hash whose
|
24
|
+
# keys are an array containing the individual values of each column and the value
|
25
|
+
# of each key would be the +count+.
|
26
|
+
#
|
27
|
+
# Article.group(:status, :category).count
|
28
|
+
# # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
|
29
|
+
# ["published", "business"]=>0, ["published", "technology"]=>2}
|
30
|
+
#
|
31
|
+
# If +count+ is used with +select+, it will count the selected columns:
|
32
|
+
#
|
33
|
+
# Person.select(:age).count
|
34
|
+
# # => counts the number of different age values
|
35
|
+
#
|
36
|
+
# Note: not all valid +select+ expressions are valid +count+ expressions. The specifics differ
|
37
|
+
# between databases. In invalid cases, an error from the database is thrown.
|
56
38
|
def count(column_name = nil, options = {})
|
57
|
-
|
39
|
+
if options.present? && !ActiveRecord.const_defined?(:DeprecatedFinders)
|
40
|
+
raise ArgumentError, "Relation#count does not support finder options anymore. " \
|
41
|
+
"Please build a scope and then call count on it or use the " \
|
42
|
+
"activerecord-deprecated_finders gem to enable this functionality."
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
# TODO: Remove options argument as soon we remove support to
|
47
|
+
# activerecord-deprecated_finders.
|
58
48
|
calculate(:count, column_name, options)
|
59
49
|
end
|
60
50
|
|
61
51
|
# Calculates the average value on a given column. Returns +nil+ if there's
|
62
52
|
# no row. See +calculate+ for examples with options.
|
63
53
|
#
|
64
|
-
# Person.average(
|
54
|
+
# Person.average(:age) # => 35.8
|
65
55
|
def average(column_name, options = {})
|
56
|
+
# TODO: Remove options argument as soon we remove support to
|
57
|
+
# activerecord-deprecated_finders.
|
66
58
|
calculate(:average, column_name, options)
|
67
59
|
end
|
68
60
|
|
@@ -70,8 +62,10 @@ module ActiveRecord
|
|
70
62
|
# with the same data type of the column, or +nil+ if there's no row. See
|
71
63
|
# +calculate+ for examples with options.
|
72
64
|
#
|
73
|
-
# Person.minimum(
|
65
|
+
# Person.minimum(:age) # => 7
|
74
66
|
def minimum(column_name, options = {})
|
67
|
+
# TODO: Remove options argument as soon we remove support to
|
68
|
+
# activerecord-deprecated_finders.
|
75
69
|
calculate(:minimum, column_name, options)
|
76
70
|
end
|
77
71
|
|
@@ -79,8 +73,10 @@ module ActiveRecord
|
|
79
73
|
# with the same data type of the column, or +nil+ if there's no row. See
|
80
74
|
# +calculate+ for examples with options.
|
81
75
|
#
|
82
|
-
# Person.maximum(
|
76
|
+
# Person.maximum(:age) # => 93
|
83
77
|
def maximum(column_name, options = {})
|
78
|
+
# TODO: Remove options argument as soon we remove support to
|
79
|
+
# activerecord-deprecated_finders.
|
84
80
|
calculate(:maximum, column_name, options)
|
85
81
|
end
|
86
82
|
|
@@ -88,128 +84,144 @@ module ActiveRecord
|
|
88
84
|
# with the same data type of the column, 0 if there's no row. See
|
89
85
|
# +calculate+ for examples with options.
|
90
86
|
#
|
91
|
-
# Person.sum(
|
87
|
+
# Person.sum(:age) # => 4562
|
92
88
|
def sum(*args)
|
93
|
-
|
94
|
-
self.to_a.sum(*args) {|*block_args| yield(*block_args)}
|
95
|
-
else
|
96
|
-
calculate(:sum, *args)
|
97
|
-
end
|
89
|
+
calculate(:sum, *args)
|
98
90
|
end
|
99
91
|
|
100
92
|
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
101
|
-
# minimum, and maximum have been added as shortcuts.
|
102
|
-
# <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
|
93
|
+
# minimum, and maximum have been added as shortcuts.
|
103
94
|
#
|
104
95
|
# There are two basic forms of output:
|
105
|
-
#
|
96
|
+
#
|
97
|
+
# * Single aggregate value: The single value is type cast to Integer for COUNT, Float
|
106
98
|
# 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
99
|
#
|
110
|
-
#
|
100
|
+
# * Grouped values: This returns an ordered hash of the values and groups them. It
|
101
|
+
# takes either a column name, or the name of a belongs_to association.
|
102
|
+
#
|
103
|
+
# values = Person.group('last_name').maximum(:age)
|
111
104
|
# puts values["Drake"]
|
112
|
-
# => 43
|
105
|
+
# # => 43
|
113
106
|
#
|
114
|
-
# drake = Family.
|
115
|
-
# values = Person.maximum(:age
|
107
|
+
# drake = Family.find_by(last_name: 'Drake')
|
108
|
+
# values = Person.group(:family).maximum(:age) # Person belongs_to :family
|
116
109
|
# puts values[drake]
|
117
|
-
# => 43
|
110
|
+
# # => 43
|
118
111
|
#
|
119
112
|
# values.each do |family, max_age|
|
120
113
|
# ...
|
121
114
|
# end
|
122
115
|
#
|
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
116
|
# Person.calculate(:count, :all) # The same as Person.count
|
141
117
|
# 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
118
|
#
|
145
119
|
# # Selects the minimum age for any family without any minors
|
146
|
-
# Person.
|
120
|
+
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
|
147
121
|
#
|
148
122
|
# Person.sum("2 * age")
|
149
123
|
def calculate(operation, column_name, options = {})
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
124
|
+
# TODO: Remove options argument as soon we remove support to
|
125
|
+
# activerecord-deprecated_finders.
|
126
|
+
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
|
127
|
+
column_name = attribute_alias(column_name)
|
128
|
+
end
|
154
129
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
perform_calculation(operation, column_name, options)
|
160
|
-
end
|
161
|
-
else
|
162
|
-
relation.calculate(operation, column_name, options)
|
163
|
-
end
|
130
|
+
if has_include?(column_name)
|
131
|
+
construct_relation_for_association_calculations.calculate(operation, column_name, options)
|
132
|
+
else
|
133
|
+
perform_calculation(operation, column_name, options)
|
164
134
|
end
|
165
|
-
rescue ThrowResult
|
166
|
-
0
|
167
135
|
end
|
168
136
|
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
137
|
+
# Use <tt>pluck</tt> as a shortcut to select one or more attributes without
|
138
|
+
# loading a bunch of records just to grab the attributes you want.
|
139
|
+
#
|
140
|
+
# Person.pluck(:name)
|
141
|
+
#
|
142
|
+
# instead of
|
143
|
+
#
|
144
|
+
# Person.all.map(&:name)
|
172
145
|
#
|
173
|
-
#
|
146
|
+
# Pluck returns an <tt>Array</tt> of attribute values type-casted to match
|
147
|
+
# the plucked column names, if they can be deduced. Plucking an SQL fragment
|
148
|
+
# returns String values by default.
|
174
149
|
#
|
175
|
-
# Person.pluck(:id)
|
176
|
-
#
|
177
|
-
#
|
150
|
+
# Person.pluck(:id)
|
151
|
+
# # SELECT people.id FROM people
|
152
|
+
# # => [1, 2, 3]
|
178
153
|
#
|
179
|
-
|
180
|
-
|
181
|
-
|
154
|
+
# Person.pluck(:id, :name)
|
155
|
+
# # SELECT people.id, people.name FROM people
|
156
|
+
# # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
|
157
|
+
#
|
158
|
+
# Person.pluck('DISTINCT role')
|
159
|
+
# # SELECT DISTINCT role FROM people
|
160
|
+
# # => ['admin', 'member', 'guest']
|
161
|
+
#
|
162
|
+
# Person.where(age: 21).limit(5).pluck(:id)
|
163
|
+
# # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
|
164
|
+
# # => [2, 3]
|
165
|
+
#
|
166
|
+
# Person.pluck('DATEDIFF(updated_at, created_at)')
|
167
|
+
# # SELECT DATEDIFF(updated_at, created_at) FROM people
|
168
|
+
# # => ['0', '27761', '173']
|
169
|
+
#
|
170
|
+
def pluck(*column_names)
|
171
|
+
column_names.map! do |column_name|
|
172
|
+
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
|
173
|
+
attribute_alias(column_name)
|
174
|
+
else
|
175
|
+
column_name.to_s
|
176
|
+
end
|
182
177
|
end
|
183
178
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
179
|
+
if has_include?(column_names.first)
|
180
|
+
construct_relation_for_association_calculations.pluck(*column_names)
|
181
|
+
else
|
182
|
+
relation = spawn
|
183
|
+
relation.select_values = column_names.map { |cn|
|
184
|
+
columns_hash.key?(cn) ? arel_table[cn] : cn
|
185
|
+
}
|
186
|
+
result = klass.connection.select_all(relation.arel, nil, relation.arel.bind_values + bind_values)
|
187
|
+
result.cast_values(klass.column_types)
|
189
188
|
end
|
190
189
|
end
|
191
190
|
|
191
|
+
# Pluck all the ID's for the relation using the table's primary key
|
192
|
+
#
|
193
|
+
# Person.ids # SELECT people.id FROM people
|
194
|
+
# Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id
|
195
|
+
def ids
|
196
|
+
pluck primary_key
|
197
|
+
end
|
198
|
+
|
192
199
|
private
|
193
200
|
|
201
|
+
def has_include?(column_name)
|
202
|
+
eager_loading? || (includes_values.present? && ((column_name && column_name != :all) || references_eager_loaded_tables?))
|
203
|
+
end
|
204
|
+
|
194
205
|
def perform_calculation(operation, column_name, options = {})
|
206
|
+
# TODO: Remove options argument as soon we remove support to
|
207
|
+
# activerecord-deprecated_finders.
|
195
208
|
operation = operation.to_s.downcase
|
196
209
|
|
197
|
-
# If #count is used
|
198
|
-
distinct =
|
210
|
+
# If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
|
211
|
+
distinct = self.distinct_value
|
199
212
|
|
200
213
|
if operation == "count"
|
201
|
-
column_name ||=
|
214
|
+
column_name ||= select_for_count
|
202
215
|
|
203
216
|
unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
|
204
217
|
distinct = true
|
205
218
|
end
|
206
219
|
|
207
220
|
column_name = primary_key if column_name == :all && distinct
|
208
|
-
|
209
|
-
distinct = nil if column_name =~ /\s*DISTINCT\s+/i
|
221
|
+
distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
|
210
222
|
end
|
211
223
|
|
212
|
-
if
|
224
|
+
if group_values.any?
|
213
225
|
execute_grouped_calculation(operation, column_name, distinct)
|
214
226
|
else
|
215
227
|
execute_simple_calculation(operation, column_name, distinct)
|
@@ -230,48 +242,66 @@ module ActiveRecord
|
|
230
242
|
|
231
243
|
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
|
232
244
|
# Postgresql doesn't like ORDER BY when there are no GROUP BY
|
233
|
-
relation =
|
245
|
+
relation = unscope(:order)
|
246
|
+
|
247
|
+
column_alias = column_name
|
248
|
+
|
249
|
+
bind_values = nil
|
234
250
|
|
235
251
|
if operation == "count" && (relation.limit_value || relation.offset_value)
|
236
252
|
# Shortcut when limit is zero.
|
237
253
|
return 0 if relation.limit_value == 0
|
238
254
|
|
239
255
|
query_builder = build_count_subquery(relation, column_name, distinct)
|
256
|
+
bind_values = query_builder.bind_values + relation.bind_values
|
240
257
|
else
|
241
258
|
column = aggregate_column(column_name)
|
242
259
|
|
243
260
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
244
261
|
|
262
|
+
column_alias = select_value.alias
|
263
|
+
column_alias ||= @klass.connection.column_name_for_operation(operation, select_value)
|
245
264
|
relation.select_values = [select_value]
|
246
265
|
|
247
266
|
query_builder = relation.arel
|
267
|
+
bind_values = query_builder.bind_values + relation.bind_values
|
248
268
|
end
|
249
269
|
|
250
|
-
|
270
|
+
result = @klass.connection.select_all(query_builder, nil, bind_values)
|
271
|
+
row = result.first
|
272
|
+
value = row && row.values.first
|
273
|
+
column = result.column_types.fetch(column_alias) do
|
274
|
+
type_for(column_name)
|
275
|
+
end
|
276
|
+
|
277
|
+
type_cast_calculated_value(value, column, operation)
|
251
278
|
end
|
252
279
|
|
253
280
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
254
|
-
group_attrs =
|
281
|
+
group_attrs = group_values
|
255
282
|
|
256
283
|
if group_attrs.first.respond_to?(:to_sym)
|
257
|
-
association = @klass.
|
258
|
-
associated = group_attrs.size == 1 && association && association.
|
284
|
+
association = @klass._reflect_on_association(group_attrs.first)
|
285
|
+
associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations
|
259
286
|
group_fields = Array(associated ? association.foreign_key : group_attrs)
|
260
287
|
else
|
261
288
|
group_fields = group_attrs
|
262
289
|
end
|
290
|
+
group_fields = arel_columns(group_fields)
|
263
291
|
|
264
|
-
group_aliases = group_fields.map { |field|
|
292
|
+
group_aliases = group_fields.map { |field|
|
293
|
+
column_alias_for(field)
|
294
|
+
}
|
265
295
|
group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
|
266
|
-
[aliaz,
|
296
|
+
[aliaz, field]
|
267
297
|
}
|
268
298
|
|
269
|
-
group =
|
299
|
+
group = group_fields
|
270
300
|
|
271
301
|
if operation == 'count' && column_name == :all
|
272
302
|
aggregate_alias = 'count_all'
|
273
303
|
else
|
274
|
-
aggregate_alias = column_alias_for(operation, column_name)
|
304
|
+
aggregate_alias = column_alias_for([operation, column_name].join(' '))
|
275
305
|
end
|
276
306
|
|
277
307
|
select_values = [
|
@@ -280,7 +310,7 @@ module ActiveRecord
|
|
280
310
|
operation,
|
281
311
|
distinct).as(aggregate_alias)
|
282
312
|
]
|
283
|
-
select_values +=
|
313
|
+
select_values += self.select_values unless having_values.empty?
|
284
314
|
|
285
315
|
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
286
316
|
if field.respond_to?(:as)
|
@@ -290,10 +320,11 @@ module ActiveRecord
|
|
290
320
|
end
|
291
321
|
}
|
292
322
|
|
293
|
-
relation = except(:group)
|
323
|
+
relation = except(:group)
|
324
|
+
relation.group_values = group
|
294
325
|
relation.select_values = select_values
|
295
326
|
|
296
|
-
calculated_data = @klass.connection.select_all(relation)
|
327
|
+
calculated_data = @klass.connection.select_all(relation, nil, relation.arel.bind_values + bind_values)
|
297
328
|
|
298
329
|
if association
|
299
330
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
@@ -301,13 +332,18 @@ module ActiveRecord
|
|
301
332
|
key_records = Hash[key_records.map { |r| [r.id, r] }]
|
302
333
|
end
|
303
334
|
|
304
|
-
|
305
|
-
key = group_columns.map { |aliaz,
|
335
|
+
Hash[calculated_data.map do |row|
|
336
|
+
key = group_columns.map { |aliaz, col_name|
|
337
|
+
column = calculated_data.column_types.fetch(aliaz) do
|
338
|
+
type_for(col_name)
|
339
|
+
end
|
306
340
|
type_cast_calculated_value(row[aliaz], column)
|
307
341
|
}
|
308
342
|
key = key.first if key.size == 1
|
309
343
|
key = key_records[key] if associated
|
310
|
-
|
344
|
+
|
345
|
+
column_type = calculated_data.column_types.fetch(aggregate_alias) { type_for(column_name) }
|
346
|
+
[key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
|
311
347
|
end]
|
312
348
|
end
|
313
349
|
|
@@ -319,10 +355,12 @@ module ActiveRecord
|
|
319
355
|
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
320
356
|
# column_alias_for("count(*)") # => "count_all"
|
321
357
|
# column_alias_for("count", "id") # => "count_id"
|
322
|
-
def column_alias_for(
|
323
|
-
keys.
|
324
|
-
|
325
|
-
|
358
|
+
def column_alias_for(keys)
|
359
|
+
if keys.respond_to? :name
|
360
|
+
keys = "#{keys.relation.name}.#{keys.name}"
|
361
|
+
end
|
362
|
+
|
363
|
+
table_name = keys.to_s.downcase
|
326
364
|
table_name.gsub!(/\*/, 'all')
|
327
365
|
table_name.gsub!(/\W+/, ' ')
|
328
366
|
table_name.strip!
|
@@ -331,28 +369,26 @@ module ActiveRecord
|
|
331
369
|
@klass.connection.table_alias_for(table_name)
|
332
370
|
end
|
333
371
|
|
334
|
-
def
|
372
|
+
def type_for(field)
|
335
373
|
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
|
336
|
-
@klass.
|
374
|
+
@klass.type_for_attribute(field_name)
|
337
375
|
end
|
338
376
|
|
339
|
-
def type_cast_calculated_value(value,
|
377
|
+
def type_cast_calculated_value(value, type, operation = nil)
|
340
378
|
case operation
|
341
379
|
when 'count' then value.to_i
|
342
|
-
when 'sum' then
|
380
|
+
when 'sum' then type.type_cast_from_database(value || 0)
|
343
381
|
when 'average' then value.respond_to?(:to_d) ? value.to_d : value
|
344
|
-
else
|
382
|
+
else type.type_cast_from_database(value)
|
345
383
|
end
|
346
384
|
end
|
347
385
|
|
348
|
-
|
349
|
-
column ? column.type_cast(value) : value
|
350
|
-
end
|
351
|
-
|
386
|
+
# TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
|
352
387
|
def select_for_count
|
353
|
-
if
|
354
|
-
|
355
|
-
|
388
|
+
if select_values.present?
|
389
|
+
select_values.join(", ")
|
390
|
+
else
|
391
|
+
:all
|
356
392
|
end
|
357
393
|
end
|
358
394
|
|
@@ -362,9 +398,11 @@ module ActiveRecord
|
|
362
398
|
|
363
399
|
aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
|
364
400
|
relation.select_values = [aliased_column]
|
365
|
-
|
401
|
+
arel = relation.arel
|
402
|
+
subquery = arel.as(subquery_alias)
|
366
403
|
|
367
404
|
sm = Arel::SelectManager.new relation.engine
|
405
|
+
sm.bind_values = arel.bind_values
|
368
406
|
select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
|
369
407
|
sm.project(select_value).from(subquery)
|
370
408
|
end
|