Skip to content

Instantly share code, notes, and snippets.

@gtm19
Created April 28, 2022 20:42
Show Gist options
  • Save gtm19/81028a3d33fa973b4ea262e9687cc42b to your computer and use it in GitHub Desktop.
Save gtm19/81028a3d33fa973b4ea262e9687cc42b to your computer and use it in GitHub Desktop.
# ./encrypt.rb
# 1. Get an array of all the letters of the alphabet
# See: https://ruby-doc.org/core-3.0.2/Range.html
LETTERS = ("A".."Z").to_a
# Alternatively:
# LETTERS = Array ("A".."Z")
def encrypt(text, offset = -3)
# Parameter checking: -------------------------------------------------------
# See: https://www.exceptionalcreatures.com/bestiary/ArgumentError.html#making-a-strong-argument
raise ArgumentError.new(
"Expected `text` to be a String, but it is a #{text.class}"
) unless text.is_a? String
raise ArgumentError.new(
"Expected `offset` to be an Integer, but it is a #{offset.class}"
) unless offset.is_a? Integer
# ---------------------------------------------------------------------------
# 2. split the input into individual letters
# See: https://ruby-doc.org/core-3.0.2/String.html#method-i-split
text_split = text.split("")
# 3. iterate over each letter:
# See: https://ruby-doc.org/core-3.0.2/Enumerable.html#method-i-map
text_split.map! do |letter|
# 4. get the index of each letter in the alphabet (A = 0 ... Z = 25)
# See: https://ruby-doc.org/core-3.0.2/Array.html#method-i-index
index = LETTERS.index(letter)
# 5. add offset to index and get remainder on division by 26
# 24, 25, 26, 27, 28
# Y Z A B C
# 24, 25, 0, 1, 2
# ^ ^ ^ --- same as remainder on division by 26
# See: https://ruby-doc.org/core-3.0.2/Numeric.html#method-i-25 for % usage
# See: https://www.rubyguides.com/2019/10/ruby-ternary-operator/ for ternary operator
index ? LETTERS[(index + offset) % 26] : letter
# or (if not writing with decryption in mind:)
# since arr[-n] is equivalent to arr[arr.length + 1 - n]
# index ? LETTERS[index - 3] : letter
# See: https://ruby-doc.org/core-3.0.2/Array.html#class-Array-label-Accessing+Elements
end
# 6. join it all back up
# See: https://ruby-doc.org/core-3.0.2/Array.html#method-i-join
return text_split.join
end
def decrypt(text)
encrypt(text, offset = 3)
end
# .spec/encrypt_spec.rb
require_relative "../encrypt"
describe "#encrypt" do
it "returns a string" do
# See: https://rspec.info/documentation/3.10/rspec-expectations/#identity
expect(encrypt("SOME TEXT")).to be_a(String)
end
it "returns an empty string when param is ''" do
# See: https://rspec.info/documentation/3.10/rspec-expectations/#equivalence
expect(encrypt("")).to eq("")
end
it "raises an error if text param is not a string" do
# See: https://rspec.info/documentation/3.10/rspec-expectations/#expecting-errors
# NOTE: the expression must be in { CURLY BRACES } for expected errors
expect { encrypt(1) }.to raise_error(ArgumentError)
expect { encrypt(nil) }.to raise_error(ArgumentError)
expect { encrypt(true) }.to raise_error(ArgumentError)
end
it "returns the 3-letter backward-shifted text" do
input = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG"
encrypted = encrypt(input)
expected = "QEB NRFZH YOLTK CLU GRJMP LSBO QEB IXWV ALD"
expect(encrypted).to eq(expected)
end
end
describe "#decrypt" do
it "returns the 3-letter forward-shifted text" do
input = "FK ZOVMQLDOXMEV, X ZXBPXO ZFMEBO, XIPL HKLTK XP ZXBPXO'P ZFMEBO, QEB PEFCQ ZFMEBO, ZXBPXO'P ZLAB LO ZXBPXO PEFCQ, FP LKB LC QEB PFJMIBPQ XKA JLPQ TFABIV HKLTK BKZOVMQFLK QBZEKFNRBP. FQ FP X QVMB LC PRYPQFQRQFLK ZFMEBO FK TEFZE BXZE IBQQBO FK QEB MIXFKQBUQ FP OBMIXZBA YV X IBQQBO PLJB CFUBA KRJYBO LC MLPFQFLKP ALTK QEB XIMEXYBQ. CLO BUXJMIB, TFQE X IBCQ PEFCQ LC 3, A TLRIA YB OBMIXZBA YV X, B TLRIA YBZLJB Y, XKA PL LK. QEB JBQELA FP KXJBA XCQBO GRIFRP ZXBPXO, TEL RPBA FQ FK EFP MOFSXQB ZLOOBPMLKABKZB."
decrypted = decrypt(input)
# Paragraph from Wikipedia: https://en.wikipedia.org/wiki/Caesar_cipher
expected = "IN CRYPTOGRAPHY, A CAESAR CIPHER, ALSO KNOWN AS CAESAR'S CIPHER, THE SHIFT CIPHER, CAESAR'S CODE OR CAESAR SHIFT, IS ONE OF THE SIMPLEST AND MOST WIDELY KNOWN ENCRYPTION TECHNIQUES. IT IS A TYPE OF SUBSTITUTION CIPHER IN WHICH EACH LETTER IN THE PLAINTEXT IS REPLACED BY A LETTER SOME FIXED NUMBER OF POSITIONS DOWN THE ALPHABET. FOR EXAMPLE, WITH A LEFT SHIFT OF 3, D WOULD BE REPLACED BY A, E WOULD BECOME B, AND SO ON. THE METHOD IS NAMED AFTER JULIUS CAESAR, WHO USED IT IN HIS PRIVATE CORRESPONDENCE."
expect(decrypted).to eq(expected)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment