如何验证数组字段的成员?

| 我有这个模型:
class Campaign

  include Mongoid::Document
  include Mongoid::Timestamps

  field :name, :type => String
  field :subdomain, :type => String
  field :intro, :type => String
  field :body, :type => String
  field :emails, :type => Array
end
现在,我要验证“ 1”数组中的每个电子邮件的格式是否正确。我阅读了Mongoid和ActiveModel :: Validations文档,但找不到如何执行此操作。 你能给我指点一下吗?     
已邀请:
您可以定义自定义
ArrayValidator
。将以下内容放在ѭ3following中:
class ArrayValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, values)
    Array(values).each do |value|
      options.each do |key, args|
        validator_options = { attributes: attribute }
        validator_options.merge!(args) if args.is_a?(Hash)

        next if value.nil? && validator_options[:allow_nil]
        next if value.blank? && validator_options[:allow_blank]

        validator_class_name = \"#{key.to_s.camelize}Validator\"
        validator_class = begin
          validator_class_name.constantize
        rescue NameError
          \"ActiveModel::Validations::#{validator_class_name}\".constantize
        end

        validator = validator_class.new(validator_options)
        validator.validate_each(record, attribute, value)
      end
    end
  end
end
您可以在模型中像这样使用它:
class User
  include Mongoid::Document
  field :tags, Array

  validates :tags, array: { presence: true, inclusion: { in: %w{ ruby rails } }
end
它将针对“ 6”哈希中指定的每个验证器验证数组中的每个元素。     
Milovan的回答得到了我的认可,但是实现存在一些问题: 展平嵌套数组会更改行为并隐藏无效值。
nil
字段值被视为
[nil]
,这似乎不正确。 提供的带有example9ѭ的示例将产生
NotImplementedError
错误,因为
PresenceValidator
未实现
validate_each
。 在每次验证时为数组中的每个值实例化一个新的验证器实例的效率很低。 生成的错误消息没有显示数组元素为何无效的原因,这会导致不良的用户体验。 这是解决所有这些问题的更新的可枚举和数组验证器。为了方便起见,下面包含了该代码。
# Validates the values of an Enumerable with other validators.
# Generates error messages that include the index and value of
# invalid elements.
#
# Example:
#
#   validates :values, enum: { presence: true, inclusion: { in: %w{ big small } } }
#
class EnumValidator < ActiveModel::EachValidator

  def initialize(options)
    super
    @validators = options.map do |(key, args)|
      create_validator(key, args)
    end
  end

  def validate_each(record, attribute, values)
    helper = Helper.new(@validators, record, attribute)
    Array.wrap(values).each do |value|
      helper.validate(value)
    end
  end

  private

  class Helper

    def initialize(validators, record, attribute)
      @validators = validators
      @record = record
      @attribute = attribute
      @count = -1
    end

    def validate(value)
      @count += 1
      @validators.each do |validator|
        next if value.nil? && validator.options[:allow_nil]
        next if value.blank? && validator.options[:allow_blank]
        validate_with(validator, value)
      end
    end

    def validate_with(validator, value)
      before_errors = error_count
      run_validator(validator, value)
      if error_count > before_errors
        prefix = \"element #{@count} (#{value}) \"
        (before_errors...error_count).each do |pos|
          error_messages[pos] = prefix + error_messages[pos]
        end
      end
    end

    def run_validator(validator, value)
      validator.validate_each(@record, @attribute, value)
    rescue NotImplementedError
      validator.validate(@record)
    end

    def error_messages
      @record.errors.messages[@attribute]
    end

    def error_count
      error_messages ? error_messages.length : 0
    end
  end

  def create_validator(key, args)
    opts = {attributes: attributes}
    opts.merge!(args) if args.kind_of?(Hash)
    validator_class(key).new(opts).tap do |validator|
      validator.check_validity!
    end
  end

  def validator_class(key)
    validator_class_name = \"#{key.to_s.camelize}Validator\"
    validator_class_name.constantize
  rescue NameError
    \"ActiveModel::Validations::#{validator_class_name}\".constantize
  end
end
    
您可能需要为电子邮件字段定义自己的自定义验证器。 因此,您将在课程定义之后添加,
validate :validate_emails

def validate_emails
  invalid_emails = self.emails.map{ |email| email.match(/^([^@\\s]+)@((?:[-a-z0-9]+\\.)+[a-z]{2,})$/i) }.select{ |e| e != nil }
  errors.add(:emails, \'invalid email address\') unless invalid_emails.empty?
end
正则表达式本身可能并不完美,但这是基本思想。您可以按以下方式查看rails指南: http://guides.rubyonrails.org/v2.3.8/activerecord_validations_callbacks.html#creating-custom-validation-methods     
发现自己刚刚尝试解决此问题。我对Tim O \的答案做了些微修改,以提供以下内容,该内容提供了更清晰的输出和错误对象的更多信息,然后您可以在视图中向用户显示这些信息。
validate :validate_emails

def validate_emails
  emails.each do |email|
    unless email.match(/^([^@\\s]+)@((?:[-a-z0-9]+\\.)+[a-z]{2,})$/i)
      errors.add(:emails, \"#{email} is not a valid email address.\")
    end
  end
end
    
这是一个可能有助于使用Rails api文档的示例:http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates   当在给定属性的一次调用中使用自定义验证器和默认验证器时,validate方法的功能就来了。
class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors[attribute] << (options[:message] || \"is not an email\") unless
      value =~ /\\A([^@\\s]+)@((?:[-a-z0-9]+\\.)+[a-z]{2,})\\z/i
  end
end

class Person
  include ActiveModel::Validations
  attr_accessor :name, :email

  validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 100 }
  validates :email, :presence => true, :email => true
end
    

要回复问题请先登录注册