This article will discuss bullet
gem which helps you watch n + 1 queries when they are being used unnecessarily, as well as helping you to determine when they should be used.
Let’s first create a project and a few models.
1
2
3
4
5
| rails new bullet
rails g scaffold User email name
rails g scaffold Post title body user:belongs_to
rails g scaffold Comment body post:belongs_to
rails db:migrate
|
In config/routes.rb
, configure the routes.
1
2
3
4
5
6
7
| # config/routes.rb
resources :users do
resources :posts do
resources :comments
end
end
|
Seed in some data.
1
2
3
4
5
6
7
8
9
10
11
| # db/seeds.rb
User.create(email: "first_user@email.com", name: "First User")
(1..10).each do |i|
Post.create(title: "Post no #{i}", body: "This is body of number #{i} post", user_id: User.first.id)
end
(1..10).each do |i|
Comment.create(body: "This is comment number #{i} post 1", post_id: Post.first.id)
end
|
Run the following command:
Define relationships.
1
2
3
| # app/models/user.rb
has_many :posts
|
1
2
3
4
| # app/models/post.rb
belongs_to :post
has_many :comments
|
1
2
3
| # app/models/comment.rb
belongs_to :post
|
You will need to change the controllers and views a bit in order to avoid errors.
Controllers
1
2
3
4
5
6
7
8
9
10
11
| # app/controllers/posts_controller.rb
def index
@posts = Post.where(user_id: params[:user_id])
end
def set_post
@post = Post.where(user_id: params[:user_id]).find(params[:id])
end
|
Views
1
2
3
4
5
6
7
8
9
10
11
| # app/views/posts/index.html.erb
<td><%= link_to 'Show', user_post_path(post.user, post) %></td>
<td><%= link_to 'Edit', edit_user_post_path(post.user, post) %></td>
<td><%= link_to 'Destroy', user_post_path(post.user, post), method: :delete, data: { confirm: 'Are you sure?' } %></td>
# add the following anywhere, we just want to test the functionality
<% post.comments.each do |comment| %>
<%= comment.body %>
<% end %>
|
1
2
3
4
| # app/views/posts/show.html.erb
<%= link_to 'Edit', edit_user_post_path(@post.user, @post) %> |
<%= link_to 'Back', user_posts_path(@post.user) %>
|
1
2
3
4
5
6
7
| # app/views/comments/index.html.erb
<td><%= link_to 'Show', user_post_comment_path(comment.post.user, comment.post, comment) %></td>
<td><%= link_to 'Edit', edit_user_post_comment_path(comment.post.user, comment.post, comment) %></td>
<td><%= link_to 'Destroy', user_post_comment_path(comment.post.user, comment.post, comment), method: :delete, data: { confirm: 'Are you sure?' } %></td>
<%= link_to 'New Comment', new_user_post_comment_path %>
|
1
2
3
4
| # app/views/comments/show.html.erb
<%= link_to 'Edit', edit_user_post_comment_path(@comment.post.user, @comment.post, @comment) %> |
<%= link_to 'Back', user_post_comments_path(@comment.post.user, @comment.post, @comment) %>
|
Let’s add dependencies.
1
2
| # Gemfile
gem 'bullet', group: 'development'
|
Run bundle install
.
Let’s configure the bullet gem. You will find a lot of options here, but we’ll stick with the basics for now.
1
2
3
4
5
6
7
8
9
| # config/environments/development.rb
config.after_initialize do
Bullet.enable = true
Bullet.alert = true
Bullet.bullet_logger = true
Bullet.console = true
Bullet.rails_logger = true
end
|
Open http://localhost:3000/users/1/posts/
and you will see an alert that warns you for not using eager loading for users and comments. Change the index method of app/controllers/posts_controller.rb
to the following to remove the warning:
1
2
3
4
5
| # app/controllers/posts_controller.rb
def index
@posts = Post.where(user_id: params[:user_id]).includes(:user, :comments)
end
|