views:

47

answers:

3

For example, the following code:

class FoosController < ApplicationController
  def index
    if [email protected]?
      render :locals => {:bar => @foo}
      return
    else 
      @foo = rand 10
      render :locals => {:bar => @foo}
    end
  end
end

if I load localhost:3000/foos multiple times, it will show different values, and it is no surprise if it is development mode, because Rails reload the controller (and model and view) every time a browser request comes in.

But even when in production mode, when everything is loaded and stay there, @foo's value won't stay across browser requests? Every time reloading the page on the web browser, a different number is shown. So Rails will wipe all values clean each time? Is there a way to cache or "memoize" the results across requests if we don't use a DBMS?


Surprisingly, I just tried using a class variable, and in development mode, it gives a different number each time. In production mode, the number stays the same on Firefox, and then Chrome will also show that number all the time, until the server is restarted:

class FoosController < ApplicationController
  @@foo = nil

  def index
    if !@@foo.nil?
      render :locals => {:bar => @@foo}
      return
    else 
      @@foo = rand 10
      render :locals => {:bar => @@foo}
    end
  end
end

Why can a class variable memoize but an instance variable can't? Is using a class variable a reliable way to "remember things" across request reliably in Rails 2.x, 3.x and Ruby 1.8.7 and 1.9.2?

+1  A: 

@@foo designates a class variable, @foo an instance variable. A new instance of a controller is created on every request. And for caching, I really recommend memcache or something similar (due to the forking nature of production rails servers).

Tass
+5  A: 

This is a deliberate and desired behavior, every request instantiates a new controller instance, so that a request doesn't have any trouble dealing with leftovers from the previous requests.

You may want to use the session or the flash hashes to store @foo across requests.

class FoosController < ApplicationController
  def index
    @foo = session[:foo] ||= rand(10)
    render :locals => {:bar => @foo}
  end
end

session[:foo] will be persistent all along the way, but you can use flash[:foo] if you just want to persist it across one request.

edgerunner
Note that session is stored in cookies by default, which are stored on user's computer (don't put sensitive data there), and have restricted file size (something like 2 or 4 kB). Also, each user has a unique session, so this would cache for a given user, but not all users.
Joshua Cheek
A: 

Even if you meemoize the way edgerunner suggests, in production you may have multiple mongrel or thin instances running and each of those controllers will have different flash variables and current approach wont work. Go ahead with what Tass says something like memcache or redis to persist such stuff in memory.

Kunday
Kunday, by default, `session` and `flash` are persisted in cookies, so multiple instances will not be a problem. The other options for session storage are Memcached and database, which also avoid the multiple instance problem. In fact the only approach mentioned here that would fail in multiple instances is the class variable approach in the original question.
edgerunner
Thanks for correcting me Edgerunner. But storing in session[:foo] constrains to a single browser right? I assume storing in Memcache or database is gonna be across multiple browsers and if the algorithm is complex(calculating per unique browser session is still expensie right?), i think we should still go with some memcached or database right? Correct me if i'm wrong.
Kunday
I'd guess if you wanted to persist something across requests, most of the time, it is a single user's consecutive requests. The cookie store appropriate for such a scenario. If you want something cumulative though, you should probably persist in the database. Memcached is fast, persistent and good, but even it isn't truly persistent. And memcached is expensive if you want to persist lots of data. In any case, the actual choice has multiple parameters to be analyzed. It is hard to give a definitive answer to that.
edgerunner
Agreed. I have never come across a scenario which had the equivalent test case. Sooner i have some work which necessitates such stuff. Hope that working on that gives some insight. Between memcached is a local server right, by telling its expensive are u mentioning the heroku's memcached cost?
Kunday