iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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

Discussion

Loading…