Rails force models to eager load
After a great deal of trial and error, I recently learned that Jason Waldrip's answer is somewhat incomplete. As of Rails 5, Rails.application.eager_load!
will indeed load all of the directories specified in config/application.rb
. But it doesn't match Rails' actual behavior in production. To do that, one must instead mirror what Rails does:
Rails.configuration.eager_load_namespaces.each(&:eager_load!)
The salient difference between the approaches is that OPs answer won't eager load the files within app directories of Engine
s that live in the gems
or vendor
folders. Whereas Rails itself will identify where subclasses of Engine
exist and will see to it that the appropriate app
subdirectories are eager-loaded.
Behind the scenes
Rails 5 adds eager load directories in railties-5.0.2/lib/rails/engine/configuration.rb:39
, where it runs
paths.add "app", eager_load: true, glob: "{*,*/concerns}"
paths.add "app/assets", glob: "*"
paths.add "app/controllers", eager_load: true
paths.add "app/channels", eager_load: true, glob: "**/*_channel.rb"
paths.add "app/helpers", eager_load: true
paths.add "app/models", eager_load: true
paths.add "app/mailers", eager_load: true
These directories are not currently included in a default Rails.application.eager_load!
How can I force Rails to load all models?
rails 2:
Dir[Pathname(RAILS_ROOT) + 'app/models/**/*.rb'].each do |path|
require path
end
rails 3:
Dir[Rails.root + 'app/models/**/*.rb'].each do |path|
require path
end
another way:
(ActiveRecord::Base.connection.tables - %w[schema_migrations]).each do |table|
table.classify.constantize rescue nil
end
Force full object load with eager loading and nested objects
This is going to be difficult to do in only one query. The most straightforward I could get using just ActiveRecord methods was this:
Order.includes(:transactions, line_items: [:product])
.where(id: LineItem.where(product: product).pluck(:order_id))
I don't know much about Arel, but I think it might be able to accomplish this in one query rather than two.
Eager load in model?
You can't "eager load" country_days from a model instance, but you can certainly skip loading them all together by using a has_many through:
. You can also skip the extra map, too.
# country.rb
class Country < ActiveRecord::Base
has_many :country_days
has_many :country_day_shops, through: :country_days #EDIT: You may have to add this relationship
has_many :shops, through: :country_day_shops #And change this one to use the new relationship above.
def country_highlights
shops.distinct_names.join(", ")
end
end
# country_day.rb
class CountryDay < ActiveRecord::Base
belongs_to :country
has_many :country_day_shops
has_many :shops, :through => :country_day_shops
end
# shop.rb
class Shop < ActiveRecord::Base
def self.distinct_names
pluck("DISTINCT shops.name") #Edit 2: You may need this instead of 'DISTINCT name' if you get an ambiguous column name error.
end
end
The has_many through:
will use a JOIN
to load the associate shop records, in effect eager loading them, rather than loading all country_day records and then for each country_day record, loading the associated shop.
pluck("DISTINCT name")
will return an array of all of the unique names of shops in the DB, using the DB to perform a SELECT DISTINCT
, so it will not return duplicate records, and the pluck
will avoid loading ActiveRecord instances when all you need is the string name
.
rails eager loading for one-to-many association
Your templates table does not have an article_id column according to the schema.rb you posted so you will need to create that reference.
Change
has_one :template
in the articles model to
belongs_to :template
Eager loading last subordinate record
Yes,
Lets take this example
class Song < ActiveRecord::Base
has_many :votes
end
class Vote < ActiveRecord::Base
belongs_to :song
end
Add a new association to the Song
# for Rails version <4:
has_one :last_vote, :class_name => 'Vote', :order => 'created_at DESC'
# for Rails version 4+
has_one :last_vote, -> { order(created_at: :desc) }, class_name: 'Vote'
this new association will always return the most recently created Vote for a Song.
To eager load it
# for Rails version <3:
songs = Song.find(:all, :conditions => 'artist_name = "frank"', :include => :last_vote)
# for Rails version 3+:
songs = Song.includes(:last_vote).where(artist_name: 'frank')
Always eager load association with entity
You can include a "default_scope" in your model.
For Rails 4:
class Book
has_many :chapters
default_scope { includes(:chapters) }
end
For Rails 3:
class Book
has_many :chapters
default_scope includes(:chapters)
end
For Rails 2:
class Book
has_many :chapters
default_scope :include => :chapters
end
Related Topics
Carrierwave - Resizing Images to Fixed Width
Why Do I Get "No Implicit Conversion of String into Integer (Typeerror)"
Concurrent Requests with Mri Ruby
Share Models Between 2 Rails API's (Separate Applications)
How to Extract the Sign of an Integer in Ruby
What Is the Best Wysiwyg for Rails - Ruby on Rails Blog
Using Gets() Gives "No Such File or Directory" Error When I Pass Arguments to My Script
How to Install 'Cocoapods' Gem from Rubygems.Org (Bad Response Backend Read Error)
Select Mailbox "Sent Mail" or "All Mail" in Ruby Net::Imap
How to Replace the Last Occurrence of a Substring in Ruby
Rails 4.1 Can't Deploy via Capistrano 3