对多个请求进行缓存查询

时间:2020-06-30 07:53:48

标签: ruby-on-rails ruby devise ruby-on-rails-5.2

我正在开发一个具有默认范围

的用户模型的应用程序
default_scope { preload(:preference, :permission, :user_detail, :log) }

每次我们调用current_user都会触发4个查询

对于current_user,我已经超越了设计

module DeviseOverrides
  def self.included(base)
    base.class_eval {
      alias_method :devise_current_user, :current_user
      helper_method :current_user, :user_signed_in?
    }

    base.send(:include, InstanceMethods)
  end

  module InstanceMethods
    def current_user
      @current_user ||= warden.try(:user) || anonymous_user
    end
 
    def user_signed_in?
      warden.user.present?
    end

  end
end

假设我们在一个页面上有5个模块。并加载我们触发ajax调用的每个模块。基本上,我们会发出5个请求。现在,对于每5个请求,@ current_user首次为nil,它将触发查询。同样,当我们在同一请求中调用current_user时,它将从缓存中返回。

我想要的是第一个请求,它将触发查询并将其存储在@current_user中。稍后,对于页面中的所有其他请求,它应从缓存中返回而不是触发查询

@current_user ||= warden.try(:user) || anonymous_user

warden.user.present?

有人可以指导我我需要做什么

class ApplicationController < ActionController::Base
  around_action :set_current_user

  def set_current_user
    User.current = current_user
    yield
  ensure
    User.current = nil
  end
end


class User < ApplicationRecord
  default_scope { preload(:preference, :permission, :user_detail, :log) 

  def self.current=(user)
    Thread.current[:current_user] = user
  end

  def self.current
    Thread.current[:current_user]
  end

end

按照@kgilpin的建议,我更新了application_controller方法

def set_current_user
    User.current = warden.try(:user) || anonymous_user
    yield
  ensure
    User.current = nil
  end

2 个答案:

答案 0 :(得分:1)

您可以初始化当前用户并将其存储在线程局部变量中,然后在过程中的任何位置访问它。

例如,您的ApplicationController会执行以下操作:

  around_action :with_current_user
 
  protected
 
  def with_current_user
    Thread.current[:current_user] = warden.try(:user) || anonymous_user
    begin
      yield
    ensure
      Thread.current[:current_user] = nil
    end
  end
end

Thread.current[:current_user]现在是Ruby进程相同线程/纤维中任何地方的当前用户。

请注意,Ruby线程本地存储语法[], []=也可以安全地用于光纤。参见https://docs.ruby-lang.org/en/2.6.0/Thread.html#method-i-5B-5D-3D

答案 1 :(得分:0)

include不可能做到,因为include模块的每个实例都有自己的@current_user。

您需要使用依赖关系注入或动态依赖关系解析机制在所有对象都可以访问它的范围内设置@current_user变量。

一个例子:

class CurrentUserHolder
 include Singleton

 attr_accessor :current_user

end

module InstanceMethods
    def current_user
      CurrentUserHolder.instance.current_user ||= warden.try(:user) || anonymous_user
    end

end