14 powerful Rails console tricks that will supercharge your debugging
As Rails developers, we spend a significant portion of our day in the Rails console. It's our go-to tool for testing ideas, debugging issues, and exploring our applications. But are you using it to its full potential? In this article, I'll share some lesser-known but incredibly powerful Rails console tricks that will make your debugging sessions more productive and enjoyable.
Silence those noisy SQL logs
Ever tried debugging with puts
statements only to have them drowned in a sea of SQL logs? When you're executing queries that generate tons of log output, it can be hard to see what's actually happening in your code:
User.last(10)
User Load (5.0ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 10]]
# ... and more lines for complex query ...
The solution? Wrap your code in a silence block:
ActiveRecord::Base.logger.silence { User.last(10) }
This keeps your console clean and your debug messages visible. Perfect for when you're running batch operations but still need to monitor progress.
Access the last returned value with the underscore
Have you ever calculated something in the console and then realized you forgot to assign the result to a variable? We've all been there! Thankfully, Rails console (through IRB) provides the underscore (_
) variable that holds the value of the last expression:
User.where(status: 'active').count
User Count (128.2ms) SELECT COUNT(*) FROM "users" WHERE "users"."status" = $1 [["status", "active"]]
# => 34
pry(main)> _
# => 34
This simple trick can save you from having to re-run potentially expensive queries just because you forgot to store the result.
Format complex data structures with YAML output
Large nested hashes and arrays can be difficult to read in their default console representation. The y
helper method formats objects as YAML, making them much more readable:
some_data = { users: { active: 34, suspend: 10 }, projects: { total: 1500 } }
y some_data
#---
:users:
:active: 34
:suspend: 10
:projects:
:total: 1500
=> nil
This hierarchical format makes it much easier to understand the structure of complex data.
Create your own console helpers
Do you find yourself repeatedly looking up the same records or performing the same operations in the console? You can add custom helper methods that are available in every console session by creating an initializer:
# config/initializers/console_helpers.rb
Rails.application.console do
puts "Loading customized console helpers..."
def current_user
AdminUser.find_by(role: 'admin')
end
end
Now you can use these helpers directly in your console sessions, saving valuable typing time.
Access view helpers in the console
Did you know you can use all your view helpers directly in the console? The helper
object gives you access to all the helpers defined in your application:
helper.diff_label(1000)
# => "difficulty: Hard"
helper.truncate('my loong text', length: 12)
# => "my loong ..."
This is particularly useful when you want to check how a helper formats something without having to load a view in the browser.
Inspect and test your routes
Working with routes in the console can save you a lot of time. You can use the app
object to both generate paths and simulate requests:
# Get path string
app.cards_path
# => "/cards"
app.card_path(id:3)
# => "/cards/3"
# Send request
app.host = 'localhost'
app.get(app.cards_path(id: 5))
app.response.status
# => 200
app.response.body
# => "{"id":5,"name":"First one"}"
This allows you to test your routes and responses without leaving the console, which is perfect for API debugging.
Invoke rake tasks from the console
Need to run a rake task during your debugging session? No need to exit the console - you can invoke them directly:
Rails.application.load_tasks
Rake::Task['db:seed'].invoke
You can even pass arguments to the task if it's set up to accept them:
Rake::Task['users:notify'].invoke('[email protected]')
Find where a method is defined with source code
When debugging a complex issue, it's often helpful to see the implementation of a method. You can find out where any method is defined by using the $
sign before calling a method to see its source:
=> $ Card.last.image
Card Load (1.9ms) SELECT "cards".* FROM "cards" ORDER BY "cards"."id" DESC LIMIT 1 /*application='Sample'*/
From: /app/models/card.rb:20
def image
'default.jpg'
end
This saves you from having to switch to your editor just to check how a method is implemented.
Find methods by pattern matching
When you're looking for a method but don't remember its exact name, you can search for it using a regular expression:
User.instance_methods.grep(/email/)
=>
[:email_required?,
:send_email_changed_notification?,
:send_email_changed_notification,
:skip_email_changed_notification!,
:devise_email_before_last_save,
:devise_will_save_change_to_email?,
:devise_email_in_database,
....
This is incredibly useful when working with large classes or exploring unfamiliar libraries.
Execute raw SQL queries
Sometimes ActiveRecord doesn't give you the flexibility you need, or you want to test a complex SQL query before adding it to your codebase. You can execute raw SQL directly in the console:
sql = "SELECT users.id, COUNT(cards.id) AS card_count
FROM users
JOIN cards ON users.id = cards.user_id
GROUP BY users.id"
results = ActiveRecord::Base.connection.select_all(sql)
results.to_a
# (0.5ms) SELECT users.id, COUNT(cards.id) AS card_count
# FROM users
# JOIN cards ON users.id = cards.user_id
# GROUP BY users.id
#=> [{"id" => 1, "card_count" => 14}]
Inspect your database schema
Need to check table names or column details? The connection object has methods for that:
# Get all table names
ActiveRecord::Base.connection.tables
=>
["schema_migrations",
"users",
"cards",
"user_settings",
"ar_internal_metadata"]
Time your operations
When optimizing code, it's useful to know how long operations take. The Benchmark
module makes this easy:
require 'benchmark'
Benchmark.measure { User.all.each(&:get_score) }
#=>
#<Benchmark::Tms:0x000000012f399060
@cstime=0.0,
@cutime=0.0,
@label="",
@real=0.02980099990963936,
@stime=0.010855000000000281,
@total=0.022064000000000306,
@utime=0.011209000000000024>
Or for comparing different approaches:
Benchmark.bm do |x|
x.report("get_score:") { User.all.each(&:get_score) }
x.report("optimized_score:") { User.find_each(&:optimized_score) }
end
# =>
# user system total real
# get_score: 0.008948 0.004759 0.013707 ( 0.016986)
# optimized_score: 0.005066 0.003216 0.008282 ( 0.009016)
Use reload! to refresh your code
When you're making changes to your application code while the console is running, use reload!
to load those changes without restarting the console:
reload!
# => Reloading...
# => true
This refreshes your code so you can test your changes immediately.
Temporary change definition of class
If you want to change the callback or something similar, you can do that by:
user = User.last
def user.some_callback
puts "my modified code"
end
user.save!
Summary
By mastering these tricks, you'll save time and reduce frustration and become more efficient at solving problems. The next time you're stuck on a tricky bug or exploring an unfamiliar codebase, remember these console techniques and watch your productivity soar.
Happy debugging!