Мы можем превратить это в общую c рекурсивную проверку. Использование any?
делает это более эффективным, оно остановится, как только обнаружит ошибку.
def errors?(hash)
# Does it have errors?
return true if hash[:errors].present?
# Iterate through each value until an error is found.
return hash.any? do |_, value|
case
# Hash values are recursively checked.
when value.is_a?(Hash)
errors?(value)
# Iterate through each element of an enumerable value.
when value.is_a?(Enumerable)
value.any? { |v| errors?(v) }
end
end
end
Но это, как и многие другие вещи, было бы проще, если бы это были объекты. Настройте несколько моделей. Каждому нужно иметь дело только с самим собой и своими непосредственными объектами.
# A module for the error attribute and detecting errors.
module HasErrors
extend ActiveSupport::Concern
included do
attr_accessor :errors
end
def errors?
errors.present?
end
end
class Product
include ActiveModel::Model
include HasErrors
attr_accessor :name, :category, :shop_id, :samples
# check itself, then check its samples
def errors?
super || samples.any? { |sample| sample.errors? }
end
end
class Sample
include ActiveModel::Model
include HasErrors
attr_accessor :name, :references
# check itself, then check its references
def errors?
super || references.any? { |ref| ref.errors? }
end
end
class Reference
include ActiveModel::Model
include HasErrors
attr_accessor :quantity, :price
end
Затем вы можете поместить данные в объекты и вызвать product.errors?
.
product = Product.new(
name: '',
category: 'toy',
shop_id: '2',
errors: {
#name: 'Cannot be empty'
},
samples: [
Sample.new(
name: 'Big',
errors: {},
references: [
Reference.new(
quantity: 2,
price: nil,
errors: {
price: 'cannot be nil'
}
)
]
),
Sample.new(
name: '12',
errors: {
name: 'cannot be numerical'
},
references: [
Reference.new(
quantity: -2,
price: 12,
errors: {
quantity: 'cannot be negative'
}
)
]
)
]
)
p product.errors?
Если мы имеем В моделях мы можем использовать валидация и ошибки не нужны.
module ValidateList
extend ActiveSupport::Concern
class_methods do
def validate_list(attribute)
validates_each attribute do |record, attr, values|
# Check if any element of the list is invalid.
record.errors.add(attr, :invalid) if values.any?(&:invalid?)
end
end
end
end
class Product
include ActiveModel::Model
include HasName
include ValidateList
attr_accessor :category, :shop_id, :samples
validate_list :samples
end
class Sample
include ActiveModel::Model
include HasName
include ValidateList
attr_accessor :references
validate_list :references
end
class Reference
include ActiveModel::Model
attr_accessor :quantity, :price
validates :price, :quantity,
presence: true,
numericality: {
greater_than_or_equal_to: 0
}
end
p product.valid?
p product.errors.details
Использование валидаций позволит этим моделям хорошо работать с другими частями Rails.