Witam w ostatniej części tego miniporadnika nt. przygotowania kodu dla mniej fajnych klientów ;)
W tej części zajmiemy się wyczyszczeniem plików rb z oryginalnych komentarzy oraz "zamieszaniem" w tychże plikach. Uruchomimy także całą resztę kodu, tak aby wszystko ładnie funkcjonowało.
A więc do dzieła!
Zacznijmy od metody która usunie nam wszystkie linijki komentarzy, ze wszystkich możliwych plików.
Metoda jest całkiem prosta. Otwieramy plik rb i usuwamy wszystkie linijki zaczynające się na "#", za wyjątkiem magicznego # coding: utf-8. Najpierw otwieramy plik do odczytu, kopiujemy linijka po linijce. Następnie otwieramy plik do zapisu (co automatycznie go wyczyści) a następnie zapisujemy tylko te linijki które nie są komentarzami. Przy okazji uważamy na #{ które nie jest komentarzem ale mogło zostać za takowy uznane.
Usuwamy także puste linijki i pozbywamy się wcięć.
# Usuwa komentarze (ale nie maskuje - tylko komentarze i białe znaki) z kodu
def self.clean(path = './deploy/')
Find.find(path) { |f|
# Jeśli to nie plik rb to idz do nastepnego pliku
next unless f[f.size-3, f.size-1] == '.rb'
filename = f
# Wrzuć info na konsole co sie dzieje
puts "Cleaning #{filename}"
lines = []
File.open(filename, "r").each_line {|l| lines << l }
File.open(filename, 'w') {|f|
lines.each { |l|
a = l.strip
# Jeśli to komentarz to przeskocz dalej
next if (a[0] == "\#" && a != '# coding: utf-8' && a[0,2] != "\#\{") || a.size == 0
f.write(a+"\n")
}
}
}
end
Po przepuszczeniu naszego "deploy" przez ten kod, otrzymamy pliki które nie mają ani komentarzy ani też wcięć.
Pozostało nam jeszcze losowo "nabałaganić" w plikach i spiąć deploy.rb w jedną sensowną całość. Może najpierw pobałaganimy ;)
Po pierwsze, maskowanie powinno dotyczyć tylko i wyłącznie plików .rb, stąd poniższa linijka, która powoduje że kod nie zostanie wykonany dla plików o innych rozszerzeniach:
next unless f[f.size-3, f.size-1] == '.rb'
Dalej jest kawałek kodu, analogiczny do tego z metody czyszczącej komentarze. Ładujemy zawartość pliku do pamięci, po czym czyścimy go i otwieramy do zapisu. Bałaganimy generując losowo od 0 do 64 linijek z losowymi ciągami lub losowymi fragmentami kodu oraz z losową ilością spacji na początku każdej linijki.
Jedyna rzecz na jaką musimy uwazać to polecenia które składają się z wielu linijek, a w których nie można umieszczać komentarzy (np. wielolinijkowe stringi). Rozwiązałem to stosunkowo prosto. Zliczam po prostu liczbę otwarć cudzysłowów i jeśli nie jest parzysta, tzn że kod jest wielolinijkowy. Do czasu aż znajdę następną linijkę z ilością nieparzystą (co oznaczać będzie domknięcie), nie wstawiam śmieci. Śmieci też wstawiam na początku i końcu każdego pliku.
def self.mask(path)
rand = Randomer.new
Find.find(path) { |f|
# Jeśli to nie plik rb to idz do nastepnego pliku
next unless f[f.size-3, f.size-1] == '.rb'
filename = f
# Wrzuć info na konsole co sie dzieje
puts "Masking: #{filename}"
lines = []
File.open(filename, "r").each_line {|l| lines << l }
File.open(filename, 'w') {|f|
# Czy wyrazenie jest wieloliniowe (nie wsadza w takie komentów)
multiline = false
i = 0
lines.each { |l|
a = l.strip
# Jeśli to komentarz to przeskocz dalej
next if (a[0] == "\#" && a != '# coding: utf-8' && a[0,2] != "\#\{") || a.size == 0
if a == '# coding: utf-8'
f.write(a+"\n")
rand(64).times { f.write(rand.class.spaces+'# '+rand.line+"\n") }
else
rand(64).times { f.write(rand.class.spaces+'# '+rand.line+"\n") } if i == 0
line = (multiline ? '' : rand.class.spaces) + a + "\n"
multiline = !multiline if line.count('"') % 2 == 1
multiline = !multiline if line.count("'") % 2 == 1
f.write(line)
end
(rand(64).times { f.write(rand.class.spaces+'# '+rand.line+"\n") }) unless multiline
i+=1
}
rand(64).times { f.write(rand.class.spaces+'# '+rand.line+"\n") }
}
}
end
Całość opakowujemy w klasę:
# Maskuje pliki rb z podanego katalogu
class Masker
def self.mask(path)
rand = Randomer.new
Find.find(path) { |f|
# Jeśli to nie plik rb to idz do nastepnego pliku
next unless f[f.size-3, f.size-1] == '.rb'
filename = f
# Wrzuć info na konsole co sie dzieje
puts "Masking: #{filename}"
lines = []
File.open(filename, "r").each_line {|l| lines << l }
File.open(filename, 'w') {|f|
# Czy wyrazenie jest wieloliniowe (nie wsadza w takie komentów)
multiline = false
i = 0
lines.each { |l|
a = l.strip
# Jeśli to komentarz to przeskocz dalej
next if (a[0] == "\#" && a != '# coding: utf-8' && a[0,2] != "\#\{") || a.size == 0
if a == '# coding: utf-8'
f.write(a+"\n")
rand(64).times { f.write(rand.class.spaces+'# '+rand.line+"\n") }
else
rand(64).times { f.write(rand.class.spaces+'# '+rand.line+"\n") } if i == 0
line = (multiline ? '' : rand.class.spaces) + a + "\n"
multiline = !multiline if line.count('"') % 2 == 1
multiline = !multiline if line.count("'") % 2 == 1
f.write(line)
end
(rand(64).times { f.write(rand.class.spaces+'# '+rand.line+"\n") }) unless multiline
i+=1
}
rand(64).times { f.write(rand.class.spaces+'# '+rand.line+"\n") }
}
}
end
# Usuwa komentarze (ale nie maskuje - tylko komentarze i białe znaki) z kodu
def self.clean(path = './deploy/')
Find.find(path) { |f|
# Jeśli to nie plik rb to idz do nastepnego pliku
next unless f[f.size-3, f.size-1] == '.rb'
filename = f
# Wrzuć info na konsole co sie dzieje
puts "Cleaning #{filename}"
lines = []
File.open(filename, "r").each_line {|l| lines << l }
File.open(filename, 'w') {|f|
lines.each { |l|
a = l.strip
# Jeśli to komentarz to przeskocz dalej
next if (a[0] == "\#" && a != '# coding: utf-8' && a[0,2] != "\#\{") || a.size == 0
f.write(a+"\n")
}
}
}
end
end
Po czym odpalamy całość kodu napisanego przez 3 części tutoriala:
# Zrób kopię zapasową do której robimy deploy
Backuper.run
# Usuń co niepotrzebne w deployu
Cleaner.run
# Wyczysc komentarze z calosci plikow
Masker.clean
# I zamaskuj reszte kodu
MASK_DIRS.each {|d| Masker.mask(d)}
Po tych operacjach otrzymujemy kod który:
- Nie zawiera elementów niepotrzebnych klientowi
- Nie zawiera plików tymczasowych
- Jest "niemiły" dla użyszkodników
To by było na tyle. Używajcie tego ilekroć macie klientów których niekoniecznie darzycie zaufaniem.
Sprawdź także pozostałe dwie części tutoriala: