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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1715 -604
- data/MIT-LICENSE +2 -2
- data/README.rdoc +40 -45
- data/examples/performance.rb +33 -22
- data/examples/simple.rb +3 -4
- data/lib/active_record/aggregations.rb +76 -51
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +54 -40
- data/lib/active_record/associations/association.rb +76 -56
- data/lib/active_record/associations/association_scope.rb +125 -93
- data/lib/active_record/associations/belongs_to_association.rb +57 -28
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +120 -32
- data/lib/active_record/associations/builder/belongs_to.rb +115 -62
- data/lib/active_record/associations/builder/collection_association.rb +61 -53
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
- data/lib/active_record/associations/builder/has_many.rb +9 -65
- data/lib/active_record/associations/builder/has_one.rb +18 -52
- data/lib/active_record/associations/builder/singular_association.rb +18 -19
- data/lib/active_record/associations/collection_association.rb +268 -186
- data/lib/active_record/associations/collection_proxy.rb +1003 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +81 -41
- data/lib/active_record/associations/has_many_through_association.rb +76 -55
- data/lib/active_record/associations/has_one_association.rb +51 -21
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
- 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 +239 -155
- data/lib/active_record/associations/preloader/association.rb +97 -62
- data/lib/active_record/associations/preloader/collection_association.rb +2 -8
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +75 -33
- data/lib/active_record/associations/preloader.rb +111 -79
- data/lib/active_record/associations/singular_association.rb +35 -13
- data/lib/active_record/associations/through_association.rb +41 -19
- data/lib/active_record/associations.rb +727 -501
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +213 -0
- data/lib/active_record/attribute_assignment.rb +32 -162
- data/lib/active_record/attribute_decorators.rb +67 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +101 -61
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +7 -6
- data/lib/active_record/attribute_methods/read.rb +56 -117
- data/lib/active_record/attribute_methods/serialization.rb +43 -96
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
- data/lib/active_record/attribute_methods/write.rb +34 -45
- data/lib/active_record/attribute_methods.rb +333 -144
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +108 -0
- data/lib/active_record/attribute_set.rb +108 -0
- data/lib/active_record/attributes.rb +265 -0
- data/lib/active_record/autosave_association.rb +285 -223
- data/lib/active_record/base.rb +95 -490
- data/lib/active_record/callbacks.rb +95 -61
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +28 -19
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
- data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
- data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
- data/lib/active_record/connection_adapters/column.rb +30 -259
- data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
- data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -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 +48 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -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/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -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 +31 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
- data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +155 -0
- data/lib/active_record/core.rb +561 -0
- data/lib/active_record/counter_cache.rb +146 -105
- data/lib/active_record/dynamic_matchers.rb +101 -64
- data/lib/active_record/enum.rb +234 -0
- data/lib/active_record/errors.rb +153 -56
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +10 -6
- data/lib/active_record/fixture_set/file.rb +77 -0
- data/lib/active_record/fixtures.rb +355 -232
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +144 -79
- data/lib/active_record/integration.rb +66 -13
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +46 -0
- data/lib/active_record/locale/en.yml +9 -1
- data/lib/active_record/locking/optimistic.rb +77 -56
- data/lib/active_record/locking/pessimistic.rb +6 -6
- data/lib/active_record/log_subscriber.rb +53 -28
- data/lib/active_record/migration/command_recorder.rb +166 -33
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +792 -264
- data/lib/active_record/model_schema.rb +192 -130
- data/lib/active_record/nested_attributes.rb +238 -145
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +89 -0
- data/lib/active_record/persistence.rb +357 -157
- data/lib/active_record/query_cache.rb +22 -43
- data/lib/active_record/querying.rb +34 -23
- data/lib/active_record/railtie.rb +88 -48
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +5 -4
- data/lib/active_record/railties/databases.rake +170 -422
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -5
- data/lib/active_record/reflection.rb +715 -189
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +203 -50
- data/lib/active_record/relation/calculations.rb +203 -194
- data/lib/active_record/relation/delegation.rb +103 -25
- data/lib/active_record/relation/finder_methods.rb +457 -261
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +167 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +153 -48
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +1019 -194
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +46 -150
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +450 -245
- data/lib/active_record/result.rb +104 -12
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +120 -94
- data/lib/active_record/schema.rb +28 -18
- data/lib/active_record/schema_dumper.rb +141 -74
- data/lib/active_record/schema_migration.rb +50 -0
- data/lib/active_record/scoping/default.rb +64 -57
- data/lib/active_record/scoping/named.rb +93 -108
- data/lib/active_record/scoping.rb +73 -121
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +7 -5
- data/lib/active_record/statement_cache.rb +113 -0
- data/lib/active_record/store.rb +173 -15
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +313 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
- data/lib/active_record/timestamp.rb +42 -24
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +233 -105
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +7 -0
- data/lib/active_record/type/date_time.rb +7 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +63 -0
- data/lib/active_record/type/time.rb +20 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type.rb +72 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +33 -18
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +66 -0
- data/lib/active_record/validations/uniqueness.rb +128 -68
- data/lib/active_record/validations.rb +48 -40
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +71 -47
- data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
- data/lib/rails/generators/active_record/migration.rb +18 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +188 -134
- 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/mysql_adapter.rb +0 -441
- 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/serializers/xml_serializer.rb +0 -203
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
- 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,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
|
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
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
|
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
|
44
|
+
# no row. See #calculate for examples with options.
|
63
45
|
#
|
64
|
-
# Person.average(
|
65
|
-
def average(column_name
|
66
|
-
calculate(:average, column_name
|
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
|
-
#
|
53
|
+
# #calculate for examples with options.
|
72
54
|
#
|
73
|
-
# Person.minimum(
|
74
|
-
def minimum(column_name
|
75
|
-
calculate(:minimum, column_name
|
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
|
-
#
|
62
|
+
# #calculate for examples with options.
|
81
63
|
#
|
82
|
-
# Person.maximum(
|
83
|
-
def maximum(column_name
|
84
|
-
calculate(:maximum, column_name
|
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
|
-
#
|
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(
|
92
|
-
def sum(
|
93
|
-
if block_given?
|
94
|
-
|
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.
|
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.
|
86
|
+
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
|
147
87
|
#
|
148
88
|
# Person.sum("2 * age")
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
-
#
|
170
|
-
#
|
171
|
-
#
|
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
|
-
#
|
127
|
+
# instead of
|
174
128
|
#
|
175
|
-
# Person.
|
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
|
-
|
180
|
-
|
181
|
-
|
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
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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
|
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
|
198
|
-
|
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 ||=
|
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
|
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
|
-
#
|
233
|
-
relation =
|
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
|
-
|
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 =
|
261
|
+
group_attrs = group_values
|
255
262
|
|
256
263
|
if group_attrs.first.respond_to?(:to_sym)
|
257
|
-
association = @klass.
|
258
|
-
associated = group_attrs.size == 1 && association && association.
|
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)
|
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 +=
|
287
|
+
select_values += select_values unless having_clause.empty?
|
284
288
|
|
285
|
-
select_values.concat
|
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)
|
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.
|
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
|
-
|
305
|
-
key = group_columns.map { |aliaz,
|
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
|
-
|
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
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
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
|
345
|
+
def type_for(field)
|
335
346
|
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
|
336
|
-
@klass.
|
347
|
+
@klass.type_for_attribute(field_name)
|
337
348
|
end
|
338
349
|
|
339
|
-
def type_cast_calculated_value(value,
|
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
|
353
|
+
when 'sum' then type.deserialize(value || 0)
|
343
354
|
when 'average' then value.respond_to?(:to_d) ? value.to_d : value
|
344
|
-
else
|
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
|
354
|
-
|
355
|
-
|
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
|
|