2008-09-25 47 views
7

我想在Ruby on Rails应用程序中记录用户的操作。在观察者中访问会话的好主意吗?

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

首先,它打破了MVC模型。其次,技术从恶作剧到奇怪,甚至可能将实施与Mongrel服务器捆绑在一起。

什么是正确的方法?

回答

3

我觉得这是一个非常有趣的问题。我会在这里大声地想一想......

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

1)什么是将违反MVC模式

2)什么是违反MVC模式

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

让我们先考虑#1。

关闭我的头顶,我认为以下可能的解决方案

A)如果你是谁是执行这些操作,应此数据存储模型中的任何方式真正感兴趣的?它会将这些信息提供给您的观察员。这也意味着你的ActiveRecord类的其他前端调用者可以获得相同的功能。 B)如果你不是真正有兴趣了解谁创建了一个条目,但更感兴趣的是自己记录Web操作,那么你可能会考虑“观察”控制器的操作。自从我引入Rails源代码以来,已经有一段时间了,所以我不确定他们的ActiveRecord :: Observer是否“观察”了模型,但是您可能可以将其调整为控制器观察者。从这个意义上讲,您不再观察模型,并且将会话和其他控制器类型的数据信息发送给该观察者是有意义的。 C)最简单的解决方案,用最少的“结构”,就是简单地将你的日志代码放在你正在观察的动作方法的最后。

现在考虑选项#2,打破MVC的做法。

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

B)可没想到这里的任何人:)

我个人的倾向,而不用知道了细节你的项目,或者是1A,如果我要附加的人的记录,或1C如果有只有几个我对此感兴趣的地方。如果您真的想为您的所有控制器和操作提供强大的日志记录解决方案,则可以考虑1B。

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

+0

我想到的可能是1A的东西,也许是因为它的简单性。 – Jaryl 2008-09-25 15:29:23

1

你是对的,它打破了MVC。我建议在你的控制器中使用回调函数,主要是因为有些情况(比如一个保存被调用但验证失败的模型),你不希望观察者记录任何东西。

6

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

这是一个有点哈克,但比许多其他Rails核心的事情,我已经看到了它没有更多的哈克。

所有你需要做的(如果你在JRuby上运行反正这只是问题),使其线程是改变cattr_accessor是一个适当的方法,并将它保存它在线程本地存储数据

+2

嗨,猎户座,刚刚找到这个,因为我正在寻找这种类型的解决方案。既然已经过了几年了!你仍然使用这个解决方案吗?好奇如果你碰到任何Threaded类型的问题? – WozPoz 2010-10-01 03:12:40

+1

@WozPoz此代码不是线程安全的,在同一进程中运行的线程将共享类变量。要绕过这个障碍,可以使用线程本地存储或者使用config.threadsafe运行你的应用程序! = false – laurie 2014-08-04 09:43:07

0

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

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

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

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

从那时起,它应该是微不足道的。