Skip to content

Instantly share code, notes, and snippets.

@estum
Created September 4, 2016 10:24
Show Gist options
  • Save estum/f7c8dab9fa2cb760c2da47be30da4215 to your computer and use it in GitHub Desktop.
Save estum/f7c8dab9fa2cb760c2da47be30da4215 to your computer and use it in GitHub Desktop.
Swallow Reader extension for Active Record Has One Through Association
# = Swallow Reader
#
# Adds <tt>:swallow</tt> option to <tt>has_one :through</tt> association builder.
# When this option set to <tt>true</tt> and through association was loaded, target record will
# be readed directly from parent without executing the join query.
#
# For example:
#
# class Review < ActiveRecord::Base
# belongs_to :song
# has_one :artist, through: :song, swallow: true
# end
#
# Review.preload(:song).artist
#
# Without swallow it executes query:
#
# SELECT "artists".* FROM "artists" INNER JOIN "songs" ON "artists"."id" = "songs"."artist_id" WHERE "songs"."id" = $1 LIMIT $2
#
# With <tt>swallow: true</tt>:
#
# SELECT "artists".* FROM "artists" WHERE "artists"."id" = $1 LIMIT $2
#
module SwallowReader
module BuilderHook
def valid_options(options)
valid = super
valid << :swallow if options[:through]
valid
end
end
module AssociationMethods
def reader(force = false)
read_swallow? ? swallow_reader(force) : super
end
def swallow_reader(force = false)
through_association.reader&.public_send(reflection.name, force)
end
private
def through_association
@owner.association(options[:through])
end
def read_swallow?
options[:swallow] && !loaded? && through_association.loaded?
end
end
end
ActiveSupport.on_load(:active_record) do
ActiveRecord::Associations::Builder::HasOne.singleton_class.prepend(SwallowReader::BuilderHook)
ActiveRecord::Associations::HasOneThroughAssociation.prepend(SwallowReader::AssociationMethods)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment