Reputation: 3998
I am working on an application where I have a User model which has a default scope
default_scope { preload(:preference, :permission, :user_detail, :log) }
Every time we call current_user it fires 4 queries
And for current_user
I have overridden the devise
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
Let suppose if we have 5 modules on a page. And loading each module we fire ajax call. Basically we will fire 5 requests. Now for every 5 request @current_user will be nil for the first time and it will fire a query. Again when we call current_user in the same request it will return from the cache.
What I want is for the first request it should fire the query and store in @current_user
. Later for all the other request in the page, it should return from the cache instead of firing a query
@current_user ||= warden.try(:user) || anonymous_user
warden.user.present?
Can anyone guide me what I need to do
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
As suggested by @kgilpin I updated my application_controller method
def set_current_user
User.current = warden.try(:user) || anonymous_user
yield
ensure
User.current = nil
end
Upvotes: 2
Views: 246
Reputation: 2226
You can initialize and store the current user in a thread-local variable and then access it from anywhere in the process.
So for example, your ApplicationController
would do something like this:
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]
is now the current user from anywhere in same thread/fiber of the Ruby process.
Note, Ruby thread-local storage syntax [], []=
is also safe to use with fibers. See https://docs.ruby-lang.org/en/2.6.0/Thread.html#method-i-5B-5D-3D.
Upvotes: 1
Reputation: 1585
What you're trying to do is impossible with include, because every instance of the included module will have it's own @current_user.
You need to setup the @current_user variable in a scope where all objects can access it, using Dependency Injection or a dynamic dependency resolving mechanism.
One example:
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
Upvotes: 0