Learning Ruby with Exercism
It was a fairly long journey to get from the point where I decided I wanted to change careers and undertake a coding bootcamp (late 2019) and actually starting at Makers (summer 2021). While I counted down the months at my previous job, I spent a lot of my free time learning Ruby, first via Chris Pine’s excellent tutorial, followed by Codecademy, then by completing a large number of katas on Codewars.
After I finished my interview with Jonny at Makers earlier this year, he suggested that I might also enjoy the Ruby track on Exercism and said that it would help me to gain familiarity with the command line and test-driven development. He was right on all counts.
Exercism is a really interesting concept: a free platform where users can choose to undertake learning tracks across a large number of programming languages. Each track consists of various core exercises (you have to complete your current exercise to unlock the next one) as well as side exercises which are unlocked as you go along. The pace of Exercism is slower than, say, Codewars because after the first couple of automated core exercises you are reliant on how quickly human mentors can review your code: these mentors offer feedback, and either progress you to the next exercise or tell you what you need to do for your next iteration.
I’ve used Exercism on both Windows and Mac, and it works well on both although it can be a bit tricky to set up (there is a step-by-step guide on getting started here). It encourages a solid workflow mentality: you ‘pull’ the exercises from the Exercism website via the command line and ‘push’ your answers when they are completed. You figure out answers to the tasks on your local machine and then check your code against the test files which are included in each exercise package. I’m now at the end of the second week of the Makers pre-course, and most of this week has involved a similar workflow in terms of completing Ruby challenges (although we haven’t started messing around with classes and methods to the same extent as Exercism yet).
The instructions for each Exercism task are typically quite concise and you have to get used to figuring out failed test messages: I often deliberately fail the tests a few times just to work out the required names of the class and methods for a particular challenge. You could always peek inside the test files but I try to work out as much as possible from the failure messages. You do eventually have to start messing around with the test files as usually a few of the required tests skip
at first: this means that once you pass the first test, you comment out skip
on the next test, pass that, and so on.
To show the value of Exercism, I’d like to highlight how good mentoring helped me develop my coding skills for the following challenge:
Manage a game player’s High Score list.
Your task is to build a high-score component of the classic Frogger game, one of the highest selling and addictive games of all time, and a classic of the arcade era. Your task is to write methods that return the highest score from the list, the last added score and the three highest scores.
In this exercise you’re going to instantiate a class and add some instance methods. http://ruby-for-beginners.rubymonstas.org/writing_classes/initializers.html
A HighScore accepts an array with one or more numbers, each representing one ‘game score’. The Array class can offer inspiration for working with arrays. https://ruby-doc.org/core-2.5.1/Array.html
For my first effort at this exercise, I found the hardest part was keeping track of the instance and local variables. My thinking was that the input array of scores (I called it scores_list
below) should be turned into an instance variable in the initialize
method, and then I could use that when writing the required methods (although once I’d defined the scores
method to return the value of @scores_list
I then used scores
in the following methods):
class HighScores
def initialize scores_list
@scores_list = scores_list
end
def scores
@scores_list
end
def latest
scores[-1]
end
def personal_best
scores.max
end
def latest_is_personal_best?
latest == personal_best
end
def personal_top_three
scores.max(3)
end
end
My mentor, while liking my use of the .max
method and saying that my code was “pretty nice” overall offered some sage advice on improving this. They suggested that I use attr_reader
to deal with the strange way I’d handled scores_list
and the scores
method, and also encouraged me to think of a way to “avoid having to do the work every time each of these methods are called”, in other words generating all the answers when a new object of the HighScores
class is initialized (of course, the assumption is that the scores don’t change — which for the purposes of this exercise is true). I’ll confess that I’m not super confident with classes in Ruby yet, but this was a good chance to practice. Here’s what I came up with for my second go:
class HighScores
attr_reader :scores, :latest, :personal_top_three, :personal_best
def initialize scores
@scores = scores
@latest = scores.last
@personal_top_three = scores.max(3)
@personal_best = personal_top_three.first
end
def latest_is_personal_best?
latest == personal_best
end
end
This is much more concise than my original effort: the only problem I had was the latest_is_person_best?
method which I could not get to work with attr_reader
owing to the question mark at the end. While not spoon-feeding me, my mentor pushed me in the right direction, suggesting that I look at alias
and undef
. I went through a few more iterations, with my mentor mainly offering advice on formatting at this stage (they were especially displeased at the amount of trailing whitespace in my code). In any case, my final iteration looked like this:
class HighScores
attr_reader :scores,
:latest,
:latest_is_personal_best,
:personal_best,
:personal_top_three
alias latest_is_personal_best? latest_is_personal_best
undef latest_is_personal_best
def initialize(scores)
@scores = scores
@latest = scores.last
@personal_top_three = scores.max(3)
@personal_best = personal_top_three.first
@latest_is_personal_best = latest == personal_best
end
end
I was really pleased! Although my original submission had worked, I couldn’t help but feel it was rather amateurish compared to my final effort. My mentor, by encouraging me slightly outside my comfort zone, helped me to get to grips with classes and writing code in a more ‘functional’ way.
I’d strongly recommend Exercism for anyone learning Ruby (and, I assume, any of the other supported languages) with two provisos: one is that Exercism is going to upgrade to v3 later this year, which means that the experience as I have described it is likely to change (and this also means that time is being diverted away from mentoring at present). The other is that every mentor is unique (and you will typically get a different mentor for each individual exercise): while almost all of my mentoring experiences have been valuable, some mentors are happy to pass code as long as it passes the tests (though they will still offer feedback), while others will help you to improve before sending you on your way.
I hope you have found this blog useful, and please do take a look at Exercism!