Using @ does have advantages as well though. Using () to pass variables
internally within the class leads to less readable code I think. For one
you need to pass variables down the chain even if you are not using them
at each point down the chain. That makes for very ugly code versus using
@ to leap frog methods.
I do see the benefit you mention about needing to call first before you
call last. In the following case
def first
name = “sam”
last(name)
end
def last(first)
name = first + " " + “jam”
end
I could call last from some other class or method. But in some cases I
see no advantage here. Sometimes when I make classes they are for very
specific tasks, a bunch of methods that make up a larger system. The
steps for that system always need to be called in order. I suppose if I
wanted to reuse the steps in other places it would be best to use ().
I also noticed Robert mentioning that he would avoid using the name
variable as it is only used once. This brings me to a second question.
Sometimes I find that using variables in this way can improve code
readability as well. Perhaps I am writing a Feistel cipher and it makes
sense to name parts @l and @r during the encryption rounds. The
resulting cipher or plaintext is then @l + @r. I then need to remove the
padding from the resulting plaintext, so even though I pass @l and @r
only once to the remove padding method, it makes sense to say
ciphertext = @l + @r
remove_padding(ciphertext)
or is this needlessly cluttering my code with variables? I see a lot of
times in my code I use an = sign like this, to make it read more
clearly, but sometimes I also find myself doing it needlessly. How do
you feel about this?
I think that my use of instance variables for @l and @r helped me make
the following code look nicer and read better.
require 'openssl'
require 'narray'
class Lioness
def initialize
@block_size_in_kb = 100
@hash_algorithm = OpenSSL::Digest::SHA256.new
end
def xor_strings(string1, string2)
string1 = NArray.to_na(string1, "byte")
string2 = NArray.to_na(string2, "byte")
(string1 ^ string2).to_s
end
def generate_subkeys(key)
@subkeys = Hash.new
4.times do |i|
@subkeys.store("key#{i}", @hash_algorithm.digest("#{i} #{key}"))
@hash_algorithm.reset
end
end
def pad_message(message)
if message.bytesize == (@block_size_in_kb * 1000) then
@padded_plaintext = message
else
bytes_to_pad = (@block_size_in_kb * 1000) - message.bytesize
@padded_plaintext = message + 128.chr + ("\0" * (bytes_to_pad - 1))
end
end
def remove_padding(message)
if message.index("#{128.chr}") then
padding_starts = message.index("#{128.chr}")
@plaintext = message.byteslice(0...padding_starts)
else
@plaintext = message
end
end
def encrypt(plaintext, key)
if plaintext.bytesize > (@block_size_in_kb * 1000) then
puts "Message size must be below #{@block_size_in_kb} kilobytes"
end
if plaintext.bytesize <= (@block_size_in_kb * 1000) then
pad_message(plaintext)
generate_subkeys(key)
@l, @r = @padded_plaintext.byteslice(0..(@sha256.digest_length -
1)), @padded_plaintext.byteslice(@sha256.digest_length..-1)
r_operation(@subkeys["key0"])
l_operation(@subkeys["key1"])
r_operation(@subkeys["key2"])
l_operation(@subkeys["key3"])
@ciphertext = @l + @r
end
end
def decrypt(plaintext, key)
generate_subkeys(key)
@l, @r = plaintext.byteslice(0..(@hash_algorithm.digest_length -
1)), plaintext.byteslice(@hash_algorithm.digest_length..-1)
l_operation(@subkeys["key3"])
r_operation(@subkeys["key2"])
l_operation(@subkeys["key1"])
r_operation(@subkeys["key0"])
padded_plaintext = @l + @r
remove_padding(padded_plaintext)
end
def r_operation(subkey)
cipher = OpenSSL::Cipher::Cipher.new 'AES-256-CTR'
cipher.encrypt
cipher.key = xor_strings(@l, subkey)
ciphertext = cipher.update(@r)
ciphertext << cipher.final
@r = ciphertext
end
def l_operation(subkey)
@l = xor_strings(@l, @hash_algorithm.digest(subkey + @r + subkey))
@hash_algorithm.reset
end
end