Page 134 of 165

Jak to RCov z Rails3 i Rubym 1.9.2 przestał działać (i jak temu zaradzić)

Po przesiadce na Rails 3.0.0 i nowego Rubiego 1.9.2, okazało się że RCov nie działa (tzn. działa ale wyniki jakie przedstawia są "lekko" niewłaściwe). Zresztą, zapuszczając RCova dostajemy taką oto wiadomość:

** WARNING: Ruby 1.9 Support is experimental at best. Don't expect correct results! **

Dodatkowo rcov mimo podawania konkretnych plików, sam zaczął testować także gemy i masę innego kodu.

Jak temu zaradzić?
Niezgodność z Rubym 1.9.2 naprawić można stosunkowo prosto. Trzeba po prostu... wrócić do Rubiego 1.8.7 :)

Dzięki skorzystaniu z RVM (Ruby Versiom Manager), jest to bardzo proste. Instalujemy RVM, zmieniamy wersję Rubiego i już.

Pozostaje jednak jeszcze jeden dziwny problem. Z jakiegoś powodu pliki nie chcą ładować się z automatu tylko trzeba każdy z nich wywołać ręcznie za pomocą polecenia:

ruby sciezka_do_pliku/plik.rb sciezka_do_plku2/plik2.rb

Ten problem rozwiązałem, pisząc sobie prosty skrypt który "zbiera" mi pliki, które mam przetestować. Następnie odpala RCova poleceniem system, podając jako parametry wszystkie pliki na których ma RCov działać. Opiszę ten kod w miarę dokładnie, a następnie pokażę sposób użycia.

Pierwszą rzeczą jaką trzeba zrobić, jest zadeklarowanie jakich bibliotek nam potrzeba, oraz zdefiniowanie tych katalogów, w których mamy nie szukać plików *.rb:

# coding: utf-8
require 'find'
require 'fileutils'

EXCLUDED = ['.svn', 'fixtures', 'additional_data', 'coverage', 'integration', 'performance']

jak więc widać, nie będziemy przeprowadzać testów integracyjnych oraz wydajnościowych. Pominiemy także repo svna i inne niepotrzebne katalogi.

Aby sprawdzić czy ścieżka którą chcemy odpytać jest ścieżką niedozwoloną, tworzymy taką metodę:

# Czy dana ścieżka nie należy do sciezek z testami
def excluded?(path)
  EXCLUDED.each{ |ex|
    return true if path.include?(ex)
  }
  return false
end

Następnie musimy stworzyć metodę, która będzie nam zwracała ilość plików *.rb w katalogu w którym jesteśmy (aby nie podawać ścieżek bez plików do RCova):

def file_nr(path)
  all = Dir.open(path).collect
  file_nr = 0
  all.each { |f|
    file_nr += 1 if !File.directory?("#{path}/#{f}") && File.exists?("#{path}/#{f}")
  }
  file_nr
end

Przyjęliśmy tutaj założenie że w katalogach z których zbieramy, nie ma plików innych niż *.rb.

Kolejna metodą której będziemy potrzebować, jest metoda przygotowująca nam z tablicy ścieżek, jednego stringa o wzorze:

katalog/*.rb katalo2/*.rb ... katalonN/*.rb

Metoda ta zbiera ścieżki i dodaje do nich *.rb:

def prepare_params(paths)
  paths.collect! { |d| d + '/*.rb'}
  params = ''
  paths.each{ |d| params += " #{d}"}
  params
end

Teraz nie pozostaje nam nic innego, jak tylko "przelecieć" katalog test/, powyciągać to co trzeba i odpalić Rcova:

helpers = []
functional = []
unit = []
# Pobierzmy listę katalogów w /test
Find.find(path = './test/'){ |f|
  if File.directory?(f) && !excluded?(f) && file_nr(f) > 0
    # Rozdzielamy testy helperow, unitow i functionals
    if f.include?('helpers')
      helpers << f
      next
    end

    if f.include?('functional')
      functional << f
      next
    end

    if f.include?('unit')
      unit << f
      next
    end

  end

}
# #{prepare_params(helpers)}
system "rcov #{prepare_params(unit)} #{prepare_params(functional)} -o ./test/coverage/ --exclude gems --exclude boot.rb"

Kod ten wrzucamy do katalogu script, a uruchamiamy wpisując:

# Zakladamy ze jestesmy w katalogu głownym aplikacji (Rails.root)
ruby script/rcov.rb

Warto pamiętać że RCov działający na 1.8.7, ale testujący kod pisany pod 1.9.2, niekoniecznie zrealizuje poprawnie wszystkie testy. Część testów (u mnie około 1%) nie zadziała. Winą za to można obarczyć chociażby to że 1.8.7 nie ma metody cover?, z której ja dość często korzystam. Nie stanowi to jednak problemu jeśli z RCova korzystamy poglądowo a do testów właściwych wykorzystujemy rake test.

Plik z gotowym kodem, do pobrania tutaj: rcov_runner

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.

Copyright © 2024 Closer to Code

Theme by Anders NorenUp ↑