diff options
author | Yehuda Katz <wycats@gmail.com> | 2009-01-30 10:53:14 -0800 |
---|---|---|
committer | Yehuda Katz <wycats@gmail.com> | 2009-01-30 10:53:19 -0800 |
commit | da10673e32718d6a0619bd0f4b4d3d796db86a1a (patch) | |
tree | 7bdb7868b0be65daec06ba729f68deccfe61a8bb /activerecord/lib/active_record | |
parent | b8fadd708b9850a77e1f64038763fffcff502499 (diff) | |
parent | ed0e5640879fd42c00fc5900e0355a0ea1dcf2ad (diff) | |
download | rails-da10673e32718d6a0619bd0f4b4d3d796db86a1a.tar.gz rails-da10673e32718d6a0619bd0f4b4d3d796db86a1a.tar.bz2 rails-da10673e32718d6a0619bd0f4b4d3d796db86a1a.zip |
Sync 'rails/rails/master'
Diffstat (limited to 'activerecord/lib/active_record')
12 files changed, 148 insertions, 77 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 86616abf52..8b51a38f48 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1531,14 +1531,14 @@ module ActiveRecord association = send(reflection.name) association.destroy unless association.nil? end - before_destroy method_name + after_destroy method_name when :delete method_name = "belongs_to_dependent_delete_for_#{reflection.name}".to_sym define_method(method_name) do association = send(reflection.name) association.delete unless association.nil? end - before_destroy method_name + after_destroy method_name else raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{reflection.options[:dependent].inspect})" end diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index 3d689098b5..a5cc3bf091 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -9,6 +9,14 @@ module ActiveRecord create_record(attributes) { |record| insert_record(record, true) } end + def columns + @reflection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns") + end + + def reset_column_information + @reflection.reset_column_information + end + protected def construct_find_options!(options) options[:joins] = @join_sql @@ -32,8 +40,6 @@ module ActiveRecord if @reflection.options[:insert_sql] @owner.connection.insert(interpolate_sql(@reflection.options[:insert_sql], record)) else - columns = @owner.connection.columns(@reflection.options[:join_table], "#{@reflection.options[:join_table]} Columns") - attributes = columns.inject({}) do |attrs, column| case column.name.to_s when @reflection.primary_key_name.to_s @@ -103,7 +109,7 @@ module ActiveRecord # clause has been explicitly defined. Otherwise you can get broken records back, if, for example, the join column also has # an id column. This will then overwrite the id column of the records coming back. def finding_with_ambiguous_select?(select_clause) - !select_clause && @owner.connection.columns(@reflection.options[:join_table], "Join Table Columns").size != 2 + !select_clause && columns.size != 2 end private diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index cca012ed55..0efccb66ee 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -327,7 +327,7 @@ module ActiveRecord #:nodoc: # User.find(user.id).preferences # => { "background" => "black", "display" => large } # # You can also specify a class option as the second parameter that'll raise an exception if a serialized object is retrieved as a - # descendent of a class not in the hierarchy. Example: + # descendant of a class not in the hierarchy. Example: # # class User < ActiveRecord::Base # serialize :preferences, Hash @@ -544,8 +544,9 @@ module ActiveRecord #:nodoc: # * <tt>:having</tt> - Combined with +:group+ this can be used to filter the records that a <tt>GROUP BY</tt> returns. Uses the <tt>HAVING</tt> SQL-clause. # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned. # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4. - # * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed) - # or named associations in the same form used for the <tt>:include</tt> option, which will perform an <tt>INNER JOIN</tt> on the associated table(s). + # * <tt>:joins</tt> - Either an SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id" (rarely needed), + # named associations in the same form used for the <tt>:include</tt> option, which will perform an <tt>INNER JOIN</tt> on the associated table(s), + # or an array containing a mixture of both strings and named associations. # 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. # Pass <tt>:readonly => false</tt> to override. # * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer @@ -755,25 +756,26 @@ module ActiveRecord #:nodoc: end end - # Delete an object (or multiple objects) where the +id+ given matches the primary_key. A SQL +DELETE+ command - # is executed on the database which means that no callbacks are fired off running this. This is an efficient method - # of deleting records that don't need cleaning up after or other actions to be taken. + # Deletes the row with a primary key matching the +id+ argument, using a + # SQL +DELETE+ statement, and returns the number of rows deleted. Active + # Record objects are not instantiated, so the object's callbacks are not + # executed, including any <tt>:dependent</tt> association options or + # Observer methods. # - # Objects are _not_ instantiated with this method, and so +:dependent+ rules - # defined on associations are not honered. + # You can delete multiple rows at once by passing an Array of <tt>id</tt>s. # - # ==== Parameters - # - # * +id+ - Can be either an Integer or an Array of Integers. + # Note: Although it is often much faster than the alternative, + # <tt>#destroy</tt>, skipping callbacks might bypass business logic in + # your application that ensures referential integrity or performs other + # essential jobs. # # ==== Examples # - # # Delete a single object + # # Delete a single row # Todo.delete(1) # - # # Delete multiple objects - # todos = [1,2,3] - # Todo.delete(todos) + # # Delete multiple rows + # Todo.delete([2,3,4]) def delete(id) delete_all([ "#{connection.quote_column_name(primary_key)} IN (?)", id ]) end @@ -849,25 +851,32 @@ module ActiveRecord #:nodoc: connection.update(sql, "#{name} Update") end - # Destroys the records matching +conditions+ by instantiating each record and calling their +destroy+ method. - # This means at least 2*N database queries to destroy N records, so avoid +destroy_all+ if you are deleting - # many records. If you want to simply delete records without worrying about dependent associations or - # callbacks, use the much faster +delete_all+ method instead. + # Destroys the records matching +conditions+ by instantiating each + # record and calling its +destroy+ method. Each object's callbacks are + # executed (including <tt>:dependent</tt> association options and + # +before_destroy+/+after_destroy+ Observer methods). Returns the + # collection of objects that were destroyed; each will be frozen, to + # reflect that no changes should be made (since they can't be + # persisted). + # + # Note: Instantiation, callback execution, and deletion of each + # record can be time consuming when you're removing many records at + # once. It generates at least one SQL +DELETE+ query per record (or + # possibly more, to enforce your callbacks). If you want to delete many + # rows quickly, without concern for their associations or callbacks, use + # +delete_all+ instead. # # ==== Parameters # - # * +conditions+ - Conditions are specified the same way as with +find+ method. + # * +conditions+ - A string, array, or hash that specifies which records + # to destroy. If omitted, all records are destroyed. See the + # Conditions section in the introduction to ActiveRecord::Base for + # more information. # - # ==== Example + # ==== Examples # # Person.destroy_all("last_login < '2004-04-04'") - # - # This loads and destroys each person one by one, including its dependent associations and before_ and - # after_destroy callbacks. - # - # +conditions+ can be anything that +find+ also accepts: - # - # Person.destroy_all(:last_login => 6.hours.ago) + # Person.destroy_all(:status => "inactive") def destroy_all(conditions = nil) find(:all, :conditions => conditions).each { |object| object.destroy } end @@ -918,7 +927,7 @@ module ActiveRecord #:nodoc: # # ==== Parameters # - # * +id+ - The id of the object you wish to update a counter on. + # * +id+ - The id of the object you wish to update a counter on or an Array of ids. # * +counters+ - An Array of Hashes containing the names of the fields # to update as keys and the amount to update the field by as values. # @@ -932,12 +941,27 @@ module ActiveRecord #:nodoc: # # SET comment_count = comment_count - 1, # # action_count = action_count + 1 # # WHERE id = 5 + # + # # For the Posts with id of 10 and 15, increment the comment_count by 1 + # Post.update_counters [10, 15], :comment_count => 1 + # # Executes the following SQL: + # # UPDATE posts + # # SET comment_count = comment_count + 1, + # # WHERE id IN (10, 15) def update_counters(id, counters) updates = counters.inject([]) { |list, (counter_name, increment)| sign = increment < 0 ? "-" : "+" list << "#{connection.quote_column_name(counter_name)} = COALESCE(#{connection.quote_column_name(counter_name)}, 0) #{sign} #{increment.abs}" }.join(", ") - update_all(updates, "#{connection.quote_column_name(primary_key)} = #{quote_value(id)}") + + if id.is_a?(Array) + ids_list = id.map {|i| quote_value(i)}.join(', ') + condition = "IN (#{ids_list})" + else + condition = "= #{quote_value(id)}" + end + + update_all(updates, "#{connection.quote_column_name(primary_key)} #{condition}") end # Increment a number field by one, usually representing a count. @@ -1691,7 +1715,7 @@ module ActiveRecord #:nodoc: end join end - joins.flatten.uniq + joins.flatten.map{|j| j.strip}.uniq else joins.collect{|j| safe_to_array(j)}.flatten.uniq end @@ -1802,15 +1826,13 @@ module ActiveRecord #:nodoc: table_name end - # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) that are turned into - # find(:first, :conditions => ["user_name = ?", user_name]) and find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password]) - # respectively. Also works for find(:all) by using find_all_by_amount(50) that is turned into find(:all, :conditions => ["amount = ?", 50]). - # - # It's even possible to use all the additional parameters to find. For example, the full interface for find_all_by_amount - # is actually find_all_by_amount(amount, options). + # Enables dynamic finders like <tt>find_by_user_name(user_name)</tt> and <tt>find_by_user_name_and_password(user_name, password)</tt> + # that are turned into <tt>find(:first, :conditions => ["user_name = ?", user_name])</tt> and + # <tt>find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])</tt> respectively. Also works for + # <tt>find(:all)</tt> by using <tt>find_all_by_amount(50)</tt> that is turned into <tt>find(:all, :conditions => ["amount = ?", 50])</tt>. # - # This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount) - # or find_or_create_by_user_and_password(user, password). + # It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+ + # is actually <tt>find_all_by_amount(amount, options)</tt>. # # Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that # are turned into scoped(:conditions => ["user_name = ?", user_name]) and scoped(:conditions => ["user_name = ? AND password = ?", user_name, password]) @@ -2032,7 +2054,11 @@ module ActiveRecord #:nodoc: # end # # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of - # <tt>:conditions</tt> and <tt>:include</tt> options in <tt>:find</tt>, which are merged. + # <tt>:conditions</tt>, <tt>:include</tt>, and <tt>:joins</tt> options in <tt>:find</tt>, which are merged. + # + # <tt>:joins</tt> options are uniqued so multiple scopes can join in the same table without table aliasing + # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the + # array of strings format for your joins. # # class Article < ActiveRecord::Base # def self.find_with_scope @@ -2086,7 +2112,11 @@ module ActiveRecord #:nodoc: (hash[method].keys + params.keys).uniq.each do |key| merge = hash[method][key] && params[key] # merge if both scopes have the same key if key == :conditions && merge - hash[method][key] = merge_conditions(params[key], hash[method][key]) + if params[key].is_a?(Hash) && hash[method][key].is_a?(Hash) + hash[method][key] = merge_conditions(hash[method][key].deep_merge(params[key])) + else + hash[method][key] = merge_conditions(params[key], hash[method][key]) + end elsif key == :include && merge hash[method][key] = merge_includes(hash[method][key], params[key]).uniq elsif key == :joins && merge @@ -2096,7 +2126,7 @@ module ActiveRecord #:nodoc: end end else - hash[method] = params.merge(hash[method]) + hash[method] = hash[method].merge(params) end else hash[method] = params @@ -2156,7 +2186,7 @@ module ActiveRecord #:nodoc: scoped_methods.last end - # Returns the class type of the record using the current module as a prefix. So descendents of + # Returns the class type of the record using the current module as a prefix. So descendants of # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass. def compute_type(type_name) modularized_name = type_name_with_module(type_name) @@ -2169,7 +2199,8 @@ module ActiveRecord #:nodoc: end end - # Returns the class descending directly from Active Record in the inheritance hierarchy. + # Returns the class descending directly from ActiveRecord::Base or an + # abstract class, if any, in the inheritance hierarchy. def class_of_active_record_descendant(klass) if klass.superclass == Base || klass.superclass.abstract_class? klass @@ -2518,14 +2549,16 @@ module ActiveRecord #:nodoc: create_or_update || raise(RecordNotSaved) end - # Deletes the record in the database and freezes this instance to reflect that no changes should - # be made (since they can't be persisted). + # Deletes the record in the database and freezes this instance to + # reflect that no changes should be made (since they can't be + # persisted). Returns the frozen instance. + # + # The row is simply removed with a SQL +DELETE+ statement on the + # record's primary key, and no callbacks are executed. # - # Unlike #destroy, this method doesn't run any +before_delete+ and +after_delete+ - # callbacks, nor will it enforce any association +:dependent+ rules. - # - # In addition to deleting this record, any defined +before_delete+ and +after_delete+ - # callbacks are run, and +:dependent+ rules defined on associations are run. + # To enforce the object's +before_destroy+ and +after_destroy+ + # callbacks, Observer methods, or any <tt>:dependent</tt> association + # options, use <tt>#destroy</tt>. def delete self.class.delete(id) unless new_record? freeze @@ -2726,7 +2759,19 @@ module ActiveRecord #:nodoc: end end - # Format attributes nicely for inspect. + # Returns an <tt>#inspect</tt>-like string for the value of the + # attribute +attr_name+. String attributes are elided after 50 + # characters, and Date and Time attributes are returned in the + # <tt>:db</tt> format. Other attributes return the value of + # <tt>#inspect</tt> without modification. + # + # person = Person.create!(:name => "David Heinemeier Hansson " * 3) + # + # person.attribute_for_inspect(:name) + # # => '"David Heinemeier Hansson David Heinemeier Hansson D..."' + # + # person.attribute_for_inspect(:created_at) + # # => '"2009-01-12 04:48:57"' def attribute_for_inspect(attr_name) value = read_attribute(attr_name) @@ -2855,7 +2900,7 @@ module ActiveRecord #:nodoc: id end - # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord::Base descendent. + # Sets the attribute used for single table inheritance to this class name if this is not the ActiveRecord::Base descendant. # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to do Reply.new without having to # set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself. No such attribute would be set for objects of the # Message class in that example. diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 65512d534a..b239c03284 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -48,30 +48,38 @@ module ActiveRecord calculate(:count, *construct_count_options_from_args(*args)) end - # Calculates the average value on a given column. The value is returned as a float. See +calculate+ for examples with options. + # Calculates the average value on a given column. The value is returned as + # a float, or +nil+ if there's no row. See +calculate+ for examples with + # options. # - # Person.average('age') + # Person.average('age') # => 35.8 def average(column_name, options = {}) calculate(:avg, column_name, options) end - # 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. + # Calculates the minimum value on a given column. The value is returned + # with the same data type of the column, or +nil+ if there's no row. See + # +calculate+ for examples with options. # - # Person.minimum('age') + # Person.minimum('age') # => 7 def minimum(column_name, options = {}) calculate(:min, column_name, options) end - # 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. + # Calculates the maximum value on a given column. The value is returned + # with the same data type of the column, or +nil+ if there's no row. See + # +calculate+ for examples with options. # - # Person.maximum('age') + # Person.maximum('age') # => 93 def maximum(column_name, options = {}) calculate(:max, column_name, options) end - # 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. + # Calculates the sum of values on a given column. The value is returned + # with the same data type of the column, 0 if there's no row. See + # +calculate+ for examples with options. # - # Person.sum('age') + # Person.sum('age') # => 4562 def sum(column_name, options = {}) calculate(:sum, column_name, options) end diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 42bfe34505..88958f4583 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -77,7 +77,7 @@ module ActiveRecord # # In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+. So, use the callback macros when # you want to ensure that a certain callback is called for the entire hierarchy, and use the regular overwriteable methods - # when you want to leave it up to each descendent to decide whether they want to call +super+ and trigger the inherited callbacks. + # when you want to leave it up to each descendant to decide whether they want to call +super+ and trigger the inherited callbacks. # # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the callbacks before specifying the # associations. Otherwise, you might trigger the loading of a child before the parent has registered the callbacks and they won't @@ -219,8 +219,9 @@ module ActiveRecord def after_save() end def create_or_update_with_callbacks #:nodoc: return false if callback(:before_save) == false - result = create_or_update_without_callbacks - callback(:after_save) + if result = create_or_update_without_callbacks + callback(:after_save) + end result end private :create_or_update_with_callbacks diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index b2345fd571..9300df28ee 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -152,6 +152,7 @@ module ActiveRecord # * <tt>:password</tt> - Defaults to nothing. # * <tt>:database</tt> - The name of the database. No default, must be provided. # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection. + # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html). # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection. # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection. # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection. @@ -563,8 +564,6 @@ module ActiveRecord private def connect - @connection.reconnect = true if @connection.respond_to?(:reconnect=) - encoding = @config[:encoding] if encoding @connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil @@ -575,6 +574,10 @@ module ActiveRecord end @connection.real_connect(*@connection_options) + + # reconnect must be set after real_connect is called, because real_connect sets it to false internally + @connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=) + configure_connection end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 9387cf8827..5390f49f04 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -306,7 +306,7 @@ module ActiveRecord end def copy_table(from, to, options = {}) #:nodoc: - options = options.merge(:id => !columns(from).detect{|c| c.name == 'id'}.nil?) + options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s)) create_table(to, options) do |definition| @definition = definition columns(from).each do |column| diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index 4c899f58e5..4a2510aa63 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -151,8 +151,8 @@ module ActiveRecord def field_changed?(attr, old, value) if column = column_for_attribute(attr) - if column.type == :integer && column.null && (old.nil? || old == 0) && value.blank? - # For nullable integer columns, NULL gets stored in database for blank (i.e. '') values. + if column.number? && column.null && (old.nil? || old == 0) && value.blank? + # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values. # Hence we don't record it as a change if the value changes from nil to ''. # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll # be typecast back to 0 (''.to_i => 0) diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml index 7e205435f7..bf8a71d236 100644 --- a/activerecord/lib/active_record/locale/en.yml +++ b/activerecord/lib/active_record/locale/en.yml @@ -37,7 +37,7 @@ en: # blank: "This is a custom blank message for User login" # Will define custom blank validation message for User model and # custom blank validation message for login attribute of User model. - models: + #models: # Translate model names. Used in Model.human_name(). #models: diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 83043c2c22..989b2a1ec5 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -39,7 +39,7 @@ module ActiveRecord # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments # for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>. # - # All \scopes are available as class methods on the ActiveRecord::Base descendent upon which the \scopes were defined. But they are also available to + # All \scopes are available as class methods on the ActiveRecord::Base descendant upon which the \scopes were defined. But they are also available to # <tt>has_many</tt> associations. If, # # class Person < ActiveRecord::Base diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index dbff4f24d6..1937abdc83 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -198,6 +198,14 @@ module ActiveRecord end end + def columns(tbl_name, log_msg) + @columns ||= klass.connection.columns(tbl_name, log_msg) + end + + def reset_column_information + @columns = nil + end + def check_validity! end diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 6a9690ba85..6d750accb0 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -904,7 +904,7 @@ module ActiveRecord configuration.update(attr_names.extract_options!) validates_each(attr_names, configuration) do |record, attr_name, value| - unless (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v } + unless (value.is_a?(Array) ? value : [value]).collect { |r| r.nil? || r.valid? }.all? record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value) end end |