Class: Mongoid::Validatable::UniquenessValidator
Relationships & Source Files | |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
ActiveModel::EachValidator
|
|
Instance Chain:
self,
Queryable ,
ActiveModel::EachValidator
|
|
Inherits: |
ActiveModel::EachValidator
|
Defined in: | lib/mongoid/validatable/uniqueness.rb |
Overview
Validates whether or not a field is unique against the documents in the database.
It is also possible to limit the uniqueness constraint to a set of documents matching certain conditions:
class Person
include Mongoid::Document
field :title
field :active, type: Boolean
validates_uniqueness_of :title, conditions: -> {where(active: true)}
end
Instance Attribute Summary
-
#case_sensitive? ⇒ true | false
readonly
private
Internal use only
Internal use only
Should the uniqueness validation be case sensitive?
Instance Method Summary
-
#validate_each(document, attribute, value) ⇒ Errors
Validate the document for uniqueness violations.
-
#add_error(document, attribute, value)
private
Internal use only
Internal use only
Add the error to the document.
-
#create_criteria(base, document, attribute, value) ⇒ Criteria
private
Internal use only
Internal use only
Create the validation criteria.
-
#criterion(document, attribute, value) ⇒ Criteria
private
Internal use only
Internal use only
Get the default criteria for checking uniqueness.
-
#filter(value) ⇒ Object | Regexp
private
Internal use only
Internal use only
Filter the value based on whether the check is case sensitive or not.
-
#localized?(document, attribute) ⇒ true | false
private
Internal use only
Internal use only
Is the attribute localized?
-
#scope(criteria, document, _attribute) ⇒ Criteria
private
Internal use only
Internal use only
Scope the criteria to the scope options provided.
-
#scope_value_changed?(document) ⇒ true | false
private
Internal use only
Internal use only
Scope reference has changed?
-
#skip_validation?(document) ⇒ true | false
private
Internal use only
Internal use only
Should validation be skipped?
-
#to_validate(document, attribute, value) ⇒ Array<Object, Object>
private
Internal use only
Internal use only
Get the name of the field and the value to validate.
-
#validate_embedded(document, attribute, value)
private
Internal use only
Internal use only
Validate an embedded document.
-
#validate_root(document, attribute, value)
private
Internal use only
Internal use only
Validate a root document.
-
#validation_required?(document, attribute) ⇒ true | false
private
Are we required to validate the document?
Queryable
- Included
#with_query | Wrap the validation inside the an execution block that alert’s the client not to clear its persistence options. |
Instance Attribute Details
#case_sensitive? ⇒ true
| false
(readonly, private)
Should the uniqueness validation be case sensitive?
# File 'lib/mongoid/validatable/uniqueness.rb', line 79
def case_sensitive? !( [:case_sensitive] == false) end
Instance Method Details
#add_error(document, attribute, value) (private)
Add the error to the document.
# File 'lib/mongoid/validatable/uniqueness.rb', line 65
def add_error(document, attribute, value) document.errors.add( attribute, :taken, ** .except(:case_sensitive, :scope).merge(value: value) ) end
#create_criteria(base, document, attribute, value) ⇒ Criteria (private)
Create the validation criteria.
# File 'lib/mongoid/validatable/uniqueness.rb', line 96
def create_criteria(base, document, attribute, value) criteria = scope(base.unscoped, document, attribute) field = document.fields[document.database_field_name(attribute)] # In the past, we were calling value.mongoize in all cases, which # would call Object's mongoize method. This is a problem for StringifiedSymbol, # because a Symbol should mongoize to a String, but calling .mongoize # on a Symbol mongoizes it to a Symbol. # Therefore, we call the field's mongoize in all cases except when the # field is localized, because by the time we arrive at this code, the # value is already in the form of { lang => translation } and calling # the field's mongoize will nest that further into { lang => # "\{ lang => translation \}"} (assuming the field type is a string). # Therefore, we call Object's mongoize method so it returns the hash as # it is. mongoized = field.try(:localized?) ? value.mongoize : field.mongoize(value) criteria.selector.update(criterion(document, attribute, mongoized)) criteria end
#criterion(document, attribute, value) ⇒ Criteria (private)
Get the default criteria for checking uniqueness.
# File 'lib/mongoid/validatable/uniqueness.rb', line 128
def criterion(document, attribute, value) field = document.database_field_name(attribute) if value && localized?(document, field) conditions = (value || {}).inject([]) { |acc, (k,v)| acc << { "#{field}.#{k}" => filter(v) }} selector = { "$or" => conditions } else selector = { field => filter(value) } end if document.persisted? && !document. selector.merge!(_id: { "$ne" => document._id }) end selector end
#filter(value) ⇒ Object
| Regexp (private)
Filter the value based on whether the check is case sensitive or not.
# File 'lib/mongoid/validatable/uniqueness.rb', line 154
def filter(value) !case_sensitive? && value ? /\A#{Regexp.escape(value.to_s)}\z/i : value end
#localized?(document, attribute) ⇒ true
| false
(private)
Is the attribute localized?
# File 'lib/mongoid/validatable/uniqueness.rb', line 300
def localized?(document, attribute) document.fields[document.database_field_name(attribute)].try(:localized?) end
#scope(criteria, document, _attribute) ⇒ Criteria (private)
Scope the criteria to the scope options provided.
# File 'lib/mongoid/validatable/uniqueness.rb', line 169
def scope(criteria, document, _attribute) Array.wrap( [:scope]).each do |item| name = document.database_field_name(item) criteria = criteria.where(item => document.attributes[name]) end criteria end
#scope_value_changed?(document) ⇒ true
| false
(private)
Scope reference has changed?
#skip_validation?(document) ⇒ true
| false
(private)
Should validation be skipped?
# File 'lib/mongoid/validatable/uniqueness.rb', line 187
def skip_validation?(document) !document._parent || document. end
#to_validate(document, attribute, value) ⇒ Array<Object
, Object
> (private)
Get the name of the field and the value to validate. This is for the case when we validate an association via the association name and not the key, we need to send the key name and value to the db, not the association object.
# File 'lib/mongoid/validatable/uniqueness.rb', line 222
def to_validate(document, attribute, value) association = document.relations[attribute.to_s] if association && association.stores_foreign_key? [ association.foreign_key, value && value._id ] else [ attribute, value ] end end
#validate_each(document, attribute, value) ⇒ Errors
Validate the document for uniqueness violations.
# File 'lib/mongoid/validatable/uniqueness.rb', line 41
def validate_each(document, attribute, value) with_query(document) do attrib, val = to_validate(document, attribute, value) return unless validation_required?(document, attrib) if document. (document, attrib, val) else validate_root(document, attrib, val) end end end
#validate_embedded(document, attribute, value) (private)
Validate an embedded document.
# File 'lib/mongoid/validatable/uniqueness.rb', line 241
def (document, attribute, value) return if skip_validation?(document) relation = document._parent.send(document.association_name) criteria = create_criteria(relation, document, attribute, value) criteria = criteria.merge( [:conditions].call) if [:conditions] criteria = criteria.read(mode: :primary).limit(2) add_error(document, attribute, value) if criteria.count > 1 end
#validate_root(document, attribute, value) (private)
Validate a root document.
# File 'lib/mongoid/validatable/uniqueness.rb', line 260
def validate_root(document, attribute, value) klass = document.class while klass.superclass.respond_to?(:validators) && klass.superclass.validators.include?(self) klass = klass.superclass end criteria = create_criteria(klass, document, attribute, value) criteria = criteria.merge( [:conditions].call) if [:conditions] if criteria.read(mode: :primary).exists? add_error(document, attribute, value) end end
#validation_required?(document, attribute) ⇒ true
| false
(private)
Are we required to validate the document?
# File 'lib/mongoid/validatable/uniqueness.rb', line 283
def validation_required?(document, attribute) document.new_record? || document.send("attribute_changed?", attribute.to_s) || scope_value_changed?(document) end