I am currently studying Ruby and have attempted Simple Cipher challenge. I am now studying the following solution and am trying to reverse engineer to understand the thought process behind of this solution. The following is the link for the solution. I will detail my understanding of each code snippet. If they are not right, could you please correct me? Thanks! https://exercism.io/tracks/ruby/exercises/simple-cipher/solutions/b200c3d9f10e497bbe2ca0d826df2661
class Cipher
ALPHABET = [*'a'..'z']
attr_reader :key
def initialize(key = nil)
@key = key || 100.times.map { ALPHABET.sample }.join
fail ArgumentError, 'invalid chars in key' unless valid?(@key)
end
def encode(text)
a = 'a'.ord
text.chars.zip(@key.chars).map do |char, key|
ALPHABET[(char.ord - a + key.ord - a) % ALPHABET.length]
end.join
end
def decode(code)
code.chars.zip(@key.chars).map do |char, key|
ALPHABET[char.ord - key.ord]
end.join
end
private
def valid?(key)
!key.empty? && key !~ /[^a-z]/
end
end
Part 1
@key = key is if key is matched. ALPHABET is randomized (with .sample method) and joined. Randomised joined alphabets are then iterated 100 times and a new array is returned (with map method).
To handle invalid key error, fail ArgumentError is used.
def initialize(key = nil)
@key = key || 100.times.map { ALPHABET.sample }.join
fail ArgumentError, 'invalid chars in key' unless valid?(@key)
end
Part 2 I understand that the purpose of this code is to convert the plain text into encrypted text. However, I am struggling to some part of the following code.
def encode(text)
a = 'a'.ord
text.chars.zip(@key.chars).map do |char, key|
ALPHABET[(char.ord - a + key.ord - a) % ALPHABET.length]
end.join
end
- .ord method - convert a character to its ASCII value. a = 'a'.ord . Why is this character selected? Not z or other characters?
- .chars method - text string is separated into each individual character with .chars method. .chars more efficient than .split since it parses the underlying bytes to return the string's characters (I have read .chars vs .split difference on the stackover flow).
- .zip method - this method is used to compare original plain text characters and encryption characters.
- .map method - This method is called with a block and returns encrpyted text.
Inside .map method block, there are two arguments: char and key. char represents character from the original plain text and key represents encrypted key character.
I have a trouble understanding this
ALPHABET[(char.ord - a + key.ord - a) % ALPHABET.length]
part. ALPHABET original plain text and encryption key text characters are converted into ASCII values with .ord method. Why a value is subtracted from those values? Why use % operator and ALPHABET.length?- .join method - I guess .join is used to join the transformed encrypted characters. Is my understanding correct?
Part 3
In this part, decode method, code is used. Code is the secret key shared between two parties (sender and receiver). But why ALPHABET[char.ord - key.ord]
? Characters ascii - key ascii value will provide the decrypted plain text. I do not understand how it works?
def decode(code)
code.chars.zip(@key.chars).map do |char, key|
ALPHABET[char.ord - key.ord]
end.join
end
Part 4 private method is used to separate this chunk of code from other classes. Valid method is to verify the key. There are two conditions: key must not be empty or must be lowercase alphabets.
private
def valid?(key)
!key.empty? && key !~ /[^a-z]/
end