Cracking Gems: Reading Ruby Code
All of us write code on a regular basis, some more often than others. But there is a major deficiency in the amount of time we spend reading others’ code.
Authors don’t produce good work unless they consider the work of the others by reading it. A similar situation is present with us developers.
Reading large codebases can be daunting. There’s just so many abstractions, all linked to one another, making a muddled mess in your brain.
But, with a little bit of experience, it becomes much easier to read code. Let’s get to it!
Ruby Gems
The standard mode of distributing code in the Ruby world is the almighty gem.
Let’s consider how Ruby gems are laid out. The quickest way to see this is with the “bundle gem” command.
Go into wherever you write code (I typically use ~/code), and run the command bundle gem <name of gem>
That should create a directory with title “name of gem”. If you navigate into the directory, you should see a few folders and a couple of files.
The bundle gem
command sets up the basic scaffolding to write a Ruby gem and the required file structure.
The directory of interest is lib/, which contains the bulk of the code for a Ruby gem. Similarly, a gem may contain the bin/ and test/ directories.
bin/ contains the executables that have to be built and put on the system. For example, the “bundler” gem itself would have the bundle
executable in this directory.
Finally, spec/ or test/ typically contain the unit and functional tests for the gem.
Preliminary Work
Before you dive into the source code of the gem, there are a few things you should keep in mind.
First of all, come up with a goal. I’ll elaborate this with a story. A couple years ago, I decided I wanted to aquaint myself with low-level development (such as kernel development). To this end, I picked up a copy of the implementation of the ext3 filesystem and decided to learn as much as possible about it.
This approach was incredibly unsuccessful.
I kept getting sidetracked by the little details, unable to see the big picture. A week or so after doing this, I decided that I needed to set a goal. I decided that I wanted to change how the filesystem did caching.
This seemed incredibly difficult when I first started – I had no idea how the code really worked! But, just by having a goal, I was able to sort through all the code and get a general idea of what the different parts did in my mind; that’s an awesome feeling!
Coming up with a goal in mind will help you quickly sort through everything.
Secondly, the editor you use is important. As for me, I typically use Vim. However, I know several people that use IDEs specifically for reading code. I can see where they’re coming from; IDEs make it very easy to jump from class to class and function to function. So, pick something that you’re comfortable with and allows you to move around quickly. I can say with certainty that you will be switching between a ton of files and searching for variable and method names a lot.
Cracking Open the Gem
Once you’ve delved into the souce, don’t be afraid to make guesses!.
What I mean by this is that you don’t have to spend time looking into the implementation of each and every function (that’d take as much time as writing the code!). Instead, use the name of the function/class/module and how it is used to try and make a guess as to what it does. Often times, it will be obvious. Other times, not so much.
If you feel that a function in consideration is crucial, then by all means, consider every line of code inside it. But, it is important to skim over the stuff that doesn’t really matter that much (once again, this is where having a goal makes a tremendous difference – you can gauge whether or not something is important).
If a gem doesn’t have sufficient documentation (which, according to some, happens far too frequently), and you need to figure out what a certain function does, consult spec/!
The unit tests of any project are an excellent way to see how the different parts of the project work together and what they’re each supposed to be doing. This can be incredibly helpful for gems that have a ton of features, but little documentation.
Taking notes as you go along is a great way to keep track of what you’ve learned so far. It also keeps you on track, which seems to be the number one problem when I read code.
Your notes should typically consist of what each class, function, and method does and how they are interrelated. For example, if you know that the method Person.get_data
downloads employee data from the server and calls on Util.parse_json
, and this is relevant to your goal, write it down.
A common trick when reading the code for executables (as opposed to libraries) is to jump straight to the so-called “main method”. This method “starts off” the action and typically communicates with the user. From this method, you can then branch off and figure out what parts on which to concentrate. I’ve found that a similar trick works well with libraries that have event loops – find the main event loop, find the calls in it that you’re interested in, and follow the crumbs.
Finally, guess what the most important trick is in trying to read the code for a gem? Actually use the gem itself! This is incredibly underrated. Consider it this way: if you’ve never used a microwave, how in the world are you supposed to know how it works?!
For example, when I started working with Event Machine, I really wanted to see how such libraries were written underneath, but, I hadn’t really ever used it. So, I wrote a little IRC bot over a week of time with a couple cool features and that gave me an understanding of how Event Machine was structured. Without that experience, it is very difficult if not impossible to correctly understand how stuff works.
Also, as you are reading through the code, it’s always a good idea to check your “findings” by actually testing them out. If you think that the get_server_ident
method fetches the name of a server, use the gem in a little dummy piece of code and see if that matches. If you’re trying to guess as much as possible and skimming over large portions of code, it is unavoidable that you’ll misunderstand some aspects at first and this is a great way to clear yourself of those misunderstandings.
Conclusion
Reading code is like flossing – its one of those things that you know you should be doing, but, you somehow just end up not doing it.
But, after reading through this, I want you to make a habit of it! It’ll make you a much better developer.
Hopefully this quick guide will better your journey through the waters of the Ruby Gems.