Каков правильный способ доступа к переменным класса в 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 включительно) для доступа к ним, но никоим образом не всегда получаю исключение.
Ответы
Ответ 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