Geoffrey H
Geoffrey H

Reputation: 1268

What is the proper way to access class variables in Ruby 1.9?

I'm trying to set some class variables to store paths in a Rails application (but I think this more a ruby question)

Basically my class looks like this

class Image < ActiveRecord::Base

   @@path_to_folder = "app/assets"
   @@images_folder = "upimages"
   @@path_to_images = File.join(@@path_to_folder, @@images_folder)

end

But when I try to access @@path_to_images from my controller by doing Image.path_to_images, I get a NoMethodError

When I try with Image.class_eval( @@path_to_images ), I get uninitialized class variable @@path_to_images in ImagesController

I've searched around and all I've seen says those would work, so I'm very confused about this

What's more, I tried defining simple classes with the ruby console like so

 class Bidule
     @@foo = "foo"
     Bar = "bar"
 end

And so I tried, I think, all the ways possible (previous 2 included) to access them but no way I always get an exception raised

Upvotes: 22

Views: 21392

Answers (7)

Sriram R
Sriram R

Reputation: 330

Best way is to set and get the value using methods. Below is a sample code

class Planet
  @@planets_count = 0

  def initialize(name)
    @name = name
    @@planets_count += 1
  end

  def self.planets_count
    @@planets_count
  end  

  def self.add_planet
    @@planets_count += 1
  end

  def add_planet_from_obj
    @@planets_count += 1
  end
end


Planet.new("uranus")
Plant.add_planet
obj = Planet.new("earth")
obj.add_planet_from_obj

Upvotes: 3

GMarx
GMarx

Reputation: 483

I would modify it like this:

class Image < ActiveRecord::Base

  @@path_to_folder = "app/assets"
  @@images_folder = "upimages"
  @@path_to_images = File.join(@@path_to_folder, @@images_folder)

  def initialize
  end 
  ...
  def self.path_to_folder
    @@path_to_folder
  end
end

What you've done here is make the class variable into a method, so you can now access is using a .method name call. Since this is a class variable though, you can only call this on the class itself, not on the instance of a class. You are getting a 'NoMethodError' because you're calling the class variable using a method which has not exist. The code above defines this method on the lines where you say:

def self.path_to_folder
  @@path_to_folder
end

This will now work:

Image.path_to_folder

Upvotes: 1

rgtk
rgtk

Reputation: 3500

If you can't or don't want to extend class use:

Image.class_variable_get(:@@path_to_images)

Upvotes: 3

Marco Prins
Marco Prins

Reputation: 7419

You can do that by wrapping it in a class method, like this:

def self.path_to_images
  @@path_to_images
end

but I should mention that you should try to avoid using class variables in rails

Upvotes: 1

Peter Alfvin
Peter Alfvin

Reputation: 29399

Although I wouldn't in general recommend it, you can access class variables by passing a string to class_eval as in:

Image.class_eval('@@path_to_folder')

at least in later versions of Ruby.

Note, however, that class variables are associated with the uppermost class in a subclassed hierarchy. In the case of ActiveRecord subclasses like this one, this means that these class variables really exist in the namespace of ActiveRecord::Base.

Upvotes: 9

Soundar Rathinasamy
Soundar Rathinasamy

Reputation: 6728

Rails provides class level attribute accessor for this functionality

Try

class Image < ActiveRecord::Base
  cattr_accessor :path_to_folder
  @@path_to_folder = "app/assets"
end

Then to access path_to_folder class variable just use

Image.path_to_folder

But people always suggest to avoid class variables due to its behavior in inheritance.So you can use constants like

class Image < ActiveRecord::Base
   PATH_TO_FOLDER = "app/assets"
end

Then you can access the constant like

Image::PATH_TO_FOLDER

Upvotes: 24

tadman
tadman

Reputation: 211580

Class variables are rarely used in Ruby applications because they have a lot of limitations and also tend to run against the grain of proper Object-Oriented design.

In nearly every case a class variable can be replaced with a proper constant, a class method, or both.

Your example is probably better described as:

class Image < ActiveRecord::Base
  PATH_TO_FOLDER = "app/assets"
  IMAGES_FOLDER = "upimages"
  PATH_TO_IMAGES = File.join(PATH_TO_FOLDER, IMAGES_FOLDER)
end

Class variables are private to the class in question and don't trickle down to sub-classes and are difficult to access from an external context. Using constants allows the use of things like:

image_path = Image::PATH_TO_FOLDER

There are some circumstances under which a class variable is more reasonable than the alternative, but these are usually very rare.

Upvotes: 2

Related Questions