Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to memoize a class method in Rails?

I have a model with a class method which is heavy in computation that I call many times per request.

Ideally i'd like to cache the result for the duration of a single request.

What's rails best practice in this situation?

Example:

class MyClass < ActiveRecord::Base
  def self.heavy_method; ... ; end
end

Then in a helper

def helper
  MyClass.heavy_method
end

This helper is used in many views

like image 444
montrealmike Avatar asked Apr 21 '13 14:04

montrealmike


People also ask

What is memoization in Rails?

Put simply, memoization is saving a method's return value so it does not have to be recomputed each time. As with all caching, you are effectively trading memory for time (i.e. you give up the memory required to store the value, but you save the time required to process the method).

What does Memoize mean Ruby?

Memoization is a technique you can use to speed up your accessor methods. It caches the results of methods that do time-consuming work, work that only needs to be done once. In Rails, you see memoization used so often that it even included a module that would memoize methods for you.

What is a Memoized function?

In programming, memoization is an optimization technique that makes applications more efficient and hence faster. It does this by storing computation results in cache, and retrieving that same information from the cache the next time it's needed instead of computing it again.

How do you create a class variable in Ruby?

Ruby Class VariablesClass variables begin with @@ and must be initialized before they can be used in method definitions. Referencing an uninitialized class variable produces an error. Class variables are shared among descendants of the class or module in which the class variables are defined.


1 Answers

Here is a very generic solution that might work for you.

class Klass
  def self.memoized_expensive_method
    @memoized_expensive_method_result ||= expensive_method
  end

  def self.expensive_method
    # ...
  end
end

Then if you want to ensure your code is re-executed on every request, you could use a filter in your controller.

class Klass
  def self.reset_expensive_method_cache!
    @memoized_expensive_method_result = nil
  end
end

class ApplicationController
  before_filter :reset_klass_expensive_method_cache

  def reset_klass_expensive_method_cache
    Klass.reset_expensive_method_cache!
  end
end

Note that storing stuff in class variable might lead to thread-safety issue because the cached result will be shared between threads.

If this can be an issue for your application you might want to store the data in the thread instead of using a class variable.

like image 63
Charles Barbier Avatar answered Sep 22 '22 16:09

Charles Barbier