Reputation: 1268
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
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
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
Reputation: 3500
If you can't or don't want to extend class use:
Image.class_variable_get(:@@path_to_images)
Upvotes: 3
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
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
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
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