Table Print with Justin Searls
If you’re not already using table_print
to debug problems in your Rails applications, you’re probably losing valuable time to tedious data exploration. Join guest chef Justin Searls for a lightning course in using this tool to quickly visualize complex app data sets. Along the way, you’ll learn about how investing time in tightening your feedback loops is one of the best ways to optimize your development process.
Video transcript & code
Today I'm excited to bring you a guest episode from Justin Searls, co-founder of the software agency Test Double, and a frequent conference speaker on Ruby and Rails topics.
On the surface of it, today's episode is about a handy Rubygem for exploring and displaying the data in your application. But the underlying topic is something deeper and much more important: it's about feedback loops. Today Justin shows us how investing some time and a little creativity into tightening our development feedback loops can pay big dividends for our overall productivity.
[su_box title="Editor's Note --- from the RubyTapas Masala Chef" box_color="#283037"]In keeping with the spirit of this episode, the script is presented in tabular form.[/su_box]
================================================================================
Table Print— Justin Searls |
![]() |
You may have heard experienced developers use the term "feedback loops" to describe the speed and quality of tests, log messages, print statements, and so on. But what does feedback mean and why does it matter? | ![]() |
Well, imagine you have a large Rails blogging app, and you just received a bug report that tags are being attributed to the wrong author.
Now imagine you lack access to |
![]() |
Suppose we suspect the bug resides in this method | ![]() |
If we solicited feedback from the system by using puts , our feedback loop would entail: |
|
1. Opening our editor to the source listing | ![]() |
2. Navigating to the method | |
3. Typing puts and the data we want to print |
![]() |
4. Exercising the code that triggers the suspected code path | ![]() |
5. Hunt through the console to find the log message | ![]() |
6. Realize the bug isn't materializing in this case | |
7. And starting the whole process over | |
Debugging with puts could take all day! That's why people more often break execution to get faster feedback with tools like byebug or pry . |
|
Byebug is helpful for understanding the behavior of the system's execution by enabling us to step through our code's flow-controls |
![]() |
Meanwhile pry is great for understanding the state of the system's object model—what messages an object can respond to and so on | ![]() |
But what if the feedback we need is about the state of our application's persisted data? Whether we're using a debugger or pry, the best we can do is print page after page of models. Neither tool is well-suited to answering this kind of question | ![]() |
For fast feedback to questions about the state of our system's _data_ , let's try this gem called table_print , which leverages an awareness of ActiveRecord to allow us to quickly build tables of results as we query our data |
![]() |
When required, table_print adds a top-level method called `tp`. Let's try passing an Arel relation of all of our system's Tag models |
![]() |
Because table_print takes an ActiveRelation , we can constrain the results just like we would with any query, so let's limit the number of records we get back to 30 rows |
![]() |
And because table_print will display every column by default, we can narrow which columns to print with additional arguments after the relation | ![]() |
Let's print the tags again, this time only looking at the tag name and the author who created the tag | |
Did you notice how the author column changed from displaying a numeric ID to a Ruby object? That's a hint that table_print lets you define columns as string expressions that navigate your models' associations. |
![]() |
So let's make the author column more readable by changing the symbol to a string and asking for its name property | |
There, that's much better. — But where table_print really shines is in how it handles even more complex associations | ![]() |
This time, let's try table printing our authors, with columns for their e-mail addresses and the titles of the posts they've written | ![]() |
Look how well table_print handles this! It printed each author once, but created a separate line for each of their posts. Here we can easily see that Jack has 2 posts and Jill has 3. | ![]() |
We can drill even deeper into our model's associations when visualizing our data with table_print . Let's go one step further and print out the names of the tags associate
d with each individual post |
![]() |
Notice how our resulting table fans out even further to list each of the tags owned by each post.
Just like ActiveRecord spares us having to think about SQL JOIN syntax, table_print allows us to express ourselves in terms of simple association names, handling things like 1-to-many relationships for us. |
![]() |
In fact, I've found that table_print's simple API is so expressive that—if you make it a production dependency—you can even prototype basic report pages by capturing its output and rendering it into an HTML <pre> tag.
To accomplish this, we instantiate a Printer object and invoke its |
![]() |
It's not especially pretty, but it's a lightweight way to demonstrate what a report might look like before investing in proper markup and styling.
|
![]() |
So not only can table_print get you fast feedback when troubleshooting problems with your data, it may be able to help you iterate on feature work more quickly, too. | ![]() |
But first, let's think back to the bug about tags being misattributed that started us down this path. At first, we tried to replicate the bug by tagging one of the posts written by the author Jack, and it seemed to attribute the tag to him correctly. | ![]() |
This time, let's try creating a tag on the same post, but attribute it to a different author, in this case, Jill | ![]() |
Going back to our table_print report, we can quickly see that we've replicated the reported bug, |
![]() |
...and that the new tag was erroneously attributed to Jack. | ![]() |
When we revisit the affected method, we know our actions in the browser would have instantiated a new Tag model, so we can hone in on that particular code branch | |
That added focus will make it easier to spot our bug: we're assigning `author` to `self.author`, which is actually the author of the post, not the author argument passed in to the method. To fix this, we just need to reference the argument instead of the attribute. | ![]() |
One might hope this would teach me not to shadow ActiveRecord model attributes with argument names, or perhaps to consider implementing application logic like this outside of my model classes entirely. |
![]() |
In conclusion, my hope is you'll find table_print to be worth carrying in your toolbox, and let it serve as an example that no form of feedback is one-size-fits-all. We can get feedback from our systems in many forms, and table_print is a useful one for exploring and visualizing the data in our Rails applications. |
==============================================================
Responses