Auto-fix N+1 queries detected by Bullet at runtime
Features · Installation · Quick Start · Configuration · How It Works · Integrations
Bullematic hooks into Bullet notifications, captures N+1 detections, and rewrites your Ruby code with includes, preload, or eager_load using Prism AST parsing. It is designed to run in development and test environments where Bullet already runs.
- Automatic N+1 fixes for Rails scopes and queries
- Runtime capture via Bullet notifications
- AST-based rewrites powered by Prism
- Dry-run mode and optional backups
- RSpec, Minitest, and Rails integrations
Add to your Gemfile:
gem 'bullematic', group: [:development, :test]Then install:
bundle install- Ruby 3.1+
- Bullet 6.0+
- Configure Bullematic in your Rails initializer or test setup:
# config/initializers/bullematic.rb (for Rails)
# or in your test setup
Bullematic.configure do |config|
config.enabled = true
config.auto_fix = true
config.target_paths = %w[app/controllers app/models app/services]
config.skip_paths = %w[app/controllers/admin]
config.dry_run = false # Set to true to preview changes without applying
config.fix_strategy = :includes # :includes, :preload, or :eager_load
config.backup = true # Create backup files before modifying
end- Enable Bullematic at runtime:
BULLEMATIC=1 bundle exec rspec# spec/spec_helper.rb or spec/rails_helper.rb
require 'bullematic/integrations/rspec'
RSpec.configure do |config|
# ... your existing config
end
Bullematic::Integrations::RSpec.setup# test/test_helper.rb
require 'bullematic/integrations/minitest'
Bullematic::Integrations::Minitest.setup
class ActiveSupport::TestCase
include Bullematic::Integrations::Minitest::TestHelper
endBullematic auto-loads via Railtie in development and test environments when the gem is loaded.
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
Boolean | true |
Enable or disable Bullematic |
auto_fix |
Boolean | true |
Automatically apply fixes |
target_paths |
Array | ['app/controllers', 'app/models', 'app/services'] |
Paths to consider for fixes |
skip_paths |
Array | [] |
Paths to skip |
dry_run |
Boolean | false |
Preview changes without applying |
backup |
Boolean | false |
Create .bullematic.bak backup files |
fix_strategy |
Symbol | :includes |
Strategy: :includes, :preload, or :eager_load |
logger |
Logger | Auto-configured | Custom logger instance |
debug |
Boolean | false |
Enable debug mode (raises errors) |
- Bullet detects an N+1 query during request or test execution
- Bullematic captures the notification and stack trace
- At the end of the run, Bullematic parses the file with Prism
- The AST finder locates the query that triggered the N+1
- The AST rewriter inserts the appropriate
includescall - The file is written back (or logged in dry-run mode)
Before:
class PostsController < ApplicationController
def index
@posts = Post.all # N+1 when accessing @posts.each { |p| p.comments }
end
endAfter:
class PostsController < ApplicationController
def index
@posts = Post.includes(:comments).all
end
end- Dynamic queries built with
sendor metaprogramming may not be fixable - Complex conditional branches can be hard to rewrite
- Already-optimized queries are skipped
git clone https://github.com/ydah/bullematic.git
cd bullematic
bin/setup
bundle exec rake specBug reports and pull requests are welcome at https://github.com/ydah/bullematic.
Released under the MIT License.
- Bullet - N+1 query detection
- bulletmark_repairer - Similar concept, inspiration for this gem
- Prism - Ruby parser used for AST manipulation