Rails Observers - когда и когда не использовать наблюдателей в Rails

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

Я хотел бы знать случаи, которые требуют наблюдателя, и те, которые люди имеют опыт либо эмпирический, либо личный, когда человек испытывает соблазн попасть в ловушку наблюдателя.

ваш ценный опыт, военные истории и мысли находятся в требовать. Пожалуйста, кричите!

3 ответов


Я чувствую, что наблюдатели получают плохой рэп во многом потому, что люди объединяют их с обратными вызовами жизненного цикла ActiveRecord как одно и то же. Я согласен со многими популярными мнениями о том, что обратные вызовы жизненного цикла легко злоупотреблять, запутывая себя, но я лично большой поклонник наблюдателей за то, чтобы держать вещи вне классов моделей, которые не являются основной ответственностью конкретной модели. Вот подсказка: наблюдатели Rails были частично вдохновлены аспектно-ориентированным программированием - они о сквозных проблемах. Если вы вкладываете бизнес-логику в наблюдателей, которые тесно связаны с моделями, которые они наблюдают, вы делаете это неправильно IMO.

они идеально подходят для хранения беспорядка из классов моделей, таких как срок действия кэша (sweepers), уведомления различных видов, обновления потока активности, запуск фоновых заданий для отслеживания пользовательских событий аналитики, теплых кэшей и т. д.

Я категорически не согласен с BlueFish о наблюдателях трудно правильно модульный тест. Это именно самый большой момент, который отличает их от обратных вызовов жизненного цикла: вы можете тестировать наблюдателей изолированно, и это не позволяет вам попадать во многие из тяжелых для государства и порядка ловушек дизайна, на которые ссылается BlueFish (что опять же, я думаю, чаще верно для обратных вызовов жизненного цикла).

вот мой рецепт:

  1. по умолчанию отключите всех наблюдателей в наборе тестов. Они не должны усложнять ваши тесты модели, потому что они должны иметь отдельные проблемы в любом случае. Вам не нужно модульный тест, который наблюдатели фактически запускают, потому что набор тестов ActiveRecord делает это, и ваши интеграционные тесты будут охватывать его. Используйте блочную форму ActiveRecord::Base.observers.enable Если вы действительно верьте, что есть веская причина, чтобы включить наблюдателя для какой-то небольшой части ваших модульных тестов, но это, вероятно, показатель неправильного использования или проблемы с дизайном.
  2. включить наблюдателей для интеграции только тесты. Интеграционные тесты, конечно, должны быть полными стеками, и вы должны проверять поведение наблюдателя в них, как и все остальное.
  3. Unit-Проверьте свои классы наблюдателей в изоляции (вызывать методы, как after_create напрямую). Если наблюдатель не является частью бизнес-логики своей наблюдаемой модели, он, вероятно, не будет сильно зависеть от деталей состояния экземпляра модели и не потребует большой настройки теста. Вы можете часто издеваться над сотрудниками здесь если вы достаточно уверены, что ваши интеграционные тесты охватывают то, что вас больше всего волнует.

вот мой стандартный spec/support/observers.rb для приложений, использующих RSpec:

RSpec.configure do |config|
  # Assure we're testing models in isolation from Observer behavior. Enable
  # them explicitly in a block if you need to integrate against an Observer --
  # see the documentation for {ActiveModel::ObserverArray}.
  config.before do
    ActiveRecord::Base.observers.disable :all
  end

  # Integration tests are full-stack, lack of isolation is by design.
  config.before(type: :feature) do
    ActiveRecord::Base.observers.enable :all
  end
end

и вот пример это, я надеюсь, иллюстрирует хороший случай для использования наблюдателя и тестирования его без боли.


ИМХО - Наблюдатели Отстой

Я пройду через ряд причин, почему я думаю, что они делают. Имейте в виду, что это относится в целом к использованию методов before_x или after_x, которые являются более фрагментарными примерами общего наблюдателя.

трудно писать модульные тесты

обычно, когда вы пишете модульные тесты, вы тестируете определенную часть функциональности. Однако, чтобы проверить наблюдателя, вам нужно "вызвать" для того, чтобы проверить его, а иногда это просто неудобно.

например. Если вы подключаете наблюдателя к before_save, то для запуска кода вам нужно сохранить модель. Это затрудняет тестирование, учитывая, что вы можете тестировать бизнес-логику, а не настойчивость. Если вы издеваетесь над сохранением, ваш триггер может не работать. И если вы позволите ему сэкономить, то ваши тесты будут медленными.

требует

исходя из того, что наблюдателей, как правило, трудно проверить, наблюдатели, как правило, также требуют много государства. Причина в том, что логика наблюдателя пытается различать различные "деловые события", и единственный способ сделать это-посмотреть на состояние объекта. Это требует большой настройки в ваших тестах, и поэтому делает тестирование сложным, утомительным и проблематичным.

непредвиденные последствия

без сомнения, вы испытали это, потому что вы можете подключить многочисленные наблюдения, вы понятия не имеете, что может вызвать различные поведения. Это приводит к непредвиденным последствиям, которые вы можете получить только через интеграцию/системное тестирование (медленная обратная связь). Отслеживание ваших наблюдателей также не очень весело.

порядок допущения проблемы

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

приводит к плохому дизайну

добавление вещей путем подключения наблюдателей приводит к плохим проектам. Это приводит к тому, что вы подключаете все для сохранения, удаления, создания событий, что, хотя и удобно, также трудно понять. Е. Г. сохранение пользователя может означать, что вы обновляете данные пользователя, или это может означать, что вы добавляете новую учетную запись на него. Знание того, что вы можете конкретно сделать с объектом, является частью причины у вас есть методы и значимые имена, основанные на действиях. Если все является наблюдателем, то это теряется и все теперь реагирует на события, и в рамках вашей наблюдательной логики вы склонны пытаться отличить событие, к которому относится деловое событие.

есть некоторые места, где наблюдатель хорош, но это обычно исключение. Гораздо лучше включить то, что можно сделать явно, а не кодировать логику неявно через обратные вызовы.


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

например, в модели оплаты AR может потребоваться доставить квитанцию после создания. Используя обычный обратный вызов AR after_create, если метод deliver_receipt не удался, платеж не будет записан в базу данных-oops! Однако в observer платеж все равно будет сохранен. Можно утверждать, что неудачи должно быть обработано спасением, но я все еще думаю, что оно не принадлежит там; оно принадлежит наблюдателю.