One of the most awesome tools I’ve learned to use while working with
Penny’s Ruby/Sinatra stack is
Tux. Tux is a Ruby REPL that augments the
standard irb
with a bunch of incredibly helpful plugins to help you with your
Ruby development. In fact, it’s probably our most important development
tool—possibly even more important than Slack. In this listicle, I’ll lay out
some of the ways Tux has won our hearts and saved our fingers.
1. History, Tab-Completion, And More
Tux includes irbtools
, which offers some basic improvements over irb
, such
as history preservation between sessions and tab-completion for methods. It can
even detect when you call the wrong method:
>> 'asdf'.spilt
NoMethodError: undefined method `spilt' for "asdf":String
Did you mean? #split
Or tell you how to call it, if you forget:
>> 'asdf'.howtocall(:first)
first(limit)
In addition, you can see which modules have been mixed into a particular object:
>> 'asdf'.mlp
=> [#<Class:#<String:0x007f66bfa28fc8>>, String, Amatch::StringMethods, ToRegexp::String, JSON::Ext::Generator::GeneratorMethods::String, Comparable, Object, Debugging, InteractiveEditor::Editors, Looksee::ObjectMixin, MethodLocator, FileUtils::Verbose, FileUtils, FileUtils::StreamUtils_, ORI::Extensions::Object, ActiveSupport::Dependencies::Loadable, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]
Or even get a handsome table of callable methods grouped by module and color-coded by visibility:
>> 'asdf'.lp
You can edit any object in the editor of your choosing, where it gets converted to YAML and back:
>> a = (0..10).to_a
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>> b = a.vim
I’m only scratching the surface here—there’s lots more along these lines if you check out the Tux docs.
2. Test Code Locally
Tux loads your Sinatra environment, which means it has access to all of your
models and all of the logic you’ve written. This means you can directly invoke
application code from Tux, so you can test specific logic without restarting
your Sinatra server and crafting a HTTP request. If you do want to make a HTTP
request, Tux lets you fake HTTP requests with rack-test
to hit your endpoints
super quickly.
>> puts get('/').status
200
>> puts get('/poop').status
404
3. Query Production with ActiveRecord
If you hook Tux up to your production database, you can use ActiveRecord’s handy DSL to query your database as well as invoke any of your application logic that you’ve written, such as a custom method on a model. There’s no easier way to tweak data, rerun a faulty piece of logic, or put a user back on their feet. Once upon a time, I kept a MySQL CLI open at all times for quick querying, but I’ve since upgraded to ActiveRecord where I can use application-specific filters, methods, and scopes.
4. Test Code Against Production Data
Taking this to the next level, you can easily paste or execute large chunks of
code against production systems. This is obviously pretty dangerous, but if
you’re iterating on an analytics tool or a read-only function, you can test it
against production before even committing it, eliminating the
write-commit-deploy-check feedback loop. For example, let’s say we’re working on
a method called guess_color
on an ActiveRecord model called Animal
:
class Animal < ActiveRecord::Base
def guess_color
...
end
end
Let’s write and test a more accurate guess_color
. Assuming the above exists on
production already, we can monkeypatch Animal
with a new guess_color
and
test it against live data to see how it performs.
>> class Animal
| def new_guess_color
| # new algorithm
| end
| end
=> :new_guess_color
>> Animal.all.select { |a| a.guess_color != a.new_guess_color }.each do
| |a| puts "#{a.id} changed behavior: #{a.guess_color} -> #{a.new_guess_c!"
| end
Now you can safely and quickly iterate on new functionality against live data, allowing you to more quickly converge on an accurate solution.
5. Display All Data With Tables
Tux integrates with hirb
to give beautiful tables for ActiveRecord results.
>> User.take(2)
The same table can be displayed vertically too, if you don’t have enough space.
In fact, you can use Hirb’s table-rendering logic yourself in Tux!
>> words = %w(correct horse battery staple)
=> ["correct", "horse", "battery", "staple"]
>> words_with_length = words.map { |w| [ w, w.length ] }
=> [["correct", 7], ["horse", 5], ["battery", 7], ["staple", 6]]
>> puts Hirb::Helpers::Table.render(words_with_length, headers: %w(word length))
+---------+--------+
| word | length |
+---------+--------+
| correct | 7 |
| horse | 5 |
| battery | 7 |
| staple | 6 |
+---------+--------+
4 rows in set
=> nil
You can imagine how useful this is for visualizing multidimensional information from the command line.
6. Customize Hirb With Smart Timestamps
You can customize Hirb’s table rendering with your own filters. Here’s an example:
>> User.take(2)
Wouldn’t be it great if that created_at
field could be automatically
transformed from a Unix timestamp into a human-readable date? It is with Tux, if
you put the following in your ~/.irbrc
:
Behold:
>> User.take(2)
7. Iterate With Cheap, Scrappy Analytics
We keep a flock of analytics methods that generate DAU graphs and do other analytics chores like cohorting, funnel tracking, and retention modeling. These methods are only invoked from Tux and never from the application server. At our small size, these mini-analytics tools are significantly easier to write and change than a separate dashboard or system. For example, here’s output from one of our tools, an onboarding funnel tracker:
Here’s another example: generating confusion matrices for pairwise binary variables.
That’s all for now! We covered Tux’s basic improvements to IRB, using Tux to accelerate development, accessing production data with Tux, using and customizing Tux’s table output, and building custom analytics tools. I’ve left out some details from this post, like how to go about connecting Tux to production, but I’ll discuss that in a future post!