Хорошая идея получить доступ к сеансу в observer или нет?

StackOverflow https://stackoverflow.com/questions/133558

Вопрос

Я хочу регистрировать действия пользователя в моем приложении Ruby on Rails.

Пока что у меня есть model observer, который вставляет журналы в базу данных после обновлений и создает.Чтобы сохранить, какой пользователь выполнил действие, которое было зарегистрировано в журнале, мне требуется доступ к сеансу, но это проблематично.

Во-первых, это нарушает модель MVC.Во-вторых, методы варьируются от хакерских до диковинных, возможно, даже привязывая реализацию к серверу Mongrel.

Какой правильный подход следует предпринять?

Это было полезно?

Решение

Я нахожу это очень интересным вопросом.Я собираюсь немного поразмыслить вслух...

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

1) Каковы возможные решения, которые позволили бы нет нарушать шаблон MVC

2) Каковы возможные решения, которые бы нарушать шаблон MVC

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

Давайте сначала рассмотрим # 1.

Навскидку я бы подумал о следующих возможных решениях

А) Если вас действительно интересует, кто выполняет эти действия, должны ли эти данные каким-либо образом храниться в модели?Это сделало бы эту информацию доступной для вашего Наблюдателя.И это также означает, что любой другой интерфейсный вызывающий элемент вашего класса ActiveRecord получает ту же функциональность.

Б) Если вы на самом деле не заинтересованы в понимании того, кто создал запись, но больше заинтересованы в протоколировании самих веб-действий, то вы могли бы рассмотреть возможность "наблюдения" за действиями контроллера.Прошло некоторое время с тех пор, как я копался в исходном коде Rails, поэтому я не уверен, кто их ActiveRecord::Observer "наблюдает" за моделью, но вы могли бы адаптировать ее к контроллеру observer.В этом смысле вы больше не наблюдаете за моделью, и имеет смысл передавать информацию о сеансе и другом типе контроллера этому наблюдателю.C) Самое простое решение с наименьшей "структурой" - это просто удалить код ведения журнала в конце ваших методов действий, которые вы просматриваете.

Теперь рассмотрим вариант № 2, нарушающий практику MVC.

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

Б) Не могу придумать здесь никаких других :)

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

Поиск данных сеанса вашим наблюдателем модели немного "вонюч" и, скорее всего, приведет к сбою, если вы попытаетесь использовать свою модель в любом другом проекте / ситуации / контексте.

Другие советы

Хм, это щекотливая ситуация.Вам в значительной степени придется нарушить MVC, чтобы заставить его работать нормально.

Я бы сделал что-то вроде этого:

class MyObserverClass < ActiveRecord::Observer
  cattr_accessor :current_user # GLOBAL VARIABLE. RELIES ON RAILS BEING SINGLE THREADED

  # other logging code goes here
end

class ApplicationController
  before_filter :set_current_user_for_observer

  def set_current_user_for_observer
    MyObserverClass.current_user = session[:user]
  end
end

Это немного халтурно, но не более халтурно, чем многие другие вещи core rails, которые я видел.

Все, что вам нужно было бы сделать, чтобы сделать его потокобезопасным (это имеет значение только в том случае, если вы все равно работаете в jruby), - это изменить cattr_accessor на правильный метод и сохранить его данные в локальном хранилище потоков

Вы правы насчет того, что это нарушает MVC.Я бы предложил использовать обратные вызовы в ваших контроллерах, в основном потому, что бывают ситуации (например, модель, которая save вызывается, но не проходит проверку), когда вы не хотели бы, чтобы наблюдатель что-либо регистрировал.

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

http://pjkh.com/articles/2009/02/02/creating-an-audit-log-in-rails

Это решение использует модель AuditLog, а также модуль TrackChanges для добавления функциональности отслеживания в любую модель.Однако вам по-прежнему требуется добавить строку в контроллер при обновлении или создании.

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

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

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

Таким образом, в начале запроса (в фильтре) вы берете user_id из сеанса и считываете пользователя, устанавливая User.current_user .

Что-то вроде этого...

class User
  cattr_accessor :current_user
end

class Application
  before_filter :retrieve_user

  def retrieve_user
    if session[:user_id].nil?
      User.current_user = nil
    else
      User.current_user = User.find(session[:user_id])
    end
  end
end

С этого момента это должно быть тривиально.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top