mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
d672a14ee7
[#2754 state:committed] Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
205 lines
7.2 KiB
Ruby
205 lines
7.2 KiB
Ruby
require 'active_support/core_ext/integer/even_odd'
|
|
|
|
module ActiveRecord
|
|
# Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
|
|
# +record+ method to retrieve the record which did not validate.
|
|
# begin
|
|
# complex_operation_that_calls_save!_internally
|
|
# rescue ActiveRecord::RecordInvalid => invalid
|
|
# puts invalid.record.errors
|
|
# end
|
|
class RecordInvalid < ActiveRecordError
|
|
attr_reader :record
|
|
def initialize(record)
|
|
@record = record
|
|
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 Errors < ActiveModel::Errors
|
|
class << self
|
|
def default_error_messages
|
|
message = "Errors.default_error_messages has been deprecated. Please use I18n.translate('activerecord.errors.messages')."
|
|
ActiveSupport::Deprecation.warn(message)
|
|
|
|
I18n.translate 'activerecord.errors.messages'
|
|
end
|
|
end
|
|
|
|
# Returns all the full error messages in an array.
|
|
#
|
|
# class Company < ActiveRecord::Base
|
|
# validates_presence_of :name, :address, :email
|
|
# validates_length_of :name, :in => 5..30
|
|
# end
|
|
#
|
|
# company = Company.create(:address => '123 First St.')
|
|
# 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 = []
|
|
|
|
each do |attribute, messages|
|
|
messages = Array.wrap(messages)
|
|
next if messages.empty?
|
|
|
|
if attribute == :base
|
|
messages.each {|m| full_messages << m }
|
|
else
|
|
attr_name = @base.class.human_attribute_name(attribute.to_s)
|
|
prefix = attr_name + I18n.t('activerecord.errors.format.separator', :default => ' ')
|
|
messages.each do |m|
|
|
full_messages << "#{prefix}#{m}"
|
|
end
|
|
end
|
|
end
|
|
|
|
full_messages
|
|
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 inheritance 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
|
|
end
|
|
|
|
module Validations
|
|
extend ActiveSupport::Concern
|
|
|
|
include ActiveSupport::Callbacks
|
|
include ActiveModel::Validations
|
|
|
|
included do
|
|
alias_method_chain :save, :validation
|
|
alias_method_chain :save!, :validation
|
|
|
|
define_callbacks :validate_on_create, :validate_on_update
|
|
end
|
|
|
|
module ClassMethods
|
|
# Creates an object just like Base.create but calls save! instead of save
|
|
# so an exception is raised if the record is invalid.
|
|
def create!(attributes = nil, &block)
|
|
if attributes.is_a?(Array)
|
|
attributes.collect { |attr| create!(attr, &block) }
|
|
else
|
|
object = new(attributes)
|
|
yield(object) if block_given?
|
|
object.save!
|
|
object
|
|
end
|
|
end
|
|
|
|
def validation_method(on)
|
|
case on
|
|
when :create
|
|
:validate_on_create
|
|
when :update
|
|
:validate_on_update
|
|
else
|
|
:validate
|
|
end
|
|
end
|
|
end
|
|
|
|
module InstanceMethods
|
|
# The validation process on save can be skipped by passing false. The regular Base#save method is
|
|
# replaced with this when the validations module is mixed in, which it is by default.
|
|
def save_with_validation(perform_validation = true)
|
|
if perform_validation && valid? || !perform_validation
|
|
save_without_validation
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
# Attempts to save the record just like Base#save but will raise a RecordInvalid exception instead of returning false
|
|
# if the record is not valid.
|
|
def save_with_validation!
|
|
if valid?
|
|
save_without_validation!
|
|
else
|
|
raise RecordInvalid.new(self)
|
|
end
|
|
end
|
|
|
|
# Runs all the specified validations and returns true if no errors were added otherwise false.
|
|
def valid?
|
|
errors.clear
|
|
|
|
run_callbacks(:validate)
|
|
|
|
if respond_to?(:validate)
|
|
ActiveSupport::Deprecation.warn("Base#validate has been deprecated, please use Base.validate :method instead")
|
|
validate
|
|
end
|
|
|
|
if new_record?
|
|
run_callbacks(:validate_on_create)
|
|
|
|
if respond_to?(:validate_on_create)
|
|
ActiveSupport::Deprecation.warn("Base#validate_on_create has been deprecated, please use Base.validate_on_create :method instead")
|
|
validate_on_create
|
|
end
|
|
else
|
|
run_callbacks(:validate_on_update)
|
|
|
|
if respond_to?(:validate_on_update)
|
|
ActiveSupport::Deprecation.warn("Base#validate_on_update has been deprecated, please use Base.validate_on_update :method instead")
|
|
validate_on_update
|
|
end
|
|
end
|
|
|
|
errors.empty?
|
|
end
|
|
|
|
# Returns the Errors object that holds all information about attribute error messages.
|
|
def errors
|
|
@errors ||= Errors.new(self)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path|
|
|
filename = File.basename(path)
|
|
require "active_record/validations/#{filename}"
|
|
end
|