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
Rails Email Error - 530-5.5.1 Authentication Required
Actioncontroller::Routingerror (No Route Matches [Put] ) for Ajax Call
How to Get the Intersection, Union, and Subset of Arrays in Ruby
Generate Unique Random String with Letters and Numbers in Lower Case
Undefined Method '>' for Nil:Nilclass <Nomethoderror>
Why Does Ruby Open-Uri's Open Return a Stringio in My Unit Test, But a Fileio in My Controller
Rails with Paypal Permissions and Paypal Express Checkout
Having Trouble Installing Any Ruby 1.9.X (With Rbenv) on MAC Osx Due to Psych Yaml Parse Errors
Advantage of Tap Method in Ruby
How to Capitalize the First Letter in a String in Ruby