簡體   English   中英

如何使用Ruby / Rails緩存方法?

[英]How do I cache a method with Ruby/Rails?

我有一個昂貴的(耗時的)外部請求到我需要做的另一個Web服務,我想緩存它。 所以我試圖通過將以下內容放在應用程序控制器中來使用這個習慣用法

def get_listings
  cache(:get_listings!)
end

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

當我打電話給get_listings! 在我的控制器中,一切都很酷,但是當我調用get_listings Rails會抱怨沒有給出阻止。 當我查找該方法時,我發現它確實期望一個塊,另外看起來該方法僅用於視圖? 所以我猜測雖然沒有說明,但這個例子只是偽代碼。

所以我的問題是,如何緩存這樣的東西? 我嘗試了其他各種方法,但無法弄明白。 謝謝!

代碼內方法看起來像這樣:

def get_listings
  @listings ||= get_listings!
end

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

它將基於每個請求緩存結果(每個請求的新控制器實例),盡管您可能希望將“memoize”幫助器視為api選項。

如果你想在請求之間共享請不要在類對象上保存數據 ,因為你的應用程序不是線程安全的 ,除非你擅長並發編程並確保線程不會干擾彼此對共享的數據訪問變量。

跨請求緩存的“rails方式”是Rails.cache存儲 Memcached經常使用,但您可能會發現文件或內存存儲符合您的需求。 這實際上取決於您的部署方式以及是否要優先考慮緩存命中,響應時間,存儲(RAM)或使用托管解決方案,例如heroku插件。

正如nruth建議的那樣,Rails的內置緩存存儲可能就是你想要的。

嘗試:

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

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

fetch()檢索指定鍵的緩存值,或者將塊的結果寫入緩存(如果它不存在)。

默認情況下,Rails緩存使用文件存儲,但在生產環境中,memcached是首選選項。

有關詳細信息,請參閱http://guides.rubyonrails.org/caching_with_rails.html的第2部分。

您可以使用cache_method gem:

gem install cache_method
require 'cache_method'

在你的代碼中:

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

你可能會注意到我擺脫了get_listings! 如果您需要一種手動刷新數據的方法,我建議:

def refresh
  clear_method_cache :get_listings
end

這是另一個小竅門:

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

你也可以使用cachethod gem( https://github.com/reneklacan/cachethod

gem 'cachethod'

然后緩存方法的結果非常簡單

class Dog
  cache_method :some_method, expires_in: 1.minutes

  def some_method arg1
    ..
  end
end

它還支持參數級緩存

有人建議使用cache_method gem,盡管它非常重。 如果需要調用不帶參數的方法,解決方案非常簡單:

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

然后你可以在任何類中使用它:

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

注意 - 這也會緩存nil,這是使用它而不是@cached_value ||=的唯一原因

其他答案非常好,但如果你想要一個簡單的手動方法,你可以做到這一點。 在您的班級中定義類似下面的方法......

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

此后,對於要緩存的每個方法,您可以將方法體包裝在這樣的內容中......

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

這比上面列出的最簡單的方法更好的一點是它將緩存為零。 它具有不需要創建重復方法的便利性。 不過,寶石方法可能比較干凈。

遲到了,但萬一有人到這里搜索。

我用這個小模塊從項目到項目,我覺得它很方便和可擴展,沒有添加額外的寶石。 它使用Rails.cache后端,所以請在只有你的后端使用它。

# lib/active_record/cache_method.rb
module ActiveRecord
  module CacheMethod
    extend ActiveSupport::Concern

    module ClassMethods
      # To be used with a block
      def cache_method(args = {})
        @caller = caller
        caller_method_name = args.fetch(:method_name)     { @caller[0][/`.*'/][1..-2] }
        expires_in         = args.fetch(:expires_in)      { 24.hours }
        cache_key          = args.fetch(:cache_key)       { "#{self.name.underscore}/methods/#{caller_method_name}" }

        Rails.cache.fetch(cache_key, expires_in: expires_in) do
          yield
        end
      end
    end

    # To be used with a block
    def cache_method(args = {})
      @caller = caller
      caller_method_name = args.fetch(:method_name) { @caller[0][/`.*'/][1..-2] }
      expires_in         = args.fetch(:expires_in)  { 24.hours }
      cache_key          = args.fetch(:cache_key)   { "#{self.class.name.underscore}-#{id}-#{updated_at.to_i}/methods/#{caller_method_name}" }

      Rails.cache.fetch(cache_key, expires_in: expires_in) do
        yield
      end
    end
  end
end

然后在初始化器中:

# config/initializers/active_record.rb
require 'active_record/cache_method'
ActiveRecord::Base.send :include, ActiveRecord::CacheMethod

然后在一個模型中:

# app/models/user.rb
class User < AR 
  def self.my_slow_class_method
    cache_method do 
      # some slow things here
    end
  end

  def this_is_also_slow(var)
    custom_key_depending_on_var = ...
    cache_method(key_name: custom_key_depending_on_var, expires_in: 10.seconds) do 
      # other slow things depending on var
    end
  end
end

此時它只適用於模型,但可以很容易地推廣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM