Uberloader is a new way of preloading associations in ActiveRecord. It works like preload
, but with the following changes:
- Custom scopes may be given.
- Nested preloads use blocks.
widgets = Widget
.where(category_id: category_ids)
# Preload category
.uberload(:category)
# Preload parts, ordered by name
.uberload(:parts, scope: Part.order(:name)) do |u|
# Preload the parts' manufacturer
u.uberload(:manufacturer)
# and their subparts, using a custom scope
u.uberload(:subparts) do
u.scope my_subparts_scope_helper
u.scope Subpart.where(kind: params[:sub_kinds]) if params[:sub_kinds]&.any?
u.uberload(:foo) do
u.uberload(:bar)
end
end
end
Install with Bundler:
bundle add uberloader
When uberload
is used, preload
and includes
are de-duped. The following will result in one query for parts
, ordered by name:
widgets = Widget
.preload(:parts)
.uberload(:parts, scope: Part.order(:name))
Regretably, none of this is possible without monkeypatching ActiveRecord::Relation
's non-public preload_associations
method. While small, the patch has no guarntee of working in the next minor, or even tiny, patch.
To assess its stability over time, I ran Uberloader's unit tests against the full matrix of (supported) ActiveRecord and Uberloader versions. They passed consistently, but with predictable clusters of failures around pre-release and X.0.0 versions.
I will keep these tests running daily, and against this project's main
branch as well. You can find the link to the results here. If something breaks, I'll try to fix it. If we're lucky, maybe this behavior could get into Rails itself someday...
Testing is fully scripted under the bin/
directory. Appraisal is used to test against various ActiveRecord versions, and Docker or Podman is used to test against various Ruby versions. The combinations to test are defined in test/matrix.
# Run all tests
bin/testall
# Filter tests
bin/testall ruby-3.3
bin/testall ar-7.1
bin/testall ruby-3.3 ar-7.1
# Run one specific line from test/matrix
bin/test ruby-3.3 ar-7.1 sqlite3
# Run a specific file
bin/test ruby-3.3 ar-7.1 sqlite3 test/uberload_test.rb
# Run a specific test
bin/test ruby-3.3 ar-7.1 sqlite3 N=test_add_preload_values
# Use podman
PODMAN=1 bin/testall
# Test all combinations of (supported) ActiveRecord versions against all uberloader versions
bin/testall-compatibility
# Output the results as a Markdown table
bin/generate-version-test-table
# Run tests for specific versions
# Appraisal ActiveRecord Uberloader
bin/test-compatibility 7.1 7.1.3.2 0.1.0
bin/test-compatibility 7.1 7.1.3.2 HEAD