简体   繁体   中英

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

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

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 .

如果您不能或不想扩展类使用:

Image.class_variable_get(:@@path_to_images)

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.

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM