i am trying to understand the .self pointer , class and instance variable and their uses. I found many useful links but nothing seems to get into my head. like this medium post ::
medium.com - @/@@ vs. self in Ruby
here is the code I tried (filename = hello.rb)
class Person
def instance_variable_get
@instance_var = "instance variable"
end
def class_var
@@class_var = "class variable"
end
def sayhi
puts "this is a ins #{@instance_var}"
puts "this is a cls #{class_var}" #note i removed the @ sign from this.
end
def self.sayhi
puts "this is a ins from class #{@instance_var}"
puts "this is a cls from class #{@class_var}"
end
end
bob = Person.new
bob.sayhi
Person.sayhi
and by executing this i got
cd@CD:~/Desktop$ ruby hello.rb
this is a ins
this is a cls class variable
this is a ins from class
this is a cls from class
how does this all thing work? what am I doing wrong? the result I am expecting from this is
this is a ins instance variable
this is a cls class variable
this is a ins from class instance variable
this is a cls from class class variable
CodePudding user response:
Because the functions which assign values to variables are not called at all. Do something like this. This ensures the variable values are set when you run a method from object or class:
class Person
@instance_var = "instance variable"
@@class_var = "class variable"
def sayhi
puts "this is a ins #{@instance_var}"
puts "this is a cls #{class_var}" #note i removed the @ sign from this.
end
def self.sayhi
puts "this is a ins from class #{@instance_var}"
puts "this is a cls from class #{@class_var}"
end
end
bob = Person.new
bob.sayhi
Person.sayhi
Additionally refer to this: Ruby class instance variable vs. class variable
CodePudding user response:
In Ruby instance variables are just lexically scoped local variables that are scoped to an object instance. They are always "private" but you can provide accessor methods to provide access from the outside:
class Person
def initialize(name)
@name = name
end
def name=(value)
@name = name
end
def name
@name
end
def hello
"Hello, my name is #{name}"
end
end
jane = Person.new("Jane")
puts jane.hello # "Hello, my name is Jane"
jane.name = "Jaayne"
puts jane.hello # "Hello, my name is Jaayne"
Inside the hello
method self
is the instance of Person that you are calling the the method on. We can simply call name
instead of self.name
since its the implicit reciever. We could also write "Hello, my name is #{@name}"
and it would give the exact same result since the getter method is just returning the instance variable.
Ending setter methods with =
is just a convention that lets you use the method with the =
operator. You can actually set instance variables from any method.
class Person
attr_accessor :name
def initialize(name)
@name = name
end
def backwardize!
@name = @name.reverse
end
end
Class variables on the other hand are a whole different cup of tea. The scope of a class variable is a class - but the are also shared with any subclasses. This is an example of why they are best avoided:
class Vehicle
def self.number_of_wheels=(value)
@@number_of_wheels = value
end
def self.number_of_wheels
@@number_of_wheels
end
end
class Car < Vehicle
self.number_of_wheels = 4
end
puts Car.number_of_wheels # 4 - good
class Bike < Vehicle
self.number_of_wheels = 2
end
puts Bike.number_of_wheels # 2 - good
puts Car.number_of_wheels # 2 - WAAAT?!
Setting class variables from an instance method which you have done in your example is not commonly done and would lead to an even bigger WAAAT!??.
Instead of class variables use class instance variables:
class Vehicle
def self.number_of_wheels=(value)
@number_of_wheels = value
end
def self.number_of_wheels
@number_of_wheels
end
end
class Car < Vehicle
# you need to explicitly use self when calling setter methods
# otherwise Ruby will think you're setting a local variable.
self.number_of_wheels = 4
end
puts Car.number_of_wheels # 4 - good
class Bike < Vehicle
self.number_of_wheels = 2
end
puts Bike.number_of_wheels # 2 - good
puts Car.number_of_wheels # 4 - good
This can be a mind boggling concept but just try to remember that in Ruby classes are just instances of Class.
You also seem to be somewhat confused about what instance_variable_get
whould be used for. Its used to violate encapsulation and get the instance variables of an object from the outside.
class Foo
def initialize
@bar = "I'm Foo's secret"
end
end
puts Foo.new.instance_variable_get(:@bar) # "I'm Foo's secret"
Violating encapsulation should not normally be how you structure your code but it can be very useful in some circumstances. It is not called when accessing instance variables from within an object.