77

attr_accessor does not work on the following code. The error says "undefined method 'things' for Parent:Class (NoMethodError)":

class Parent
  @@things = []
  attr_accessor :things
end
Parent.things << :car

p Parent.things

However the following code works

class Parent
  @@things = []
  def self.things
    @@things
  end
  def things
    @@things
  end
end
Parent.things << :car

p Parent.things
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Talespin_Kit
  • 20,830
  • 29
  • 89
  • 135
  • 5
    attr_accessor is ruby's shorthand for creating setter and getter on an instance of the object(as well as instance variables) and is not meant for use on class level variables – bjhaid Jan 14 '14 at 19:54
  • 3
    Though I know practically nothing of Rails, I believe with `require 'active_support'`, you could use [cattr_accessor :things](http://apidock.com/rails/Class/cattr_accessor). – Cary Swoveland Jan 14 '14 at 21:09
  • Related: http://stackoverflow.com/questions/895747/how-can-rubys-attr-accessor-produce-class-variables-or-class-instance-variables – Ciro Santilli OurBigBook.com May 02 '14 at 08:10

8 Answers8

118

attr_accessor defines accessor methods for an instance. If you want class level auto-generated accessors you could use it on the metaclass

class Parent
  @things = []

  class << self
    attr_accessor :things
  end
end

Parent.things #=> []
Parent.things << :car
Parent.things #=> [:car]

but note that this creates a class level instance variable not a class variable. This is likely what you want anyway, as class variables behave differently than you might expect when dealing w/ inheritance. See "Class and Instance Variables In Ruby".

Dennis Hackethal
  • 13,662
  • 12
  • 66
  • 115
Alex.Bullard
  • 5,533
  • 2
  • 25
  • 32
  • This still return the same error. class Parent @@things = [] class << self attr_accessor :things end end Parent.things << :car p Parent.things – Talespin_Kit Jan 14 '14 at 21:07
  • 1
    See my edit - I added in the initialization for you. Since it's a class level instance var not a class variable you need to only use one `@` – Alex.Bullard Jan 14 '14 at 22:31
  • What if I only want `@things` available within the class? For instance variable I can just `attr_reader`, but for class level instance variable I have to rely on `attr_accessor` to write to it myself (assume that it's not an array, but something immutable like an integer). – Franklin Yu May 26 '17 at 20:37
  • "a class level instance var not a class variable" -- I feel like half of answers tells to do the one kind, another half tells to do another. Still no one said what's the difference and how to choose between them. – Nakilon Dec 14 '22 at 20:41
19

attr_accessor generates accessors for instance variables. Class variables in Ruby are a very different thing, and they are usually not what you want. What you probably want here is a class instance variable. You can use attr_accessor with class instance variables like so:

class Something
  class << self
    attr_accessor :things
  end
end

Then you can write Something.things = 12 and it will work.

Dorian
  • 7,749
  • 4
  • 38
  • 57
Chuck
  • 234,037
  • 30
  • 302
  • 389
  • In this case if a class inherits Something, does the instance of inherited class object get a new copy of things, right? – Talespin_Kit Jan 14 '14 at 21:17
  • @Talespin_Kit: Correct — each class will have its own `@things` variable, because instance variables are specific to the object that owns them. – Chuck Jan 14 '14 at 21:21
5

Just some clarification: class variables won't be accessible using attr_accessor. It's all about instance variables:

class SomeClass
  class << self
    attr_accessor :things
  end
  @things = []
end

because in Ruby, class is an instance of the class "Class" (God, I love to say that) and attr_accessor sets accessor methods for instance variables.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Vlad Khomich
  • 5,820
  • 1
  • 27
  • 39
5

This is probably the simplest way.

class Parent
  def self.things
    @@things ||= []
  end
end
Parent.things << :car

p Parent.things
Jonah Ruiz
  • 13
  • 3
Paul Byrne
  • 1,563
  • 18
  • 24
1

Аlso note that a singleton method is a method only for a single object. In Ruby, a Class is also an object, so it too can have singleton methods! So be aware of when you might be calling them.

Example:

class SomeClass
  class << self
    def test
    end
  end
end

test_obj = SomeClass.new

def test_obj.test_2
end

class << test_obj
  def test_3
  end
end

puts "Singleton methods of SomeClass"
puts SomeClass.singleton_methods
puts '------------------------------------------'
puts "Singleton methods of test_obj"
puts test_obj.singleton_methods

Singleton methods of SomeClass

test


Singleton methods of test_obj

test_2

test_3

rmcsharry
  • 5,363
  • 6
  • 65
  • 108
artamonovdev
  • 2,260
  • 1
  • 29
  • 33
1
Parent.class_variable_get(:@@things)

That would be the built-in way. In most cases this should be sufficient I think. No need to have a class variable accessor in the instance.

su_li
  • 199
  • 9
1
class Parent
  @things = []
  
  singleton_class.send(:attr_accessor, :things)
end

This pattern is most useful when you are defining accessors dynamically or creating them inside a method:

class Foo
  def self.add_accessor(name)
    singleton_class.send(:attr_accessor, name)
  end
end

Foo.add_accessor :things
Foo.things = [:car]
Foo.things # => [:car]
Sajad Rastegar
  • 3,014
  • 3
  • 25
  • 36
0

I think what you want is class_attribute

class Base
  class_attribute :foo
  self.foo = '--UNSET--'

  def show
    puts self.foo
  end
end

class X < Base
  self.foo = 'XXX'
end

class Y < Base
  self.foo = 'YYY'
end

class Z < Base
end

puts X.foo # -> 'XXX'
puts Y.foo # -> 'YYY'
puts Z.foo # -> '--UNSET--'
X.new.show # -> 'XXX'
X.foo = 'x-x-x'
X.new.show # -> 'x-x-x'
Y.new.show # -> 'YYY'