简体   繁体   English

在Ruby 1.9中访问类变量的正确方法是什么?

[英]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) 我正在尝试设置一些类变量来存储Rails应用程序中的路径(但我认为这更像是一个红宝石问题)

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 但是当我尝试通过执行Image.path_to_images从我的控制器访问@@path_to_images ,我得到一个NoMethodError

When I try with Image.class_eval( @@path_to_images ) , I get uninitialized class variable @@path_to_images in ImagesController 当我尝试使用Image.class_eval( @@path_to_images ) ,我uninitialized class variable @@path_to_images in ImagesController获得了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 更重要的是,我尝试使用ruby控制台来定义简单的类

 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 因此,我认为,我尝试了所有可能的方式(包括前2个)来访问它们,但我总是得到一个例外

Rails provides class level attribute accessor for this functionality Rails为此功能提供类级别属性访问器

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 然后访问path_to_folder类变量就可以了

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: 虽然我一般不会推荐它,但您可以通过将字符串传递给class_eval来访问类变量,如下所示:

Image.class_eval('@@path_to_folder')

at least in later versions of Ruby. 至少在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 . 对于像这样的ActiveRecord子类,这意味着这些类变量确实存在于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. 类变量在Ruby应用程序中很少使用,因为它们有很多限制,并且往往会违反适当的面向对象设计。

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 但我应该提一下,你应该尽量避免在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. 你在这里做的是将类变量变成一个方法,所以你现在可以访问使用.method名称调用。 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. 你得到一个'NoMethodError',因为你使用一个不存在的方法调用类变量。 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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