I just started learning Ruby and thought that this relatively simple HackerRank challenge would be a good opportunity to try to write clean, tested and documented code without getting bogged down in very complex problems.
In this challenge, your task is to write a method which takes an array of strings (containing secret enemy message bits!) and decodes its elements using ROT13 cipher system; returning an array containing the final messages.
I'd like to know if this follows the "Ruby Way" (I'm willing to bet it doesn't!) and I am open to improvements on all aspects of the code, including the documentation, which is meant to follow RDoc format.
I use Ruby v 2.4. Note that, coming from a mostly Python background, there may be some patterns that are very Python-like, if so please point out if there's a more Ruby way of doing it!
I am also planning to learn about TDD libraries with Ruby soon, for now using that short solution from Stack Overflow seemed sufficient for this exercise.
# AssertionError class & assert method copied from Stack Overflow
# https://stackoverflow.com/a/3264330/3626537
class AssertionError < RuntimeError
# Extends RunTimeError
end
def assert &block
# Evaluates whether the input block is true and raises AssertionError if not
# +block+:: the block to evaluate
raise AssertionError unless yield
end
def is_uppercase_letter? str
# Evaluates whether a single is a single uppercase letter, i.e.: A-Z
# +str+:: string to evaluate
str.length == 1 && /[[:upper:]]/.match(str)
end
assert { is_uppercase_letter? "A" }
assert { !is_uppercase_letter? "a" }
assert { !is_uppercase_letter? "Hello" }
def is_lowercase_letter? str
# Evaluates whether a string is a single lowercase letter, i.e.: a-z
# +str+:: string to evaluate
str.length == 1 && /[[:lower:]]/.match(str)
end
assert { is_lowercase_letter? "a" }
assert { !is_lowercase_letter? "A" }
assert { !is_lowercase_letter? "hello" }
def rot13_char ch
# Apply ROT13 cipher to a single character
# - Each letter is rotated by half of the alphabet, or 13 places
# - Original case is preserved
# - Non-letter characters remain unchanged
# - https://en.wikipedia.org/wiki/ROT13
# +ch+:: the character to apply ROT13 to
rot_byt = ch.ord + 13
if is_uppercase_letter? ch
rot_byt <= "Z".ord ? rot_byt.chr : (rot_byt - 26).chr
elsif is_lowercase_letter? ch
rot_byt <= "z".ord ? rot_byt.chr : (rot_byt - 26).chr
else
ch
end
end
assert { rot13_char("W") == "J" }
assert { rot13_char("J") == "W" }
assert { rot13_char("j") == "w" }
assert { rot13_char("w") == "j" }
assert { rot13_char("?") == "?" }
def rot13_string str
# Apply ROT13 cipher to all characters in a string
# +str+:: the string to apply ROT13 to
chars = str.split('')
results = []
chars.each do |ch|
results.push(rot13_char ch)
end
results.join("")
end
assert { rot13_string("Why did the chicken cross the road?") == "Jul qvq gur puvpxra pebff gur ebnq?" }
assert { rot13_string("Gb trg gb gur bgure fvqr!") == "To get to the other side!" }
# puts rot13_string "Why did the chicken cross the road?"
# puts rot13_string "To get to the other side!"
# puts rot13_string "Jul qvq gur puvpxra pebff gur ebnq?"
# puts rot13_string "Gb trg gb gur bgure fvqr!"
def rot13 messages
# Apply ROT13 to an array of "messages" i.e. strings
# +messages+:: the array of strings to apply ROT13 to
rotated = []
messages.each do |msg|
rotated.push(rot13_string msg)
end
rotated
end
assert do
input = ["Why did the chicken cross the road?", "To get to the other side!"]
expected = ["Jul qvq gur puvpxra pebff gur ebnq?", "Gb trg gb gur bgure fvqr!"]
actual = rot13 input
expected == actual
end
assert do
input = ["Jul qvq gur puvpxra pebff gur ebnq?", "Gb trg gb gur bgure fvqr!"]
expected = ["Why did the chicken cross the road?", "To get to the other side!"]
actual = rot13 input
expected == actual
end