Rails 4 - проблемы с универсальной проверкой

Я только что столкнулся с проблемами Rails и хочу использовать их для проверки своих моделей. Но я хочу, чтобы проверки были общими, чтобы проверка использовалась только в том случае, если класс, в который я включаю свою проблему, имеет атрибут. Я думал, что это будет легко, но я пробовал много способов, таких как использование column_names, константизация, отправка и многие другие, но ничего не работает. Как правильно это сделать? Код:

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

Ресторан, конечно, имеет атрибут электронной почты. Можно ли проверить наличие атрибута в классе, в который входит моя проблема? Я хочу включить свои CommonValidations во многие модели, которые не будут иметь атрибута электронной почты. Я использую рельсы 4.


person Giron    schedule 31.03.2014    source источник
comment
Это очень ленивый и хрупкий рисунок/техника. Вам лучше создать EachValidator и использовать его для каждого столбца а-ля validates :email, email_address: true.   -  person coreyward    schedule 01.04.2014


Ответы (2)


Вы можете использовать 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
person vee    schedule 31.03.2014
comment
Каждый валидатор возможен, и я пересмотрю его использование, но использование response_to? не работает. Моя цель состояла в том, чтобы, например, проверить :aaa .... где :aaa - это случайная строка, которой нет в модельном ресторане. Теперь он просто вызывает NoMethodError. Короче говоря, я хочу иметь возможность включать проверку несуществующего атрибута, поэтому я могу легко включать CommonValidations во многие модели, которым не обязательно иметь все атрибуты. - person Giron; 01.04.2014
comment
@Giron, у меня это работает со следующим: validates :email, presence: true, if: lambda { |o| o.respond_to?(:email) }, не могли бы вы обновить свой вопрос, включив в него ошибку, которую вы получаете? - person vee; 01.04.2014
comment
Я обновлю его, как только вернусь домой примерно через 10 часов. - person Giron; 01.04.2014
comment
Решение верное, у меня была глупая опечатка в моем коде. Спасибо за ваше время, я могу представить сценарии, когда я буду использовать оба метода, но я согласен с Коривордом, что использование такой умной проверки может быть очень хрупким. - person Giron; 01.04.2014

Я искал что-то подобное, но с пользовательскими проверками. В итоге я получил то, чем, по моему мнению, можно было бы поделиться, включая общие тесты.

Сначала создайте концерн 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

Надеюсь, это поможет.

person Maxime Brehin    schedule 17.03.2017