Ruby class variables and access from view templates


#1

I have an app that requires me to define a class variable (actually an
array) in a controller and then access it from within a view. It seems
that the class variables aren’t working right. Here is an example of
what I’m trying to do.

class DisplayController < ApplicationController

@@thumbnail_array = Array.new

def some_method
@@thumbnail_array = SomeModel.find_all

end


end

Then in the view for some_method,

<% for thumb in @@thumbnail_array %>

do something interesting

But my app blows up at that point giving me an unitialized class
variable - @@thumbnail_array.

What am I doing wrong?


#2

Ross Ashley wrote:

class DisplayController < ApplicationController

@@thumbnail_array = Array.new

def some_method
@@thumbnail_array = SomeModel.find_all

end


end

Then in the view for some_method,

<% for thumb in @@thumbnail_array %>

do something interesting

But my app blows up at that point giving me an unitialized class
variable - @@thumbnail_array.

Incidently, this works when I use an instance variable,
@thumbnail_array, But I have to modify the array from another method in
the controller which is why I am trying to get the class variable to
work.

Suggestions?


#3

@thumbnail_array = @@thumbnail_array

or…better yet…

def thumbails
@@thumbnail_array
end

<% for thumb in thumbnails %>


– Tom M.


#4

Tom M. wrote:

@thumbnail_array = @@thumbnail_array

That allows me to access the instance variable from the view template.
Thanks.

def thumbails
@@thumbnail_array
end

<% for thumb in thumbnails %>


– Tom M.

I no longer need this info but I’m not sure how to map the call to
thumbnails back to the controller class. That is, how do you fix the
undefined local variable or method error in the controller.

But, this doesn’t fix my real problem.

The reason I have go through this is that I have to access that class
variable from a different method within the same class.

So…

class DisplayController < ApplicationController

@@thumbnail_array = Array.new

def some_method
@@thumbnail_array = SomeModel.find_all
end

def another_method
@variable = @@thumbnail_array.size
end

end

Now any call to DisplayController.another_method will cause an error. It
doesn’t matter where I put the @@thumbnail_array definition, either in
the initialize method or at the top of the class.

This leads me to believe that class variables don’t work in Rails. That
makes no sense.

Ross


#5

Kevin O. wrote:

What is probably happening here is that your first method is setting up
the array properly. At the end of the request, the entire controller
object goes out of scope and your class array with it. When the
controller gets set up for the next request, it creates and
re-initializes the class variable.

You could set up a before_filter to populate an instance variable.

That makes sense. I suppose a class variable in Ruby is not really the
same thing as a static variable in C++. It explains why I had to resort
to global variables in another section.

Thanks.


#6

What is probably happening here is that your first method is setting up
the array properly. At the end of the request, the entire controller
object goes out of scope and your class array with it. When the
controller gets set up for the next request, it creates and
re-initializes the class variable.

You could set up a before_filter to populate an instance variable.

_Kevin


#7

I asked about this once on the IRC channel and the answer was basically,
“use a database for this” … I suspect it’s a reasonable thing to
suggest.
I think it really has to do with the lifecycle of classes, when they are
created, destroyed, etc.

Tony


#8

Ross Ashley wrote:

That makes sense. I suppose a class variable in Ruby is not really the
same thing as a static variable in C++. It explains why I had to resort
to global variables in another section.

Hmm, why not? I thought “@@” variables are available to all instances.
How is that not
like “static”?

And you make a “static” method by defining it as MyClass.methodname or
self.methodname…

b


#9

Ross Ashley wrote:

instantiated in Rails, any @@ variables get reinitialized.

So how is that not just a @ variable?

IOW, has anyone gotten @@ variables in a controller class to work?

–Ross

I’ve been wondering about this too. Here’s what I just tried:

I put this in my environment.rb:

@@foo = ["Start foo: " + Time.now.to_s]

I put this in a controller:

logger.info("foo output: " + @@foo.to_s)
@@foo << "foo again: " + Time.now.to_s

When I hit that action the “foo output: …” shows up in my log and on
each request I get
another addition to my array with the time on it.

However, if I move the initialization of @@foo into the controller, I
get just “Start
foo…” on every request. So, yes, rails somehow renders @@ variable
useless. Guess that’s
why I never see them in code examples.

You can, however, put the global in environment.rb and it’ll retain it’s
state… at least
in webrick. Not sure how FastCGI handles that.

b


#10

Tony C. wrote:

I asked about this once on the IRC channel and the answer was basically,
“use a database for this” … I suspect it’s a reasonable thing to
suggest.
I think it really has to do with the lifecycle of classes, when they are
created, destroyed, etc.

Tony

In C++ when you instantiate a class, if it has a static variable, that
variable is available to any instance of that class, regardless of
whether all instances of it go out of scope. That is, when a new object
is created, even if all previous instantiations have gone out of scope,
it has access to the static variable and to it’s previously defined
value. I thought that was what @@ meant. But, apparently, if all
instances of a class are out of scope, when a new object is
instantiated, the class variable loses it’s previously defined value.

I wish this weren’t the case, but when a new controller class is
instantiated in Rails, any @@ variables get reinitialized.

So how is that not just a @ variable?

IOW, has anyone gotten @@ variables in a controller class to work?

–Ross


#11

I would be interested to see if there are differences in class variable
handling between development and production mode. In development mode a
lot of the code gets reloaded every request, but in production mode this
doesn’t happen so much.

As a general rule it is probably best to avoid using class variables to
store stuff. If you scale your app to multiple web servers, the next
request might not go back to the same box and would not have access to
anything you put in the class variable.

You should probably use one of the following to get the same effect…

  1. store it in the database
  2. use the session hash
  3. use a before_filter
  4. write it to a temp file

An enterprising person could probably write a plugin that would emulate
the typical class variable behavior by using one the previously
mentioned methods to store the contents.

_Kevin