How Can Ruby's Attr_Accessor Produce Class Variables or Class Instance Variables Instead of Instance 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".

Does Ruby's attr_accessor automatically create instance variables for attributes?

No. Instance variables are not defined until you assign to them, and attr_accessor doesn't do so automatically.

Attempting to access an undefined instance variable returns nil, but doesn't define that variable. They don't actually get defined until you write to them. attr_accessor relies on this behaviour and doesn't do anything except define a getter/setter.

You can verify this by checking out .instance_variables:

class Test
attr_accessor :something
end

A new instance of x has no instance variables:

x = Test.new # => #<Test:0xb775314c>
x.instance_variables # => []

Invoking the getter does not cause @something to become defined:

x.something # => nil
x.instance_variables # => []

Invoking the setter does cause @something to become defined:

x.something = 3 # => 3
x.instance_variables # => ["@something"]

Setting something back to nil doesn't cause instance_variables to revert, so we can be sure that the first empty array returned isn't simply a case of instance_variables omitting nil values:

x.something = nil # => nil
x.instance_variables # => ["@something"]

You can also verify that this isn't simply behaviour specific to attr_accessor:

class Test
def my_method

@x # nil

instance_variables # []

@x = 3

instance_variables # ["@x"]
end
end

Test.new.my_method

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.

Difference between @instance_variable and attr_accessor

An instance variable is not visible outside the object it is in; but when you create an attr_accessor, it creates an instance variable and also makes it visible (and editable) outside the object.

Example with instance variable (not attr_accessor)

class MyClass
def initialize
@greeting = "hello"
end
end

m = MyClass.new
m.greeting #results in the following error:
#NoMethodError: undefined method `greeting' for #<MyClass:0x007f9e5109c058 @greeting="hello">

Example using attr_accessor:

class MyClass
attr_accessor :greeting

def initialize
@greeting = "hello"
end
end

m2 = MyClass.new
m2.greeting = "bonjour" # <-- set the @greeting variable from outside the object
m2.greeting #=> "bonjour" <-- didn't blow up as attr_accessor makes the variable accessible from outside the object

Hope that makes it clear.

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>

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.



Related Topics



Leave a reply



Submit