Подтвердить что ты не робот

Каков правильный способ доступа к переменным класса в Ruby 1.9?

Я пытаюсь установить некоторые переменные класса для хранения путей в приложении Rails (но я думаю, что это более рубиновый вопрос)

В основном мой класс выглядит следующим образом

class Image < ActiveRecord::Base

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

end

Но когда я пытаюсь получить доступ к @@path_to_images из моего контроллера, выполнив Image.path_to_images, я получаю NoMethodError

Когда я пытаюсь использовать Image.class_eval( @@path_to_images ), я получаю uninitialized class variable @@path_to_images in ImagesController

Я искал вокруг, и все, что я видел, говорит, что они будут работать, поэтому я очень смущен этим

Что еще, я попробовал определить простые классы с консолью ruby, например,

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

И поэтому я пытался, я думаю, использовать все возможные способы (предыдущие 2 включительно) для доступа к ним, но никоим образом не всегда получаю исключение.

4b9b3361

Ответ 1

Rails предоставляет атрибут атрибутов уровня класса для этой функции

Try

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

Затем для доступа к переменной класса path_to_folder просто используйте

Image.path_to_folder

Но люди всегда предлагают избегать переменных класса из-за его поведения в наследовании. Таким образом, вы можете использовать константы типа

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

Затем вы можете получить доступ к константе, как

Image::PATH_TO_FOLDER

Ответ 2

Хотя я бы вообще этого не рекомендовал, вы можете получить доступ к переменным класса, передав строку в class_eval, как в:

Image.class_eval('@@path_to_folder')

по крайней мере в более поздних версиях Ruby.

Обратите внимание, однако, что переменные класса связаны с самым верхним классом в подклассовой иерархии. В случае подклассов ActiveRecord, подобных этому, это означает, что эти переменные класса действительно существуют в пространстве имен ActiveRecord::Base.

Ответ 3

Если вы не можете или не хотите расширять использование класса:

Image.class_variable_get(:@@path_to_images)

Ответ 4

Переменные класса редко используются в приложениях Ruby, потому что они имеют множество ограничений и также имеют тенденцию работать против зерна надлежащего объектно-ориентированного дизайна.

Почти в каждом случае переменную класса можно заменить на правильную константу, метод класса или и то, и другое.

Ваш пример, вероятно, лучше описывается как:

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

Переменные класса являются закрытыми для рассматриваемого класса и не стекаются к подклассам и труднодоступны из внешнего контекста. Использование констант позволяет использовать такие вещи, как:

image_path = Image::PATH_TO_FOLDER

Существуют некоторые обстоятельства, при которых переменная класса более разумна, чем альтернатива, но они обычно очень редки.

Ответ 5

Лучший способ - установить и получить значение с помощью методов. Ниже приведен пример кода

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

Ответ 6

Вы можете сделать это, обернув его в методе класса, например:

def self.path_to_images
  @@path_to_images
end

но я должен упомянуть, что вы должны стараться избегать использования переменных класса в rails

Ответ 7

Я бы изменил его следующим образом:

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

Что вы здесь сделали, превратите переменную класса в метод, так что теперь вы можете получить доступ к вызову имени .method. Так как это переменная класса, вы можете вызвать ее только в самом классе, а не в экземпляре класса. Вы получаете "NoMethodError", потому что вы вызываете переменную класса с помощью метода, который не существует. Приведенный выше код определяет этот метод в строках, где вы говорите:

def self.path_to_folder
  @@path_to_folder
end

Теперь это будет работать:

Image.path_to_folder