Как отфильтровать результат запроса ActiveRecord путем сравнения с другим экземпляром модели?

У меня есть простой запрос ActiveRecord в соответствии с этим:

similar_changes = Notification.where(change_owner: 'foo1', change_target: 'foo2', change_cancelled: false)

Каждый объект уведомления имеет поле change_type и у меня есть другая функция, которая проверяет одно уведомление change_type с одним другим Уведомлением об обратных изменениях (изменениях, которые отменяют друг друга в контексте моего приложения).

Мне нужно принять это уведомление change_type и сравнить его со всеми остальными в массиве. Я должен ссылаться на объекты так: similar_changes[0]['change_type'] где первый индекс - каждая ActiveRecord в массиве, а второй - словарь, который определяет, какое свойство в объекте Notification.

У меня есть ощущение, что я мог бы сделать это вручную с помощью двух вложенных циклов и операторов if, но я также знаю Ruby и считаю, что это то, что он должен был встроить.

Я не прав или есть лучший способ сделать это?

Вот код (обратите внимание, что весь этот код еще не закончен, так что терпите меня, если он не идеален):

def self.group_similar_changes(owner, target, change_type)
  # long query where it selects all rows where change_owner and change_target 
  # are the same as original        
  # also where cancelled is false
  # determine if cancelled (yaml)
  # if cancelled (do nothing)
  similar_changes = Notification.where(
    change_owner: owner, 
    change_target: target, 
    change_cancelled: false
  )
  similar_changes.each do |change|
    cancel_inverse_change(change, change.change_type)
      if change.cancelled?
        similar_changes.delete(change)
      end
    end
  end
end

def cancel_inverse_change(change, change_type)
  if change.inverse?(change_type)
    change.cancel
  end
end

def inverse?(possible_inverse_change)
  is_inverse = false
  change_types = YAML.load_file(File.join(NotificationManager::Engine.root, 'config/change_types.yaml'))
  if self.change_type == change_types[possible_inverse_change]['inverse']
    is_inverse = true
  end
  return is_inverse
end

1 ответ

Решение

Да, твой цикл закончился similar_changes может быть улучшена.

  • Это сбивает с толку, чтобы изменить массив, который вы зацикливаете. Я даже не знаю, надежно ли это, потому что я никогда этого не делаю!
  • Также не идиоматично, чтобы Ruby полагался на возвращаемое значение each, each обычно используется, чтобы сделать что-то с элементами Enumerable, который уже существует, поэтому использование его возвращаемого значения кажется странным.

Я бы написал это как

similar_changes.reject do |change|
  cancel_inverse_change(change, change.change)
  change.cancelled?
end
Другие вопросы по тегам