Search code examples
ruby-on-railsdevisewarden

How to make Devise return a subclass of user


I have devise wich authenticates a user. User is split up into subclasses such as User::AsProfile < User or User::AsRegistrant< User. These are not Single Table Inheritance classes, just plain old ruby-objects that inherit from User.

I'd like devises' current_user helper to return an instance of User::AsProfile.

Unfortunately, Device has a lot of metaprogamming hidden away in class_eval so it is hard to understand what is going on.

Can I configure Devise to return another class (User::AsProfile) when running current_user in a controller?

Or must I override current_user in my controller to do so, and if so, how should I call warden to authenticate properly?


Solution

  • I ended up re-implementing current_user in my ApplicationController.

    Since I don't need the ability to have one user use multiple sessions, I could simplify the current_user a lot:

    class ApplicationController < ActionController::Base
      def current_user
        return @current_user if @current_user
        current = warden.authenticate(scope: :user)
        @current_user = current.becomes(User::AsProfile) if current
      end
    end
    

    By using ActiveRecords becomes, the user is turned into a User::AsProfile. I'd rather not have this conversion and use User::AsProfile#find directly, but that seems only possible whith monkeypatching Warden.

    As a sidenote: This problem shows well what happens when a library implements a poorly designed class-inheritance; or, in this case, no inheritance at all. The Devise code simply monkeypatches your ApplicationController and adds methods there. Never a good idea. With hindsight, Devise should simply be some modules (Concerns) that a developer can pick-and-choose and add to his controllers.