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.

Files changed (185) hide show
  1. data/CHANGELOG +2454 -34
  2. data/README +1 -1
  3. data/RUNNING_UNIT_TESTS +3 -34
  4. data/Rakefile +98 -77
  5. data/install.rb +1 -1
  6. data/lib/active_record.rb +13 -22
  7. data/lib/active_record/aggregations.rb +38 -49
  8. data/lib/active_record/associations.rb +452 -333
  9. data/lib/active_record/associations/association_collection.rb +66 -20
  10. data/lib/active_record/associations/association_proxy.rb +9 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
  12. data/lib/active_record/associations/has_many_association.rb +21 -57
  13. data/lib/active_record/associations/has_many_through_association.rb +38 -18
  14. data/lib/active_record/associations/has_one_association.rb +30 -14
  15. data/lib/active_record/attribute_methods.rb +253 -0
  16. data/lib/active_record/base.rb +719 -494
  17. data/lib/active_record/calculations.rb +62 -63
  18. data/lib/active_record/callbacks.rb +57 -83
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
  30. data/lib/active_record/fixtures.rb +503 -113
  31. data/lib/active_record/locking/optimistic.rb +72 -34
  32. data/lib/active_record/migration.rb +80 -57
  33. data/lib/active_record/observer.rb +13 -10
  34. data/lib/active_record/query_cache.rb +16 -57
  35. data/lib/active_record/reflection.rb +35 -38
  36. data/lib/active_record/schema.rb +5 -5
  37. data/lib/active_record/schema_dumper.rb +35 -13
  38. data/lib/active_record/serialization.rb +98 -0
  39. data/lib/active_record/serializers/json_serializer.rb +71 -0
  40. data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
  41. data/lib/active_record/timestamp.rb +20 -21
  42. data/lib/active_record/transactions.rb +39 -43
  43. data/lib/active_record/validations.rb +256 -107
  44. data/lib/active_record/version.rb +3 -3
  45. data/lib/activerecord.rb +1 -0
  46. data/test/aaa_create_tables_test.rb +15 -2
  47. data/test/abstract_unit.rb +24 -17
  48. data/test/active_schema_test_mysql.rb +20 -8
  49. data/test/adapter_test.rb +23 -5
  50. data/test/adapter_test_sqlserver.rb +15 -1
  51. data/test/aggregations_test.rb +16 -1
  52. data/test/all.sh +2 -2
  53. data/test/associations/ar_joins_test.rb +0 -0
  54. data/test/associations/callbacks_test.rb +51 -30
  55. data/test/associations/cascaded_eager_loading_test.rb +1 -29
  56. data/test/associations/eager_singularization_test.rb +145 -0
  57. data/test/associations/eager_test.rb +42 -6
  58. data/test/associations/extension_test.rb +6 -1
  59. data/test/associations/inner_join_association_test.rb +88 -0
  60. data/test/associations/join_model_test.rb +47 -16
  61. data/test/associations_test.rb +449 -226
  62. data/test/attribute_methods_test.rb +97 -0
  63. data/test/base_test.rb +251 -105
  64. data/test/binary_test.rb +22 -27
  65. data/test/calculations_test.rb +37 -5
  66. data/test/callbacks_test.rb +23 -0
  67. data/test/connection_test_firebird.rb +2 -2
  68. data/test/connection_test_mysql.rb +30 -0
  69. data/test/connections/native_mysql/connection.rb +3 -0
  70. data/test/connections/native_sqlite/connection.rb +5 -14
  71. data/test/connections/native_sqlite3/connection.rb +5 -14
  72. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  73. data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
  74. data/test/datatype_test_postgresql.rb +178 -27
  75. data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
  76. data/test/defaults_test.rb +8 -1
  77. data/test/deprecated_finder_test.rb +7 -128
  78. data/test/finder_test.rb +192 -54
  79. data/test/fixtures/all/developers.yml +0 -0
  80. data/test/fixtures/all/people.csv +0 -0
  81. data/test/fixtures/all/tasks.yml +0 -0
  82. data/test/fixtures/author.rb +12 -5
  83. data/test/fixtures/binaries.yml +130 -435
  84. data/test/fixtures/category.rb +6 -0
  85. data/test/fixtures/company.rb +8 -1
  86. data/test/fixtures/computer.rb +1 -0
  87. data/test/fixtures/contact.rb +16 -0
  88. data/test/fixtures/customer.rb +2 -2
  89. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  90. data/test/fixtures/db_definitions/db2.sql +4 -0
  91. data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
  92. data/test/fixtures/db_definitions/firebird.sql +6 -0
  93. data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
  94. data/test/fixtures/db_definitions/frontbase.sql +5 -0
  95. data/test/fixtures/db_definitions/openbase.sql +41 -25
  96. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  97. data/test/fixtures/db_definitions/oracle.sql +5 -0
  98. data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
  99. data/test/fixtures/db_definitions/postgresql.sql +87 -58
  100. data/test/fixtures/db_definitions/postgresql2.sql +1 -2
  101. data/test/fixtures/db_definitions/schema.rb +280 -0
  102. data/test/fixtures/db_definitions/schema2.rb +11 -0
  103. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  104. data/test/fixtures/db_definitions/sqlite.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/sybase.sql +4 -0
  107. data/test/fixtures/developer.rb +10 -0
  108. data/test/fixtures/example.log +1 -0
  109. data/test/fixtures/flowers.jpg +0 -0
  110. data/test/fixtures/item.rb +7 -0
  111. data/test/fixtures/items.yml +4 -0
  112. data/test/fixtures/joke.rb +0 -3
  113. data/test/fixtures/matey.rb +4 -0
  114. data/test/fixtures/mateys.yml +4 -0
  115. data/test/fixtures/minimalistic.rb +2 -0
  116. data/test/fixtures/minimalistics.yml +2 -0
  117. data/test/fixtures/mixins.yml +2 -100
  118. data/test/fixtures/parrot.rb +13 -0
  119. data/test/fixtures/parrots.yml +27 -0
  120. data/test/fixtures/parrots_pirates.yml +7 -0
  121. data/test/fixtures/pirate.rb +5 -0
  122. data/test/fixtures/pirates.yml +9 -0
  123. data/test/fixtures/post.rb +1 -0
  124. data/test/fixtures/project.rb +3 -2
  125. data/test/fixtures/reserved_words/distinct.yml +5 -0
  126. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  127. data/test/fixtures/reserved_words/group.yml +14 -0
  128. data/test/fixtures/reserved_words/select.yml +8 -0
  129. data/test/fixtures/reserved_words/values.yml +7 -0
  130. data/test/fixtures/ship.rb +3 -0
  131. data/test/fixtures/ships.yml +5 -0
  132. data/test/fixtures/tagging.rb +4 -0
  133. data/test/fixtures/taggings.yml +8 -1
  134. data/test/fixtures/topic.rb +13 -1
  135. data/test/fixtures/treasure.rb +4 -0
  136. data/test/fixtures/treasures.yml +10 -0
  137. data/test/fixtures_test.rb +205 -24
  138. data/test/inheritance_test.rb +7 -1
  139. data/test/json_serialization_test.rb +180 -0
  140. data/test/lifecycle_test.rb +1 -1
  141. data/test/locking_test.rb +85 -2
  142. data/test/migration_test.rb +206 -40
  143. data/test/mixin_test.rb +13 -515
  144. data/test/pk_test.rb +3 -6
  145. data/test/query_cache_test.rb +104 -0
  146. data/test/reflection_test.rb +16 -0
  147. data/test/reserved_word_test_mysql.rb +177 -0
  148. data/test/schema_dumper_test.rb +38 -3
  149. data/test/serialization_test.rb +47 -0
  150. data/test/transactions_test.rb +74 -23
  151. data/test/unconnected_test.rb +1 -1
  152. data/test/validations_test.rb +322 -32
  153. data/test/xml_serialization_test.rb +121 -44
  154. metadata +48 -41
  155. data/examples/associations.rb +0 -87
  156. data/examples/shared_setup.rb +0 -15
  157. data/examples/validation.rb +0 -85
  158. data/lib/active_record/acts/list.rb +0 -256
  159. data/lib/active_record/acts/nested_set.rb +0 -211
  160. data/lib/active_record/acts/tree.rb +0 -96
  161. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
  162. data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
  163. data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
  164. data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
  165. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
  166. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
  167. data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
  168. data/lib/active_record/deprecated_associations.rb +0 -104
  169. data/lib/active_record/deprecated_finders.rb +0 -44
  170. data/lib/active_record/vendor/simple.rb +0 -693
  171. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  172. data/lib/active_record/wrappings.rb +0 -58
  173. data/test/connections/native_sqlserver/connection.rb +0 -23
  174. data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
  175. data/test/deprecated_associations_test.rb +0 -396
  176. data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
  177. data/test/fixtures/db_definitions/mysql.sql +0 -234
  178. data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
  179. data/test/fixtures/db_definitions/mysql2.sql +0 -5
  180. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
  181. data/test/fixtures/db_definitions/sqlserver.sql +0 -243
  182. data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
  183. data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
  184. data/test/fixtures/mixin.rb +0 -63
  185. 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 by conditions or joins: This API has been deprecated and will be removed in Rails 2.0
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 last approach, count using options, accepts an option hash as the only parameter. The options are:
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>: An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
19
- # The records will be returned read-only since they will have attributes that do not correspond to the table's columns.
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, but not
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 count by +conditions+ and +joins+ (this has been deprecated):
33
- # Person.count("age > 26") # returns the number of people older than 26
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, *construct_count_options_from_legacy_args(*args))
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.. See #calculate for examples with options.
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.. See #calculate for examples with options.
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 value on a given column. The value is returned with the same data type of the column.. See #calculate for examples with options.
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: Methods for count, sum, average, minimum, and maximum have been added as shortcuts.
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>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro.
99
- # * <tt>:joins</tt>: An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed).
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>: An SQL fragment like "created_at DESC, name" (really only used with GROUP BY calculations).
102
- # * <tt>:group</tt>: An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
103
- # * <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
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>: Set this to true to make this a distinct calculation, such as SELECT COUNT(DISTINCT posts.id) ...
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 construct_count_options_from_legacy_args(*args)
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
- # count(conditions=nil, joins=nil) # deprecated
137
- if args.size > 2
138
- raise ArgumentError, "Unexpected parameters passed to count(options={}): #{args.inspect}"
139
- elsif args.size > 0
140
- if args[0].is_a?(Hash)
141
- options = args[0]
142
- elsif args[1].is_a?(Hash)
143
- column_name, options = args
144
- else
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 merged_includes.any? && operation.to_s.downcase == 'count'
170
- options[:distinct] = true
171
- column_name = options[:select] || [table_name, primary_key] * '.'
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 = "SELECT #{operation}(#{'DISTINCT ' if options[:distinct]}#{column_name}) AS #{aggregate_alias}"
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 = Base.connection.adapter_name == 'FrontBase' ? :group_alias : :group_field
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 Base.connection.adapter_name == 'FrontBase'
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 = associated ? key_records[row[group_alias].to_i] : type_cast_calculated_value(row[group_alias], group_column)
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
- # as a usable column name.
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 allows you to trigger logic
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+ is called. Contrast this to the situation where we've implemented the save behavior through overwriteable
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 when you
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 plings (') are used so the #{id} part isn't evaluated until the callback is triggered. Also note that these
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), 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
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* returning statements
161
+ # == <tt>before_validation*</tt> returning statements
162
162
  #
163
- # If the returning value of a before_validation callback can be evaluated to false, the process will be aborted and Base#save will return false.
164
- # If Base#save! is called it will raise a RecordNotSave error.
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
- # == Cancelling callbacks
167
+ # == Canceling callbacks
168
168
  #
169
- # If a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* 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
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(ClassMethods)
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
- [:initialize, :create_or_update, :valid?, :create, :update, :destroy].each do |method|
188
- alias_method_chain method, :callbacks
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
- module ClassMethods #:nodoc:
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
- def initialize_with_callbacks(attributes = nil) #:nodoc:
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 _:dependent_ option on your associations.
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." ) }