Attr_Accessor on Class Variables

Attr_accessor on class variables

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".

how to give attr_accessor to all the instance variables in class ruby

A bit hackish and limited to your specific example, but just answering the question (in its second incarnation):

class A
def initialize
@foo = 1
@bar = 2
#add as many instance variables you need
end
attr_accessor *A.new.instance_variables.map { |s| s[1..-1] }
end

obj = A.new
obj.foo #=> 1
obj.bar #=> 2
obj.bar = 3 #=> 3
obj.bar #=> 3

See Object#instance_variables for more info.

Class variable using attr_accessor inside class self

your second example isn't a class variable, it's a class instance variable to this class the proof is that if you inherited like this you'll get nil

class A
class << self
attr_accessor :foo
end
self.foo = 'foo'
end

class B < A
end

B.foo # => nil

So if you want to add a class variable in class << self you can use @@

class A

class << self
@@foo = :foo
def foo
@@foo
end
end
end

attr_accessor on class variable

attr_reader just creates a method like this:

def masterPassword
@masterPassword
end

So you either must access the correct class instance variable in session (e.g. @masterPassword) or forego using attr_reader and write your own accessor:

def masterPassword
@@masterPassword
end

Unless you happen to have ActiveSupport which does provide cattr_accessor, cattr_reader, & cattr_writer.

See also Why should @@class_variables be avoided in Ruby? for why you perhaps should just use class instance variables instead.

What happens when attr_accessor is within a class method?

When in a class method, self is the class itself. Therefore, the following are equivalent in what they ultimately do:

class Test
def self.abc
# `self` in this context is Test.
# This sends the message `:attr_accessor, :john` to `Test`
attr_accessor :john
end
end

class Test
# `self` in this context is Test.
# This sends the message `:attr_accessor, :john` to `Test`
attr_accessor :john
end

However, as you noted, Test::abc isn't executed when the class is parsed, so attr_accessor isn't called, and the instance methods aren't added. It is perfectly valid to do this at runtime, and in fact, is the basis of much of the metaprogramming performed in Rails.

Typically, if you expect to add accessors via a class method, you might call that class method after definition but still during class declaration:

class Test
def self.abc
attr_accessor :john
end

abc
end

This will actually run, and properly declare the accessors on the class!

As to your question:

How come a running of a class method dynamically add methods to already created objects?

This is because instantiating a class doesn't instantiate a "snapshot" of the class at the time of instantiation - it creates an object which delegates much of its functionality (including discovery of the instance methods on it) to the class associated with it. Note that it is possible to define new methods on an instance which don't extend back to the class, too:

class Test
attr_accessor :foo
end

t1 = Test.new
t2 = Test.new

Test.send(:define_method, :bar) {}
puts t1.respond_to? :foo # => true
puts t2.respond_to? :foo # => true

puts t1.respond_to? :bar # => true
puts t2.respond_to? :bar # => true

t1.define_singleton_method(:baz) {}

puts t1.respond_to? :baz # => true
puts t2.respond_to? :baz # => false

ruby about attr_accessor, instance variables, local varibles

First, read this answer, which is the most-upvoted Ruby post in StackOverflow history. It will help you understand attr_accessor and its cousins attr_reader and attr_writer.

Besides that, your code has many problems.

First, you should not name an Array with a singular variable name like item. Use a plural items to make its purpose clear.

Second, the name item2 is not good. For your attribute, use something descriptive like counter, and for the variable passed as an argument to initialize it, let's use something descriptive like initial_count.

Third, your increment method takes an optional argument but then ignores it. Wouldn't it be surprising if someone called box.increment(2) and the attribute was incremented by only 1? The intent of this method is to use counter += n instead of counter += 1.

Fourth, to set counter from within the class, we need to use self. So instead of counter += n, we have to do self.counter += n.

Finally, consider whether you want the attributes to be readable and writable from an outside source, or whether you want to reserve write privileges to the object itself. Because you have methods to add things to items and to increment counter, you probably want to conceal write privileges. I would use attr_reader publicly and attr_writer privately.

Incorporating these suggestions, here's the resulting code:

class Box
attr_reader :counter, :items

def initialize(initial_count)
@counter = initial_count
@items = []
end

def add(product)
items << product
end

def empty?
items.empty?
end

def increment(n = 1)
self.counter += n
end

private

attr_writer :counter, :items
end

Now you can do this, all of which makes sense, more or less:

>> cart = Box.new(123)
>> cart.increment(2)
>> cart.counter
#> 125
>> cart.add('A product')
>> cart.add('Another product')
>> cart.items
#> ["A product", "Another product"]

But if you try to set counter or items directly, you'll get an error:

>> cart.counter = 1
#> NoMethodError: private method `counter=' called for #<Box:0x007fc13e17dc50>


Related Topics



Leave a reply



Submit