Ruby class variable initial value [duplicate] - ruby

This question already has answers here:
Total newbie: Instance variables in ruby?
(4 answers)
Closed 5 years ago.
In PHP I write:
Class Test{
public $a=100;
}
$a=new Test();
echo $a->a; //prints 100
It prints 100, but In ruby when I write:
class Test
#a=100
attr_accessor :a
end
a=Test.new
puts a.a #=> prints nil
a is nil and it prints nil. Why?

You're mixing things a bit.
What you've defined with #a = 100 is a class instance variable.
What you're going to have access to with attr_accessor :a is an instance variable #a.
Example of instance variable usage:
class A
def initialize a
#a = a
end
attr_accessor :a
end
instance = A.new(2)
#=> 2
instance.instance_variables
#=> [:#a]
instance.a
#=> 2
Example of class instance variable usage:
class A
#a = 1
class << self
attr_accessor :a
end
end
A.a
#=> nil
A.a = 2
#=> 2
A.a
#=> 2
instance = A.new
instance.class.a # access instance's class instance variable
#=> 2

Instances variables belong to objects (aka instances), that's why they are called "instance variables" after all.
There are two objects here: Test, which is an instance of Class, and a, which is an instance of Test. Both are objects just like any other object. Both can have instance variables just like any other object.
Both have an instance variable called #a. Foo's #a has been initialized to 100. a's #a hasn't been initialized at all, and unitialized instance variables evaluate to nil.
So, your problem is that you are confusing which instance you are looking at. Instance variables are always looked up in self, and inside a class definition body, self is the class being defined.
This may sound pedantic, but I find that understanding that Ruby is actually much simpler than people sometimes want you to believe, will ultimately help you.

Try this instead:
class Test
attr_accessor :a
def initialize
#a = 100
end
end
a = Test.new
puts a.a

The reason this is happening is that this line:
#a = 100
is assigning to an instance variable but not the one you think. It is being assigned to Test.a, (Test being an instance of Class) rather than to an instance variable a belonging to each instance of Test
In turn, this line:
attr_accessor :a
is declaring an accessor to an instance variable called a (accessible through any instance of Test)
Thus you should either have
class Test
#a = 100
class << self
attr_accessor :a
end
end
puts Test.a #=> will print 100
or
class Test
def initialize
#a = 100
end
attr_accessor :a
end
a = Test.new
puts a.a #=> will print 100
It depends if you just want a class constant or if you want an attribute with a default value on each instance. The latter is a direct translation of your PHP code.

Related

Ruby: Why is an instance variable defined inside a class nil?

class Something
#b = [4432]
def screen
puts #b.class
end
end
s = Something.new
s.screen
outputs 'Nilclass'. Was wondering, why does an instance variable which is defined inside a class always part of NilClass?
Instance variables belong to an object (aka an instance), that's why they are called instance variables. Every instance has its own instance variables.
In your case, there are two objects: Something (which is an instance of Class) and s (which is an instance of Something). Each of those two objects has its own set of instance variables. Something has an instance variable called #b which points to [4432]. s has no instance variable named #b because you never assign to it, and uninitialized instance variables evaluate to nil.
You need to set it like this:
class Something
def initialize
#b = [4432]
end
def screen
puts #b.class
end
end
The way you did it, the variable belongs to Something class itself, not its instance. Observe:
class Something
#b = [4432]
end
s = Something.new
s.instance_variable_get(:#b) # => nil # !> instance variable #b not initialized
Something.instance_variable_get(:#b) # => [4432]
Generally the instance variable must be defined inside the constructor whereas in ruby the default constructor is initialize the syntax is
def initialize
end #these is the default constructor in ruby
so when we define the insatnce variable inside the constructor and when we create the instance of a class then that instance/object will contain the copy of instance variables
most important thing is that though the instance/object contains the instance variable the instance/object cannot access it why because by default the instance data is private so in order to access it we need to define the getters and setter for those instance variable
class Something
attr_accessor:b
def initialize
#b = [4432]
end
s=Something.new
puts"#{s.b}"
Because the variable #b does not exist!. For e.g. the following would produce the same results you see.
class Something
#b = [4432]
def screen
puts #a.class #=> note #a which is non-existent
end
end
s = Something.new
s.screen
Whereas
class Something
#b = [4432]
def screen
puts #a.class
end
def self.screen
puts #b.class
end
end
s = Something.new
s.screen #=> NilClass
Something.screen #=> Array
if you initialize #b outside the initializer, you want #b scope to be a class variable, so you have to call it ##b :
##b has the same value for all Instance of your Something Class
like :
class Somthing
##b = [4432]
def initialize
#[...]
end
def screen
puts ##b.class
end
end
#Jörg W Mittag answer is correct. I just wont to add, that defining an instance variable in class != defining instance variable in an instance of that class. To create an instance variable, in your case in s instance you need to add an initialize method witch gets triggered when new method is called on a class.
def initialize(b_value = default_value)
#b = b_value
end

Can I declare a Ruby class member?

In Java, I can declare a public member of a class, but it seems I cannot do this in Ruby.
class Person
#a = 1
def hello()
puts(#a)
end
end
p = Person.new
p.hello()
#nil
Why is the output nil rather than 1?
Because instance variable #a is not initialized for the instance pr.
class Person
#a = 1
def hello()
puts(#a)
end
end
pr = Person.new
pr.instance_variable_get(:#a) # => nil
Now see below:-
class Person
def initialize(a)
#a=a
end
def hello()
puts(#a)
end
end
pr = Person.new(1)
pr.instance_variables # => [:#a]
Person.instance_variables # => []
pr.instance_variable_get(:#a) # => 1
pr.hello # => 1
Instance variables
An instance variable has a name beginning with #, and its scope is confined to whatever object self refers to. Two different objects, even if they belong to the same class, are allowed to have different values for their instance variables. From outside the object, instance variables cannot be altered or even observed (i.e., ruby's instance variables are never public) except by whatever methods are explicitly provided by the programmer. As with globals, instance variables have the nil value until they are initialized.
Now look here:-
class Person
#a = 1
def self.hello()
puts(#a)
end
end
Person.hello # => 1
Person.instance_variables # => [:#a]
Person.new.instance_variables # => []
So in this example #a is the instance variable of the object Person,not the instances of Person.Very good tips is here - Class Level Instance Variables.

Ruby newbie, what is the difference between #var and ##var in a class

as the title says,
what is the difference between #var and ##var in a class definition?
Also, what is the difference between self.mymethod and mymethod in defining a method?
##var is a class variable, it is shared between class and all instances of this class. You can access this variable from class methods and from instance methods.
class C
##a = 1
def self.m1 # define class method (this is similar to def C.m1, because self will evaluate to C in this context)
##a
end
def m2 # define instance method
##a
end
end
C.m1 # => 1
C.new.m2 # => 1
#var is a class instance variable. Normally you can get access to this instance variable from the class methods.
class C
#a = 1
def self.m1
#a
end
def m2
# no direct access to #a because in this context #a will refer to regular instance variable, not instance variable of an object that represent class C
end
end
C.m1 # => 1
These variables might be confusing and you should always know the context where you define instance variable #... - it might be defined in the instance of an object that represent a class or might be an instance of regular object.
self always refers to the current object.Check the following Eg:-
class Test
def test
puts "At the instance level, self is #{self}"
end
def self.test
puts "At the class level, self is #{self}"
end
end
Test.test
#=> At the class level, self is Test
Test.new.test
#=> At the instance level, self is #<Test:0x28190>
object variables are so named because they have scope within, and are associated
to, the current object.an object variable, is then accessible from any other method inside that object.
Class variables are particularly useful for storing information relevant to all objects
of a certain class.
In intuitive terms, instance vars are used to keep track of the state of each object. On the other hand, class variables are used to keep track of the state of all instances of the class. E.g. you might use ##count to keep track of the number of this class' objects that have been instantiated, like so:
class User
##count = 0
attr_reader :name
def initialize(name)
##count += 1
#name = name
end
end
User.count gives you the number of users that have been instantiated so far.
user = User.new('Peter') increases User.count by one and user.name returns Peter.

Ruby and class variables in inherit class

class A
def set(v)
##v = v
end
def put
puts ##v
end
end
class B < A
end
class C < A
end
B.new.set 'b'
B.new.put # => b
C.new.set 'c'
C.new.put # => c
B.new.put # => c
Why? And how should I write this to have 'b' in last B.new.put?
Here is a nice article on the subject - Class and Instance Variables In Ruby.
Basically, what you can do is:
class A
class << self
attr_accessor :class_var
end
def set_class_var(value)
self.class.class_var = value
end
def get_class_var
self.class.class_var
end
end
class B < A; end
A.class_var = 'a'
B.class_var = 'b'
puts A.class_var # => a
puts B.class_var # => b
A.new.set_class_var 'aa'
B.new.set_class_var 'bb'
puts A.new.get_class_var # => aa
puts B.new.get_class_var # => bb
To understand it you should think about A as an instance of Class class (and that's how it is in Ruby). But every object in Ruby has its own singleton class that stores object-specific stuff like methods defined on object itself:
a = A.new
def a.foo
puts 'foo'
end
In that case foo is method defined only for a object and not for every instance of A class. And another way to define method in object's singleton class is like that:
class << a # open a's singleton class
def bar # define method that will be available only on 'a' object
puts 'bar'
end
end
In the first code snippet we use that approach to define class_var attribute accessor in the context of singleton class of our A class (it's a bit tricky, so you need to think about it). As the result class itself has class_var variable as well as its descendant class B. The difference is that every one of them has its own class_var variable that do not interfere.
Another option is to pull out class_inheritable_accessor code from Rails and include its behavior in your classes. See here for a good discussion and the guts of the code.
Perhaps you don't really want a class variable, though.
Assigning a value to a class variable (an ## variable) sets it for EVERY instance of the class. It even "sets" it for instances that "aren't created yet." So, consider this...
B.new.set 'b' # OK, that set ##v for that particular instance of B
B.new.put # Hey, you just created *another* new instance of B!
How can ##v have a value in that one? The second object's value of ##v would be unset, except for the fact that ##v is a class variable, so it has the same value for every instance of the class.

Dynamically creating class in Ruby

I have a class that should look something like this:
class Family_Type1
#people = Array.new(3)
#people[0] = Policeman.new('Peter', 0)
#people[1] = Accountant.new('Paul', 0)
#people[2] = Policeman.new('Mary', 0)
def initialize(*ages)
for i in 0 ... #people.length
#people[i].age = ages[i]
end
end
end
I want to be able to define a bunch of classes similar to this one at runtime (define them once at startup) where the size of the array and the type assigned to each parameter is defined at runtime from an external specification file.
I sort of got it to work using evals but this is really ugly. Any better way?
From what I understand, you need meta-programming. Here is a snippet of code for creating classes dynamically (on the fly) with initialize method that initializes instance variables-
class_name = 'foo'.capitalize
klass = Object.const_set(class_name,Class.new)
names = ['instance1', 'instance2'] # Array of instance vars
klass.class_eval do
attr_accessor *names
define_method(:initialize) do |*values|
names.each_with_index do |name,i|
instance_variable_set("#"+name, values[i])
end
end
# more...
end
Hope you can tweak it to suit your requirements.
First off, part of the reason your example code isn't working for you is that you have two different #people variables - one is an instance variable and the other is a class instance variable.
class Example
# we're in the context of the Example class, so
# instance variables used here belong to the actual class object,
# not instances of that class
self.class #=> Class
self == Example #=> true
#iv = "I'm a class instance variable"
def initialize
# within instance methods, we're in the context
# of an _instance_ of the Example class, so
# instance variables used here belong to that instance.
self.class #=> Example
self == Example #=> false
#iv = "I'm an instance variable"
end
def iv
# another instance method uses the context of the instance
#iv #=> "I'm an instance variable"
end
def self.iv
# a class method, uses the context of the class
#iv #=> "I'm a class instance variable"
end
end
If you want to create variables one time in a class to use in instance methods of that class, use constants or class variables.
class Example
# ruby constants start with a capital letter. Ruby prints warnings if you
# try to assign a different object to an already-defined constant
CONSTANT_VARIABLE = "i'm a constant"
# though it's legit to modify the current object
CONSTANT_VARIABLE.capitalize!
CONSTANT_VARIABLE #=> "I'm a constant"
# class variables start with a ##
##class_variable = "I'm a class variable"
def c_and_c
[ ##class_variable, CONSTANT_VARIABLE ] #=> [ "I'm a class variable", "I'm a constant" ]
end
end
Even so, in the context of your code, you probably don't want all your instances of Family_Type1 to refer to the same Policemen and Accountants right? Or do you?
If we switch to using class variables:
class Family_Type1
# since we're initializing ##people one time, that means
# all the Family_Type1 objects will share the same people
##people = [ Policeman.new('Peter', 0), Accountant.new('Paul', 0), Policeman.new('Mary', 0) ]
def initialize(*ages)
##people.zip(ages).each { |person, age| person.age = age }
end
# just an accessor method
def [](person_index)
##people[person_index]
end
end
fam = Family_Type1.new( 12, 13, 14 )
fam[0].age == 12 #=> true
# this can lead to unexpected side-effects
fam2 = Family_Type1.new( 31, 32, 29 )
fam[0].age == 12 #=> false
fam2[0].age == 31 #=> true
fam[0].age == 31 #=> true
The runtime initialization can be done with metaprogramming, as Chirantan said, but if you are only initializing a few classes, and you know what their name is, you can also do it just by using whatever you read from the file:
PARAMS = File.read('params.csv').split("\n").map { |line| line.split(',') }
make_people = proc do |klasses, params|
klasses.zip(params).map { |klass,name| klass.new(name, 0) }
end
class Example0
##people = make_people([ Fireman, Accountant, Fireman ], PARAMS[0])
end
class Example1
##people = make_people([ Butcher, Baker, Candlestickmaker ], PARAMS[0])
end
Assuming you want to create different classes per type/array size at runtime:
If (like in Python) a Ruby class is defined when executed (I think it is), then you can do this:
Define your class inside a function. Have the function recieve array size and type as parameters and return the class in its result. That way, you have a sort of class factory to call for each definition in your spec file :)
If on the other hand you want to just initialize #params based on actual data, keep in mind, that Ruby is a dynamically typed language: Just reassign #params in your constructor to the new array!

Resources