Rails 3 с составной моделью и валидацией - PullRequest
3 голосов
/ 15 июля 2011

У меня есть эта модель домена:

class Person < ActiveRecord::Base
  composed_of :address,
              mapping: [%w(address_street street), %w(address_city city), %w(address_zip_code zip_code), %w(address_country country)]

  validates :name, presence: true, length: { maximum: 50 }
  validates :surname, presence: true, length: { maximum: 50 }

  validates_associated  :address
end

class Address
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  attr_reader :street, :city, :zip_code, :country

  validates :street, presence: true
  validates :city, presence: true
  validates :zip_code, presence: true
  validates :country, presence: true

  def initialize(street, city, zip_code, country)
    @street, @city, @zip_code, @country = street, city, zip_code, country
  end

  def ==(other_address)
    street == other_address.street && city == other_address.city && zip_code == other_address.zip_code && country == other_address.country
  end

  def persisted?
    false
  end
end

Когда я пытаюсь сохранить недействительную модель:

> p = Person.new
=> #<Person id: nil, name: nil, surname: nil, address_street: nil, address_city: nil, address_zip_code: nil, address_country: nil
> p.valid?
=> false
> p.errors
=> {:name=>["can't be blank"], :surname=>["can't be blank"], :address=>["is invalid"]}

Это нормально, но я бы предпочел заполнить массив ошибок сообщениями об ошибках Address, например:

=> {:name=>["can't be blank"], :surname=>["can't be blank"], :address_street=>["can't be blank"], :address_city=>["can't be blank"], :address_zip_code=>["can't be blank"], :address_country=>["can't be blank"]}

Вопрос: есть ли чистый Rails способ сделать это? Или просто я должен переместить код проверки с адреса на человека (довольно уродливо)? Любое другое решение?

Большое спасибо.

1 Ответ

2 голосов
/ 15 июля 2011

Когда вы определяете атрибут composed_of, он становится объектом сам по себе.Я вижу два способа ответить на ваши вопросы.

1) добавить сообщение об ошибке в ваш Address класс

Заменить текущие проверки на:

validates_each :street, :city, :zip_code, :country do |record, attr, value|  
  record.errors.add attr, 'should not be blank' if value.blank?
end

Таким образом,вы сможете получить доступ к сообщениям об ошибках, выполнив:

p = Person.new
p.address.errors

2) Настройте только сообщение об ошибке address

validates_associated  :address, 
                      :message => lambda { |i18n_key, object| self.set_address_error_msg(object[:value]) }

def self.set_address_error_msg address
  errors_array = Array.new
  address.instance_variables.each do |var|
    errors_array << "#{var[1..-1]} should not be blank" if address.send(var[1..-1]).blank?
  end
  errors_array.join(", ")
end       

Это будет выглядеть примерно так:

=> #<OrderedHash {:address=>["country should not be blank, zip_code should not be blank, validation_context should not be blank, city should not be blank"]}> 

Наконец, вы можете переписать валидаторы в вашем классе Profile, но, как вы сказали, это действительно ужасно.

...