为什么在模块中使用实例变量而不是局部变量?

时间:2017-12-26 15:56:36

标签: ruby memoization

我在Ruby on Rails应用程序中看到了这段代码:

module SessionsHelper
  def current_user
    @current_user ||= User.find_by id: session[:user_id]
  end

  ...
end

为什么在current_user方法中使用实例变量而不是局部变量?当我将其更改为局部变量时,所有内容都与使用实例变量相同。

我读过一个实例变量可以用在包含这个模块的许多其他类中。但在这种情况下,它们是用current_user方法定义的,用于存储用户的值@current_user。如何在许多其他类中使用它?

2 个答案:

答案 0 :(得分:2)

  

@current_user || = User.find_by id:session [:user_id]

这里使用了一些Ruby快捷方式,所以让我们扩展为:

 if @current_user == nil
   @current_user = User.find_by id: session[:user_id]
 end

 return @current_user

好的,太好了。但是这个数据库查找实际上可能需要很长时间(比如我们每次调用它都需要几十毫秒)。这可能不会感觉很长时间,但它最终会加起来--Rails应用程序会调用当前用户(仅请求一次与几十次也会减少数据库服务器上的负载)。 (没有任何类型的缓存ActiveRecord和Rails正在你背后做......

因此我们将结果保存到我们以后的实例变量中。

所以,如果你做了类似的事情,那就是你的current_user方法

if my_current_user
  my_current_user = User.find_by id: session[:user_id]
end

return my_current_user

这将始终进入if语句,始终进行查找,将答案放在本地并返回。下次它不知道它已经查找了,所以将再次进行相同的数据库查询,得到结果等等。所以它可能会工作,但它会比在这里使用实例变量慢。

当然有一个缺点。 Ruby中的模块可以导入到类中 - 模块中的方法成为类的方法。所以是的,现在您已经为导入模块的所有类创建了一个实例变量。这是......可能不完全是那种(如果其他一些模块也使用那个名字怎么办?),但是ehhhhhhhhhhh ....在一个小的初学者代码库中它没关系。

答案 1 :(得分:0)

这大致意味着如果@current_user作为全局请求/响应范围(Controller / View / Helpers)的实例变量已经定义,则将其用作current_user。否则,为其分配与会话中存储的id相对应的用户执行的操作。

换句话说,这类似于:

def current_user
    if @current_user # this will be false in every hit, for the same request
        @current_user
    else
        User.find_by id: session[:user_id]
    end
end

这应该足够了。

但是,您可以在代码的其他部分再次使用current_user,让我们在控制器中说同样的请求。

这意味着,current_user必须再次点击数据库(缓存除外)。如果可以的话,你想避免这种情况。那么,为什么不将用户存储在全局实例变量中呢?

因此:

def current_user
    if @current_user # this will be false for the first hit, true for every subsequent request
        @current_user 
    else
        @current_user = User.find_by id: session[:user_id]
    end
end

既然我们这样做,为什么不使用简写方法呢?

所以,最后:

def current_user
    @current_user ||= User.find_by id: session[:user_id]
end

答案是,您在同一请求/响应中执行此操作以进行可重用性抖动。