class ActiveRecord::Validations::GlobalizedUniquenessValidator

Public Class Methods

new(options) click to toggle source
Calls superclass method
# File lib/active_record/validations/globalized_uniqueness.rb, line 6
def initialize(options)
  super(options.reverse_merge(:case_sensitive => true))
end

Public Instance Methods

setup(klass) click to toggle source

Unfortunately, we have to tie Uniqueness validators to a class.

# File lib/active_record/validations/globalized_uniqueness.rb, line 11
def setup(klass)
  @klass = klass
end
validate_each(record, attribute, value) click to toggle source
# File lib/active_record/validations/globalized_uniqueness.rb, line 15
def validate_each(record, attribute, value)
  finder_class = find_finder_class_for(record)
  table = finder_class.arel_table

  coder = record.class.serialized_attributes[attribute.to_s] # FIXME add/test support for serialized attributes with globalize3

  # determine table / attr_column_class respecting globalize3 translations
  globalized_column_class = find_globalized_column_class_for(record, attribute)
  globalized = globalized_column_class.present?
  if globalized
    attr_column_class = globalized_column_class
    table = attr_column_class.arel_table
  else
    attr_column_class = finder_class
  end

  if value && coder
    value = coder.dump value
  end

  # build relation respecting globalize3 translations
  relation = build_relation(attr_column_class, table, attribute, value)
  relation = relation.and(finder_class.arel_table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?

  Array.wrap(options[:scope]).each do |scope_item|
    if globalized && ([:locale] | record.class.translated_attribute_names).include?(scope_item)
      # handle globalize3 translated attribute scopes and :locale scope
      scope_value = if scope_item == :locale
                      Globalize.locale.to_s
                    else
                      record.read_attribute(scope_item)
                    end
      relation = relation.and(table[scope_item].eq(scope_value))
    else
      scope_value = record.read_attribute(scope_item)
      relation = relation.and(finder_class.arel_table[scope_item].eq(scope_value))
    end
  end

  # finalize building & execute query (respecting globalize3 translations)
  scoped = finder_class.unscoped
  scoped = scoped.joins(:translations) if globalized
  if scoped.where(relation).exists?
    record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
  end
end

Protected Instance Methods

find_globalized_column_class_for(record, attribute) click to toggle source

If the attribute of the record is globalized, returns the translation class; otherwise, returns nil.

# File lib/active_record/validations/globalized_uniqueness.rb, line 80
def find_globalized_column_class_for(record, attribute)
  class_hierarchy = [record.class]

  while class_hierarchy.first != @klass
    class_hierarchy.insert(0, class_hierarchy.first.superclass)
  end

  klass = class_hierarchy.detect { |klass| !klass.abstract_class? && klass.respond_to?(:translation_class) }

  if klass && record.class.translated_attribute_names.include?(attribute)
    klass.translation_class
  else
    nil
  end
end