Instiki 0.17.2: Security Release
This release upgrades Instiki to Rails 2.3.4, which patches two security holes in Rails. See http://weblog.rubyonrails.org/2009/9/4/ruby-on-rails-2-3-4 There are also some new features, and the usual boatload of bugfixes. See the CHANGELOG for details.
This commit is contained in:
parent
34c4306867
commit
4bdf703ab2
211 changed files with 3959 additions and 1325 deletions
|
@ -34,11 +34,13 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
|
||||
class HasManyThroughCantAssociateThroughHasManyReflection < ActiveRecordError #:nodoc:
|
||||
class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError #:nodoc:
|
||||
def initialize(owner, reflection)
|
||||
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
|
||||
end
|
||||
end
|
||||
HasManyThroughCantAssociateThroughHasManyReflection = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection', 'ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection')
|
||||
|
||||
class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
|
||||
def initialize(owner, reflection)
|
||||
super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.")
|
||||
|
@ -410,6 +412,32 @@ module ActiveRecord
|
|||
# @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
|
||||
# @firm.invoices # selects all invoices by going through the Client join model.
|
||||
#
|
||||
# Similarly you can go through a +has_one+ association on the join model:
|
||||
#
|
||||
# class Group < ActiveRecord::Base
|
||||
# has_many :users
|
||||
# has_many :avatars, :through => :users
|
||||
# end
|
||||
#
|
||||
# class User < ActiveRecord::Base
|
||||
# belongs_to :group
|
||||
# has_one :avatar
|
||||
# end
|
||||
#
|
||||
# class Avatar < ActiveRecord::Base
|
||||
# belongs_to :user
|
||||
# end
|
||||
#
|
||||
# @group = Group.first
|
||||
# @group.users.collect { |u| u.avatar }.flatten # select all avatars for all users in the group
|
||||
# @group.avatars # selects all avatars by going through the User join model.
|
||||
#
|
||||
# An important caveat with going through +has_one+ or +has_many+ associations on the join model is that these associations are
|
||||
# *read-only*. For example, the following would not work following the previous example:
|
||||
#
|
||||
# @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around.
|
||||
# @group.avatars.delete(@group.avatars.last) # so would this
|
||||
#
|
||||
# === Polymorphic Associations
|
||||
#
|
||||
# Polymorphic associations on models are not restricted on what types of models they can be associated with. Rather, they
|
||||
|
@ -759,7 +787,7 @@ module ActiveRecord
|
|||
# [:through]
|
||||
# Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
|
||||
# are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
|
||||
# or <tt>has_many</tt> association on the join model.
|
||||
# <tt>has_one</tt> or <tt>has_many</tt> association on the join model.
|
||||
# [:source]
|
||||
# Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
|
||||
# inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
|
||||
|
@ -1241,7 +1269,11 @@ module ActiveRecord
|
|||
|
||||
if association_proxy_class == HasOneThroughAssociation
|
||||
association.create_through_record(new_value)
|
||||
self.send(reflection.name, new_value)
|
||||
if new_record?
|
||||
association_instance_set(reflection.name, new_value.nil? ? nil : association)
|
||||
else
|
||||
self.send(reflection.name, new_value)
|
||||
end
|
||||
else
|
||||
association.replace(new_value)
|
||||
association_instance_set(reflection.name, new_value.nil? ? nil : association)
|
||||
|
@ -1293,7 +1325,7 @@ module ActiveRecord
|
|||
|
||||
define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
|
||||
ids = (new_value || []).reject { |nid| nid.blank? }
|
||||
send("#{reflection.name}=", reflection.class_name.constantize.find(ids))
|
||||
send("#{reflection.name}=", reflection.klass.find(ids))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1838,7 +1870,7 @@ module ActiveRecord
|
|||
descendant
|
||||
end.flatten.compact
|
||||
|
||||
remove_duplicate_results!(reflection.class_name.constantize, parent_records, associations[name]) unless parent_records.empty?
|
||||
remove_duplicate_results!(reflection.klass, parent_records, associations[name]) unless parent_records.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -208,6 +208,7 @@ module ActiveRecord
|
|||
# Note that this method will _always_ remove records from the database
|
||||
# ignoring the +:dependent+ option.
|
||||
def destroy(*records)
|
||||
records = find(records) if records.any? {|record| record.kind_of?(Fixnum) || record.kind_of?(String)}
|
||||
remove_records(records) do |records, old_records|
|
||||
old_records.each { |record| record.destroy }
|
||||
end
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
module ActiveRecord
|
||||
module Associations
|
||||
class HasAndBelongsToManyAssociation < AssociationCollection #:nodoc:
|
||||
def initialize(owner, reflection)
|
||||
super
|
||||
@primary_key_list = {}
|
||||
end
|
||||
|
||||
def create(attributes = {})
|
||||
create_record(attributes) { |record| insert_record(record) }
|
||||
end
|
||||
|
@ -17,6 +22,12 @@ module ActiveRecord
|
|||
@reflection.reset_column_information
|
||||
end
|
||||
|
||||
def has_primary_key?
|
||||
return @has_primary_key unless @has_primary_key.nil?
|
||||
@has_primary_key = (ActiveRecord::Base.connection.supports_primary_key? &&
|
||||
ActiveRecord::Base.connection.primary_key(@reflection.options[:join_table]))
|
||||
end
|
||||
|
||||
protected
|
||||
def construct_find_options!(options)
|
||||
options[:joins] = @join_sql
|
||||
|
@ -29,6 +40,11 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def insert_record(record, force = true, validate = true)
|
||||
if has_primary_key?
|
||||
raise ActiveRecord::ConfigurationError,
|
||||
"Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})."
|
||||
end
|
||||
|
||||
if record.new_record?
|
||||
if force
|
||||
record.save!
|
||||
|
|
|
@ -74,6 +74,7 @@ module ActiveRecord
|
|||
"#{@reflection.primary_key_name} = NULL",
|
||||
"#{@reflection.primary_key_name} = #{owner_quoted_id} AND #{@reflection.klass.primary_key} IN (#{ids})"
|
||||
)
|
||||
@owner.class.update_counters(@owner.id, cached_counter_attribute_name => -records.size) if has_cached_counter?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,7 +17,17 @@ module ActiveRecord
|
|||
|
||||
def create(attrs = nil)
|
||||
transaction do
|
||||
self << (object = attrs ? @reflection.klass.send(:with_scope, :create => attrs) { @reflection.create_association } : @reflection.create_association)
|
||||
object = if attrs
|
||||
@reflection.klass.send(:with_scope, :create => attrs) {
|
||||
@reflection.create_association
|
||||
}
|
||||
else
|
||||
@reflection.create_association
|
||||
end
|
||||
raise_on_type_mismatch(object)
|
||||
add_record_to_target_with_callbacks(object) do |r|
|
||||
insert_record(object, false)
|
||||
end
|
||||
object
|
||||
end
|
||||
end
|
||||
|
@ -44,7 +54,7 @@ module ActiveRecord
|
|||
options[:select] = construct_select(options[:select])
|
||||
options[:from] ||= construct_from
|
||||
options[:joins] = construct_joins(options[:joins])
|
||||
options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil?
|
||||
options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil? && @reflection.source_reflection.options[:include]
|
||||
end
|
||||
|
||||
def insert_record(record, force = true, validate = true)
|
||||
|
@ -96,7 +106,7 @@ module ActiveRecord
|
|||
# Construct attributes for :through pointing to owner and associate.
|
||||
def construct_join_attributes(associate)
|
||||
# TODO: revist this to allow it for deletion, supposing dependent option is supported
|
||||
raise ActiveRecord::HasManyThroughCantAssociateThroughHasManyReflection.new(@owner, @reflection) if @reflection.source_reflection.macro == :has_many
|
||||
raise ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(@owner, @reflection) if [:has_one, :has_many].include?(@reflection.source_reflection.macro)
|
||||
join_attributes = construct_owner_attributes(@reflection.through_reflection).merge(@reflection.source_reflection.primary_key_name => associate.id)
|
||||
if @reflection.options[:source_type]
|
||||
join_attributes.merge!(@reflection.source_reflection.options[:foreign_type] => associate.class.base_class.name.to_s)
|
||||
|
|
|
@ -9,8 +9,14 @@ module ActiveRecord
|
|||
|
||||
if current_object
|
||||
new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
|
||||
else
|
||||
@owner.send(@reflection.through_reflection.name, klass.send(:create, construct_join_attributes(new_value))) if new_value
|
||||
elsif new_value
|
||||
if @owner.new_record?
|
||||
self.target = new_value
|
||||
through_association = @owner.send(:association_instance_get, @reflection.through_reflection.name)
|
||||
through_association.build(construct_join_attributes(new_value))
|
||||
else
|
||||
@owner.send(@reflection.through_reflection.name, klass.create(construct_join_attributes(new_value)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -249,9 +249,10 @@ module ActiveRecord
|
|||
unless valid = association.valid?
|
||||
if reflection.options[:autosave]
|
||||
unless association.marked_for_destruction?
|
||||
association.errors.each do |attribute, message|
|
||||
attribute = "#{reflection.name}_#{attribute}"
|
||||
errors.add(attribute, message) unless errors.on(attribute)
|
||||
association.errors.each_error do |attribute, error|
|
||||
error = error.dup
|
||||
error.attribute = "#{reflection.name}_#{attribute}"
|
||||
errors.add(error) unless errors.on(error.attribute)
|
||||
end
|
||||
end
|
||||
else
|
||||
|
|
|
@ -1364,7 +1364,7 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
defaults << options[:default] if options[:default]
|
||||
defaults.flatten!
|
||||
defaults << attribute_key_name.humanize
|
||||
defaults << attribute_key_name.to_s.humanize
|
||||
options[:count] ||= 1
|
||||
I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => [:activerecord, :attributes]))
|
||||
end
|
||||
|
@ -2294,20 +2294,24 @@ module ActiveRecord #:nodoc:
|
|||
# And for value objects on a composed_of relationship:
|
||||
# { :address => Address.new("123 abc st.", "chicago") }
|
||||
# # => "address_street='123 abc st.' and address_city='chicago'"
|
||||
def sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name)
|
||||
def sanitize_sql_hash_for_conditions(attrs, default_table_name = quoted_table_name)
|
||||
attrs = expand_hash_conditions_for_aggregates(attrs)
|
||||
|
||||
conditions = attrs.map do |attr, value|
|
||||
table_name = default_table_name
|
||||
|
||||
unless value.is_a?(Hash)
|
||||
attr = attr.to_s
|
||||
|
||||
# Extract table name from qualified attribute names.
|
||||
if attr.include?('.')
|
||||
table_name, attr = attr.split('.', 2)
|
||||
table_name = connection.quote_table_name(table_name)
|
||||
attr_table_name, attr = attr.split('.', 2)
|
||||
attr_table_name = connection.quote_table_name(attr_table_name)
|
||||
else
|
||||
attr_table_name = table_name
|
||||
end
|
||||
|
||||
attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value)
|
||||
attribute_condition("#{attr_table_name}.#{connection.quote_column_name(attr)}", value)
|
||||
else
|
||||
sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
|
||||
end
|
||||
|
@ -3028,16 +3032,22 @@ module ActiveRecord #:nodoc:
|
|||
|
||||
def execute_callstack_for_multiparameter_attributes(callstack)
|
||||
errors = []
|
||||
callstack.each do |name, values|
|
||||
callstack.each do |name, values_with_empty_parameters|
|
||||
begin
|
||||
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
|
||||
# in order to allow a date to be set without a year, we must keep the empty values.
|
||||
# Otherwise, we wouldn't be able to distinguish it from a date with an empty day.
|
||||
values = values_with_empty_parameters.reject(&:nil?)
|
||||
|
||||
if values.empty?
|
||||
send(name + "=", nil)
|
||||
else
|
||||
|
||||
value = if Time == klass
|
||||
instantiate_time_object(name, values)
|
||||
elsif Date == klass
|
||||
begin
|
||||
values = values_with_empty_parameters.collect do |v| v.nil? ? 1 : v end
|
||||
Date.new(*values)
|
||||
rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
|
||||
instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
|
||||
|
@ -3065,10 +3075,8 @@ module ActiveRecord #:nodoc:
|
|||
attribute_name = multiparameter_name.split("(").first
|
||||
attributes[attribute_name] = [] unless attributes.include?(attribute_name)
|
||||
|
||||
unless value.empty?
|
||||
attributes[attribute_name] <<
|
||||
[ find_parameter_position(multiparameter_name), type_cast_attribute_value(multiparameter_name, value) ]
|
||||
end
|
||||
parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
|
||||
attributes[attribute_name] << [ find_parameter_position(multiparameter_name), parameter_value ]
|
||||
end
|
||||
|
||||
attributes.each { |name, values| attributes[name] = values.sort_by{ |v| v.first }.collect { |v| v.last } }
|
||||
|
|
|
@ -190,6 +190,8 @@ module ActiveRecord
|
|||
sql << ", #{options[:group_field]} AS #{options[:group_alias]}" if options[:group]
|
||||
if options[:from]
|
||||
sql << " FROM #{options[:from]} "
|
||||
elsif scope && scope[:from] && !use_workaround
|
||||
sql << " FROM #{scope[:from]} "
|
||||
else
|
||||
sql << " FROM (SELECT #{distinct}#{column_name}" if use_workaround
|
||||
sql << " FROM #{connection.quote_table_name(table_name)} "
|
||||
|
|
|
@ -277,7 +277,6 @@ module ActiveRecord
|
|||
add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
|
||||
column_sql
|
||||
end
|
||||
alias to_s :to_sql
|
||||
|
||||
private
|
||||
|
||||
|
@ -316,6 +315,20 @@ module ActiveRecord
|
|||
@base = base
|
||||
end
|
||||
|
||||
#Handles non supported datatypes - e.g. XML
|
||||
def method_missing(symbol, *args)
|
||||
if symbol.to_s == 'xml'
|
||||
xml_column_fallback(args)
|
||||
end
|
||||
end
|
||||
|
||||
def xml_column_fallback(*args)
|
||||
case @base.adapter_name.downcase
|
||||
when 'sqlite', 'mysql'
|
||||
options = args.extract_options!
|
||||
column(args[0], :text, options)
|
||||
end
|
||||
end
|
||||
# Appends a primary key definition to the table definition.
|
||||
# Can be called multiple times, but this is probably not a good idea.
|
||||
def primary_key(name)
|
||||
|
@ -508,7 +521,7 @@ module ActiveRecord
|
|||
# concatenated together. This string can then be prepended and appended to
|
||||
# to generate the final SQL to create the table.
|
||||
def to_sql
|
||||
@columns * ', '
|
||||
@columns.map(&:to_sql) * ', '
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -706,3 +719,4 @@ module ActiveRecord
|
|||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ module ActiveRecord
|
|||
# See also TableDefinition#column for details on how to create columns.
|
||||
def create_table(table_name, options = {})
|
||||
table_definition = TableDefinition.new(self)
|
||||
table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name)) unless options[:id] == false
|
||||
table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
|
||||
|
||||
yield table_definition
|
||||
|
||||
|
@ -321,7 +321,7 @@ module ActiveRecord
|
|||
schema_migrations_table.column :version, :string, :null => false
|
||||
end
|
||||
add_index sm_table, :version, :unique => true,
|
||||
:name => 'unique_schema_migrations'
|
||||
:name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
|
||||
|
||||
# Backwards-compatibility: if we find schema_info, assume we've
|
||||
# migrated up to that point:
|
||||
|
|
|
@ -54,6 +54,13 @@ module ActiveRecord
|
|||
false
|
||||
end
|
||||
|
||||
# Can this adapter determine the primary key for tables not attached
|
||||
# to an ActiveRecord class, such as join tables? Backend specific, as
|
||||
# the abstract adapter always returns +false+.
|
||||
def supports_primary_key?
|
||||
false
|
||||
end
|
||||
|
||||
# Does this adapter support using DISTINCT within COUNT? This is +true+
|
||||
# for all adapters except sqlite.
|
||||
def supports_count_distinct?
|
||||
|
|
|
@ -52,12 +52,7 @@ module ActiveRecord
|
|||
socket = config[:socket]
|
||||
username = config[:username] ? config[:username].to_s : 'root'
|
||||
password = config[:password].to_s
|
||||
|
||||
if config.has_key?(:database)
|
||||
database = config[:database]
|
||||
else
|
||||
raise ArgumentError, "No database specified. Missing argument: database."
|
||||
end
|
||||
database = config[:database]
|
||||
|
||||
# Require the MySQL driver and define Mysql::Result.all_hashes
|
||||
unless defined? Mysql
|
||||
|
@ -80,7 +75,7 @@ module ActiveRecord
|
|||
module ConnectionAdapters
|
||||
class MysqlColumn < Column #:nodoc:
|
||||
def extract_default(default)
|
||||
if type == :binary || type == :text
|
||||
if sql_type =~ /blob/i || type == :text
|
||||
if default.blank?
|
||||
return null ? nil : ''
|
||||
else
|
||||
|
@ -94,7 +89,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def has_default?
|
||||
return false if type == :binary || type == :text #mysql forbids defaults on blob and text columns
|
||||
return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns
|
||||
super
|
||||
end
|
||||
|
||||
|
@ -212,6 +207,10 @@ module ActiveRecord
|
|||
true
|
||||
end
|
||||
|
||||
def supports_primary_key? #:nodoc:
|
||||
true
|
||||
end
|
||||
|
||||
def supports_savepoints? #:nodoc:
|
||||
true
|
||||
end
|
||||
|
@ -554,6 +553,12 @@ module ActiveRecord
|
|||
keys.length == 1 ? [keys.first, nil] : nil
|
||||
end
|
||||
|
||||
# Returns just a table's primary key
|
||||
def primary_key(table)
|
||||
pk_and_sequence = pk_and_sequence_for(table)
|
||||
pk_and_sequence && pk_and_sequence.first
|
||||
end
|
||||
|
||||
def case_sensitive_equality_operator
|
||||
"= BINARY"
|
||||
end
|
||||
|
@ -573,6 +578,10 @@ module ActiveRecord
|
|||
@connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
|
||||
end
|
||||
|
||||
@connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout]
|
||||
@connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout]
|
||||
@connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout]
|
||||
|
||||
@connection.real_connect(*@connection_options)
|
||||
|
||||
# reconnect must be set after real_connect is called, because real_connect sets it to false internally
|
||||
|
|
|
@ -39,6 +39,12 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
module ConnectionAdapters
|
||||
class TableDefinition
|
||||
def xml(*args)
|
||||
options = args.extract_options!
|
||||
column(args[0], 'xml', options)
|
||||
end
|
||||
end
|
||||
# PostgreSQL-specific extensions to column definitions in a table.
|
||||
class PostgreSQLColumn < Column #:nodoc:
|
||||
# Instantiates a new PostgreSQL column definition in a table.
|
||||
|
@ -67,7 +73,7 @@ module ActiveRecord
|
|||
# depending on the server specifics
|
||||
super
|
||||
end
|
||||
|
||||
|
||||
# Maps PostgreSQL-specific data types to logical Rails types.
|
||||
def simplified_type(field_type)
|
||||
case field_type
|
||||
|
@ -99,10 +105,10 @@ module ActiveRecord
|
|||
:string
|
||||
# XML type
|
||||
when /^xml$/
|
||||
:string
|
||||
:xml
|
||||
# Arrays
|
||||
when /^\D+\[\]$/
|
||||
:string
|
||||
:string
|
||||
# Object identifier types
|
||||
when /^oid$/
|
||||
:integer
|
||||
|
@ -111,7 +117,7 @@ module ActiveRecord
|
|||
super
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Extracts the value from a PostgreSQL column default definition.
|
||||
def self.extract_value_from_default(default)
|
||||
case default
|
||||
|
@ -194,7 +200,8 @@ module ActiveRecord
|
|||
:time => { :name => "time" },
|
||||
:date => { :name => "date" },
|
||||
:binary => { :name => "bytea" },
|
||||
:boolean => { :name => "boolean" }
|
||||
:boolean => { :name => "boolean" },
|
||||
:xml => { :name => "xml" }
|
||||
}
|
||||
|
||||
# Returns 'PostgreSQL' as adapter name for identification purposes.
|
||||
|
@ -249,6 +256,11 @@ module ActiveRecord
|
|||
true
|
||||
end
|
||||
|
||||
# Does PostgreSQL support finding primary key on non-ActiveRecord tables?
|
||||
def supports_primary_key? #:nodoc:
|
||||
true
|
||||
end
|
||||
|
||||
# Does PostgreSQL support standard conforming strings?
|
||||
def supports_standard_conforming_strings?
|
||||
# Temporarily set the client message level above error to prevent unintentional
|
||||
|
@ -272,7 +284,7 @@ module ActiveRecord
|
|||
def supports_ddl_transactions?
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
def supports_savepoints?
|
||||
true
|
||||
end
|
||||
|
@ -364,7 +376,7 @@ module ActiveRecord
|
|||
if value.kind_of?(String) && column && column.type == :binary
|
||||
"#{quoted_string_prefix}'#{escape_bytea(value)}'"
|
||||
elsif value.kind_of?(String) && column && column.sql_type =~ /^xml$/
|
||||
"xml '#{quote_string(value)}'"
|
||||
"xml E'#{quote_string(value)}'"
|
||||
elsif value.kind_of?(Numeric) && column && column.sql_type =~ /^money$/
|
||||
# Not truly string input, so doesn't require (or allow) escape string syntax.
|
||||
"'#{value.to_s}'"
|
||||
|
@ -563,7 +575,7 @@ module ActiveRecord
|
|||
def rollback_db_transaction
|
||||
execute "ROLLBACK"
|
||||
end
|
||||
|
||||
|
||||
if defined?(PGconn::PQTRANS_IDLE)
|
||||
# The ruby-pg driver supports inspecting the transaction status,
|
||||
# while the ruby-postgres driver does not.
|
||||
|
@ -810,6 +822,12 @@ module ActiveRecord
|
|||
nil
|
||||
end
|
||||
|
||||
# Returns just a table's primary key
|
||||
def primary_key(table)
|
||||
pk_and_sequence = pk_and_sequence_for(table)
|
||||
pk_and_sequence && pk_and_sequence.first
|
||||
end
|
||||
|
||||
# Renames a table.
|
||||
def rename_table(name, new_name)
|
||||
execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
|
||||
|
@ -908,18 +926,18 @@ module ActiveRecord
|
|||
sql = "DISTINCT ON (#{columns}) #{columns}, "
|
||||
sql << order_columns * ', '
|
||||
end
|
||||
|
||||
|
||||
# Returns an ORDER BY clause for the passed order option.
|
||||
#
|
||||
#
|
||||
# PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so we work around this
|
||||
# by wrapping the +sql+ string as a sub-select and ordering in that query.
|
||||
def add_order_by_for_association_limiting!(sql, options) #:nodoc:
|
||||
return sql if options[:order].blank?
|
||||
|
||||
|
||||
order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
|
||||
order.map! { |s| 'DESC' if s =~ /\bdesc$/i }
|
||||
order = order.zip((0...order.size).to_a).map { |s,i| "id_list.alias_#{i} #{s}" }.join(', ')
|
||||
|
||||
|
||||
sql.replace "SELECT * FROM (#{sql}) AS id_list ORDER BY #{order}"
|
||||
end
|
||||
|
||||
|
@ -1032,7 +1050,7 @@ module ActiveRecord
|
|||
if res.ftype(cell_index) == MONEY_COLUMN_TYPE_OID
|
||||
# Because money output is formatted according to the locale, there are two
|
||||
# cases to consider (note the decimal separators):
|
||||
# (1) $12,345,678.12
|
||||
# (1) $12,345,678.12
|
||||
# (2) $12.345.678,12
|
||||
case column = row[cell_index]
|
||||
when /^-?\D+[\d,]+\.\d{2}$/ # (1)
|
||||
|
@ -1092,3 +1110,4 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -16,6 +16,10 @@ module ActiveRecord
|
|||
db.results_as_hash = true if defined? SQLite::Version
|
||||
db.type_translation = false
|
||||
|
||||
message = "Support for SQLite2Adapter and DeprecatedSQLiteAdapter has been removed from Rails 3. "
|
||||
message << "You should migrate to SQLite 3+ or use the plugin from git://github.com/rails/sqlite2_adapter.git with Rails 3."
|
||||
ActiveSupport::Deprecation.warn(message)
|
||||
|
||||
# "Downgrade" deprecated sqlite API
|
||||
if SQLite.const_defined?(:Version)
|
||||
ConnectionAdapters::SQLite2Adapter.new(db, logger, config)
|
||||
|
@ -27,6 +31,10 @@ module ActiveRecord
|
|||
|
||||
private
|
||||
def parse_sqlite_config!(config)
|
||||
if config.include?(:dbfile)
|
||||
ActiveSupport::Deprecation.warn "Please update config/database.yml to use 'database' instead of 'dbfile'"
|
||||
end
|
||||
|
||||
config[:database] ||= config[:dbfile]
|
||||
# Require database.
|
||||
unless config[:database]
|
||||
|
@ -104,6 +112,10 @@ module ActiveRecord
|
|||
true
|
||||
end
|
||||
|
||||
def supports_primary_key? #:nodoc:
|
||||
true
|
||||
end
|
||||
|
||||
def requires_reloading?
|
||||
true
|
||||
end
|
||||
|
|
|
@ -143,7 +143,7 @@ module ActiveRecord
|
|||
if partial_updates?
|
||||
# Serialized attributes should always be written in case they've been
|
||||
# changed in place.
|
||||
update_without_dirty(changed | self.class.serialized_attributes.keys)
|
||||
update_without_dirty(changed | (attributes.keys & self.class.serialized_attributes.keys))
|
||||
else
|
||||
update_without_dirty
|
||||
end
|
||||
|
|
|
@ -621,7 +621,8 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
|
|||
targets.each do |target|
|
||||
join_fixtures["#{label}_#{target}"] = Fixture.new(
|
||||
{ association.primary_key_name => row[primary_key_name],
|
||||
association.association_foreign_key => Fixtures.identify(target) }, nil)
|
||||
association.association_foreign_key => Fixtures.identify(target) },
|
||||
nil, @connection)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -705,12 +706,12 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
|
|||
|
||||
yaml_value.each do |fixture|
|
||||
raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each)
|
||||
fixture.each do |name, data|
|
||||
fixture.each do |name, data|
|
||||
unless data
|
||||
raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
|
||||
end
|
||||
|
||||
self[name] = Fixture.new(data, model_class)
|
||||
self[name] = Fixture.new(data, model_class, @connection)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -723,7 +724,7 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
|
|||
reader.each do |row|
|
||||
data = {}
|
||||
row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
|
||||
self["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class)
|
||||
self["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class, @connection)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -761,7 +762,8 @@ class Fixture #:nodoc:
|
|||
|
||||
attr_reader :model_class
|
||||
|
||||
def initialize(fixture, model_class)
|
||||
def initialize(fixture, model_class, connection = ActiveRecord::Base.connection)
|
||||
@connection = connection
|
||||
@fixture = fixture
|
||||
@model_class = model_class.is_a?(Class) ? model_class : model_class.constantize rescue nil
|
||||
end
|
||||
|
@ -783,14 +785,14 @@ class Fixture #:nodoc:
|
|||
end
|
||||
|
||||
def key_list
|
||||
columns = @fixture.keys.collect{ |column_name| ActiveRecord::Base.connection.quote_column_name(column_name) }
|
||||
columns = @fixture.keys.collect{ |column_name| @connection.quote_column_name(column_name) }
|
||||
columns.join(", ")
|
||||
end
|
||||
|
||||
def value_list
|
||||
list = @fixture.inject([]) do |fixtures, (key, value)|
|
||||
col = model_class.columns_hash[key] if model_class.respond_to?(:ancestors) && model_class.ancestors.include?(ActiveRecord::Base)
|
||||
fixtures << ActiveRecord::Base.connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
|
||||
fixtures << @connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
|
||||
end
|
||||
list * ', '
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ module I18n
|
|||
|
||||
protected
|
||||
def interpolate_with_deprecated_syntax(locale, string, values = {})
|
||||
return string unless string.is_a?(String)
|
||||
return string unless string.is_a?(String) && !values.empty?
|
||||
|
||||
string = string.gsub(/%d|%s/) do |s|
|
||||
instead = DEPRECATED_INTERPOLATORS[s]
|
||||
|
|
|
@ -23,8 +23,12 @@ en:
|
|||
less_than_or_equal_to: "must be less than or equal to {{count}}"
|
||||
odd: "must be odd"
|
||||
even: "must be even"
|
||||
record_invalid: "Validation failed: {{errors}}"
|
||||
# Append your own errors here or at the model/attributes scope.
|
||||
|
||||
full_messages:
|
||||
format: "{{attribute}} {{message}}"
|
||||
|
||||
# You can define own errors for models or model attributes.
|
||||
# The values :model, :attribute and :value are always available for interpolation.
|
||||
#
|
||||
|
|
|
@ -89,12 +89,7 @@ module ActiveRecord
|
|||
when Hash
|
||||
options
|
||||
when Proc
|
||||
case parent_scope
|
||||
when Scope
|
||||
with_scope(:find => parent_scope.proxy_options) { options.call(*args) }
|
||||
else
|
||||
options.call(*args)
|
||||
end
|
||||
options.call(*args)
|
||||
end, &block)
|
||||
end
|
||||
(class << self; self end).instance_eval do
|
||||
|
|
|
@ -297,7 +297,7 @@ module ActiveRecord
|
|||
raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
|
||||
end
|
||||
|
||||
unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
|
||||
unless [:belongs_to, :has_many, :has_one].include?(source_reflection.macro) && source_reflection.options[:through].nil?
|
||||
raise HasManyThroughSourceAssociationMacroError.new(self)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -84,7 +84,6 @@ HEADER
|
|||
elsif @connection.respond_to?(:primary_key)
|
||||
pk = @connection.primary_key(table)
|
||||
end
|
||||
pk ||= 'id'
|
||||
|
||||
tbl.print " create_table #{table.inspect}"
|
||||
if columns.detect { |c| c.name == pk }
|
||||
|
@ -180,4 +179,4 @@ HEADER
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -74,12 +74,14 @@ module ActiveRecord #:nodoc:
|
|||
# {"comments": [{"body": "Don't think too hard"}],
|
||||
# "title": "So I was thinking"}]}
|
||||
def to_json(options = {})
|
||||
hash = Serializer.new(self, options).serializable_record
|
||||
hash = { self.class.model_name.element => hash } if include_root_in_json
|
||||
ActiveSupport::JSON.encode(hash)
|
||||
super
|
||||
end
|
||||
|
||||
def as_json(options = nil) self end #:nodoc:
|
||||
def as_json(options = nil) #:nodoc:
|
||||
hash = Serializer.new(self, options).serializable_record
|
||||
hash = { self.class.model_name.element => hash } if include_root_in_json
|
||||
hash
|
||||
end
|
||||
|
||||
def from_json(json)
|
||||
self.attributes = ActiveSupport::JSON.decode(json)
|
||||
|
|
|
@ -178,7 +178,7 @@ module ActiveRecord #:nodoc:
|
|||
end
|
||||
|
||||
def root
|
||||
root = (options[:root] || @record.class.to_s.underscore).to_s
|
||||
root = (options[:root] || @record.class.model_name.singular).to_s
|
||||
reformat_name(root)
|
||||
end
|
||||
|
||||
|
@ -320,7 +320,11 @@ module ActiveRecord #:nodoc:
|
|||
|
||||
protected
|
||||
def compute_type
|
||||
type = @record.class.serialized_attributes.has_key?(name) ? :yaml : @record.class.columns_hash[name].type
|
||||
type = if @record.class.serialized_attributes.has_key?(name)
|
||||
:yaml
|
||||
else
|
||||
@record.class.columns_hash[name].try(:type)
|
||||
end
|
||||
|
||||
case type
|
||||
when :text
|
||||
|
|
|
@ -10,15 +10,122 @@ module ActiveRecord
|
|||
attr_reader :record
|
||||
def initialize(record)
|
||||
@record = record
|
||||
super("Validation failed: #{@record.errors.full_messages.join(", ")}")
|
||||
errors = @record.errors.full_messages.join(I18n.t('support.array.words_connector', :default => ', '))
|
||||
super(I18n.t('activerecord.errors.messages.record_invalid', :errors => errors))
|
||||
end
|
||||
end
|
||||
|
||||
class Error
|
||||
attr_accessor :base, :attribute, :type, :message, :options
|
||||
|
||||
def initialize(base, attribute, type = nil, options = {})
|
||||
self.base = base
|
||||
self.attribute = attribute
|
||||
self.type = type || :invalid
|
||||
self.options = options
|
||||
self.message = options.delete(:message) || self.type
|
||||
end
|
||||
|
||||
def message
|
||||
generate_message(@message, options.dup)
|
||||
end
|
||||
|
||||
def full_message
|
||||
attribute.to_s == 'base' ? message : generate_full_message(message, options.dup)
|
||||
end
|
||||
|
||||
alias :to_s :message
|
||||
|
||||
def value
|
||||
@base.respond_to?(attribute) ? @base.send(attribute) : nil
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Translates an error message in it's default scope (<tt>activerecord.errrors.messages</tt>).
|
||||
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there,
|
||||
# it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the
|
||||
# default message (e.g. <tt>activerecord.errors.messages.MESSAGE</tt>). The translated model name,
|
||||
# translated attribute name and the value are available for interpolation.
|
||||
#
|
||||
# When using inheritence in your models, it will check all the inherited models too, but only if the model itself
|
||||
# hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt>
|
||||
# error +message+ for the <tt>title</tt> +attribute+, it looks for these translations:
|
||||
#
|
||||
# <ol>
|
||||
# <li><tt>activerecord.errors.models.admin.attributes.title.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.models.admin.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.models.user.attributes.title.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.models.user.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.messages.blank</tt></li>
|
||||
# <li>any default you provided through the +options+ hash (in the activerecord.errors scope)</li>
|
||||
# </ol>
|
||||
def generate_message(message, options = {})
|
||||
keys = @base.class.self_and_descendants_from_active_record.map do |klass|
|
||||
[ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
|
||||
:"models.#{klass.name.underscore}.#{message}" ]
|
||||
end.flatten
|
||||
|
||||
keys << options.delete(:default)
|
||||
keys << :"messages.#{message}"
|
||||
keys << message if message.is_a?(String)
|
||||
keys << @type unless @type == message
|
||||
keys.compact!
|
||||
|
||||
options.reverse_merge! :default => keys,
|
||||
:scope => [:activerecord, :errors],
|
||||
:model => @base.class.human_name,
|
||||
:attribute => @base.class.human_attribute_name(attribute.to_s),
|
||||
:value => value
|
||||
|
||||
I18n.translate(keys.shift, options)
|
||||
end
|
||||
|
||||
# Wraps an error message into a full_message format.
|
||||
#
|
||||
# The default full_message format for any locale is <tt>"{{attribute}} {{message}}"</tt>.
|
||||
# One can specify locale specific default full_message format by storing it as a
|
||||
# translation for the key <tt>:"activerecord.errors.full_messages.format"</tt>.
|
||||
#
|
||||
# Additionally one can specify a validation specific error message format by
|
||||
# storing a translation for <tt>:"activerecord.errors.full_messages.[message_key]"</tt>.
|
||||
# E.g. the full_message format for any validation that uses :blank as a message
|
||||
# key (such as validates_presence_of) can be stored to <tt>:"activerecord.errors.full_messages.blank".</tt>
|
||||
#
|
||||
# Because the message key used by a validation can be overwritten on the
|
||||
# <tt>validates_*</tt> class macro level one can customize the full_message format for
|
||||
# any particular validation:
|
||||
#
|
||||
# # app/models/article.rb
|
||||
# class Article < ActiveRecord::Base
|
||||
# validates_presence_of :title, :message => :"title.blank"
|
||||
# end
|
||||
#
|
||||
# # config/locales/en.yml
|
||||
# en:
|
||||
# activerecord:
|
||||
# errors:
|
||||
# full_messages:
|
||||
# title:
|
||||
# blank: This title is screwed!
|
||||
def generate_full_message(message, options = {})
|
||||
options.reverse_merge! :message => self.message,
|
||||
:model => @base.class.human_name,
|
||||
:attribute => @base.class.human_attribute_name(attribute.to_s),
|
||||
:value => value
|
||||
|
||||
key = :"full_messages.#{@message}"
|
||||
defaults = [:'full_messages.format', '{{attribute}} {{message}}']
|
||||
|
||||
I18n.t(key, options.merge(:default => defaults, :scope => [:activerecord, :errors]))
|
||||
end
|
||||
end
|
||||
|
||||
# Active Record validation is reported to and from this object, which is used by Base#save to
|
||||
# determine whether the object is in a valid state to be saved. See usage example in Validations.
|
||||
class Errors
|
||||
include Enumerable
|
||||
|
||||
|
||||
class << self
|
||||
def default_error_messages
|
||||
ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use I18n.translate('activerecord.errors.messages').")
|
||||
|
@ -43,11 +150,19 @@ module ActiveRecord
|
|||
# error can be added to the same +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
|
||||
# If no +messsage+ is supplied, :invalid is assumed.
|
||||
# If +message+ is a Symbol, it will be translated, using the appropriate scope (see translate_error).
|
||||
def add(attribute, message = nil, options = {})
|
||||
message ||= :invalid
|
||||
message = generate_message(attribute, message, options) if message.is_a?(Symbol)
|
||||
# def add(attribute, message = nil, options = {})
|
||||
# message ||= :invalid
|
||||
# message = generate_message(attribute, message, options)) if message.is_a?(Symbol)
|
||||
# @errors[attribute.to_s] ||= []
|
||||
# @errors[attribute.to_s] << message
|
||||
# end
|
||||
|
||||
def add(error_or_attr, message = nil, options = {})
|
||||
error, attribute = error_or_attr.is_a?(Error) ? [error_or_attr, error_or_attr.attribute] : [nil, error_or_attr]
|
||||
options[:message] = options.delete(:default) if options.has_key?(:default)
|
||||
|
||||
@errors[attribute.to_s] ||= []
|
||||
@errors[attribute.to_s] << message
|
||||
@errors[attribute.to_s] << (error || Error.new(@base, attribute, message, options))
|
||||
end
|
||||
|
||||
# Will add an error message to each of the attributes in +attributes+ that is empty.
|
||||
|
@ -66,49 +181,6 @@ module ActiveRecord
|
|||
add(attr, :blank, :default => custom_message) if value.blank?
|
||||
end
|
||||
end
|
||||
|
||||
# Translates an error message in it's default scope (<tt>activerecord.errrors.messages</tt>).
|
||||
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there,
|
||||
# it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the
|
||||
# default message (e.g. <tt>activerecord.errors.messages.MESSAGE</tt>). The translated model name,
|
||||
# translated attribute name and the value are available for interpolation.
|
||||
#
|
||||
# When using inheritence in your models, it will check all the inherited models too, but only if the model itself
|
||||
# hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt>
|
||||
# error +message+ for the <tt>title</tt> +attribute+, it looks for these translations:
|
||||
#
|
||||
# <ol>
|
||||
# <li><tt>activerecord.errors.models.admin.attributes.title.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.models.admin.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.models.user.attributes.title.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.models.user.blank</tt></li>
|
||||
# <li><tt>activerecord.errors.messages.blank</tt></li>
|
||||
# <li>any default you provided through the +options+ hash (in the activerecord.errors scope)</li>
|
||||
# </ol>
|
||||
def generate_message(attribute, message = :invalid, options = {})
|
||||
|
||||
message, options[:default] = options[:default], message if options[:default].is_a?(Symbol)
|
||||
|
||||
defaults = @base.class.self_and_descendants_from_active_record.map do |klass|
|
||||
[ :"models.#{klass.name.underscore}.attributes.#{attribute}.#{message}",
|
||||
:"models.#{klass.name.underscore}.#{message}" ]
|
||||
end
|
||||
|
||||
defaults << options.delete(:default)
|
||||
defaults = defaults.compact.flatten << :"messages.#{message}"
|
||||
|
||||
key = defaults.shift
|
||||
value = @base.respond_to?(attribute) ? @base.send(attribute) : nil
|
||||
|
||||
options = { :default => defaults,
|
||||
:model => @base.class.human_name,
|
||||
:attribute => @base.class.human_attribute_name(attribute.to_s),
|
||||
:value => value,
|
||||
:scope => [:activerecord, :errors]
|
||||
}.merge(options)
|
||||
|
||||
I18n.translate(key, options)
|
||||
end
|
||||
|
||||
# Returns true if the specified +attribute+ has errors associated with it.
|
||||
#
|
||||
|
@ -138,8 +210,9 @@ module ActiveRecord
|
|||
# company.errors.on(:email) # => "can't be blank"
|
||||
# company.errors.on(:address) # => nil
|
||||
def on(attribute)
|
||||
errors = @errors[attribute.to_s]
|
||||
return nil if errors.nil?
|
||||
attribute = attribute.to_s
|
||||
return nil unless @errors.has_key?(attribute)
|
||||
errors = @errors[attribute].map(&:to_s)
|
||||
errors.size == 1 ? errors.first : errors
|
||||
end
|
||||
|
||||
|
@ -163,7 +236,11 @@ module ActiveRecord
|
|||
# # name - can't be blank
|
||||
# # address - can't be blank
|
||||
def each
|
||||
@errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
|
||||
@errors.each_key { |attr| @errors[attr].each { |error| yield attr, error.message } }
|
||||
end
|
||||
|
||||
def each_error
|
||||
@errors.each_key { |attr| @errors[attr].each { |error| yield attr, error } }
|
||||
end
|
||||
|
||||
# Yields each full error message added. So <tt>Person.errors.add("first_name", "can't be empty")</tt> will be returned
|
||||
|
@ -194,22 +271,10 @@ module ActiveRecord
|
|||
# company.errors.full_messages # =>
|
||||
# ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"]
|
||||
def full_messages(options = {})
|
||||
full_messages = []
|
||||
|
||||
@errors.each_key do |attr|
|
||||
@errors[attr].each do |message|
|
||||
next unless message
|
||||
|
||||
if attr == "base"
|
||||
full_messages << message
|
||||
else
|
||||
attr_name = @base.class.human_attribute_name(attr)
|
||||
full_messages << attr_name + I18n.t('activerecord.errors.format.separator', :default => ' ') + message
|
||||
end
|
||||
end
|
||||
@errors.values.inject([]) do |full_messages, errors|
|
||||
full_messages + errors.map { |error| error.full_message }
|
||||
end
|
||||
full_messages
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if no errors have been added.
|
||||
def empty?
|
||||
|
@ -254,7 +319,11 @@ module ActiveRecord
|
|||
full_messages.each { |msg| e.error(msg) }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def generate_message(attribute, message = :invalid, options = {})
|
||||
ActiveSupport::Deprecation.warn("ActiveRecord::Errors#generate_message has been deprecated. Please use ActiveRecord::Error#generate_message.")
|
||||
Error.new(@base, attribute, message, options).to_s
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
@ -437,7 +506,7 @@ module ActiveRecord
|
|||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation")
|
||||
record.errors.add(attr_name, :confirmation, :default => configuration[:message])
|
||||
record.errors.add(attr_name, :confirmation, :default => configuration[:message])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -479,7 +548,7 @@ module ActiveRecord
|
|||
|
||||
validates_each(attr_names,configuration) do |record, attr_name, value|
|
||||
unless value == configuration[:accept]
|
||||
record.errors.add(attr_name, :accepted, :default => configuration[:message])
|
||||
record.errors.add(attr_name, :accepted, :default => configuration[:message])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -499,7 +568,7 @@ module ActiveRecord
|
|||
#
|
||||
# Configuration options:
|
||||
# * <tt>message</tt> - A custom error message (default is: "can't be blank").
|
||||
# * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>,
|
||||
# * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>,
|
||||
# <tt>:update</tt>).
|
||||
# * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
|
||||
# occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>).
|
||||
|
@ -599,7 +668,7 @@ module ActiveRecord
|
|||
validates_each(attrs, options) do |record, attr, value|
|
||||
value = options[:tokenizer].call(value) if value.kind_of?(String)
|
||||
unless !value.nil? and value.size.method(validity_checks[option])[option_value]
|
||||
record.errors.add(attr, key, :default => custom_message, :count => option_value)
|
||||
record.errors.add(attr, key, :default => custom_message, :count => option_value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -687,7 +756,7 @@ module ActiveRecord
|
|||
# ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
|
||||
# rare case that a race condition occurs, the database will guarantee
|
||||
# the field's uniqueness.
|
||||
#
|
||||
#
|
||||
# When the database catches such a duplicate insertion,
|
||||
# ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
|
||||
# exception. You can either choose to let this error propagate (which
|
||||
|
@ -696,7 +765,7 @@ module ActiveRecord
|
|||
# that the title already exists, and asking him to re-enter the title).
|
||||
# This technique is also known as optimistic concurrency control:
|
||||
# http://en.wikipedia.org/wiki/Optimistic_concurrency_control
|
||||
#
|
||||
#
|
||||
# Active Record currently provides no way to distinguish unique
|
||||
# index constraint errors from other types of database errors, so you
|
||||
# will have to parse the (database-specific) exception message to detect
|
||||
|
@ -726,7 +795,7 @@ module ActiveRecord
|
|||
comparison_operator = "IS ?"
|
||||
elsif column.text?
|
||||
comparison_operator = "#{connection.case_sensitive_equality_operator} ?"
|
||||
value = column.limit ? value.to_s[0, column.limit] : value.to_s
|
||||
value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s
|
||||
else
|
||||
comparison_operator = "= ?"
|
||||
end
|
||||
|
@ -794,7 +863,7 @@ module ActiveRecord
|
|||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
unless value.to_s =~ configuration[:with]
|
||||
record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
|
||||
record.errors.add(attr_name, :invalid, :default => configuration[:message], :value => value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -828,7 +897,7 @@ module ActiveRecord
|
|||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
unless enum.include?(value)
|
||||
record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value)
|
||||
record.errors.add(attr_name, :inclusion, :default => configuration[:message], :value => value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -862,7 +931,7 @@ module ActiveRecord
|
|||
|
||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||
if enum.include?(value)
|
||||
record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value)
|
||||
record.errors.add(attr_name, :exclusion, :default => configuration[:message], :value => value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -970,7 +1039,7 @@ module ActiveRecord
|
|||
case option
|
||||
when :odd, :even
|
||||
unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
|
||||
record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
|
||||
record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message])
|
||||
end
|
||||
else
|
||||
record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option]) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
|
||||
|
|
|
@ -2,7 +2,7 @@ module ActiveRecord
|
|||
module VERSION #:nodoc:
|
||||
MAJOR = 2
|
||||
MINOR = 3
|
||||
TINY = 3
|
||||
TINY = 4
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue