require 'active_support/core_ext/integer/even_odd' module ActiveRecord # Raised by save! and create! 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 (activerecord.errrors.messages). # Error messages are first looked up in models.MODEL.attributes.ATTRIBUTE.MESSAGE, if it's not there, # it's looked up in models.MODEL.MESSAGE and if that is not there it returns the translation of the # default message (e.g. activerecord.errors.messages.MESSAGE). 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 class Admin < User; end and you wanted the translation for the :blank # error +message+ for the title +attribute+, it looks for these translations: # #
    #
  1. activerecord.errors.models.admin.attributes.title.blank
  2. #
  3. activerecord.errors.models.admin.blank
  4. #
  5. activerecord.errors.models.user.attributes.title.blank
  6. #
  7. activerecord.errors.models.user.blank
  8. #
  9. activerecord.errors.messages.blank
  10. #
  11. any default you provided through the +options+ hash (in the activerecord.errors scope)
  12. #
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