Skip to content

can? unnecessarily loads relationships in memory when it could leverage accessible_by #812

@oboxodo

Description

@oboxodo

Steps to reproduce

You can find a full gist demonstrating the problem and potential solution in here.

Given the following code:

class Book < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base; end

class Author < ActiveRecord::Base
  has_many :books
end

class Ability
  include CanCan::Ability

  def initialize(user)
    can :read, Author, books: { user: user }
  end
end

Asking if a user can read an author, shouldn't instantiate all the author's books in order to check the ability:

author.books.loaded? # => false. This is good.
Ability.new(user).can?(:read, author)
author.books.loaded? # => true. This is BAD. There could be millions of records here.

Ideally, given all the defined abilities on Author are defined using the best practice of hash conditions, can? should be able to leverage accessible_by like this:

Author.accessible_by(Ability.new(user), action).exists?(author.id)
author.books.loaded? # => false. Great.

Expected behavior

The ability is checked without instantiating an unlimited number of records in memory.

Actual behavior

Relationships involved in the hash conditions are called potentially instantiating an unlimited number of records in memory.

System configuration

Rails version: 7.0.4
Ruby version: 3.0.2
CanCanCan version: 3.4.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions