Quick tip #2: Memoizable 2

Posted by wijet
on Wednesday, April 22

Gdy jakaś metoda wykonuje kosztowne obliczenia, a jej wyniku używamy wielokrotnie, rozsądnym jest obliczenia wykonać raz, a wynik zapamietać, tzw. cachowanie za pomoca zmiennych instancji. W takim wypadku, najczęsciej używamy idiomu "||="

class Bar
  def foo
    @foo ||= lambda {
      # jakies kosztowne obliczenia
      ...
    }.call
  end
end

Od railsów 2.2 możemy korzystać z metody :memoize, należy dodać metody z modułu ActiveSupport::Memoizable jako metody klasy.

class Bar
  extend ActiveSupport::Memoizable

  def foo
    # jakies kosztowne obliczenia
    ...
  end

  memoize :foo
end

Spowoduje to że za pierwszym wywołaniem metody :foo, metoda ta faktycznie zostanie wywołana, a wartość przez nią zwrócona zapamiętana (dziala to tak jak cachowanie w zmiennej instancji, czyli tylko w obrebie danego obiektu).

Każde następne wywołanie metody :foo, na tym konkretnym obiekcie, spowoduje zwrócenie zapamiętanej wartośći.

Jeśli chcemy wymusić wywołanie metody (tzn. bez cachu), jako ostatni parametr należy podać wartość true.

Linki

Module ActiveSupport::Memoizable

Memo-what? - A Guide to Memoization

Railscasts: Memoization

What's New in Edge Rails: Easy Memoization

Quick tip #1: tap i returning 3

Posted by wijet
on Saturday, April 11

Tym postem chcę rozpocząć serię którkich wpisów, o konkretnych metodach/konstrukcjach dostępnych w railsach, lub w samym Ruby, Za pomocą których możemy zrobić coś krócej, ładniej, bardziej ruby way.

Czasami pojawia się potrzeba wykonania na obiekcie pewnych operacji, a następnie zwrócenie go.

def foo(name)
  product = Product.find_or_initialize_by_name(name)
  # tu cos mieszamy z produktem
  ...
  # zapisujemy
  product.save
  product
end

jak widać na końcu musi być 'product' aby metoda zwracała product, inaczej :foo zwrociła by nam prawde lub fałsz, czyli wynik product.save

Możemy to bardzo elegancko, zastapić, poniższa konstrukcją

returning

def foo(name)
  returning Product.find_or_initialize_by_name(name) do |product|
    # tu cos mieszamy z produktem
    ...
    # zapisujemy
    product.save
  end
end

:returning przyjmuje obiekt, przekazuje go do bloku który otrzymuje, a następnie zwraca ten obiekt

tap

Za pomocą :tap możemy zrobić to samo, w troche inny sposob:

def foo(name)
  Product.find_or_initialize_by_name(name).tap do |product|
    # tu cos mieszamy z produktem
    ...
    # zapisujemy
    product.save
  end
end

Metoda :tap przyjmuje blok do którego przekazuje self (obiekt na którym została wywołana), a następnie po wykonaniu bloku zwraca self.

Obydwie metody sa dostepne w railsach. Warto wiedzieć że metoda :tap została włączona do Ruby w wersji 1.9.

Za pomocą metody :tap mozemy nawet wiecej poczarować, polecam przejrzenie poniższych linków:

Linki

Eavesdropping on Expressions

Tapping method chains with Ruby 1.9

Rails ActiveSupport returning Method

Mining ActiveSupport: Object#returning

Named scopes w ActiveRecord 3

Posted by wijet
on Thursday, April 02

Od railsów 2.1 możemy korzystać z dobrodziejstw named_scopes, za pomocą których możemy definiować w łatwy sposób findery. Mowiąc prościej, możemy budować zapytania do bazy danych. Nie są to jednak zwykłe metody ktore wyszukują nam coś w bazie, zamiast prostego zbioru wyników, zwracany jest scope (zakres).

Aby rzucić więcej swiatła na sprawę, zacznijmy od przykładu:

class Product < ActiveRecord::Base
  named_scope :available, :conditions => "amount > 0"
  named_scope :featured, :conditions => {:featured => true}
end