Claudi
Claudi

Reputation: 5416

Ruby on Rails: caching data in an object

I've come up with an issue I can't figure out how to solve. I'm new to both Ruby and Rails, and sure there is a simple way to achieve what I'm looking for.

This is the ERB of the show view, showing two equal lines:

<p><%= @user.foo %></p>
<p><%= @user.foo %></p>

Imagine that foo is an intense computational method so I want to cache the result of the first call in order to use it in the second line without having to call foo again. The simplest option would be defining a variable and cache it:

<% foo_cache = @user.foo %>
<p><%= foo_cache %></p>
<p><%= foo_cache %></p>

But I don't want to clutter the "global" scope. A nicer way would be that foo itself could save a cache of the value it returns when it's called the first time:

def foo
    return self.cached_foo if self.cached_foo  #WARNING: pseudocode here!
    #Not cached. Do stuff
    ...
    self.cached_foo = computed_value
    computed_value
end

My question is if it's possible to attach data to an object instance dynamically without interfering with the model behind (i.e. without making save and company functions deal with this attached data). Or maybe is there another better way to achieve what I'm looking for?

Thanks.

Upvotes: 0

Views: 878

Answers (3)

Agush
Agush

Reputation: 5116

What you are looking for is called memoization

def foo
  @foo ||= calculate_foo
end

def calculate_foo
   # heavy stuff
end

This works thanks to conditional assignment (the||=)

It's an extensive topic so I'll leave you a couple of links about it:

http://rails-bestpractices.com/posts/59-use-memoization

http://gavinmiller.io/2013/basics-of-ruby-memoization/

Plus advanced memoization in case you need to do more complicated stuff such as parameters, storing nil values

http://gavinmiller.io/2013/advanced-memoization-in-ruby/

In fact Active Support had memoizable but it was deprecated and then extracted into a gem

In case you want to use it check it out on: https://github.com/matthewrudy/memoist

Upvotes: 4

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230561

This is called memoization and it's a common idiom in ruby. It is usually expressed like this:

def foo
  @cached_foo ||= begin
    # do your heavy stuff here
  end
end

@cached_foo should not interfere with ActiveRecord (like make it try to save cached_foo to the database).

Upvotes: 5

Sebastian vom Meer
Sebastian vom Meer

Reputation: 5251

This should do it. And don't be afraid, the instance variable has no impact on the persistence layer.

def foo
  @foo ||= compute_foo
end

private

def compute_foo
  # ...
end

Upvotes: 3

Related Questions