Struct, Data and Value Objects
20.1 Struct
Point = Struct.new(:x, :y) do
def distance_to(other) = Math.hypot(x - other.x, y - other.y)
def to_s = "(#{x}, #{y})"
end
a = Point.new(0, 0)
b = Point.new(x: 3, y: 4) # keyword init (Ruby 3.2+ accepts keywords by default)
a.distance_to(b) # 5.0
a.x = 10 # Structs are mutable
a == Point.new(10, 0) # value equality
x, y = *a # to_a destructuring
a.to_h
Customer = Struct.new(:name, :email, keyword_init: true)20.2 Data (Ruby 3.2+: Immutable Value Objects)
Money = Data.define(:amount, :currency) do
def +(other)
raise ArgumentError, "currency mismatch" unless currency == other.currency
with(amount: amount + other.amount) # returns a modified copy
end
def to_s = format("%.2f %s", amount, currency)
end
price = Money.new(amount: 10, currency: "USD")
total = price + Money.new(20, "USD")
total.amount # 30
# total.amount = 5 # NoMethodError: immutable
case total
in { amount: Integer | Float => amt, currency: "USD" } then puts "USD #{amt}"
end20.3 OpenStruct and Alternatives
Prefer Struct, Data, plain hashes, or proper classes. OpenStruct is slow and dynamic.
require "ostruct" # a bundled gem in Ruby 3.5/4.0+; slow, so avoid it in hot code
config = OpenStruct.new(host: "localhost", port: 8080)
config.port; config.debug = true