So we’ve figured out what we want our gem to do and have taken the initial steps by running
bundle gem. Now what?
We start building.
I won’t go into exactly how I built RubyTutor. The act of building a gem will be different for everyone. No two projects are the same and everyone’s thought pattern is different.
It wouldn’t be beneficial for me to layout exactly how I created every method and class. While there may be some pearls of wisdom you will gain from a detailed process, you will have to filter out a lot of noise that doesn’t pertain to you.
If you do wish to check out how I built RubyTutor and my thought process behind it, you can check out the code here.
Instead, what I’ll focus on are the lessons that I learned and reflect on the mistakes I made that you can avoid.
Lesson 1: Research (or Be Like a Toddler With a TV)
Like an author crafting a fine novel or a director filming a documentary, research is crucial and can help mitigate the amount of headaches and frustration one will feel later.
I did not do enough research. Not nearly enough.
The reason I know that many newcomers will be intimidated by all the files that the
bundle gem command gives us is because I felt intimidated by it and thought it would be too much of a hassle to read about it. I didn’t want to feel dumb so I ignored what I didn’t understand and stuck to what I knew: building and testing.
All of the research you read in Part 2 of this series was done after building and releasing my gem. I had no idea what a Rakefile was or did and now I’m kicking myself for it. I can’t remember the amount of times I had to build, install and then delete the .gem file to test it out in IRB, only to learn I could have done all of that with a single
When diving into a new experience, be like a toddler with a new TV.
The toddler has no clue what anything on the TV does, so they press every button on the remote to see what happens. Sure, they may get scolded for changing the channel or end up on some strange menu that is hard to click out of, but they learn through those mistakes. They don’t care if they look stupid because, well, they’re a toddler.
Soon enough, however, Mom and Dad realize their toddler is the only one in the house who knows how to use the TV. All of the sudden, they stop scolding them and start asking them how to program the DVR.
So be like that toddler and start pressing all the buttons. Don’t worry about looking dumb, because soon enough, you’ll be the smartest one in the room.
Lesson 2: Commit Liberally
I have terrible commit habits. The commit history on RubyTutor is pretty pathetic. My first commit wasn’t until I was halfway done. Not good.
As Seth Robertson puts it, “Commit Early, Commit Often.” If you, the reader, is working on something right now, commit it. You’ve probably done enough already to deserve a commit.
Having a thorough commit history is important.
- It’s the only way you can look back in time on how you made your project.
- You can spot where you made a bug and the effects of it.
- Other people can learn “how the sausage is made.”
If you’re like me and simply forget to commit and/or are too lazy, might I suggest you commit every time you save in the editor, or every 3–5 minutes. If you don’t commit often because you don’t want other people to see the mistakes you make and/or look dumb, see Lesson 1 above.
Lesson 3: Test Driven Development
If you have not seen this term before, you will see it more often in the future. It is the idea of writing tests that fail before building features. It is very popular in agile development and something I would advocate.
A drawback is that it assumes we know how we want a particular test to pass (i.e. we know how that feature will act) — which is not always the case. However, it does give us a specific goal to shoot for instead of charging forward without a clear direction.
Another benefit of TDD is that our testing will stay current with our development and we won’t have to play catch up; a common problem with beginners. I’m ashamed to say I didn’t start building RubyTutor with TDD. I was about half way through when I did start and it was torture to write dozens of tests to get up to speed
Once I started, however, I loved it. It’s like a challenge to ourselves — “I dare me to make this test pass.” It’s a way of gameifying programming; I’ve even seen gems that run the tests and return a score depending on how many pass.
If you haven’t tried Test-Driven Development before, I suggest giving it a shot. It’s a paradigm shift since TDD requires more thought than just diving into our editor and reeling off lines of code. However, that upfront planning leads to much more efficient development. That’s why many agile teams and startups prefer it.
One specific test problem I ran into was testing what was being output to the console. Each RubyTutor method outputs a lot of text to the console and always returns
nil, so there was no point in testing the return value. However, one can capture the standard output text into a string and test that:
class RubyTutorTest < Minitest::Test
output = StringIO.new
$stdout = output
end def test_simple_explain
assert_nil RubyTutor.explain 'a'
assert $stdout.string.match(/Class: String/)
assert $stdout.string.match(/Value: a/)
assert $stdout.string.match(/Length: 1/)
assert_nil $stdout.string.match(/instance of the String class./)
assert_nil $stdout.string.match(/A String object is an/)
end def teardown
$stdout = STDOUT
$stdout is a variable that contains all the text that is output to the console. In my
setup we can see that I set
$stdout to a new instance of
StringIO, essentially catching any output. I can then use the
.string method on
$stdout to turn that text into a string and test it. In the
teardown, it reassigns
$stdout back to the standard output,
So if you ever find yourself needing to test console output, keep that tip in mind.
Lesson 4: A Touch of Individuality
This is your project, your baby. This is your chance to flex your skills and show off what you’ve learned to the world. You can make your gem however you like, so take ownership of that.
Put your own brand of personality into it.
Personally, I think a lot of programming can be dry. Even though we’re working on a computer doesn’t mean our code has to look like it was written by one. I love making my code, and thus my computer, sound like its human.
That’s why I decided to only make class methods for
RubyTutor. That way, you’re forced to write
RubyTutor.explain 5 or
RubyTutor.describe String. It’s like I’m actually asking a tutor to explain the Fixnum 5 or the Class
String to me. It’s my own way of injecting humor into my code and adding some individuality.
Give your gem a little touch of you.
Continue to Part 4 for the conclusion of the My First Ruby Gem series.