hedgesky
hedgesky

Reputation: 3311

How to implement polymorphic current_user nicely

In my app I have two types of users (User and Shop now, will add Admin soon) which are authenticated through the same auth strategy, and thus are stored in the same current_profile variable. So, it could be an instance of several models, that's why I'm fetching it with following method:

class ApplicationController < ActionController::Base
  before_action :fetch_current_user

  private

  def fetch_current_user
    if session[:current_profile_type].blank?
      @current_profile = nil
      return
    end

    case session[:current_profile_type]
    when 'Shop'
      model = Shop
    when 'User'
      model = User
    end

    @current_profile = model.find_by(id: session[:current_profile_id])
  end
end

But I'm unhappy with this solution, it looks like too noisy for me. Maybe there is a better/simpler approach?

P.S. I don't like to use constantize here: while be more readable it'll slow down every app's request.

Upvotes: 0

Views: 48

Answers (1)

Wizard of Ogz
Wizard of Ogz

Reputation: 12643

Here is one possibility

class ApplicationController < ActionController::Base
  PROFILE_TYPES = {
    'Shop': Shop,
    'User': User
  }

  before_action :fetch_current_user

  private

  def fetch_current_user
    profile_type = PROFILE_TYPES[session[:current_profile_type]]
    @current_profile = profile_type && profile_type.find_by(id: session[:current_profile_id])
  end
end

Or how about caching the results of constantize? That way you do not need to manually maintain a Hash map like in the example above.

class ApplicationController < ActionController::Base
  before_action :fetch_current_user

  private

  def fetch_current_user
    profile_type = (self.class.cached_profile_types[session[:current_profile_type]] ||= session[:current_profile_type].constantize)  # Constantizes each type only once after each application boot.
    @current_profile = profile_type && profile_type.find_by(id: session[:current_profile_id])
  end

  def self.cached_profile_types
    @@cached_profile_types ||= {}  # Cache across all subclasses of ApplicationController
  end
end

Upvotes: 1

Related Questions