CrackedRuby logo

CrackedRuby

Class Instance Variables

Overview

Class instance variables are created by defining instance variables at the class level, outside of any method definitions. Each class maintains its own separate set of these variables, and they are not inherited by subclasses. This makes them useful for storing data that should be associated with a particular class but not shared across a class hierarchy.

class User
  @count = 0

  def self.increment_count
    @count += 1
  end

  def self.count
    @count
  end
end

User.count           # => 0
User.increment_count
User.count           # => 1

Basic Usage

Defining Class Instance Variables

Class instance variables are defined by using the @ symbol at the class level:

class Product
  @inventory = []
  @total_sales = 0

  def self.add_to_inventory(item)
    @inventory << item
  end

  def self.inventory
    @inventory
  end

  def self.record_sale(amount)
    @total_sales += amount
  end

  def self.total_sales
    @total_sales
  end
end

Product.add_to_inventory("Widget")
Product.record_sale(100)
Product.inventory    # => ["Widget"]
Product.total_sales  # => 100

Accessing Class Instance Variables

Class instance variables can only be accessed through class methods or within the class definition itself:

class Counter
  @value = 0

  def self.increment
    @value += 1
  end

  def self.value
    @value
  end

  def self.reset
    @value = 0
  end
end

Inheritance Behavior

Unlike class variables (@@variable), class instance variables are not inherited:

class Animal
  @species_count = 0

  def self.add_species
    @species_count += 1
  end

  def self.species_count
    @species_count
  end
end

class Dog < Animal
  @breed_count = 0

  def self.add_breed
    @breed_count += 1
  end

  def self.breed_count
    @breed_count
  end
end

Animal.add_species
Dog.add_breed

Animal.species_count  # => 1
Dog.species_count     # => 0 (not inherited)
Dog.breed_count       # => 1 (Dog's own variable)

Advanced Usage

Using Class Instance Variables with Modules

Class instance variables work within modules and can be used to create sophisticated patterns:

module Trackable
  def self.included(base)
    base.instance_variable_set(:@tracked_objects, [])
  end

  def track_creation
    self.class.instance_variable_get(:@tracked_objects) << self
  end

  module ClassMethods
    def tracked_objects
      @tracked_objects ||= []
    end

    def clear_tracking
      @tracked_objects = []
    end
  end

  def self.included(base)
    base.extend(ClassMethods)
    base.instance_variable_set(:@tracked_objects, [])
  end
end

class User
  include Trackable

  def initialize(name)
    @name = name
    track_creation
  end
end

user1 = User.new("Alice")
user2 = User.new("Bob")
User.tracked_objects.length  # => 2

Dynamic Class Instance Variable Management

You can manipulate class instance variables dynamically using Ruby's metaprogramming capabilities:

class ConfigurableClass
  def self.set_config(key, value)
    instance_variable_set("@#{key}", value)
  end

  def self.get_config(key)
    instance_variable_get("@#{key}")
  end

  def self.config_keys
    instance_variables.map { |var| var.to_s[1..-1] }
  end
end

ConfigurableClass.set_config(:database_url, "postgres://localhost")
ConfigurableClass.set_config(:api_key, "secret123")
ConfigurableClass.get_config(:database_url)  # => "postgres://localhost"
ConfigurableClass.config_keys                # => ["database_url", "api_key"]

Common Pitfalls

Confusion with Class Variables

A common mistake is confusing class instance variables with class variables:

class Example
  @@class_var = "shared"        # Class variable (inherited)
  @class_instance_var = "not shared"  # Class instance variable (not inherited)

  def self.class_var
    @@class_var
  end

  def self.class_instance_var
    @class_instance_var
  end
end

class Child < Example
end

Example.class_var          # => "shared"
Child.class_var            # => "shared" (inherited)

Example.class_instance_var # => "not shared"
Child.class_instance_var   # => nil (not inherited)

Uninitialized Variable Access

Accessing an uninitialized class instance variable returns nil:

class Test
  def self.uninitialized_var
    @never_set
  end
end

Test.uninitialized_var  # => nil

Thread Safety Concerns

Class instance variables are not automatically thread-safe:

class ThreadUnsafe
  @counter = 0

  def self.increment
    # This is not atomic and can cause race conditions
    @counter += 1
  end

  def self.counter
    @counter
  end
end

# Better approach with synchronization
class ThreadSafe
  @counter = 0
  @mutex = Mutex.new

  def self.increment
    @mutex.synchronize { @counter += 1 }
  end

  def self.counter
    @mutex.synchronize { @counter }
  end
end

Testing Strategies

When testing classes that use class instance variables, remember to reset state between tests:

# RSpec example
RSpec.describe MyClass do
  before(:each) do
    MyClass.instance_variable_set(:@count, 0)
  end

  it "increments count" do
    expect { MyClass.increment }.to change { MyClass.count }.by(1)
  end
end

Reference

Key Characteristics

  • Scope: Belong to the class object itself, not instances
  • Inheritance: Not inherited by subclasses
  • Access: Only accessible through class methods
  • Naming: Use @variable_name syntax
  • Thread Safety: Not thread-safe by default

Common Methods for Management

Method Purpose Example
instance_variable_get Read variable value self.instance_variable_get(:@var)
instance_variable_set Set variable value self.instance_variable_set(:@var, value)
instance_variable_defined? Check if variable exists self.instance_variable_defined?(:@var)
instance_variables List all instance variables self.instance_variables
remove_instance_variable Remove a variable self.remove_instance_variable(:@var)

Comparison with Related Concepts

Concept Inheritance Scope Access
Instance variables (@var) No Per instance Instance methods
Class instance variables (@var at class level) No Per class Class methods
Class variables (@@var) Yes Shared across hierarchy Both class and instance methods
Constants (CONST) Yes (with warnings if redefined) Class hierarchy Both class and instance methods