TLDR: Ruby class variables (@@foo
) are dangerous in many ways. You should avoid them at all cost. See bottom of this card for alternatives.
Class variables are shared between a class hierarchy
When you declare a class variable, it is shared between this and all descending (inheriting) classes. This is rarely what you want.
Class variables are bound at compile-time
Like unqualified constants, class variables are bound to your current scope when the .rb
file is being parsed.
This will probably trip you up when you are writing a module or trait that defines class variables
for other classes.
Say you have the following code:
module Mod
def foo
@@foo
end
end
class Klass
include Mod
end
@@foo
does not mean Klass::@@foo
(qualifier added to make my point; it is not valid Ruby expression). It actually means Mod::@@foo
and is shared between Mod
, all classes including Mod
as well as all their subclasses.
Also note that only the class
and module
keywords can be used to change the scope to which class variables are bound. You can not change scope by using class_eval
or instance_eval
. For instance, the code below declares a variable @@counter
that is shared between all classes of your application:
ActiveRecord::Base.class_eval do
@@counter = 1
end
When encountering such code, Ruby will warn you:
warning: class variable access from toplevel
Unlike constants, which you can qualify like Klass::CONSTANT
, there is no way to qualify a class variable like Klass::@@variable
. When you want to tell Ruby that you mean another scope than the current scope, you need to use
class_variable_get
Show archive.org snapshot
and
class_variable_set
Show archive.org snapshot
.
Alternatives to using class variables
- If a value only needs to be shared between a single class and its instances (not descending classes), just use an instance variable (
@foo
) in class context. - Sometimes you want to share a variable with sub-classes, but give sub-classes a way to override values without affecting the parent class. Rails has many helpers for this such as
class_attribute
Show archive.org snapshot andclass_inheritable_accessor
Show archive.org snapshot . Unfortunately their semantics are hard to understand, the helpers available differ for different versions of Rails and the behavior is subtly different. Make sure you read and understand the API before using these.