Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created June 21, 2022 18:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/57205d59e42a1271174c3b0850820a49 to your computer and use it in GitHub Desktop.
Save JoshCheek/57205d59e42a1271174c3b0850820a49 to your computer and use it in GitHub Desktop.
ar_lazy_preload example (for graphql without N+1 issues)
# Config
require 'rails'
require 'ar_lazy_preload' # https://github.com/DmitryTsepelev/ar_lazy_preload
require 'active_record'
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
# not sure where this is supposed to happen, but it's necessary b/c ar_lazy_preload
# adds its functionality in a callback after active record finishes loading.
ArLazyPreload::Railtie.config.to_prepare_blocks.each(&:call)
# Schema
ActiveRecord::Schema.define do
self.verbose = false
create_table :users do |t|
t.string :name
end
create_table :posts do |t|
t.string :name
t.integer :user_id
end
create_table :comments do |t|
t.string :body
t.integer :post_id
end
end
# Models
class User < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
end
# Seed Data
user = User.create! name: 'Josh'
post1, post2 = user.posts = [Post.new(name: 'yo ho ho'), Post.new(name: 'and a bottle of rum')]
post1.comments.create! body: 'a pirate!'
post1.comments.create! body: 'blow the man down!'
post2.comments.create! body: 'or whiskey!'
user = User.create! name: 'Jan'
post, * = user.posts = [Post.new(name: 'Grape Jelly 4 lyfe!')]
post.comments.create! body: 'jan likes jam!'
# Helper Functions
def self.capture_queries(&block)
queries = []
capturer = -> *, data { queries << data.values_at(:name, :sql).select(&:present?).join(": ") }
ActiveSupport::Notifications.subscribed capturer, "sql.active_record", &block
queries
end
# ===== Examples =====
# Only loads users, b/c did not try to access posts
capture_queries do
User
.all
.lazy_preload(posts: :comments)
.map(&:name) # => ["Josh", "Jan"]
end
# => ["User Load: SELECT \"users\".* FROM \"users\""]
# Only loads users, and posts, b/c did not try to access comments
capture_queries do
User
.all
.lazy_preload(posts: :comments)
.map do |u|
{ name: u.name,
posts: u.posts.map { |p| p.name },
}
end
# => [{:name=>"Josh", :posts=>["yo ho ho", "and a bottle of rum"]},
# {:name=>"Jan", :posts=>["Grape Jelly 4 lyfe!"]}]
end
# => ["User Load: SELECT \"users\".* FROM \"users\"",
# "Post Load: SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" IN (?, ?)"]
# Loads users, posts, and comments, b/c accessed all of them
capture_queries do
User
.all
.lazy_preload(posts: :comments)
.map do |u|
{ name: u.name,
posts: u.posts.map { |p|
{ name: p.name, comments: p.comments.map(&:body) }
},
}
end
# => [{:name=>"Josh",
# :posts=>
# [{:name=>"yo ho ho", :comments=>["a pirate!", "blow the man down!"]},
# {:name=>"and a bottle of rum", :comments=>["or whiskey!"]}]},
# {:name=>"Jan",
# :posts=>
# [{:name=>"Grape Jelly 4 lyfe!", :comments=>["jan likes jam!"]}]}]
end
# => ["User Load: SELECT \"users\".* FROM \"users\"",
# "Post Load: SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" IN (?, ?)",
# "Comment Load: SELECT \"comments\".* FROM \"comments\" WHERE \"comments\".\"post_id\" IN (?, ?, ?)"]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment