2010-08-30 12 views
10

Tôi có một yêu cầu bên ngoài tốn kém (tốn thời gian) cho một dịch vụ web khác mà tôi cần thực hiện và tôi muốn lưu nó vào bộ nhớ cache. Vì vậy, tôi cố gắng sử dụng idiom này, bằng cách đặt những điều sau đây trong bộ điều khiển ứng dụng:Làm cách nào để cache một phương thức với Ruby/Rails?

def get_listings 
    cache(:get_listings!) 
end 

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

Khi tôi gọi get_listings! trong điều khiển tất cả mọi thứ của tôi là mát mẻ, nhưng khi tôi gọi get_listings Rails phàn nàn rằng không có khối đã được đưa ra. Và khi tôi nhìn lên phương pháp đó tôi thấy rằng nó thực sự mong đợi một khối, và ngoài ra nó trông giống như phương pháp đó là chỉ để sử dụng trong quan điểm? Vì vậy, tôi đoán rằng mặc dù nó không được tuyên bố, rằng ví dụ chỉ là giả mã.

Vì vậy, câu hỏi của tôi là, làm thế nào để lưu vào bộ nhớ cache một cái gì đó như thế này? Tôi đã thử nhiều cách khác nhau nhưng không thể hiểu được. Cảm ơn!

Trả lời

9

Như nruth gợi ý, cửa hàng bộ nhớ cache tích hợp của Rails có lẽ là những gì bạn muốn.

Hãy thử:

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

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

lấy() lấy giá trị được lưu trữ cho phím chỉ định, hoặc viết kết quả của khối vào bộ nhớ cache nếu nó không tồn tại.

Theo mặc định, bộ đệm Rails sử dụng kho lưu trữ tệp, nhưng trong môi trường sản xuất, memcached là tùy chọn ưa thích.

Xem phần 2 của http://guides.rubyonrails.org/caching_with_rails.html để biết thêm chi tiết.

11

một cách tiếp cận trong mã có thể giống như thế này:

def get_listings 
    @listings ||= get_listings! 
end 

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

mà sẽ cache kết quả trên một cơ sở cho mỗi yêu cầu (ví dụ điều khiển mới theo yêu cầu), mặc dù bạn có thể muốn nhìn vào ' ghi nhớ 'người trợ giúp làm tùy chọn api.

Nếu bạn muốn chia sẻ qua yêu cầu không lưu dữ liệu trên lớp đối tượng, như ứng dụng của bạn sẽ không threadsafe, trừ khi bạn giỏi lập trình đồng thời & hãy chắc chắn các chủ đề không ảnh hưởng với quyền truy cập dữ liệu của nhau vào biến được chia sẻ.

Cách "đường ray" vào bộ nhớ cache trên các yêu cầu là Rails.cache store. Memcached được sử dụng rất nhiều, nhưng bạn có thể tìm thấy các tập tin hoặc các cửa hàng bộ nhớ phù hợp với nhu cầu của bạn. Nó thực sự phụ thuộc vào cách bạn triển khai và bạn có muốn ưu tiên số lần truy cập bộ nhớ cache, thời gian phản hồi, bộ nhớ (RAM) hay sử dụng giải pháp lưu trữ, ví dụ: một addon heroku.

+0

Vâng, tôi chắc chắn muốn lưu vào bộ nhớ cache giữa các yêu cầu. Tôi đã xem xét việc lưu trữ nó trong cơ sở dữ liệu và làm tất cả bằng tay, nhưng tôi hy vọng có một cách dễ dàng hơn để làm điều đó. –

+1

Có thể hãy xem tài liệu/api cho ActiveSupport :: Cache :: Store - có thể phù hợp với những gì bạn đang theo dõi. Tôi đã không làm việc với nó bản thân mình nhưng chắc chắn có bài đăng trên blog trên đó hoặc những người khác ở đây đã sử dụng ví dụ này với memcached. – nruth

3

Bạn có thể sử dụng cache_method đá quý:

gem install cache_method 
require 'cache_method' 

Trong mã của bạn:

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

Bạn có thể nhận thấy tôi đã thoát khỏi get_listings!.Nếu bạn cần một cách để làm mới dữ liệu bằng tay, tôi đề nghị:

def refresh 
    clear_method_cache :get_listings 
end 

Đây là một miếng ngon:

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

Nhân tiện, bạn nên xem tài liệu đá quý để xem cách cấu hình đá quý. Gợi ý: CacheMethod.config.storage = Rails.cache –

1

Bạn cũng có thể sử dụng cachethod đá quý (https://github.com/reneklacan/cachethod)

gem 'cachethod' 

Sau đó, nó là kết quả của phương pháp cache đơn giản chết người

class Dog 
    cache_method :some_method, expires_in: 1.minutes 

    def some_method arg1 
    .. 
    end 
end 

Nó cũng hỗ trợ bộ nhớ đệm mức đối số

0

Các câu trả lời khác là tuyệt vời nhưng nếu bạn muốn tiếp cận bằng tay, bạn có thể thực hiện việc này. Xác định một phương pháp như bên dưới một trong lớp học của bạn ...

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 

Sau đó, đối với mỗi phương pháp bạn muốn để cache bạn có thể đặt quấn cơ thể phương pháp trong một cái gì đó như thế này ...

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

Một điều mà điều này làm tốt hơn so với phương pháp đơn giản nhất được liệt kê ở trên là nó sẽ cache một nil. Nó có sự tiện lợi mà nó không yêu cầu tạo các phương thức trùng lặp. Có lẽ cách tiếp cận đá quý là sạch hơn, mặc dù.

0

Đã đề xuất cache_method đá quý, mặc dù nó khá nặng. Nếu bạn cần gọi phương thức không có đối số, giải pháp rất đơn giản:

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 

sau đó bạn có thể sử dụng nó trong bất kỳ lớp:

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

Lưu ý - điều này cũng sẽ nhớ cache bằng không, đó là lý do duy nhất để sử dụng nó thay vì @cached_value ||=

Các vấn đề liên quan