【问题标题】:Rails 4 - concerns for generic validationRails 4 - 通用验证的关注点
【发布时间】:2014-03-31 20:01:43
【问题描述】:

我刚刚遇到了 Rails 问题,我想用它们来验证我的模型。但是我希望验证是通用的,以便仅当我关注的类具有该属性时才使用验证。我认为这很容易,但我尝试了很多方法,比如使用 column_names、constantize、send 和许多其他方法,但没有任何效果。正确的方法是什么?代码:

module CommonValidator
  extend ActiveSupport::Concern

  included do
    validates :email, presence: { message: I18n.t(:"validations.commons.email_missing") }, 
                      format: { with: /\A[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\z/i, 
                      message: I18n.t(:"validations.commons.email_wrong_format"), 
                            allow_blank: true } if self.column_names.include? :email
  end
end

class Restaurant < ActiveRecord::Base
  include CommonValidator
  .
  .
  .
end

Restaurant 当然有 email 属性。是否可以检查包含我关注的类中是否存在属性?我想将我的 CommonValidations 包含到许多没有电子邮件属性的模型中。我正在使用 Rails 4。

【问题讨论】:

  • 这是一个非常懒惰和脆弱的模式/技术。你最好创建一个EachValidator 并在每列中使用它,例如validates :email, email_address: true

标签: ruby-on-rails validation activesupport-concern


【解决方案1】:

您可以在当前实例上使用respond_to?,如下所示:

validates :email, presence: { message: I18n.t(:"validations.commons.email_missing") }, 
                  format: { with: /\A[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\z/i, 
                  message: I18n.t(:"validations.commons.email_wrong_format"), 
                  allow_blank: true }, 
                  if: lambda { |o| o.respond_to?(:email) }

@coreyward 建议的另一个选项是定义一个扩展EachValidator 的类。例如,对于电子邮件验证:

# app/validators/email_validator.rb
class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value =~ /\A[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\z/i
      record.errors[attribute] << (options[:message] || I18n.t(:"validations.commons.email_wrong_format"))
    end
  end
end

然后您可以将验证调用更新为:

validates :email, 
          presence: { message: I18n.t(:"validations.commons.email_missing") },
          email: true, 
          allow_blank: true

【讨论】:

  • EachValidator 是一种可能性,我会重新考虑它的用法,但是 respond_to 的用法呢?不管用。我的目标是拥有例如“验证:aaa ....”,其中:aaa 是模型餐厅没有的随机字符串。现在它只会引发 NoMethodError。简而言之,我希望能够对不存在的属性进行验证,因此我可以轻松地将 CommonValidations 包含在许多不必具有所有属性的模型中。
  • @Giron,它对我有用:validates :email, presence: true, if: lambda { |o| o.respond_to?(:email) },您能否更新您的问题以包含您遇到的错误?
  • 我会在大约 10 小时后回家更新。
  • 解决方案是对的,我的代码中有一个愚蠢的错字。感谢您抽出宝贵时间,我可以想象同时使用这两种方法的情况,但我同意 coreyward 的观点,这种“智能”验证的使用可能非常脆弱。
【解决方案2】:

我正在寻找类似的东西,但有自定义验证。 我最终得到了一些我认为可以共享的东西,包括通用测试。

首先,设置关注app/models/concern/my_concern.rb

请注意,我们没有将validate_my_field 定义到ClassMethods 模块中。

module MyConcern
  extend ActiveSupport::Concern

  included do
    validate :my_field, :validate_my_field
  end

private

  def validate_my_field
    ...
  end

end

在你的模型中加入关注app/models/my_model.rb

class MyModel < ActiveRecord::Base
  include MyConcern
end

加载关注spec/support/rails_helper中的共享示例:

…
  Dir[Rails.root.join('spec/concerns/**/*.rb')].each { |f| require f }
…

创建关注点共享示例spec/concerns/models/my_field_concern_spec.rb

RSpec.shared_examples_for 'my_field_concern' do
  let(:model) { described_class } # the class that includes the concern

  it 'has a valid my_field' do
    instance = create(model.to_s.underscore.to_sym, my_field: …)
    expect(instance).not_to be_valid
    …
  end
end

然后最后将共享示例调用到您的模型规范中spec/models/my_model_spec.rb

require 'rails_helper'

RSpec.describe MyModel do
  include_examples 'my_field_concern'

  it_behaves_like 'my_field_concern'
end

我希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 2016-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多