在观察者中访问会话是个好主意吗?

发布于 2024-07-05 22:48:31 字数 205 浏览 14 评论 0原文

我想在 Ruby on Rails 应用程序中记录用户的操作。

到目前为止,我有一个模型观察者,它在更新和创建后将日志插入数据库。 为了存储哪个用户执行了所记录的操作,我需要访问会话,但这是有问题的。

首先,它打破了MVC模型。 其次,技术范围从黑客到古怪,甚至可能将实现与 Mongrel 服务器联系起来。

正确的做法是什么?

I want to log user's actions in my Ruby on Rails application.

So far, I have a model observer that inserts logs to the database after updates and creates. In order to store which user performed the action that was logged, I require access to the session but that is problematic.

Firstly, it breaks the MVC model. Secondly, techniques range from the hackish to the outlandish, perhaps maybe even tying the implementation to the Mongrel server.

What is the right approach to take?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

女中豪杰 2024-07-12 22:48:31

过去,在做这样的事情时,我倾向于扩展 User 模型类以包含“当前用户”的想法。

查看前面的答案,我看到了在会话中存储实际活动记录用户的建议。 这有几个缺点。

  • 它在会话数据库中存储可能很大的对象,
  • 这意味着用户的副本始终被“缓存”(或直到强制注销)。 这意味着在用户注销并重新登录之前,该用户状态的任何更改都不会被识别。这意味着,例如,尝试禁用用户将等待他注销并重新登录。 这可能不是您想要的行为。

因此,在请求开始时(在过滤器中),您从会话中获取 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

以后应该都是小事了。

In the past, when doing something like this, I have tended towards extending the User model class to include the idea of the 'current user'

Looking at the previous answers, I see suggestions to store the actual active record user in the session. This has several disadvantages.

  • It stores a possibly large object in the session database
  • It means that the copy of the user is 'cached' for all time (or until logout is forced). This means that any changes in status of this user will not be recognised until the user logs out and logs back in. This means for instance, that attempting to disable the user will await him logging off and back on. This is probably not the behaviour you want.

So that at the beginning of a request (in a filter) you take the user_id from the session and read the user, setting User.current_user.

Something like this...

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

From then on it should be trivial.

青春有你 2024-07-12 22:48:31

你说得对,它破坏了 MVC。 我建议在控制器中使用回调,主要是因为在某些情况下(例如调用保存但验证失败的模型)您不希望观察者记录任何内容。

You're right about it breaking MVC. I would suggest using callbacks in your controllers, mostly because there are situations (like a model which save is called but fails validation) where you wouldn't want an observer logging anything.

走过海棠暮 2024-07-12 22:48:31

我找到了一种干净的方法来执行我选择的答案所建议的操作。

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

该解决方案使用 AuditLog 模型以及 TrackChanges 模块向任何模型添加跟踪功能。 但它仍然要求您在更新或创建时向控制器添加一行。

I found a clean way to do what is suggested by the answer I picked.

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

This solution uses an AuditLog model as well as a TrackChanges module to add tracking functionality to any model. It still requires you to add a line to the controller when you update or create though.

几味少女 2024-07-12 22:48:31

我觉得这是一个非常有趣的问题。 我要在这里大声思考一下......

最终,我们面临的是违反设计模式可接受的实践以实现一组特定功能的决定。 因此,我们必须问自己

1) 哪些可能的解决方案不会违反 MVC 模式

2) 哪些可能的解决方案违反 MVC 模式

3) 哪个选项是最好的? 我认为设计模式和标准实践非常重要,但同时如果坚持它们会使您的代码变得更加复杂,那么正确的解决方案很可能是违反实践。 有些人可能不同意我的观点。

让我们首先考虑#1。

我突然想到以下可能的解决方案

A)如果您真的对谁在执行这些操作感兴趣,那么这些数据是否应该以任何方式存储在模型中? 它将向您的观察者提供此信息。 这也意味着 ActiveRecord 类的任何其他前端调用者都可以获得相同的功能。

B) 如果您并不是真正有兴趣了解谁创建了条目,而是对记录 Web 操作本身更感兴趣,那么您可能会考虑“观察”控制器操作。 自从我研究 Rails 源代码以来已经有一段时间了,所以我不确定他们的 ActiveRecord::Observer “观察”模型是谁,但您也许能够将其调整为控制器观察者。 从这个意义上说,您不再观察模型,并且向该观察者提供会话和其他控制器类型数据信息是有意义的。
C) 最简单的解决方案,具有最少的“结构”,就是简单地将日志代码放在您正在观看的操作方法的末尾。

现在考虑选项#2,打破 MVC 实践。

A)正如您所建议的,您可以找到让模型观察者访问会话数据的方法。 您已将模型与业务逻辑耦合起来。

B) 这里想不出任何其他的:)

在不知道有关你的项目的更多细节的情况下,我个人的倾向是 1A(如果我想将人员附加到记录)或 1C(如果我只在几个地方)有兴趣这样做。 如果您确实想要为所有控制器和操作提供强大的日志记录解决方案,您可以考虑 1B。

让模型观察者找到会话数据有点“臭”,如果您尝试在任何其他项目/情况/上下文中使用您的模型,则可能会中断。

I find this to be a very interesting question. I'm going to think out loud here a moment...

Ultimately, what we are faced with is a decision to violate a design-pattern acceptable practice in order to achieve a specific set of functionality. So, we must ask ourselves

1) What are the possible solutions that would not violate MVC pattern

2) What are the possible solutions that would violate the MVC pattern

3) Which option is best? I consider design patterns and standard practices very important, but at the same time if holding to them makes your code more complex, then the right solution may very well be to violate the practice. Some people might disagree with me on that.

Lets consider #1 first.

Off the top of my head, I would think of the following possible solutions

A) If you are really interested in who is performing these actions, should this data be stored in the model any way? It would make this information available to your Observer. And it also means that any other front-end caller of your ActiveRecord class gets the same functionality.

B) If you are not really interested in understanding who created a entry, but more interested in logging the web actions themselves, then you might consider "observing" the controller actions. It's been some time since I've poked around Rails source, so I'm not sure who their ActiveRecord::Observer "observes" the model, but you might be able to adapt it to a controller observer. In this sense, you aren't observing the model anymore, and it makes sense to make session and other controller type data information to that observer.
C) The simplest solution, with the least "structure", is to simply drop your logging code at the end of your action methods that you're watching.

Consider option #2 now, breaking MVC practices.

A) As you propose, you could find the means to getting your model Observer to have access to the Session data. You've coupled your model to your business logic.

B) Can't think of any others here :)

My personal inclination, without knowing anymore details about your project, is either 1A, if I want to attach people to records, or 1C if there are only a few places where I'm interested in doing this. If you are really wanting a robust logging solution for all your controllers and actions, you might consider 1B.

Having your model observer find session data is a bit "stinky", and would likely break if you tried to use your model in any other project/situation/context.

宁愿没拥抱 2024-07-12 22:48:31

嗯,这是一个棘手的情况。 你几乎必须违反 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

它有点老套,但它并不比我见过的许多其他核心导轨更老套。

要使其线程安全,您需要做的就是将 cattr_accessor 更改为正确的方法,并将其数据存储在线程本地存储中(这仅在您在 jruby 上运行时才重要)

Hrm, this is a sticky situation. You pretty much HAVE to violate MVC to get it working nicely.

I'd do something like this:

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

It is a bit hacky, but it's no more hacky than many other core rails things I've seen.

All you'd need to do to make it threadsafe (this only matters if you run on jruby anyway) is to change the cattr_accessor to be a proper method, and have it store it's data in thread-local storage

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文