CrackedRuby logo

CrackedRuby

Class Definition and Instantiation

Overview

Ruby implements class definition through the class keyword followed by a constant name. Classes serve as blueprints for creating objects, defining instance variables, methods, and behavior inheritance. Ruby creates classes as instances of the Class class, making them first-class objects that support dynamic modification and introspection.

Class instantiation occurs through the new method, which allocates memory for the object and calls the initialize method if defined. Ruby supports multiple instantiation patterns including factory methods, singleton objects, and alternative constructors.

class Vehicle
  def initialize(make, model)
    @make = make
    @model = model
  end
end

car = Vehicle.new("Toyota", "Camry")
# => #<Vehicle:0x00007f8b1c0a2b40 @make="Toyota", @model="Camry">

The class definition establishes the object's structure, while instantiation creates specific instances with unique state. Ruby maintains references to class objects, allowing runtime modifications and metaprogramming capabilities.

class Account
  @@next_id = 1
  
  def initialize(balance = 0)
    @id = @@next_id
    @@next_id += 1
    @balance = balance
  end
  
  attr_reader :id, :balance
end

account1 = Account.new(100)
account2 = Account.new
# => Each instance gets unique @id values

Ruby classes support inheritance through the < operator, creating parent-child relationships where child classes inherit methods and can override behavior. The object instantiation process respects the inheritance chain, calling parent initialize methods when super is invoked.

Basic Usage

Class definition begins with the class keyword followed by a CamelCase constant name. The class body contains method definitions, instance variable declarations, and class-level configuration. Ruby evaluates class bodies during definition, executing any code present.

class BankAccount
  def initialize(initial_balance)
    @balance = initial_balance
    @transactions = []
  end
  
  def deposit(amount)
    @balance += amount
    @transactions << { type: :deposit, amount: amount }
  end
  
  def withdraw(amount)
    return false if @balance < amount
    @balance -= amount
    @transactions << { type: :withdrawal, amount: amount }
    true
  end
  
  def balance
    @balance
  end
end

Object instantiation uses the new method, which Ruby defines on all classes. The new method allocates memory, creates the object, then calls initialize with the provided arguments. The initialize method sets up instance variables and performs object setup.

account = BankAccount.new(1000)
account.deposit(500)
account.withdraw(200)
puts account.balance  # => 1300

Instance variables use the @ prefix and belong to specific object instances. Each object maintains its own copy of instance variables, providing encapsulation and state isolation. Instance variables remain private unless explicitly exposed through accessor methods.

Class variables use the @@ prefix and are shared among all instances of a class and its subclasses. Class variables maintain state across all objects, making them suitable for tracking class-wide information like counters or configuration.

class Employee
  @@total_employees = 0
  
  def initialize(name, department)
    @name = name
    @department = department
    @@total_employees += 1
  end
  
  def self.total_employees
    @@total_employees
  end
end

emp1 = Employee.new("Alice", "Engineering")
emp2 = Employee.new("Bob", "Sales")
Employee.total_employees  # => 2

Ruby provides attr_reader, attr_writer, and attr_accessor methods for generating getter and setter methods automatically. These class methods create instance methods during class definition, reducing boilerplate code for simple attribute access patterns.

class Product
  attr_reader :name, :price
  attr_accessor :stock_quantity
  
  def initialize(name, price, stock)
    @name = name
    @price = price
    @stock_quantity = stock
  end
end

product = Product.new("Laptop", 999.99, 50)
product.stock_quantity = 45  # Uses generated setter
puts product.name            # Uses generated getter

Advanced Usage

Ruby supports multiple inheritance patterns through modules and mixins. Classes can include modules to share behavior without creating inheritance hierarchies. Module inclusion adds methods to the class's ancestors chain, affecting method lookup and super behavior.

module Auditable
  def self.included(base)
    base.extend(ClassMethods)
    base.class_eval do
      attr_accessor :created_at, :updated_at
    end
  end
  
  module ClassMethods
    def create_with_timestamp(attrs)
      instance = new(attrs)
      instance.created_at = Time.now
      instance.updated_at = Time.now
      instance
    end
  end
  
  def touch
    self.updated_at = Time.now
  end
end

class Document
  include Auditable
  
  def initialize(title, content)
    @title = title
    @content = content
  end
end

doc = Document.create_with_timestamp(title: "Report", content: "Content")
doc.touch

Class methods defined with self.method_name or within class << self blocks operate on the class object itself rather than instances. These methods often serve as factory methods, configuration points, or class-level utilities.

class Database
  @@connections = {}
  
  class << self
    def connection(name = :default)
      @@connections[name] ||= establish_connection(name)
    end
    
    def establish_connection(name)
      # Connection establishment logic
      OpenStruct.new(name: name, connected: true)
    end
    
    def disconnect_all
      @@connections.each_value(&:close)
      @@connections.clear
    end
  end
end

db = Database.connection(:primary)
Database.disconnect_all

Ruby supports alternative constructor patterns through class methods that create and return instances. These factory methods can perform validation, apply defaults, or create objects from different data formats.

class User
  attr_reader :name, :email, :role
  
  def initialize(name, email, role = :user)
    @name = name
    @email = email
    @role = role
  end
  
  def self.from_hash(data)
    new(data[:name], data[:email], data[:role])
  end
  
  def self.admin(name, email)
    new(name, email, :admin)
  end
  
  def self.guest
    new("Guest", "guest@example.com", :guest)
  end
end

user1 = User.from_hash(name: "Alice", email: "alice@example.com")
admin = User.admin("Bob", "bob@admin.com")
guest = User.guest

Metaprogramming capabilities allow dynamic class modification and method generation. Classes can define methods programmatically, modify their behavior at runtime, and respond to method calls dynamically.

class ConfigurableModel
  def self.define_attribute(name, type = :string)
    define_method(name) do
      instance_variable_get("@#{name}")
    end
    
    define_method("#{name}=") do |value|
      converted_value = convert_type(value, type)
      instance_variable_set("@#{name}", converted_value)
    end
    
    (@attributes ||= []) << { name: name, type: type }
  end
  
  def self.attributes
    @attributes || []
  end
  
  private
  
  def convert_type(value, type)
    case type
    when :integer then value.to_i
    when :float then value.to_f
    when :boolean then !!value
    else value.to_s
    end
  end
end

class Settings < ConfigurableModel
  define_attribute :debug_mode, :boolean
  define_attribute :max_connections, :integer
  define_attribute :timeout, :float
end

settings = Settings.new
settings.debug_mode = "true"
settings.max_connections = "50"
puts settings.debug_mode      # => true
puts settings.max_connections # => 50

Common Pitfalls

Class variable inheritance creates unexpected behavior when subclasses modify shared state. Class variables are shared between parent and child classes, leading to unintended side effects when multiple classes access the same variable.

class Parent
  @@count = 0
  
  def self.increment
    @@count += 1
  end
  
  def self.count
    @@count
  end
end

class Child < Parent
end

Parent.increment  # => @@count = 1
Child.increment   # => @@count = 2 (same variable!)
Parent.count      # => 2 (affected by Child)

The solution involves using class instance variables instead of class variables for class-specific state:

class Parent
  @count = 0
  
  class << self
    attr_accessor :count
    
    def increment
      @count += 1
    end
  end
end

class Child < Parent
  @count = 0  # Separate variable for Child
end

Method visibility rules can cause confusion when defining private methods within class definitions. Methods defined after private remain private until visibility changes or the class definition ends. This affects method accessibility in unexpected ways.

class Example
  def public_method
    "public"
  end
  
  private
  
  def private_method
    "private"
  end
  
  # This method is also private!
  def another_method
    "accidentally private"
  end
end

obj = Example.new
obj.another_method  # => NoMethodError

Initialize method return values get ignored because new always returns the newly created object. Returning different values from initialize does not affect the instantiation process, leading to confusion about object creation.

class Problem
  def initialize(value)
    @value = value
    return "not the object"  # This return value is ignored
  end
end

obj = Problem.new(42)  # => Returns the Problem instance, not the string

Constant redefinition warnings occur when classes are redefined or reopened. Ruby allows class modification at runtime but issues warnings when constants are reassigned, indicating potential code organization issues.

class MyClass
  VERSION = "1.0"
end

# Later in the code...
class MyClass
  VERSION = "2.0"  # => warning: already initialized constant
end

Instance variable access from class methods fails because class methods execute in the class context, not the instance context. Instance variables belong to specific objects and remain inaccessible from class method scope.

class Broken
  def initialize
    @instance_var = "value"
  end
  
  def self.try_access
    @instance_var  # => nil (this is a class instance variable)
  end
end

obj = Broken.new
Broken.try_access  # => nil, not "value"

Reference

Class Definition Methods

Method Parameters Returns Description
class ClassName Block Class Defines new class or reopens existing class
attr_reader *names nil Creates getter methods for named attributes
attr_writer *names nil Creates setter methods for named attributes
attr_accessor *names nil Creates getter and setter methods for named attributes
include Module self Includes module methods as instance methods
extend Module self Includes module methods as class methods
prepend Module self Prepends module to ancestor chain

Instance Creation Methods

Method Parameters Returns Description
new *args, **kwargs, &block Object Creates new instance and calls initialize
allocate None Object Allocates memory without calling initialize
initialize *args, **kwargs, &block Ignored Sets up new instance (called by new)

Visibility Control

Method Parameters Returns Description
private *method_names self Makes methods private (default if no names given)
protected *method_names self Makes methods protected
public *method_names self Makes methods public

Variable Types

Type Syntax Scope Description
Instance Variable @variable Object instance Unique to each object instance
Class Variable @@variable Class hierarchy Shared among class and subclasses
Class Instance Variable @variable (in class context) Single class Belongs to specific class object
Constant CONSTANT Global/Class Unchanging values with lexical scope

Inheritance Hierarchy Methods

Method Parameters Returns Description
superclass None Class or nil Returns parent class
ancestors None Array Returns ancestor chain including modules
< Class Class Defines inheritance relationship
super Optional args Object Calls parent method implementation

Introspection Methods

Method Parameters Returns Description
class None Class Returns object's class
instance_of? Class Boolean Tests exact class membership
is_a? Class Boolean Tests class or ancestor membership
respond_to? Symbol/String Boolean Tests method availability
methods include_super = true Array Lists available methods
instance_variables None Array Lists instance variable names

Class Configuration Constants

Constant Value Description
Class::VISIBILITY_LEVELS [:private, :protected, :public] Method visibility options
Object::RESERVED_METHODS Method names Methods that cannot be undefined

Common Class Patterns

Pattern Implementation Use Case
Factory Method def self.create(params) Alternative constructors
Singleton include Singleton Single instance classes
Builder Chain setter methods Complex object construction
Template Method Abstract methods with raise NotImplementedError Subclass customization