2010-08-30 8 views
10

Tengo una solicitud externa costosa (que consume mucho tiempo) a otro servicio web que necesito hacer, y me gustaría guardarla en caché. Así que he tratado de utilizar este idiom, poniendo lo siguiente en el controlador de aplicación:¿Cómo guardo en caché un método con Ruby/Rails?

def get_listings 
    cache(:get_listings!) 
end 

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

Cuando llamo get_listings! en mi controlador de todo lo que es bueno, pero cuando llamo get_listings rieles se queja de que se le dio ningún bloque. Y cuando busco ese método veo que de hecho espera un bloqueo, y además parece que ese método es solo para usar en vistas. Así que supongo que, aunque no fue declarado, que el ejemplo es solo pseudocódigo.

Así que mi pregunta es, ¿cómo guardo en caché algo como esto? Intenté otras formas pero no pude resolverlo. ¡Gracias!

Respuesta

9

Como nruth sugiere, la memoria caché incorporada de Rails es probablemente lo que usted desea.

Probar:

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

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

fetch() recupera el valor almacenado en caché de la clave especificada, o escribe el resultado del bloque de la caché si no existe.

De forma predeterminada, la memoria caché de Rails usa el almacén de archivos, pero en un entorno de producción, Memcached es la opción preferida.

Ver la sección 2 de http://guides.rubyonrails.org/caching_with_rails.html para más detalles.

11

un enfoque en el código podría ser algo como esto:

def get_listings 
    @listings ||= get_listings! 
end 

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

que almacenar en caché el resultado en función de cada solicitud (nueva instancia del controlador por solicitud), aunque es posible que gusta mirar a la ' memoize 'helpers como una opción de API.

Si desea compartir entre peticiones no guarde los datos de la clase de objetos, ya que su aplicación no será threadsafe, a menos que usted es bueno en la programación concurrente & asegúrese de que los hilos no interfieran con el acceso de datos del otro a la variable compartida.

La "manera de los carriles" para almacenar en caché las solicitudes es Rails.cache store. Memcached se usa mucho, pero puede encontrar que el archivo o la memoria se ajusta a sus necesidades. Realmente depende de cómo se está implementando y de si desea priorizar los hits de la memoria caché, el tiempo de respuesta, el almacenamiento (RAM) o el uso de una solución alojada, p. un complemento heroku.

+0

Sí, definitivamente quiero guardarlo en caché entre las solicitudes. Consideré almacenarlo en la base de datos y hacerlo manualmente, pero espero que haya una manera más fácil de hacerlo. –

+1

Tal vez eche un vistazo a los documentos/api para ActiveSupport :: Cache :: Store - podría encajar con lo que está buscando. No he trabajado con él, pero estoy seguro de que hay publicaciones de blog en él o de otros aquí que han usado esto, p. con memcached – nruth

3

Puede utilizar el cache_method joya:

gem install cache_method 
require 'cache_method' 

En su código:

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

Usted puede notar que se deshizo de get_listings!.Si usted necesita una manera de actualizar los datos manualmente, sugiero:

def refresh 
    clear_method_cache :get_listings 
end 

Aquí hay otro dato:

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

Por cierto, deberías mirar los documentos de gemas para ver cómo configurar la gema. Sugerencia: CacheMethod.config.storage = Rails.cache –

1

También puede utilizar la gema cachethod (https://github.com/reneklacan/cachethod)

gem 'cachethod' 

Entonces es mortalmente fácil de guardar en caché el resultado del método

class Dog 
    cache_method :some_method, expires_in: 1.minutes 

    def some_method arg1 
    .. 
    end 
end 

También es compatible con el almacenamiento en caché de nivel de argumento

0

Otras respuestas son excelentes, pero si desea un enfoque simple enrollado a mano, puede hacerlo. Definir un método como el siguiente uno en su clase ...

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 

A partir de entonces, para cada método que desea almacenar en caché se puede poner envolver el cuerpo del método en algo como esto ...

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

Una cosa que hace mejor que el método más simple enumerado anteriormente es que almacenará en caché un cero. Tiene la conveniencia de que no requiere la creación de métodos duplicados. Sin embargo, probablemente el enfoque de la gema sea más limpio.

0

Se sugirió la gema cache_method, aunque es bastante pesada. Si necesita llamar al método sin argumentos, la solución es muy sencilla:

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 

entonces usted puede utilizarlo en cualquier clase:

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

Nota - Esto también nula caché, que es la única razón para usarlo en lugar de @cached_value ||=

Cuestiones relacionadas