Braille translator in ruby (with TDD)
11 Mar 2016
This post is about Test Driven Development in Ruby, and how to implement it even in small projects. I recently watched a talk from Uncle Bob about TDD as a discipline, and jumped to this article. I’ve never done TDD before, so I wanted to give it a try. The 3 laws of TDD are:
- You can’t write any production code until you have first written a failing unit test.
- You can’t write more of a unit test than is sufficient to fail, and not compiling is failing.
- You can’t write more production code than is sufficient to pass the currently failing unit test.
Aparently there are Unicode Braille patterns So, in order to implement something with TDD, let’s make a ruby program to translate letters to their spanish braille equivalent.
I’ll use rspec for testing.
Here is the convertion we want to make:
⠁ a, 1 ⠞ t ⠷ á
⠃ b, 2 ⠥ u ⠮ é
⠉ c, 3 ⠧ v ⠌ í
⠙ d, 4 ⠺ w ⠬ ó
⠑ e, 5 ⠭ x ⠾ ú
⠋ f, 6 ⠽ y ⠳ ü
⠛ g, 7 ⠵ z
⠓ h, 8 ⠨ Signo de mayúsculas
⠊ i, 9 ⠼ Signo de número
⠚ j, 0 ⠄ Punto (.) (punto 3)
⠅ k ⠂ Coma (,) (punto 2)
⠇ l
⠢ Signos de interrogación (¿?)
⠍ m ⠆ Punto y coma (;)
⠝ n ⠖ Signos de exclamación (¡ !)
⠻ ñ ⠒ Dos puntos (:)
⠕ o ⠦ Comillas (de cualquier tipo)
⠏ p ⠣ Abrir paréntesis "("
⠟ q ⠜ Cerrar paréntesis ")"
⠗ r ⠤ Guion (-)
⠎ s ⠀ Espacio (ningún punto)
Let’s start with a simple test:
it "changes 'a' to '⠁'" do
translator = Braille::Translator.new
expect(translator.call('a')).to eq('⠁')
end
Excelent! we have our first test!. If we try to run it, the test will fail. And that is what it is supoused to do. Now, let’s make the test pass with some code:
class Translator
def call(word)
return "⠁"
end
end
I know it seems silly, but it is a good start. For every iteration of testing and programming, you will have a completelly tested code that works. If we run the test suite now, the test will pass, and in order to continue we should add a new test.
The result
Working this way was very interesting for learning, and it was very funny. The final code was very clean, and I finished with a very reliable code. And I am using this gem in production for my website.
The complete algorithm:
class Translator
def call(group_of_words)
group_of_words.split(' ').map do |word|
translate_word(word)
end.join('⠀')
end
def translate_word(word)
if /\d+/.match(word)
translate_number(word)
else
word.split('').map do |char|
translate_character(char)
end.join
end
end
def translate_number(number_in_letters)
'⠼' + number_in_letters.tr('1234567890','⠁⠃⠉⠙⠑⠋⠛⠓⠊⠚')
end
def translate_character(character)
case character
when ('a'..'z')
character.tr('abcdefghijklmnopqrstuvwxyz',
'⠁⠃⠉⠙⠑⠋⠛⠓⠊⠚⠅⠇⠍⠝⠕⠏⠟⠗⠎⠞⠥⠧⠺⠭⠽⠵')
when ('A'..'Z')
'⠨' + translate_character(character.downcase)
when /á|é|í|ó|ú|ü|ñ/
character.tr('áéíóúüñ','⠷⠮⠌⠬⠾⠳⠻')
when /.|;|¡|!|:|"|\'|(|)|-/
character.tr('.;¡!:"\'()-','⠄⠆⠖⠖⠒⠦⠦⠣⠜⠤')
else
''
end
end
end
Check out the repo and analize the commits if you want to see how I did it testing function by function.