Я только что столкнулся с проблемами 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

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

1
Giron 1 Апр 2014 в 00:01
1
Это очень ленивый и хрупкий рисунок/техника. Вам лучше создать EachValidator и использовать его для каждого столбца а-ля validates :email, email_address: true.
 – 
coreyward
1 Апр 2014 в 00:05

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
1
vee 1 Апр 2014 в 00:11
Каждый валидатор возможен, и я пересмотрю его использование, но использование response_to? не работает. Моя цель состояла в том, чтобы иметь, например, "валидирует: aaa ....", где: aaa - это случайная строка, которой нет в модельном ресторане. Теперь он просто вызывает NoMethodError. Короче говоря, я хочу иметь возможность включать проверку несуществующего атрибута, поэтому я могу легко включать CommonValidations во многие модели, которые не обязательно должны иметь все атрибуты.
 – 
Giron
1 Апр 2014 в 00:30
@Giron, у меня это работает со следующим: validates :email, presence: true, if: lambda { |o| o.respond_to?(:email) }, можете ли вы обновить свой вопрос, включив в него ошибку, которую вы получаете?
 – 
vee
1 Апр 2014 в 01:15
Я обновлю его, как только вернусь домой примерно через 10 часов.
 – 
Giron
1 Апр 2014 в 08:14
Решение верное, у меня была глупая опечатка в моем коде. Спасибо за ваше время, я могу представить сценарии, когда я бы использовал оба метода, но я согласен с Коривордом, что использование такой «умной» проверки может быть очень хрупким.
 – 
Giron
1 Апр 2014 в 19:33

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

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

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

1
Maxime Brehin 17 Мар 2017 в 12:16