Проверьте, являются ли ошибки атрибута пустыми или нет в сложной вложенности хэшей и массивов - PullRequest
0 голосов
/ 04 марта 2020

Допустим, у меня есть продукт, который имеет много образцов, и каждый образец имеет много ссылок . В какой-то момент кода я получаю ruby га sh (представляющий продукт с вложенными атрибутами: образцы> ссылки) примерно так:

{
 name : '',
 category: 'toy',
 shop_id: '2',
 errors: {
   name: 'Cannot be empty'
 },
 samples_attributes: [
   {
     name: 'Big', 
     errors: {},
     references_attributes: [
       {
          quantity: 2, 
          price: nil, 
          errors: {price: 'cannot be nil'}
        }
      ]
   }, 
   {
     name: '12', 
     errors: {name: 'cannot be numerical'},
     references_attributes: [
       {
          quantity: -2, 
          price: 12, 
          errors: {quantity: 'cannot be negative'}
       }
      ]
     }
   }
 ]
}

Я хотел бы найти чистый способ / хитрость чтобы узнать, получил ли каждый из элементов атрибут: errors пустой или нет. Он может вернуть true или false в методе #nested_resources_got_errors?

Конечно, я мог бы сделать это, вложив .each методов, но это грязно:

def the_nested_resource_tree_has_errors?(product)
  !product[:errors].any? &&
  product[:samples_attributes].each do |sample|
    !sample[:errors].any? &&                                      
    sample[:references_attributes].each do |reference|
      !reference[:errors].any?
    end
  end
end

Код должен быть обычный ruby.

1 Ответ

1 голос
/ 05 марта 2020

Мы можем превратить это в общую 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...