Ruby Classes

Class Basics

Class definition and initialization
class Host
  attr_reader   :hostname           # getter only
  attr_writer   :status             # setter only
  attr_accessor :ip, :port          # both getter and setter

  @@instance_count = 0

  def initialize(hostname, ip, port = 22)
    @hostname = hostname
    @ip = ip
    @port = port
    @@instance_count += 1
  end

  def to_s
    "#{@hostname} (#{@ip}:#{@port})"
  end

  def self.count
    @@instance_count
  end
end

vault = Host.new("vault-01", "10.50.1.40", 8200)
puts vault                   # => "vault-01 (10.50.1.40:8200)"
puts vault.hostname          # => "vault-01"
puts Host.count              # => 1

Inheritance

Single inheritance with super
class NetworkDevice
  attr_accessor :hostname, :ip

  def initialize(hostname, ip)
    @hostname = hostname
    @ip = ip
  end

  def to_s
    "#{self.class.name}: #{@hostname} (#{@ip})"
  end
end

class Switch < NetworkDevice
  attr_accessor :vlans

  def initialize(hostname, ip, vlans = [])
    super(hostname, ip)
    @vlans = vlans
  end

  def trunk_ports
    @vlans.length > 1 ? "trunk" : "access"
  end
end

sw = Switch.new("sw-core-01", "10.50.1.2", [10, 20, 30])
puts sw                        # => "Switch: sw-core-01 (10.50.1.2)"
puts sw.is_a?(NetworkDevice)   # => true

Access Control

Visibility modifiers
class SecureDevice < NetworkDevice
  def connect
    authenticate
    open_session
  end

  private

  def authenticate
    puts "Authenticating to #{@hostname}"
  end

  protected

  def open_session
    puts "Session to #{@ip}"
  end
end

Comparable and Struct

Operator overloading
class Subnet
  include Comparable
  attr_reader :network, :prefix

  def initialize(cidr)
    @network, prefix_str = cidr.split("/")
    @prefix = prefix_str.to_i
  end

  def <=>(other)
    prefix <=> other.prefix
  end

  def to_s
    "#{@network}/#{@prefix}"
  end
end

subnets = [Subnet.new("10.50.1.0/24"), Subnet.new("10.50.0.0/16")]
puts subnets.sort            # /16, /24
Struct and Data
# Struct -- auto-generates initialize, accessors, ==
Host = Struct.new(:name, :ip, :port, keyword_init: true)
h = Host.new(name: "vault", ip: "10.50.1.40", port: 8200)

# Data (Ruby 3.2+) -- immutable value object
Endpoint = Data.define(:host, :port)
ep = Endpoint.new(host: "10.50.1.20", port: 443)