2010-08-30 10 views
10

Ich habe eine teure (zeitaufwendige) externe Anfrage an einen anderen Webservice, den ich machen muss, und ich möchte ihn zwischenspeichern. Also habe ich versucht, diese idiom zu verwenden, indem Sie die folgende in der Applikationssteuerung setzen:Wie cache ich eine Methode mit Ruby/Rails?

def get_listings 
    cache(:get_listings!) 
end 

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

Als ich get_listings! in meinem Controller alles nennen, ist cool, aber wenn ich rufe get_listings Rails beklagt, dass kein Block gegeben wurde. Und wenn ich diese Methode nachschaue, sehe ich, dass sie tatsächlich einen Block erwartet, und außerdem sieht es so aus, als ob diese Methode nur für Ansichten verwendet wird. Ich vermute also, dass, obwohl nicht gesagt wurde, das Beispiel nur Pseudocode ist.

Also meine Frage ist, wie cache ich so etwas? Ich habe verschiedene andere Möglichkeiten ausprobiert, konnte es aber nicht herausfinden. Vielen Dank!

Antwort

9

Wie von nruth vorgeschlagen, ist der integrierte Cache-Speicher von Rails wahrscheinlich das, was Sie wollen.

Versuchen:

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

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

holen() ruft die im Cache gespeicherten Wert für den Schlüssel angegeben, oder schreibt das Ergebnis des Blocks in den Cache, wenn es nicht existiert.

Standardmäßig verwendet der Rails-Cache den Dateispeicher, in einer Produktionsumgebung ist Memcached jedoch die bevorzugte Option.

Weitere Informationen finden Sie in Abschnitt 2 von http://guides.rubyonrails.org/caching_with_rails.html.

11

ein in-Code-Ansatz wie folgt aussehen könnte:

def get_listings 
    @listings ||= get_listings! 
end 

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

, die auf einer Pro-Anfrage und (neue Controller-Instanz pro Anfrage) das Ergebnis zwischengespeichert werden, wenn Sie "aussehen kann wie bei der memoize 'Helfer als eine API-Option.

Wenn Sie THREAD wird nicht über Anfragen nicht speichern Daten über die Klasse Objekte, wie Ihre App zu teilen, es sei denn, Sie bei Concurrent Programming gut sind & die Fäden stellen Sie sicher nicht stören mit dem Datenzugriff der anderen auf die gemeinsame Variable.

Der "Rails-Weg" zum cachen über Anfragen hinweg ist Rails.cache store. Memcached wird häufig verwendet, aber Sie können die Datei oder die Speicher finden, die Ihren Bedürfnissen entsprechen. Es hängt wirklich davon ab, wie Sie bereitstellen und ob Sie Cache-Treffer, Antwortzeit, Speicher (RAM) priorisieren oder eine gehostete Lösung z. ein Heroku-Addon.

+0

Ja, ich möchte es definitiv zwischen Anfragen zwischenspeichern. Ich hatte darüber nachgedacht, es in der Datenbank zu speichern und alles manuell zu machen, aber ich hoffe, dass es einen einfacheren Weg gibt. –

+1

Vielleicht werfen Sie einen Blick auf die docs/api für ActiveSupport :: Cache :: Store - könnte passen, was Sie suchen. Ich habe nicht selbst damit gearbeitet, bin aber sicher, dass es Blogposts oder andere hier gibt, die dieses z. mit Memcached. – nruth

3

können Sie die cache_method gem verwenden:

gem install cache_method 
require 'cache_method' 

In Ihrem Code:

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

Sie können feststellen, ich get_listings! losgeworden.Wenn Sie eine Möglichkeit benötigen die Daten manuell zu aktualisieren, schlage ich vor:

def refresh 
    clear_method_cache :get_listings 
end 

Hier ist ein weiterer Leckerbissen:

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

Übrigens sollten Sie sich die Edelstein-Dokumente anschauen, um zu sehen, wie Sie den Edelstein konfigurieren. Hinweis: CacheMethod.config.storage = Rails.cache –

1

Sie auch cachethod gem (https://github.com/reneklacan/cachethod)

gem 'cachethod' 

Dann kann es verwenden ist tödlich einfach zu Cache-Methode Ergebnis

class Dog 
    cache_method :some_method, expires_in: 1.minutes 

    def some_method arg1 
    .. 
    end 
end 

Es unterstützt auch Argument-Level-Caching

0

Andere Antworten sind hervorragend, aber wenn Sie eine einfache Hand-Roll-Ansatz möchten, können Sie dies tun. Definieren Sie eine Methode, wie die unten in Ihrer Klasse ...

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 

Danach für jede Methode, die Sie cachen möchten, können Sie die Methode Körper in etwas so sagen wickeln ...

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

Eine Sache, die das besser als die einfachste oben aufgelistete Methode macht, ist, dass sie eine Null speichert. Es hat den Vorteil, dass keine doppelten Methoden erstellt werden müssen. Wahrscheinlich ist der Edelsteinansatz sauberer.

0

Es wurde vorgeschlagen, cache_method Edelstein, obwohl es ziemlich schwer ist. Wenn Sie die Methode ohne Argumente aufrufen müssen, Lösung ist sehr einfach:

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 

dann können Sie es in jeder Klasse verwenden:

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

Hinweis - dies wird auch Null-Cache, der der einzige Grund, um es zu verwenden @cached_value ||=