Class variables in Ruby

Hello all,

I am trying to cache lookup table values from a database table into a
hash that is stored in a class variable. This will allow me to read and
store the id/descr value pairs from the database one time only after
which I store them in the hash and retrieve them from there.

The problem is the class variable appears to be cleared out each time a
new request comes in to my controller. I am using rails in the
development enrionment but I am hoping this is a Ruby issue. I thought
maybe rails was reloading the class each time a request comes in
because I read this is standard behavior in the development
environment. However, I believe the reloadable? method is supposed to
care of that?

Here is the code from my model class. The code is alwasy going to super
to read from the database. If anyone can steer me straight on this I
would appreciate it!

Thanks in advance.
Paul

class GrammarTenseCode < ActiveRecord::Base
has_many :words
@@cachedDescr = {}

def self.reloadable?
   false
end

def descr
  if  @@cachedDescr[id.to_s] == nil
      @@cachedDescr[id.to_s] = super
  else
      @@cachedDescr[id.to_s]
  end
end

end

On Sat, 19 Aug 2006, Paul wrote:

@@cachedDescr = {}

If you think reloading is happening, try:

@@cacheDescr = {} unless @@cacheDescr

Kirk H.

@@cacheDescr = {} unless @@cacheDescr

Or, to be more idiomatic:

@@cache_descr ||= {}

On Sat, 19 Aug 2006, Daniel W. wrote:

@@cacheDescr = {} unless @@cacheDescr

Or, to be more idiomatic:

@@cache_descr ||= {}

Which I usually prefer, but the variant that uses unless reads more like
a
sentence if a person isn’t sure what’s happening there.

I wish I could use a simple structure like that for constants.

Foo = 7 unless const_defined? :Foo

is wordier than I like.

Kirk H.

On Sat, 19 Aug 2006, Paul wrote:

Kirk H.

Hi Kirk,

I tried @@cacheDescr = {} unless @@cacheDescr but I get the same results. I
would expect the initialization of @@cacheDescr to take place when the .rb
package is loaded. I will keep playing with it and post a solution when I
figure it out.

but the file may be getting loaded each time… depending on mode. fyi.

-a

[email protected] wrote:

On Sat, 19 Aug 2006, Paul wrote:

@@cachedDescr = {}

If you think reloading is happening, try:

@@cacheDescr = {} unless @@cacheDescr

Kirk H.

Hi Kirk,

I tried @@cacheDescr = {} unless @@cacheDescr but I get the same
results. I would expect the initialization of @@cacheDescr to take
place when the .rb package is loaded. I will keep playing with it and
post a solution when I figure it out.

Thanks,
Paul

On Aug 18, 2006, at 9:05 PM, Paul wrote:

To keep rails from reloading the class every time you
can change the …\config\environments\development.rd file.

You really, really don’t want to do this. Trust me. :wink:

You’ve already noted that this makes development mode suck. If you
go this way, you will need to toggle the setting every time you need
it to work or go without Rails’s best development feature. Ouch.

That should be reason enough, but it gets worse!

Depending on how you deploy your application, it’s quite likely you
could have two or more separate interpreters running your production
code. These will not share class variables. This is the way of
pain and could be a source of all kinds of nasty issues.

If you need to remember a value in Rails it needs to be in the
database or the session.

James Edward G. II

SOLUTION: Rails, in the development environment, was reloading the
class for every request coming into the application and therefore
causing the Class variable to be reset. Something I read led me to
believe I could add the following method to my class to cause it not to
be reloaded

def self.reloadable?
    false
end

That was false. To keep rails from reloading the class every time you
can change the …\config\environments\development.rd file. It has a
“config.cache_classes = false” line that you can change to
which will prevent the classes from being reloaded. You are REQUIRED to
stop the WEBrick server though to pick up this change as well as
whenever to want to pick up any recent coding changes. BUT, you can
change this parameter temporarily to test out Class variables that are
expected to store data throughout your session. Don’t forget to change
it back to to be back in the default mode which is the
desirable mode for development.

James,

Thanks for the comments. I am coming from a Smalltalk background and we
would typically cache code/description lookup table rows into a class
variable for performance. That is, when we wanted to look up a
description we would check the cache first and then hit the database
only if it had not been retrieved yet.

In this scenario, it’s not a problem if the cache is maintained
individually over separate instances of the interpreter. This way every
user benefits from the cache rather than storing it in a session unless
I misunderstand the life cycle of class variables. I would think they
would live until the class is reloaded.

However, I do see your point and agree and that under most application
situations this would not be the way to go.

Thanks,
Paul

On Sat, 19 Aug 2006, James Edward G. II wrote:

If you need to remember a value in Rails it needs to be in the database or
the session.

or you need to configure your app so that only one instance will run at
once -
assuming fcgi execution. this is actually an easy solution for some
simple
apps. however, james is quite correct.

-a

On Aug 19, 2006, at 12:00 PM, Paul wrote:

Thanks for the comments. I am coming from a Smalltalk background
and we
would typically cache code/description lookup table rows into a class
variable for performance.

Just a side note. It isn’t necessary to use class variables to
accomplish your goal. A plain old instance variable of the class object
is just fine. I’m not a big fan of Ruby class variables. I think they
are often used simply because they are confused with instance variables
of a class object.

Gary W.

Hi Gary,

I’m not quite sure what you mean by using “an instance variable of the
class object”. I am familiar with class variables and instance
variables. In my case I wanted to cache the data in a class variable
because any instance could reference the single class variable. Is
there yet another type of variable (other than temporary method scope
variables) that one could define?

Thanks,
Paul

Hi James,

I do appreciate your comments and I was not aware of the caching
capabilities built into rails. I will take a look into the link you
provided to see how I can utilize this built in support.

Thanks again,
Paul

On 8/19/06, Paul [email protected] wrote:

Hi James,

I do appreciate your comments and I was not aware of the caching
capabilities built into rails. I will take a look into the link you
provided to see how I can utilize this built in support.

Thanks again,
Paul

This is getting more into rails-talk, but just fyi - the built in
caching support doesn’t really help with things like look up tables.
All the rails caching is based on the view level - actions, pages, and
fragmets. If you want to cache models, which is ideal for things like
look up tables, you could look into something like cached_model, which
uses memcached for its cache. Assuming its really a concern, you’ve
profiled, etc…

more info here:
http://nubyonrails.com/articles/2006/08/17/memcached-basics-for-rails

  • Rob

On Aug 19, 2006, at 11:00 AM, Paul wrote:

I am coming from a Smalltalk background and we
would typically cache code/description lookup table rows into a class
variable for performance. That is, when we wanted to look up a
description we would check the cache first and then hit the database
only if it had not been retrieved yet.

This smells like premature optimization to me. You’ve timed it and
it’s too slow or you have users complaining about the speed? If not,
I worry you might be complicating this before you need to.

You are right that in this case, it shouldn’t hurt to use a class
variable. It is a waste of memory though and you may not get great
cache hit rates.

The right way to handle this is with Rails’s cache support:

http://api.rubyonrails.com/classes/ActionController/Caching.html

This should be faster and more natural to implement, since you can
develop normally and then apply caching as needed. It will also work
across interpreters.

I’m not trying to tell you how to run your site. I hope it doesn’t
sound like I am. I’m just trying to help you avoid choices I’m
pretty sure you will regret down the road.

Best of luck with your site.

James Edward G. II

On 8/19/06, Paul [email protected] wrote:

Paul
This comes from Ruby’s object model (and this may sound familiar to
you as a smalltalker): in Ruby, everything is an object, so any class
is an object as well. A class is an instance of class Class. Therefore
any class can have its instance variables (because it is an object) in
addition to class variables (class’ special behaviour).

Class variables (@@variable) are shared among all sublasses of the
class.

Class instance variables are those, that are defined in class methods
i.e.

class A
def self.m()
@a = 1 # ← HERE!
end
end

or:

class A
class << self
def m()
@a = 1 # ← HERE!
end
end
end

These are not shared among subclasses.

HTH,
J.

Jan and David:

Yes, this makes perfect sense now that you mention it. I had not seen
anything written up on it yet. Thanks for the examples.

Paul

Hi –

On Sun, 20 Aug 2006, Jan S. wrote:

Thanks,
Class instance variables are those, that are defined in class methods i.e.
class << self
def m()
@a = 1 # <- HERE!
end
end
end

These are not shared among subclasses.

Just one additional comment: you’ll also see instance variables of
class objects outside of any method definition, like this:

class C
@var = 1
end

etc. Every instance variable belongs to ‘self’ – so whenever ‘self’
is a class, you’re seeing instance variables that belong to that
class.

David

On 8/19/06, Paul [email protected] wrote:

Hi Gary,

I’m not quite sure what you mean by using “an instance variable of the
class object”. I am familiar with class variables and instance
variables. In my case I wanted to cache the data in a class variable
because any instance could reference the single class variable. Is
there yet another type of variable (other than temporary method scope
variables) that one could define?

Smalltalk has pretty much the same thing, there’s a difference between
class variables which are shared by the class and it’s subclasses, and
are traditionally implemented by binding an association between the
name and value into compiled methods, and class instance variables
which are instance variables of the class object, and are implemented
as instance variable slots in the class objects, so every subclass has
it’s own value. It’s been awhile, and there are Smalltalk dialect
differences but these get declared in messages to the classes subclass
something like:

Object subclass: #Foo ...
            instanceVariables: 'a b'
            classVariables: 'C1 C2'
            classInstanceVariables: 'ci1 ci2'

We also worked on a completely declarative model for class definition
in Smalltalk in X3J20, but I don’t know if anyone picked it up.

In Ruby these are defined as they are encountered in execution and
marked with sigils, @ for instance and class variables, and @@ for
class variables, the difference between instance and class instance
variables being WHERE they are encountered.

In Smaltalk, class variables are in the scope of both class and
methods of the class which declares them, and its subclasses.
Instance variables are visible in methods of the class and it’s
subclasses, and class instance variables to class methods of the class
and it’s subclasses. This the same as in Ruby.

Getting back to the original question about caching. It seems to me
that one of the differences between Ruby and Smalltalk in their
‘traditional’ settings which affects the use of patterns such as using
class variables as a cache is that the Smalltalk execution environment
is usually a long-lived object memory, sometimes even with
‘reincarnation’ if the image is saved, while the Ruby environment
usually gets set up, runs a while and then gets destroyed. In a
server environment like rails, the lifecycle and even the number of
instances of the Ruby environment is configured during deployment, so
it’s best if application code doesn’t depend on a particular
lifecycle, or that application instances can communicate via classes
because they might not share the same class objects.

Smalltalkers who worked in transactional environments like MVS
Smalltalk running under CICS probably are more attuned to such
considerations.

Hi Rob,

Yes, MemCache looks like what I am after. Right now I’m doing
development on Windows XP and MemCache looks like it only runs on *nix.
When I have the environment to run it on I will certainly check this
out.

Thanks,
Paul