Providing a scope for nested associations when preloading with ActiveRecord::Associations::Preloader

Hi :wave:

We have a method which accepts an array of associations and relies on ActiveRecord::Associations::Preloader to preload the associations.

ActiveRecord::Associations::Preloader
  .new(
    records: bookmarks,
    associations: [topic: :topic_users]
  )
  .call

In some cases, we would like to preload the associations with a scope based on the current user. It turns out that we can indeed do this by passing the scope keyword argument. For example, the following will preload the bookmarkable associations WHERE topics.id =1.

bookmarks = Bookmark.limit(10).to_a
ActiveRecord::Associations::Preloader.new(records: bookmarks, associations: :topic, scope: Topic.where(id: 1)).call

However, I found out that the scope is applied to all associations so doing something like this will not work.

bookmarks = Bookmark.limit(10).to_a
ActiveRecord::Associations::Preloader.new(records: bookmarks, associations: [:topic, :user], scope: Topic.where(id: 1)).call

Looking through the internals, this behaviour is expected since the same scope is used for all the associations passed to the preloader. Therefore, I was wondering if the API for ActiveRecord::Associations::Preloader such that we can provide scopes for specific associations which we’re trying to preload. Perhaps a scopes keyword argument that accepts a hash?

ActiveRecord::Associations::Preloader
  .new(
    records: bookmarks,
    associations: [{ topics: :topic_users }, :user],
    scopes: {
      user: User.where(id: 1),
      topic_users: TopicUser.where(user_id: 5)
    }
  )
  .call
3 Likes