Tag: deploy

Przygotowanie kodu aplikacji w Ruby on Rails do deployu – część III

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:

Przygotowanie kodu aplikacji w Ruby on Rails do deployu – część II

Witam w części drugiej poradnika poświęconego przygotowaniu kodu do umieszczenia na serwerze. W części tej zajmiemy się skonstruowaniem modelu który będzie generował nam losowe kawałki kodu oraz ew. losowe ciągi na potrzeby zaśmiecania.

Pierwszą rzeczą jaką sobie ustalimy, będzie to ile potrzebujemy takich komentarzy. Ja założyłem sobie że różnych potrzebujemy około 10^4, tak więc ustawiłem stałą na takową wartość:

    # Ilość komentarzy
    COMMENT_NR = 10**4

Opis klasy którą stworzymy, zacznę niejako od końca. Jednak jest to dość ważne. Nie każdy plik jaki mamy w systemie ma w sobie włączoną obsługę utf-8. Z tego względu przenosząc nasze "losowe" kawałki kodu, musimy m.in. pozbyć się polskich znaków. Wykonać to można bardzo prosto, korzystając z poniższego kodu:

    private

    # Usuwa polskie znaki z komentarzy zeby sie nie czepiał ruby ze nie utf
    def self.to_comment(temp)
      temp.gsub!(/[âäàãáäå�?ăąǎǟǡǻ�?ȃȧẵặ]/,'a')
      temp.gsub!(/[ëêéèẽēĕėẻȅȇẹȩęḙḛ�?ếễểḕḗệ�?]/,'e')
      temp.gsub!(/[�?iìíîĩīĭïỉ�?ịįȉȋḭɨḯ]/,'i')
      temp.gsub!(/[òóôõ�?�?ȯö�?őǒ�?�?ơǫ�?ɵøồốỗổȱȫȭ�?�?ṑṓ�?ớỡởợǭộǿ]/,'o')
      temp.gsub!(/[ùúûũūŭüủůűǔȕȗưụṳųṷṵṹṻǖǜǘǖǚừứữửự]/,'u')
      temp.gsub!(/[ỳýŷỹȳ�?ÿỷẙƴỵ]/,'y')
      temp.gsub!(/[ñǹń]/,'n')
      temp.gsub!(/[çć]/,'c')
      temp.gsub!(/[ß]/,'ss')
      temp.gsub!(/[œ]/,'oe')
      temp.gsub!(/[ij]/,'ij')
      temp.gsub!(/[�?ł]/,'l')
      temp.gsub!(/[ś]/,'s')
      temp.gsub!(/[źż]/,'z')
      temp.gsub!(/\#\{/, '\\#\\{')
      temp
    end

Kolejną (już nie prywatną) metodą, jest metoda generująca losową ilość spacji. Ot tak żeby kod wyglądał na jeszcze bardziej "rozwalony":

    # Zwraca losową ilość spacji
    def self.spaces
      value = ''
      (100...rand(256)).collect { |n| value  << '  ' }.join()
      value
    end

Przyda się też generator losowych stringów:

    # Zwraca losowego stringa
    def self.string
      value = ''
      (50...(rand(256)+51)).collect{ |n|
        chr =  (65 + rand(55)).chr
        value  << chr if chr != '\\'
      }.join
      value
    end

Oraz najważniejsze, czyli generator "kawałków" kodu. Skąd wziąć takie kawałki? Z innych plików z naszego systemu. W ten sposób powstanie "zupa" która na pierwszy rzut oka wygląda strasznie (a taki właśnie efekt chcemy osiągnąć).

Przygotowanie takich śmieciowych kawałków jest bardzo proste. Czytamy katalog app i wybieramy z niego losowe linijki które są kodem (odrzucamy komentarze).

      # Narazie nie ma komentarzy żadnych
      @trash = []

      # Najpierw do zmiennej tymczasowej załadujmy kawałki kodu aplikacji
      app_parts = []
      Find.find(path){ |f|
        # Jeśli to nie plik .rb (czyli z kodem) to idz dalej

        next unless f[f.size-3, f.size-1] == '.rb'
        file = File.open(f, "r")
        file.each_line {|l|
          a = l.strip
          next if a[0] == "\#" || a.size < 3
          app_parts << self.class.to_comment(l.strip)
        }

        break if app_parts.size >= COMMENT_NR
      }

Mając już losowe kawałki kodu, "przemieszajmy" je w proporcjach 3:1 z jakimiś losowo wygenerowanymi stringami:

      COMMENT_NR.times { |nr|
        if nr % 4 == 0
          @trash << self.class.string
        else
          @trash << app_parts[rand(app_parts.size-1)]
        end
      }

Ostatnią rzeczą jakiej potrzebujemy, jest metoda do zwracania losowej linijki:

    # Zwraca linie z jakimś losowym czymś
    def line(line = nil)
      @trash[line] if line
      @trash[rand(@trash.size-1)] unless line
    end

Z całości korzysta się bardzo prosto:

# Tworzymy randomera do generowania smieciowych kawałków tekstu
rand = Randomer.new
# Generujemy losową ilość spacji
rand.class.spaces
# Generujemy sobie jakąś losową linijkę
rand.line

Cały model wygląda tak:

  # Najpierw stworzymy sobie "śmietnisko" komentarzowe
  # Czyli załadujemy dużo różnych kawałków kodu ;)
  class Randomer
    # Ilość komentarzy
    COMMENT_NR = 10**4

    # Załadujemy sobie tutaj do tablicy - losowe komentarze, na zmianę
    # generowane dynamicznie (ciągi znaków) jak i kawałki kodu z innych plików
    def initialize(path = './app/')
      # Narazie nie ma komentarzy żadnych
      @trash = []

      # Najpierw do zmiennej tymczasowej załadujmy kawałki kodu aplikacji
      app_parts = []
      Find.find(path){ |f|
        # Jeśli to nie plik .rb (czyli z kodem) to idz dalej

        next unless f[f.size-3, f.size-1] == '.rb'
        file = File.open(f, "r")
        file.each_line {|l|
          a = l.strip
          next if a[0] == "\#" || a.size < 3
          app_parts << self.class.to_comment(l.strip)
        }

        break if app_parts.size >= COMMENT_NR
      }

      COMMENT_NR.times { |nr|
        if nr % 4 == 0
          @trash << self.class.string
        else
          @trash << app_parts[rand(app_parts.size-1)]
        end
      }
    end

    # Zwraca linie z jakimś losowym czymś
    def line(line = nil)
      @trash[line] if line
      @trash[rand(@trash.size-1)] unless line
    end

    # Zwraca losowego stringa
    def self.string
      value = ''
      (50...(rand(256)+51)).collect{ |n|
        chr =  (65 + rand(55)).chr
        value  << chr if chr != '\\'
      }.join
      value
    end

    # Zwraca losową ilość spacji
    def self.spaces
      value = ''
      (100...rand(256)).collect { |n| value  << '  ' }.join()
      value
    end

    private

    # Usuwa polskie znaki z komentarzy zeby sie nie czepiał ruby ze nie utf
    def self.to_comment(temp)
      temp.gsub!(/[âäàãáäå�?ăąǎǟǡǻ�?ȃȧẵặ]/,'a')
      temp.gsub!(/[ëêéèẽēĕėẻȅȇẹȩęḙḛ�?ếễểḕḗệ�?]/,'e')
      temp.gsub!(/[�?iìíîĩīĭïỉ�?ịįȉȋḭɨḯ]/,'i')
      temp.gsub!(/[òóôõ�?�?ȯö�?őǒ�?�?ơǫ�?ɵøồốỗổȱȫȭ�?�?ṑṓ�?ớỡởợǭộǿ]/,'o')
      temp.gsub!(/[ùúûũūŭüủůűǔȕȗưụṳųṷṵṹṻǖǜǘǖǚừứữửự]/,'u')
      temp.gsub!(/[ỳýŷỹȳ�?ÿỷẙƴỵ]/,'y')
      temp.gsub!(/[ñǹń]/,'n')
      temp.gsub!(/[çć]/,'c')
      temp.gsub!(/[ß]/,'ss')
      temp.gsub!(/[œ]/,'oe')
      temp.gsub!(/[ij]/,'ij')
      temp.gsub!(/[�?ł]/,'l')
      temp.gsub!(/[ś]/,'s')
      temp.gsub!(/[źż]/,'z')
      temp.gsub!(/\#\{/, '\\#\\{')
      temp
    end
  end

W tej części zrobiliśmy "śmieciarza" który będzie nam brudził w kodzie. W następnej (ostatniej już) części sprawimy że to wszystko zacznie działać :)

Tutaj możesz zobaczyć pozostałe części tutoriala:

Copyright © 2024 Closer to Code

Theme by Anders NorenUp ↑