module ActiveRecordFollowAssoc::QueryMethods

Public Instance Methods

follow_assoc(*association_names) click to toggle source

Query the specified association of the records that the current query would return.

Returns a new relation (also known as a query) which:

  • targets the association's model. So +Post.follow_assoc(:comments)+ will return comments.

  • only returns the records that are associated with those that the receiver would return. So +Post.where(published: true).follow_assoc(:comments)+ only returns the comments of published posts.

You could say this is a way of doing a #flat_map of the association on the result of the current relation, but without loading the records of the first relation and without having to worry about eager loading.

Examples (with equivalent #flat_map)

# Comments of published posts
Post.where(published: true).follow_assoc(:comments)
# Somewhat equivalent to. (Need to use preload to avoid the N+1 query problem)
Post.where(published: true).preload(:comments).flat_map(:comments)

The main differences between the #flat_map and #follow_assoc approaches:

  • #follow_assoc returns a relation (or query or scope, however you call it), so you can use other scoping methods, such as #where, #limit, #order.

  • #flat_map returns an Array, so you cannot use other scoping methods.

  • #flat_map must be used with eager loading. Forgetting to do so makes N+1 query likely.

  • #follow_assoc only loads the final matched records.

  • #flat_map loads every associations on the way, this is wasteful when you don't need them.

association_names

The first argument(s) are the associations that you want to follow. They are the names of your #belongs_to, #has_many, #has_one, #has_and_belongs_to_many.

If you pass in more than one, they will be followed in order. Ex: +Post.follow_assoc(:comments, :author)+ gives you the authors of the comments of the posts.

options

Following are the options that can be passed as last argument.

If you are passing multiple association_names, the options only affect the last association.

option :ignore_limit

When true, #has_one will be treated like a #has_many.

Main reasons to use ignore_limit: true

  • Needed for MySQL to be able to do anything with #has_one associations because MySQL doesn't support sub-limit.
    See MySQL doesn’t support limit
    Note, this does mean the #has_one will be treated as if it was a #has_many for MySQL too.

  • You have a #has_one association which you know can never have more than one record and are dealing with a heavy/slow query. The query used to deal with #has_many is less complex, and may prove faster.

  • For this one special case, you want to check the other records that match your has_one

option :poly_belongs_to

If the last association of association_names is a polymorphic belongs_to, then by default, #follow_assoc will raise an exception. This is because there are many unrelated models that could be the one referred to by the records, but an ActiveRecord relation can only target a single Model.

For this reason, you must choose which Model to “look into” when following a polymorphic belongs_to. This is what the :poly_belongs_to option does.

For example, you can't just go from “Picture” and follow_assoc the polymorphic belongs_to association “imageable”. But if what you are looking for is only the employees, then this works:

employee_scope = pictures_scope.follow_assoc(:imageable, poly_belongs_to: Employee)
# File lib/active_record_follow_assoc/query_methods.rb, line 73
def follow_assoc(*association_names)
  options = association_names.extract_options!
  ActiveRecordFollowAssoc::CoreLogic.follow_assoc(self, association_names, options)
end