activerecord 1.15.6 → 2.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.
- data/CHANGELOG +2454 -34
- data/README +1 -1
- data/RUNNING_UNIT_TESTS +3 -34
- data/Rakefile +98 -77
- data/install.rb +1 -1
- data/lib/active_record.rb +13 -22
- data/lib/active_record/aggregations.rb +38 -49
- data/lib/active_record/associations.rb +452 -333
- data/lib/active_record/associations/association_collection.rb +66 -20
- data/lib/active_record/associations/association_proxy.rb +9 -8
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +38 -18
- data/lib/active_record/associations/has_one_association.rb +30 -14
- data/lib/active_record/attribute_methods.rb +253 -0
- data/lib/active_record/base.rb +719 -494
- data/lib/active_record/calculations.rb +62 -63
- data/lib/active_record/callbacks.rb +57 -83
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
- data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
- data/lib/active_record/fixtures.rb +503 -113
- data/lib/active_record/locking/optimistic.rb +72 -34
- data/lib/active_record/migration.rb +80 -57
- data/lib/active_record/observer.rb +13 -10
- data/lib/active_record/query_cache.rb +16 -57
- data/lib/active_record/reflection.rb +35 -38
- data/lib/active_record/schema.rb +5 -5
- data/lib/active_record/schema_dumper.rb +35 -13
- data/lib/active_record/serialization.rb +98 -0
- data/lib/active_record/serializers/json_serializer.rb +71 -0
- data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
- data/lib/active_record/timestamp.rb +20 -21
- data/lib/active_record/transactions.rb +39 -43
- data/lib/active_record/validations.rb +256 -107
- data/lib/active_record/version.rb +3 -3
- data/lib/activerecord.rb +1 -0
- data/test/aaa_create_tables_test.rb +15 -2
- data/test/abstract_unit.rb +24 -17
- data/test/active_schema_test_mysql.rb +20 -8
- data/test/adapter_test.rb +23 -5
- data/test/adapter_test_sqlserver.rb +15 -1
- data/test/aggregations_test.rb +16 -1
- data/test/all.sh +2 -2
- data/test/associations/ar_joins_test.rb +0 -0
- data/test/associations/callbacks_test.rb +51 -30
- data/test/associations/cascaded_eager_loading_test.rb +1 -29
- data/test/associations/eager_singularization_test.rb +145 -0
- data/test/associations/eager_test.rb +42 -6
- data/test/associations/extension_test.rb +6 -1
- data/test/associations/inner_join_association_test.rb +88 -0
- data/test/associations/join_model_test.rb +47 -16
- data/test/associations_test.rb +449 -226
- data/test/attribute_methods_test.rb +97 -0
- data/test/base_test.rb +251 -105
- data/test/binary_test.rb +22 -27
- data/test/calculations_test.rb +37 -5
- data/test/callbacks_test.rb +23 -0
- data/test/connection_test_firebird.rb +2 -2
- data/test/connection_test_mysql.rb +30 -0
- data/test/connections/native_mysql/connection.rb +3 -0
- data/test/connections/native_sqlite/connection.rb +5 -14
- data/test/connections/native_sqlite3/connection.rb +5 -14
- data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
- data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
- data/test/datatype_test_postgresql.rb +178 -27
- data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
- data/test/defaults_test.rb +8 -1
- data/test/deprecated_finder_test.rb +7 -128
- data/test/finder_test.rb +192 -54
- data/test/fixtures/all/developers.yml +0 -0
- data/test/fixtures/all/people.csv +0 -0
- data/test/fixtures/all/tasks.yml +0 -0
- data/test/fixtures/author.rb +12 -5
- data/test/fixtures/binaries.yml +130 -435
- data/test/fixtures/category.rb +6 -0
- data/test/fixtures/company.rb +8 -1
- data/test/fixtures/computer.rb +1 -0
- data/test/fixtures/contact.rb +16 -0
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +4 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
- data/test/fixtures/db_definitions/firebird.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase.sql +5 -0
- data/test/fixtures/db_definitions/openbase.sql +41 -25
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +5 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
- data/test/fixtures/db_definitions/postgresql.sql +87 -58
- data/test/fixtures/db_definitions/postgresql2.sql +1 -2
- data/test/fixtures/db_definitions/schema.rb +280 -0
- data/test/fixtures/db_definitions/schema2.rb +11 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +4 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
- data/test/fixtures/db_definitions/sybase.sql +4 -0
- data/test/fixtures/developer.rb +10 -0
- data/test/fixtures/example.log +1 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/item.rb +7 -0
- data/test/fixtures/items.yml +4 -0
- data/test/fixtures/joke.rb +0 -3
- data/test/fixtures/matey.rb +4 -0
- data/test/fixtures/mateys.yml +4 -0
- data/test/fixtures/minimalistic.rb +2 -0
- data/test/fixtures/minimalistics.yml +2 -0
- data/test/fixtures/mixins.yml +2 -100
- data/test/fixtures/parrot.rb +13 -0
- data/test/fixtures/parrots.yml +27 -0
- data/test/fixtures/parrots_pirates.yml +7 -0
- data/test/fixtures/pirate.rb +5 -0
- data/test/fixtures/pirates.yml +9 -0
- data/test/fixtures/post.rb +1 -0
- data/test/fixtures/project.rb +3 -2
- data/test/fixtures/reserved_words/distinct.yml +5 -0
- data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
- data/test/fixtures/reserved_words/group.yml +14 -0
- data/test/fixtures/reserved_words/select.yml +8 -0
- data/test/fixtures/reserved_words/values.yml +7 -0
- data/test/fixtures/ship.rb +3 -0
- data/test/fixtures/ships.yml +5 -0
- data/test/fixtures/tagging.rb +4 -0
- data/test/fixtures/taggings.yml +8 -1
- data/test/fixtures/topic.rb +13 -1
- data/test/fixtures/treasure.rb +4 -0
- data/test/fixtures/treasures.yml +10 -0
- data/test/fixtures_test.rb +205 -24
- data/test/inheritance_test.rb +7 -1
- data/test/json_serialization_test.rb +180 -0
- data/test/lifecycle_test.rb +1 -1
- data/test/locking_test.rb +85 -2
- data/test/migration_test.rb +206 -40
- data/test/mixin_test.rb +13 -515
- data/test/pk_test.rb +3 -6
- data/test/query_cache_test.rb +104 -0
- data/test/reflection_test.rb +16 -0
- data/test/reserved_word_test_mysql.rb +177 -0
- data/test/schema_dumper_test.rb +38 -3
- data/test/serialization_test.rb +47 -0
- data/test/transactions_test.rb +74 -23
- data/test/unconnected_test.rb +1 -1
- data/test/validations_test.rb +322 -32
- data/test/xml_serialization_test.rb +121 -44
- metadata +48 -41
- data/examples/associations.rb +0 -87
- data/examples/shared_setup.rb +0 -15
- data/examples/validation.rb +0 -85
- data/lib/active_record/acts/list.rb +0 -256
- data/lib/active_record/acts/nested_set.rb +0 -211
- data/lib/active_record/acts/tree.rb +0 -96
- data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
- data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
- data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
- data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
- data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
- data/lib/active_record/deprecated_associations.rb +0 -104
- data/lib/active_record/deprecated_finders.rb +0 -44
- data/lib/active_record/vendor/simple.rb +0 -693
- data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
- data/lib/active_record/wrappings.rb +0 -58
- data/test/connections/native_sqlserver/connection.rb +0 -23
- data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
- data/test/deprecated_associations_test.rb +0 -396
- data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
- data/test/fixtures/db_definitions/mysql.sql +0 -234
- data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
- data/test/fixtures/db_definitions/mysql2.sql +0 -5
- data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
- data/test/fixtures/db_definitions/sqlserver.sql +0 -243
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
- data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
- data/test/fixtures/mixin.rb +0 -63
- data/test/mixin_nested_set_test.rb +0 -196
@@ -6,79 +6,82 @@ module ActiveRecord
|
|
6
6
|
end
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
# Count operates using three different approaches.
|
9
|
+
# Count operates using three different approaches.
|
10
10
|
#
|
11
11
|
# * Count all: By not passing any parameters to count, it will return a count of all the rows for the model.
|
12
|
-
# * Count
|
12
|
+
# * Count using column : By passing a column name to count, it will return a count of all the rows for the model with supplied column present
|
13
13
|
# * Count using options will find the row count matched by the options used.
|
14
14
|
#
|
15
|
-
# The
|
15
|
+
# The third approach, count using options, accepts an option hash as the only parameter. The options are:
|
16
16
|
#
|
17
17
|
# * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
|
18
|
-
# * <tt>:joins</tt>:
|
19
|
-
#
|
18
|
+
# * <tt>:joins</tt>: Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
|
19
|
+
# or names associations in the same form used for the :include option.
|
20
|
+
# If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table's columns.
|
21
|
+
# Pass :readonly => false to override.
|
22
|
+
# See adding joins for associations under Associations.
|
23
|
+
#
|
20
24
|
# * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer
|
21
|
-
# to already defined associations. When using named associations count returns the number DISTINCT items for the model you're counting.
|
25
|
+
# to already defined associations. When using named associations, count returns the number of DISTINCT items for the model you're counting.
|
22
26
|
# See eager loading under Associations.
|
23
27
|
# * <tt>:order</tt>: An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
|
24
28
|
# * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
|
25
|
-
# * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join
|
29
|
+
# * <tt>:select</tt>: By default, this is * as in SELECT * FROM, but can be changed if you, for example, want to do a join but not
|
26
30
|
# include the joined columns.
|
27
31
|
# * <tt>:distinct</tt>: Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
|
28
32
|
#
|
29
33
|
# Examples for counting all:
|
30
34
|
# Person.count # returns the total count of all people
|
31
35
|
#
|
32
|
-
# Examples for
|
33
|
-
# Person.count(
|
34
|
-
# Person.find("age > 26 AND job.salary > 60000", "LEFT JOIN jobs on jobs.person_id = person.id") # returns the total number of rows matching the conditions and joins fetched by SELECT COUNT(*).
|
36
|
+
# Examples for counting by column:
|
37
|
+
# Person.count(:age) # returns the total count of all people whose age is present in database
|
35
38
|
#
|
36
39
|
# Examples for count with options:
|
37
40
|
# Person.count(:conditions => "age > 26")
|
38
41
|
# Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job) # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN.
|
39
|
-
# Person.count(:conditions => "age > 26 AND job.salary > 60000", :joins => "LEFT JOIN jobs on jobs.person_id = person.id") # finds the number of rows matching the conditions and joins.
|
42
|
+
# Person.count(:conditions => "age > 26 AND job.salary > 60000", :joins => "LEFT JOIN jobs on jobs.person_id = person.id") # finds the number of rows matching the conditions and joins.
|
40
43
|
# Person.count('id', :conditions => "age > 26") # Performs a COUNT(id)
|
41
44
|
# Person.count(:all, :conditions => "age > 26") # Performs a COUNT(*) (:all is an alias for '*')
|
42
45
|
#
|
43
46
|
# Note: Person.count(:all) will not work because it will use :all as the condition. Use Person.count instead.
|
44
47
|
def count(*args)
|
45
|
-
calculate(:count, *
|
48
|
+
calculate(:count, *construct_count_options_from_args(*args))
|
46
49
|
end
|
47
50
|
|
48
|
-
# Calculates average value on a given column. The value is returned as a float. See #calculate for examples with options.
|
51
|
+
# Calculates the average value on a given column. The value is returned as a float. See #calculate for examples with options.
|
49
52
|
#
|
50
53
|
# Person.average('age')
|
51
54
|
def average(column_name, options = {})
|
52
55
|
calculate(:avg, column_name, options)
|
53
56
|
end
|
54
57
|
|
55
|
-
# Calculates the minimum value on a given column. The value is returned with the same data type of the column
|
58
|
+
# Calculates the minimum value on a given column. The value is returned with the same data type of the column. See #calculate for examples with options.
|
56
59
|
#
|
57
60
|
# Person.minimum('age')
|
58
61
|
def minimum(column_name, options = {})
|
59
62
|
calculate(:min, column_name, options)
|
60
63
|
end
|
61
64
|
|
62
|
-
# Calculates the maximum value on a given column. The value is returned with the same data type of the column
|
65
|
+
# Calculates the maximum value on a given column. The value is returned with the same data type of the column. See #calculate for examples with options.
|
63
66
|
#
|
64
67
|
# Person.maximum('age')
|
65
68
|
def maximum(column_name, options = {})
|
66
69
|
calculate(:max, column_name, options)
|
67
70
|
end
|
68
71
|
|
69
|
-
# Calculates the sum
|
72
|
+
# Calculates the sum of values on a given column. The value is returned with the same data type of the column. See #calculate for examples with options.
|
70
73
|
#
|
71
74
|
# Person.sum('age')
|
72
75
|
def sum(column_name, options = {})
|
73
76
|
calculate(:sum, column_name, options)
|
74
77
|
end
|
75
78
|
|
76
|
-
# This calculates aggregate values in the given column
|
77
|
-
# Options such as :conditions, :order, :group, :having, and :joins can be passed to customize the query.
|
79
|
+
# This calculates aggregate values in the given column. Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
|
80
|
+
# Options such as :conditions, :order, :group, :having, and :joins can be passed to customize the query.
|
78
81
|
#
|
79
82
|
# There are two basic forms of output:
|
80
83
|
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float for AVG, and the given column's type for everything else.
|
81
|
-
# * Grouped values: This returns an ordered hash of the values and groups them by the :group option. It takes either a column name, or the name
|
84
|
+
# * Grouped values: This returns an ordered hash of the values and groups them by the :group option. It takes either a column name, or the name
|
82
85
|
# of a belongs_to association.
|
83
86
|
#
|
84
87
|
# values = Person.maximum(:age, :group => 'last_name')
|
@@ -95,14 +98,15 @@ module ActiveRecord
|
|
95
98
|
# end
|
96
99
|
#
|
97
100
|
# Options:
|
98
|
-
# * <tt>:conditions</tt
|
99
|
-
# * <tt>:
|
101
|
+
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
|
102
|
+
# * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything, the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
|
103
|
+
# * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
|
100
104
|
# The records will be returned read-only since they will have attributes that do not correspond to the table's columns.
|
101
|
-
# * <tt>:order</tt
|
102
|
-
# * <tt>:group</tt
|
103
|
-
# * <tt>:select</tt
|
105
|
+
# * <tt>:order</tt> - An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
|
106
|
+
# * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
|
107
|
+
# * <tt>:select</tt> - By default, this is * as in SELECT * FROM, but can be changed if you for example want to do a join, but not
|
104
108
|
# include the joined columns.
|
105
|
-
# * <tt>:distinct</tt
|
109
|
+
# * <tt>:distinct</tt> - Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
|
106
110
|
#
|
107
111
|
# Examples:
|
108
112
|
# Person.calculate(:count, :all) # The same as Person.count
|
@@ -125,60 +129,54 @@ module ActiveRecord
|
|
125
129
|
end
|
126
130
|
|
127
131
|
protected
|
128
|
-
def
|
132
|
+
def construct_count_options_from_args(*args)
|
129
133
|
options = {}
|
130
134
|
column_name = :all
|
131
|
-
|
135
|
+
|
132
136
|
# We need to handle
|
133
137
|
# count()
|
138
|
+
# count(:column_name=:all)
|
134
139
|
# count(options={})
|
135
140
|
# count(column_name=:all, options={})
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
# Deprecated count(conditions, joins=nil)
|
146
|
-
ActiveSupport::Deprecation.warn(
|
147
|
-
"You called count(#{args[0].inspect}, #{args[1].inspect}), which is a deprecated API call. " +
|
148
|
-
"Instead you should use count(column_name, options). Passing the conditions and joins as " +
|
149
|
-
"string parameters will be removed in Rails 2.0.", caller(2)
|
150
|
-
)
|
151
|
-
options.merge!(:conditions => args[0])
|
152
|
-
options.merge!(:joins => args[1]) if args[1]
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
141
|
+
case args.size
|
142
|
+
when 1
|
143
|
+
args[0].is_a?(Hash) ? options = args[0] : column_name = args[0]
|
144
|
+
when 2
|
145
|
+
column_name, options = args
|
146
|
+
else
|
147
|
+
raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}"
|
148
|
+
end if args.size > 0
|
149
|
+
|
156
150
|
[column_name, options]
|
157
151
|
end
|
158
152
|
|
159
153
|
def construct_calculation_sql(operation, column_name, options) #:nodoc:
|
160
154
|
operation = operation.to_s.downcase
|
161
|
-
options = options.symbolize_keys
|
162
|
-
|
155
|
+
options = options.symbolize_keys
|
156
|
+
|
163
157
|
scope = scope(:find)
|
164
158
|
merged_includes = merge_includes(scope ? scope[:include] : [], options[:include])
|
165
159
|
aggregate_alias = column_alias_for(operation, column_name)
|
166
|
-
use_workaround = !Base.connection.supports_count_distinct? && options[:distinct] && operation.to_s.downcase == 'count'
|
167
|
-
join_dependency = nil
|
168
160
|
|
169
|
-
if
|
170
|
-
|
171
|
-
|
161
|
+
if operation == 'count'
|
162
|
+
if merged_includes.any?
|
163
|
+
options[:distinct] = true
|
164
|
+
column_name = options[:select] || [connection.quote_table_name(table_name), primary_key] * '.'
|
165
|
+
end
|
166
|
+
|
167
|
+
if options[:distinct]
|
168
|
+
use_workaround = !connection.supports_count_distinct?
|
169
|
+
end
|
172
170
|
end
|
173
171
|
|
174
|
-
sql
|
172
|
+
sql = "SELECT #{operation}(#{'DISTINCT ' if options[:distinct]}#{column_name}) AS #{aggregate_alias}"
|
175
173
|
|
176
174
|
# A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT.
|
177
175
|
sql = "SELECT COUNT(*) AS #{aggregate_alias}" if use_workaround
|
178
|
-
|
176
|
+
|
179
177
|
sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
|
180
178
|
sql << " FROM (SELECT DISTINCT #{column_name}" if use_workaround
|
181
|
-
sql << " FROM #{table_name} "
|
179
|
+
sql << " FROM #{connection.quote_table_name(table_name)} "
|
182
180
|
if merged_includes.any?
|
183
181
|
join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merged_includes, options[:joins])
|
184
182
|
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
@@ -188,17 +186,17 @@ module ActiveRecord
|
|
188
186
|
add_limited_ids_condition!(sql, options, join_dependency) if join_dependency && !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit])
|
189
187
|
|
190
188
|
if options[:group]
|
191
|
-
group_key =
|
189
|
+
group_key = connection.adapter_name == 'FrontBase' ? :group_alias : :group_field
|
192
190
|
sql << " GROUP BY #{options[group_key]} "
|
193
191
|
end
|
194
192
|
|
195
193
|
if options[:group] && options[:having]
|
196
194
|
# FrontBase requires identifiers in the HAVING clause and chokes on function calls
|
197
|
-
if
|
195
|
+
if connection.adapter_name == 'FrontBase'
|
198
196
|
options[:having].downcase!
|
199
197
|
options[:having].gsub!(/#{operation}\s*\(\s*#{column_name}\s*\)/, aggregate_alias)
|
200
198
|
end
|
201
|
-
|
199
|
+
|
202
200
|
sql << " HAVING #{options[:having]} "
|
203
201
|
end
|
204
202
|
|
@@ -231,7 +229,8 @@ module ActiveRecord
|
|
231
229
|
end
|
232
230
|
|
233
231
|
calculated_data.inject(ActiveSupport::OrderedHash.new) do |all, row|
|
234
|
-
key =
|
232
|
+
key = type_cast_calculated_value(row[group_alias], group_column)
|
233
|
+
key = key_records[key] if associated
|
235
234
|
value = row[aggregate_alias]
|
236
235
|
all << [key, type_cast_calculated_value(value, column, operation)]
|
237
236
|
end
|
@@ -243,7 +242,7 @@ module ActiveRecord
|
|
243
242
|
end
|
244
243
|
|
245
244
|
# Converts a given key to the value that the database adapter returns as
|
246
|
-
#
|
245
|
+
# a usable column name.
|
247
246
|
# users.id #=> users_id
|
248
247
|
# sum(id) #=> sum_id
|
249
248
|
# count(distinct users.id) #=> count_distinct_users_id
|
@@ -261,7 +260,7 @@ module ActiveRecord
|
|
261
260
|
operation = operation.to_s.downcase
|
262
261
|
case operation
|
263
262
|
when 'count' then value.to_i
|
264
|
-
when 'avg' then value.to_f
|
263
|
+
when 'avg' then value && value.to_f
|
265
264
|
else column ? column.type_cast(value) : value
|
266
265
|
end
|
267
266
|
end
|
@@ -1,25 +1,25 @@
|
|
1
1
|
require 'observer'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
-
# Callbacks are hooks into the lifecycle of an Active Record object that
|
4
|
+
# Callbacks are hooks into the lifecycle of an Active Record object that allow you to trigger logic
|
5
5
|
# before or after an alteration of the object state. This can be used to make sure that associated and
|
6
|
-
# dependent objects are deleted when destroy is called (by overwriting before_destroy) or to massage attributes
|
7
|
-
# before they're validated (by overwriting before_validation). As an example of the callbacks initiated, consider
|
8
|
-
# the Base#save call:
|
9
|
-
#
|
10
|
-
# * (-) save
|
11
|
-
# * (-) valid
|
12
|
-
# * (1) before_validation
|
13
|
-
# * (2) before_validation_on_create
|
14
|
-
# * (-) validate
|
15
|
-
# * (-) validate_on_create
|
16
|
-
# * (3) after_validation
|
17
|
-
# * (4) after_validation_on_create
|
18
|
-
# * (5) before_save
|
19
|
-
# * (6) before_create
|
20
|
-
# * (-) create
|
21
|
-
# * (7) after_create
|
22
|
-
# * (8) after_save
|
6
|
+
# dependent objects are deleted when destroy is called (by overwriting +before_destroy+) or to massage attributes
|
7
|
+
# before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
|
8
|
+
# the <tt>Base#save</tt> call:
|
9
|
+
#
|
10
|
+
# * (-) <tt>save</tt>
|
11
|
+
# * (-) <tt>valid</tt>
|
12
|
+
# * (1) <tt>before_validation</tt>
|
13
|
+
# * (2) <tt>before_validation_on_create</tt>
|
14
|
+
# * (-) <tt>validate</tt>
|
15
|
+
# * (-) <tt>validate_on_create</tt>
|
16
|
+
# * (3) <tt>after_validation</tt>
|
17
|
+
# * (4) <tt>after_validation_on_create</tt>
|
18
|
+
# * (5) <tt>before_save</tt>
|
19
|
+
# * (6) <tt>before_create</tt>
|
20
|
+
# * (-) <tt>create</tt>
|
21
|
+
# * (7) <tt>after_create</tt>
|
22
|
+
# * (8) <tt>after_save</tt>
|
23
23
|
#
|
24
24
|
# That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the
|
25
25
|
# Active Record lifecycle.
|
@@ -62,8 +62,8 @@ module ActiveRecord
|
|
62
62
|
# before_destroy :destroy_readers
|
63
63
|
# end
|
64
64
|
#
|
65
|
-
# Now, when Topic#destroy is run only +destroy_author+ is called. When Reply#destroy is run both +destroy_author+ and
|
66
|
-
# +destroy_readers+
|
65
|
+
# Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is run, both +destroy_author+ and
|
66
|
+
# +destroy_readers+ are called. Contrast this to the situation where we've implemented the save behavior through overwriteable
|
67
67
|
# methods:
|
68
68
|
#
|
69
69
|
# class Topic < ActiveRecord::Base
|
@@ -74,9 +74,9 @@ module ActiveRecord
|
|
74
74
|
# def before_destroy() destroy_readers end
|
75
75
|
# end
|
76
76
|
#
|
77
|
-
# In that case, Reply#destroy would only run +destroy_readers+ and _not_ +destroy_author+. So use the callback macros when
|
78
|
-
# you want to ensure that a certain callback is called for the entire hierarchy and the regular overwriteable methods
|
79
|
-
# want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
|
77
|
+
# In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+. So, use the callback macros when
|
78
|
+
# you want to ensure that a certain callback is called for the entire hierarchy, and use the regular overwriteable methods
|
79
|
+
# when you want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks.
|
80
80
|
#
|
81
81
|
# *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the
|
82
82
|
# associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won't
|
@@ -143,7 +143,7 @@ module ActiveRecord
|
|
143
143
|
# before_destroy 'self.class.delete_all "parent_id = #{id}"'
|
144
144
|
# end
|
145
145
|
#
|
146
|
-
# Notice that single
|
146
|
+
# Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback is triggered. Also note that these
|
147
147
|
# inline callbacks can be stacked just like the regular ones:
|
148
148
|
#
|
149
149
|
# class Topic < ActiveRecord::Base
|
@@ -151,23 +151,23 @@ module ActiveRecord
|
|
151
151
|
# 'puts "Evaluated after parents are destroyed"'
|
152
152
|
# end
|
153
153
|
#
|
154
|
-
# == The after_find and after_initialize exceptions
|
154
|
+
# == The +after_find+ and +after_initialize+ exceptions
|
155
155
|
#
|
156
|
-
# Because after_find and after_initialize are called for each object found and instantiated by a finder, such as Base.find(:all)
|
157
|
-
# to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, after_find and
|
158
|
-
# after_initialize will only be run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
|
156
|
+
# Because +after_find+ and +after_initialize+ are called for each object found and instantiated by a finder, such as <tt>Base.find(:all)</tt>, we've had
|
157
|
+
# to implement a simple performance constraint (50% more speed on a simple test case). Unlike all the other callbacks, +after_find+ and
|
158
|
+
# +after_initialize+ will only be run if an explicit implementation is defined (<tt>def after_find</tt>). In that case, all of the
|
159
159
|
# callback types will be called.
|
160
160
|
#
|
161
|
-
# == before_validation
|
161
|
+
# == <tt>before_validation*</tt> returning statements
|
162
162
|
#
|
163
|
-
# If the returning value of a before_validation callback can be evaluated to false
|
164
|
-
# If Base#save
|
163
|
+
# If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be aborted and <tt>Base#save</tt> will return +false+.
|
164
|
+
# If <tt>Base#save!</tt> is called it will raise a +RecordNotSaved+ exception.
|
165
165
|
# Nothing will be appended to the errors object.
|
166
166
|
#
|
167
|
-
# ==
|
167
|
+
# == Canceling callbacks
|
168
168
|
#
|
169
|
-
# If a before_
|
170
|
-
# false
|
169
|
+
# If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are cancelled. If an <tt>after_*</tt> callback returns
|
170
|
+
# +false+, all the later callbacks are cancelled. Callbacks are generally run in the order they are defined, with the exception of callbacks
|
171
171
|
# defined as methods on the model, which are called last.
|
172
172
|
module Callbacks
|
173
173
|
CALLBACKS = %w(
|
@@ -177,16 +177,10 @@ module ActiveRecord
|
|
177
177
|
)
|
178
178
|
|
179
179
|
def self.included(base) #:nodoc:
|
180
|
-
base.extend
|
181
|
-
base.class_eval do
|
182
|
-
class << self
|
183
|
-
include Observable
|
184
|
-
alias_method_chain :instantiate, :callbacks
|
185
|
-
end
|
180
|
+
base.extend Observable
|
186
181
|
|
187
|
-
|
188
|
-
|
189
|
-
end
|
182
|
+
[:create_or_update, :valid?, :create, :update, :destroy].each do |method|
|
183
|
+
base.send :alias_method_chain, method, :callbacks
|
190
184
|
end
|
191
185
|
|
192
186
|
CALLBACKS.each do |method|
|
@@ -199,39 +193,16 @@ module ActiveRecord
|
|
199
193
|
end
|
200
194
|
end
|
201
195
|
|
202
|
-
|
203
|
-
def instantiate_with_callbacks(record)
|
204
|
-
object = instantiate_without_callbacks(record)
|
205
|
-
|
206
|
-
if object.respond_to_without_attributes?(:after_find)
|
207
|
-
object.send(:callback, :after_find)
|
208
|
-
end
|
209
|
-
|
210
|
-
if object.respond_to_without_attributes?(:after_initialize)
|
211
|
-
object.send(:callback, :after_initialize)
|
212
|
-
end
|
213
|
-
|
214
|
-
object
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
# Is called when the object was instantiated by one of the finders, like Base.find.
|
196
|
+
# Is called when the object was instantiated by one of the finders, like <tt>Base.find</tt>.
|
219
197
|
#def after_find() end
|
220
198
|
|
221
|
-
# Is called after the object has been instantiated by a call to Base.new
|
199
|
+
# Is called after the object has been instantiated by a call to <tt>Base.new</tt>.
|
222
200
|
#def after_initialize() end
|
223
201
|
|
224
|
-
|
225
|
-
initialize_without_callbacks(attributes)
|
226
|
-
result = yield self if block_given?
|
227
|
-
callback(:after_initialize) if respond_to_without_attributes?(:after_initialize)
|
228
|
-
result
|
229
|
-
end
|
230
|
-
|
231
|
-
# Is called _before_ Base.save (regardless of whether it's a create or update save).
|
202
|
+
# Is called _before_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
|
232
203
|
def before_save() end
|
233
204
|
|
234
|
-
# Is called _after_ Base.save (regardless of whether it's a create or update save).
|
205
|
+
# Is called _after_ <tt>Base.save</tt> (regardless of whether it's a +create+ or +update+ save).
|
235
206
|
#
|
236
207
|
# class Contact < ActiveRecord::Base
|
237
208
|
# after_save { logger.info( 'New contact saved!' ) }
|
@@ -243,11 +214,12 @@ module ActiveRecord
|
|
243
214
|
callback(:after_save)
|
244
215
|
result
|
245
216
|
end
|
217
|
+
private :create_or_update_with_callbacks
|
246
218
|
|
247
|
-
# Is called _before_ Base.save on new objects that haven't been saved yet (no record exists).
|
219
|
+
# Is called _before_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
|
248
220
|
def before_create() end
|
249
221
|
|
250
|
-
# Is called _after_ Base.save on new objects that haven't been saved yet (no record exists).
|
222
|
+
# Is called _after_ <tt>Base.save</tt> on new objects that haven't been saved yet (no record exists).
|
251
223
|
def after_create() end
|
252
224
|
def create_with_callbacks #:nodoc:
|
253
225
|
return false if callback(:before_create) == false
|
@@ -255,11 +227,12 @@ module ActiveRecord
|
|
255
227
|
callback(:after_create)
|
256
228
|
result
|
257
229
|
end
|
230
|
+
private :create_with_callbacks
|
258
231
|
|
259
|
-
# Is called _before_ Base.save on existing objects that have a record.
|
232
|
+
# Is called _before_ <tt>Base.save</tt> on existing objects that have a record.
|
260
233
|
def before_update() end
|
261
234
|
|
262
|
-
# Is called _after_ Base.save on existing objects that have a record.
|
235
|
+
# Is called _after_ <tt>Base.save</tt> on existing objects that have a record.
|
263
236
|
def after_update() end
|
264
237
|
|
265
238
|
def update_with_callbacks #:nodoc:
|
@@ -268,26 +241,27 @@ module ActiveRecord
|
|
268
241
|
callback(:after_update)
|
269
242
|
result
|
270
243
|
end
|
244
|
+
private :update_with_callbacks
|
271
245
|
|
272
|
-
# Is called _before_ Validations.validate (which is part of the Base.save call).
|
246
|
+
# Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
|
273
247
|
def before_validation() end
|
274
248
|
|
275
|
-
# Is called _after_ Validations.validate (which is part of the Base.save call).
|
249
|
+
# Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call).
|
276
250
|
def after_validation() end
|
277
251
|
|
278
|
-
# Is called _before_ Validations.validate (which is part of the Base.save call) on new objects
|
252
|
+
# Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
|
279
253
|
# that haven't been saved yet (no record exists).
|
280
254
|
def before_validation_on_create() end
|
281
255
|
|
282
|
-
# Is called _after_ Validations.validate (which is part of the Base.save call) on new objects
|
256
|
+
# Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on new objects
|
283
257
|
# that haven't been saved yet (no record exists).
|
284
258
|
def after_validation_on_create() end
|
285
259
|
|
286
|
-
# Is called _before_ Validations.validate (which is part of the Base.save call) on
|
260
|
+
# Is called _before_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
|
287
261
|
# existing objects that have a record.
|
288
262
|
def before_validation_on_update() end
|
289
263
|
|
290
|
-
# Is called _after_ Validations.validate (which is part of the Base.save call) on
|
264
|
+
# Is called _after_ <tt>Validations.validate</tt> (which is part of the <tt>Base.save</tt> call) on
|
291
265
|
# existing objects that have a record.
|
292
266
|
def after_validation_on_update() end
|
293
267
|
|
@@ -304,13 +278,13 @@ module ActiveRecord
|
|
304
278
|
return result
|
305
279
|
end
|
306
280
|
|
307
|
-
# Is called _before_ Base.destroy
|
281
|
+
# Is called _before_ <tt>Base.destroy</tt>.
|
308
282
|
#
|
309
283
|
# Note: If you need to _destroy_ or _nullify_ associated records first,
|
310
|
-
# use the
|
284
|
+
# use the <tt>:dependent</tt> option on your associations.
|
311
285
|
def before_destroy() end
|
312
286
|
|
313
|
-
# Is called _after_ Base.destroy (and all the attributes have been frozen).
|
287
|
+
# Is called _after_ <tt>Base.destroy</tt> (and all the attributes have been frozen).
|
314
288
|
#
|
315
289
|
# class Contact < ActiveRecord::Base
|
316
290
|
# after_destroy { |record| logger.info( "Contact #{record.id} was destroyed." ) }
|