2010-08-30 15 views
10

Ho una richiesta esterna costosa (che richiede molto tempo) a un altro servizio Web che devo effettuare e vorrei memorizzarlo nella cache. Così ho cercato di usare questa idiom, inserendo la seguente nel controller di applicazione:Come posso memorizzare in cache un metodo con Ruby/Rails?

def get_listings 
    cache(:get_listings!) 
end 

def get_listings! 
    return Hpricot.XML(open(xml_feed)) 
end 

Quando chiamo get_listings! nel mio controller tutto è fresco, ma quando chiamo get_listings Rails lamenta che nessun blocco è stato dato. E quando guardo su quel metodo vedo che effettivamente si aspetta un blocco, e inoltre sembra che quel metodo sia solo per l'uso nelle viste? Quindi immagino che, sebbene non sia stato affermato, l'esempio è solo pseudocodice.

Quindi la mia domanda è, come posso memorizzare qualcosa di simile? Ho provato vari altri modi, ma non riuscivo a capirlo. Grazie!

risposta

9

Come suggerisce nruth, l'archivio cache integrato di Rails è probabilmente quello che si desidera.

Prova:

def get_listings 
    Rails.cache.fetch(:listings) { get_listings! } 
end 

def get_listings! 
    return Hpricot.XML(open(xml_feed)) 
end 

fetch() recupera il valore memorizzato nella cache per la chiave specificata, o scrive il risultato del blocco alla cache se non esiste.

Per impostazione predefinita, la cache di Rails utilizza l'archivio file, ma in un ambiente di produzione, memcached è l'opzione preferita.

Vedere la sezione 2 di http://guides.rubyonrails.org/caching_with_rails.html per ulteriori dettagli.

11

un approccio in-codice potrebbe essere simile a questa:

def get_listings 
    @listings ||= get_listings! 
end 

def get_listings! 
    Hpricot.XML(open(xml_feed)) 
end 

che in cache il risultato su una base per-richiesta (nuova istanza di controllo per ogni richiesta), anche se come si può guardare il ' memoize 'helper come opzione API.

Se si desidera condividere tra le richieste non salvare i dati sulla classe oggetti, come la vostra applicazione non sarà threadsafe, a meno che non sei bravo a programmazione concorrente & assicurarsi che i fili non interferiscano con i reciproci dati di accesso alla variabile condivisa.

Il "modo rotaie" per memorizzare nella cache tra le richieste è il Rails.cache store. Memcached si abitua molto, ma potresti trovare il file o la memoria adatta alle tue esigenze. Dipende davvero da come si sta distribuendo e se si desidera dare la priorità a hit della cache, tempo di risposta, memoria (RAM) o utilizzare una soluzione ospitata, ad es. un addon di heroku.

+0

Sì, voglio sicuramente memorizzarlo tra le richieste. Avevo pensato di archiviarlo nel database e farlo manualmente, ma spero che ci sia un modo più semplice per farlo. –

+1

Forse dare un'occhiata a docs/api per ActiveSupport :: Cache :: Store - potrebbe adattarsi a quello che stai cercando. Non ci ho lavorato io stesso ma sono sicuro che ci siano post di blog su di esso o altri qui che hanno utilizzato questo ad es. con memcached. – nruth

3

È possibile utilizzare il cache_method gemma:

gem install cache_method 
require 'cache_method' 

Nel codice:

def get_listings 
    Hpricot.XML(open(xml_feed)) 
end 
cache_method :get_listings 

Si potrebbe notare mi sono liberato di get_listings!.Se avete bisogno di un modo per aggiornare i dati manualmente, suggerisco:

def refresh 
    clear_method_cache :get_listings 
end 

Ecco un altro bocconcino:

def get_listings 
    Hpricot.XML(open(xml_feed)) 
end 
cache_method :get_listings, (60*60) # automatically expire cache after an hour 
+0

A proposito, dovresti guardare i documenti della gemma per vedere come configurare la gemma. Suggerimento: CacheMethod.config.storage = Rails.cache –

1

È inoltre possibile utilizzare cachethod gemma (https://github.com/reneklacan/cachethod)

gem 'cachethod' 

Poi è mortalmente semplice memorizzare nella cache il risultato del metodo

class Dog 
    cache_method :some_method, expires_in: 1.minutes 

    def some_method arg1 
    .. 
    end 
end 

Supporta anche il caching a livello di argomenti

0

Altre risposte sono eccellenti ma se si desidera un approccio a mano semplice è possibile farlo. Definire un metodo come di sotto di un nella tua classe ...

def use_cache_if_available(method_name,&hard_way) 
@cached_retvals ||= {} # or initialize in constructor 
return @cached_retvals[method_name] if @cached_retvals.has_key?(method_name) 
@cached_retvals[method_name] = hard_way.call 
end 

Successivamente, per ogni metodo che si desidera memorizzare nella cache si può mettere avvolgere il corpo del metodo in qualcosa di simile ...

def some_expensive_method(arg1, arg2, arg3) 
    use_cache_if_available(__method__) { 
    calculate_it_the_hard_way_here 
    } 
end 

Una cosa che fa meglio del metodo più semplice sopra elencato è che memorizzerà un valore in cache a zero. Ha la comodità che non richiede la creazione di metodi duplicati. Probabilmente l'approccio alla gemma è più pulito, però.

0

È stata suggerita la gemma cache_method, anche se è piuttosto pesante. Se è necessario chiamare il metodo senza argomenti, la soluzione è molto semplice:

Object.class_eval do 

    def self.cache_method(method_name) 
    original_method_name = "_original_#{method_name}" 
    alias_method original_method_name, method_name 
    define_method method_name do 
     @cache ||= {} 
     @cache[method_name] = send original_method_name unless @cache.key?(method_name) 
     @cache[method_name] 
    end 
    end 

end 

quindi è possibile utilizzarlo in qualsiasi classe:

def get_listings 
    Hpricot.XML(open(xml_feed)) 
end 
cache_method :get_listings 

Nota - questa sarà anche la cache pari a zero, che è l'unico motivo per usarlo invece di @cached_value ||=

Problemi correlati