[CanCan] "Składanie" uprawnień

W aplikacji, którą piszę mam model User zawierający wszystkich użytkowników systemu. Użytkownicy ci mają przydzielone role aplikacyjne. Część z nich to “obsługa” a część to klienci.
W akcji Users#index chcę wyświetlić listę użytkowników systemu ale z ograniczeniami, “reseller” może widzieć i aktualizować siebie oraz widzieć i zarządzać swoimi użytkownikami.
Trochę kodu dla wyjaśnienia:

    class User < ActiveRecord::Base
        has_secure_password
    
        # for resellers and more
        has_many :cs, class_name: "Client", foreign_key: :customer_service_id
        # for client and user
        has_one :client
    
        include RoleModel
    
        # 1 - root
        # 2 - admin
        # 4 - manager
        # 8 - seller
        # 16 - reseller
        # 32 - client
        # 64 - user
        roles :root, :admin, :manager, :seller, :reseller, :client, :user
    (...)    
    end
    
    class Client < ActiveRecord::Base
      belongs_to :user, -> { where 'roles_mask = 32' }
      belongs_to :customer_service, -> { where 'roles_mask < 32' }, class_name: 'User'
    (...)  
    end
    
    class Ability
      include CanCan::Ability
    
      def initialize(user)
          @user = user
          @user ||= User.new # guest user (not logged in)
          
          if @user.root?
             can :manage, :all
          end
          case
          when @user.admin?
              admin_ability
          when @user.manager?
              manager_ability
          when @user.seller?
              seller_ability
          when @user.reseller?
              reseller_ability
          when @user.client?
              client_ability
          when @user.user?
              @user_ability
          else
              guest_ability
          end
      end
    (...)
      def reseller_ability
        can :read, User, id: @user.id
        can :read, User, client: { customer_service_id: @user.id }
        can :update, User, id: @user.id
        can :update, User, client: { customer_service_id: @user.id }
        can [:create, :destroy], User, client: { customer_service_id: @user.id }
        cannot :destroy, User, id: @user.id
      end
    (...)
    end

Niby wygląda OK bo pierwsze dwa warunki powinnu byś złożone przez OR. Tylko, że to nie działa bo CanCan tworzy zapytanie:

SELECT "users".* FROM "users" 
INNER JOIN "clients" ON "clients"."user_id" = "users"."id" 
WHERE (roles_mask > 1) 
AND (("clients"."customer_service_id" = 402741401) OR ("users"."id" = 402741401))

W efekcie otrzymuję listę klientów “resellera” ale nie mam informacji o samym “resellerze” bo “INNER JOIN” eliminuje go z wyników.

Jak sobie z tym poradzić?

w cancan a teraz cancancan możesz użyć https://github.com/ryanb/cancan/wiki/Defining-Abilities-with-Blocks, ale block nie dziala przy korzystaniu z accessible_by.

  1. cancancan nie wykonuje wogóle złączenia i powstaje błąd bo nie ma kolumny clients.customer_service_id
  2. dodanie bloku powoduje kolejny błąd: nie można łączyć AR scoped i bloków

Im dłużej używam cancan tym coraz bardziej jestem przekonany do porzucenia cancana na rzecz innego gema. Nie wszystko w nim działa tak jak by się chciało.

Ja ostatnio dałem szansę https://github.com/elabs/pundit póki co nie narzekam.

Można by opakować zapytania w pojedyńczego scope’a.

can :read, User, User.hyper_scope_with_everything_u_want