attr_accessor
Ruby attribute accessors: attr_reader / attr_writer / attr_accessor and the small idioms that keep classes tidy.
Ruby — attr_accessor
EXAMPLE
# ===== The verbose way =====
class User
def initialize(name, email)
@name = name; @email = email
end
def name; @name; end
def email; @email; end
def name=(value); @name = value; end
def email=(value); @email = value; end
end
# ===== attr_reader (getter) =====
class User
attr_reader :name, :email
def initialize(name, email)
@name = name; @email = email
end
end
u = User.new('Alex', 'a@x.io')
u.name # 'Alex'
# u.name = 'Sam' # NoMethodError (no setter)
# ===== attr_writer (setter) =====
class User
attr_reader :name
attr_writer :name # 'name=' setter
end
# ===== attr_accessor (both) =====
class User
attr_accessor :name, :email
end
u = User.new
u.name = 'Alex'
u.name # 'Alex'
# ===== Combining =====
class Order
attr_reader :id # immutable from outside
attr_accessor :note
attr_writer :status # write-only
def initialize(id)
@id = id
@status = 'new'
end
end
# ===== With default values =====
class Settings
attr_accessor :theme, :language
def initialize(theme: 'light', language: 'en')
@theme = theme
@language = language
end
end
s = Settings.new
s.theme # 'light'
# ===== When to NOT use attr_accessor =====
# - You need validation in the setter:
class User
attr_reader :email
def email=(value)
raise ArgumentError, 'invalid' unless value.include?('@')
@email = value
end
end
# - You want a computed getter:
class Order
def initialize(cents)
@cents = cents
end
def total_aud
@cents / 100.0
end
end
# - You want a side effect on read or write (logging, dirty tracking)
# ===== Comparable via attribute =====
class User
attr_reader :id
include Comparable
def initialize(id); @id = id; end
def <=>(other); @id <=> other.id; end
end
[User.new(2), User.new(1)].sort.map(&:id) # [1, 2]
# ===== Inspect / to_s overrides (often paired with accessors) =====
class User
attr_reader :name, :email
def initialize(name, email); @name = name; @email = email; end
def to_s; "#{@name} <#{@email}>"; end
def inspect; "#<User #{@name}>"; end
end
# ===== Frozen / immutable values =====
class Point
attr_reader :x, :y
def initialize(x, y); @x = x; @y = y; freeze; end
end
p = Point.new(1, 2)
# p.instance_variable_set(:@x, 99) # FrozenError
# ===== Patterns to internalise =====
# - attr_reader for immutable; attr_accessor for plain DTOs; attr_writer rare
# - Override the setter when you need validation
# - Pair attr_reader + freeze for value objects
# - Use Comparable when sortable by a single key
# ===== Pitfalls =====
# - attr_accessor on everything -> no invariants
# - Forgetting that direct @var access skips the setter (so validation is bypassed)
# - Using attr_accessor for sensitive fields (passwords, tokens)
# - Re-defining the accessor with attr_accessor then a method below — confusing
Why it matters
attr_reader / attr_writer / attr_accessor generate the obvious getters and setters. Use attr_reader for immutability, attr_accessor for plain DTOs, and write a custom setter when validation matters. Combine with Comparable + to_s for value objects that read naturally.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
class Book
attr_accessor :title # getter + setter
attr_reader :isbn # getter only
end
Try it Yourself »
Exercise
Generate getter + setter.
:title
Starts with attr_.
Discussion
Loading…