Separation of concerns

Mam projekt w ktorym uzywamy dosc mocno logowania i statsd. Coraz czesciej napotykam metody w rodzaju:

def run(label, opts)
  logger.info "Some log message"

  $statsd.time("myclass.run.#{label}") do
    result = make_request(opts)
  end

  result
end

To jest dosc uproszczony przyklad, ale jak widac metoda bylaby o wiele prostsza gdyby nie dodatkowe logowanie. Jak sobie radzicie z czyms takim?

Krok pierwszy, stworzysz ogólny module StatsdDecorator, w którym tworzysz metodę klasy statsd_time, która przyjmuje jako argument inną metodę, po czym używa define_method do stworzenia nowej metody o tej samej nazwie, która robi to samo co ta oryginalna, tyle że wcześniej loguje to co chcesz i zbiera i przesyła statystyki do statsd.

Krok drugi, w klasie właściwej robimy include StatsdDecorator, który pozwala nam definiować metody w taki sposób:

[code]statsd_timer def run(label, opts)

end
[/code]

Efekt jest taki, że piszemy kod jak bez statsd, a jak chcemy wysyłać dane do statsd, to puszczamy metodę przez nasz dekorator. Czyli mamy DRY i czytelny kod. Dekorator testujemy osobno.

Oczywiście, nie działa w starych wersjach Ruby, bo def nie zwraca zdefiniowanej metody.

1 Like

Brzmi ciekawie, mozesz troche rozwinac? Co robi statsd_timer?

Zedytowane powyżej. Postarałem się jaśniej opisać ten proces. Jak będzie coś nadal niejasne, to może jutro znajdę czas, żeby wrzucić przykładowy kod.

Tak myslalam ze o to chodzilo. Dzieki, wyprobuje.

Btw. to o czym mówi @sharnik działa w wersji Rubiego >= 2.1.0, bardzo dobra zmiana, można tworzyć fajne konstrukcje jak wyżej wymieniona, tutaj więcej info:
http://franck.verrot.fr/blog/2013/08/21/method-definitions-in-ruby-2-1-0-will-not-be-void-anymore/

Można też dodać, że bardziej uniwersalne względem wersji rubiego będzie:

def run(label, opts)
    # ...
end

statsd_timer :run

To ja bym zrobil to tak:

statsd_timer { run(label, opts) }

To byś metody z tego nie miał.