Introduction
A class instance variable is an instance variable (@var) defined on the class object itself โ not on instances of the class. Because every class in Ruby is an object (an instance of Class), it can have its own instance variables. This is distinct from class variables (@@var), which are shared across the entire inheritance hierarchy.
Basic Example
class People
@kind = "human" # class instance variable
class << self
attr_accessor :kind # getter and setter for @kind on the class
end
end
class Student < People
end
p People.kind # => "human"
p Student.kind # => nil (Student has its own @kind, unset)
Student inherits the accessor method (kind) from People, but not the value of @kind. Each class object has its own separate @kind instance variable. Student.kind returns nil because Student’s @kind was never set.
Why Values Don’t Inherit
Instance variables belong to specific objects. People and Student are two different objects (both instances of Class), so they each have their own @kind:
People.instance_variables # => [:@kind]
Student.instance_variables # => [] (Student has no @kind set)
This is the key difference from class variables (@@kind), which are shared:
class People
@@kind = "human"
def self.kind; @@kind; end
end
class Student < People; end
People.kind # => "human"
Student.kind # => "human" (shared!)
Student.class_variable_set(:@@kind, "student")
People.kind # => "student" (changed by Student!)
Methods Are Inherited, Values Are Not
This distinction is important: the method (kind) is inherited, but the value (@kind) is not:
class Animal
@sound = "..."
class << self
attr_accessor :sound
end
end
class Dog < Animal
@sound = "Woof"
end
class Cat < Animal
@sound = "Meow"
end
puts Animal.sound # => "..."
puts Dog.sound # => "Woof"
puts Cat.sound # => "Meow"
# Each class has its own @sound โ no interference
Dog.sound = "Bark"
puts Cat.sound # => "Meow" (unchanged)
puts Animal.sound # => "..." (unchanged)
This pattern is used extensively in Rails for per-model configuration.
The Design Principle
Objects are distinguished from each other by their instance variables (data). Methods are shared and reused through inheritance. This is a fundamental principle of object-oriented design:
- Instance variables โ differentiate objects (each has its own state)
- Methods โ shared behavior (inherited and reused)
In Go, this separation is enforced by the language โ structs hold data, interfaces define behavior, and they’re kept strictly separate. Ruby is more flexible but the same principle applies.
Rails class_attribute
Rails builds on class instance variables with class_attribute, which provides inheritable class-level attributes where subclasses get their own copy that starts with the parent’s value:
class ApplicationRecord < ActiveRecord::Base
class_attribute :per_page, default: 25
end
class User < ApplicationRecord
# Inherits per_page = 25
end
class Post < ApplicationRecord
self.per_page = 10 # Override for Post only
end
puts User.per_page # => 25 (inherited)
puts Post.per_page # => 10 (overridden)
# Changing User doesn't affect Post
User.per_page = 50
puts Post.per_page # => 10 (unchanged)
puts ApplicationRecord.per_page # => 25 (unchanged)
This is different from a plain class instance variable โ class_attribute copies the value to the subclass on first write, so changes to the subclass don’t affect the parent.
Implementing class_attribute Manually
Here’s a simplified version of what class_attribute does:
module ClassAttribute
def class_attribute(*attrs, default: nil)
attrs.each do |attr|
# Set default on this class
instance_variable_set("@#{attr}", default)
# Define getter โ walks up ancestor chain if not set locally
define_singleton_method(attr) do
if instance_variable_defined?("@#{attr}")
instance_variable_get("@#{attr}")
else
superclass.send(attr) if superclass.respond_to?(attr)
end
end
# Define setter โ sets on this class only
define_singleton_method("#{attr}=") do |value|
instance_variable_set("@#{attr}", value)
end
end
end
end
class Base
extend ClassAttribute
class_attribute :timeout, default: 30
end
class Child < Base; end
puts Base.timeout # => 30
puts Child.timeout # => 30 (reads from parent)
Child.timeout = 60
puts Child.timeout # => 60 (own value)
puts Base.timeout # => 30 (unchanged)
Practical Use Cases
Per-Model Table Configuration (Rails)
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
class LegacyUser < ApplicationRecord
self.table_name = "old_users" # class instance variable
self.primary_key = "user_id"
end
class ModernUser < ApplicationRecord
self.table_name = "users"
end
Plugin/Gem Configuration
module Searchable
def self.included(base)
base.extend(ClassMethods)
base.instance_variable_set(:@searchable_fields, [])
end
module ClassMethods
def searchable(*fields)
@searchable_fields = fields
end
def searchable_fields
@searchable_fields || []
end
end
end
class Article
include Searchable
searchable :title, :body, :tags
end
class Product
include Searchable
searchable :name, :description, :sku
end
puts Article.searchable_fields.inspect # => [:title, :body, :tags]
puts Product.searchable_fields.inspect # => [:name, :description, :sku]
Summary
| Feature | Class Instance Variable (@var) |
Class Variable (@@var) |
|---|---|---|
| Scope | Per-class object | Shared across hierarchy |
| Inherited value | No (nil in subclass) | Yes (same variable) |
| Subclass can change parent | No | Yes (dangerous!) |
| Rails equivalent | class_attribute |
Avoid |
| Recommended | Yes | Rarely |
Class instance variables give you clean, per-class state without the surprising shared-mutation behavior of class variables. They’re the foundation of Rails’ per-model configuration system.
Comments