0.0
No release in over 3 years
Control which columns are loaded when preloading associations; prevents N+1 queries while reducing memory usage.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

 Project Readme

skinny_includes

Select specific columns when preloading associations. Prevents N+1 queries, reduces memory usage.

Why?

Big beautiful columns

I have a lot of columns that have associated JSON directly on the table.

class ThingWithJsonDataColumn < ApplicationRecord 
  # has a `data` column name with column type of `json` 
end

Sometimes I load 5k of these ThingWithJsonDataColumn objects in an HTTP request. This is expensive.

I could write a custom association that excludes the column I don't care about, but that's silly.

Use case

The obvious use case is large JSON or text columns that slow queries and inflate memory. Instead of writing custom scoped associations, exclude them:

post.without_columns(comments: [:metadata_json, :body])

Loads all columns except metadata_json and body.

Installation

bundle add skinny_includes

Or manually:

gem 'skinny_includes'

Usage

Two methods:

with_columns — whitelist columns:

Post.with_columns(comments: [:author, :upvotes])
Post.includes(:comments).with_columns(comments: [:author])

without_columns — blacklist columns:

Post.without_columns(comments: [:body, :metadata])
Post.includes(:comments).without_columns(comments: :body)

Both work with multiple associations:

Post.with_columns(comments: [:author], tags: [:name])
Post.without_columns(comments: [:body], tags: [:description])

Primary keys and foreign keys are always included, even if excluded.

Scoped Associations

Scoped associations are fully supported:

class Post < ApplicationRecord
  has_many :published_comments, -> { where(published: true) }, class_name: 'Comment'
end

Post.includes(:published_comments).with_columns(published_comments: [:body, :author])

The scope is respected—only published comments are loaded, and only the specified columns are selected.

Loading Strategies

Works with all ActiveRecord loading strategies:

  • includes - Recommended, lets Rails choose the strategy
  • preload - Always uses separate queries
  • eager_load - Converts to the gem's loading strategy automatically

Supported Association Types

  • has_many
  • has_one
  • belongs_to

All association types work with both with_columns and without_columns.

Nested Includes

Nested/chained includes are fully supported with hash syntax:

Post.with_columns(
  comments: {
    columns: [:body],
    include: { author: [:name] }
  }
)

This loads posts with their comments (only body column) and each comment's author (only name column). Foreign keys are automatically included as needed.

Multi-level Nesting

You can nest as deep as you want:

Post.with_columns(
  comments: {
    columns: [:body],
    include: {
      author: {
        columns: [:name],
        include: { profile: [:website] }
      }
    }
  }
)

Automatic Foreign Key Inclusion

Foreign keys are automatically included when you use nested includes, even if you don't explicitly specify them:

# author_id is automatically selected to load the nested author association
Post.with_columns(
  comments: {
    columns: [:body],  # author_id NOT listed, but will be included
    include: { author: [:name] }
  }
)

Nested with without_columns

Works the same way:

Post.without_columns(
  comments: {
    columns: [:metadata],  # Exclude metadata from comments
    include: { author: [:bio] }  # Exclude bio from authors
  }
)

Requirements

  • Ruby 3.0+
  • ActiveRecord 7.0+