Tag: Ruby

Rails3 + link_to + update z pominięciem edit

Ostatnio przeglądając kod Redmine'a natknąłem się na fajny kawałek kodu. Nigdy się nad tym nie zastanawiałem i w zasadzie, nigdy zbytnio nie potrzebowałem.

Jak za pomocą linka, zaktualizować wybrany atrybut instancji modelu?

Zazwyczaj do aktualizacji danej instancji korzystam z edit'a oraz forma odpowiedniego do tego. Dzisiaj jednak nadarzyła się  ku temu okazja.

Mam w systemie model o nazwie Cron który odpowiada za cykliczne odpalanie komend/metod które są w nim yieldowane. Np:

    Cron.backup{
      # Wykonaj nową kopię zapasową
      Backup.create
      # Usuń stare kopie zapasowe
      Backup.sweep(Setting::Cron.backup_lifetime)
    }

Powyższy "cron" o nazwie backup odpala się co 7 dni wykonując kopię zapasową systemu. Usuwa przy okazji kopie starsze niż backup_lifetime. Z ciekawostek warto wspomnieć, że dzięki "fajności" Rubiego, mogłem napisać sobie w bardzo prosty sposób własne mapowanie, nie po kolumnach a po wierszach. Właśnie dzięki temu metody modelu Cron są generowane dynamicznie. Ale to jest temat na oddzielną notkę.

Wracając do sedna sprawy. Model Cron korzysta z pola updated_at aby określić kiedy Cron był ostatnio odpalany. Jeśli było to dawniej niż zadana w instancji częstotliwość - uruchamia kod yieldowany.

Problem pojawił się kiedy chciałem dorobić "wymuszone" odpalenie instancji Crona. Najprostszą metodą do zrobienia tego, było wyedytowanie updated_at i nadanie mu wartości np. 1.year.ago. Rozwiązać można to było tworząc metodę w stylu force. Nie byłoby to jednak zgodne z RESTem. Bardzo fajnie rozwiązano to w Redminie. Mianowicie tworzony jest link_to który zawiera wszelkie potrzebne informacje. Jak taki link wygenerować? Bardzo prosto:

link_to "Wymuś", cron_path(task, 'cron[updated_at]' => 1.year.ago), :method => :put

Metoda link_to stworzy nam link który zostanie zrealizowany przez metodę update na instancji modelu Cron. Dzięki dodaniu parametru:

'cron[updated_at]' => 1.year.ago

informujemy Railsy, że ma naszemu cronowi przypisać datę ostatniego wykonania na rok wstecz. Dzięki temu, przy najbliższej możliwej okazji, kod z Crona zostanie zrealizowany.

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 © 2026 Closer to Code

Theme by Anders NorenUp ↑