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
That code doesn't actually work since you're not calling any of methods that set the variables. Its kind of a mess and I think I can explain this better with more idiomatic examples.
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 (and the insider also):
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. If you have an instance method that changes the behavior of all other instances of the same class it will often lead to a large amount of swearing.
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. I don't think I have ever seen anyone redefine the method - just because you can doesn't mean you should.
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
Related
class Restaurant
attr_accessor :average_rating, :city
def initialize(city, name)
#city = city
#name = name
#number_of_ratings = 0
#sum_of_ratings = 0
end
def rate(new_rate)
#number_of_ratings += 1
#sum_of_ratings += new_rate
#average_rating = #sum_of_ratings.to_f / #number_of_ratings
end
def self.filter_by_city(restaurants, city)
restaurants.select { |restaurant| restaurant.city == city }
end
end
The above code is part of a challenge and I kept failing the tests for the #filter_by_city method. I checked the solution and the only difference was the self. prior to the method name. I've tried to understand what self does exactly but it's difficult to understand without context. In this particular class method, what exactly is self doing? I know what the body of the method is doing i.e. the filtering of the restaurants by city, but how does it run exactly?
self is the class Restaurant. def self.method is how you implement a method on the class itself rather than an instance of the class. Restaurant.filter_by_city(...) rather than Restaurant.new.filter_by_city(...).
self changes in Ruby depending on context. Within a method, self is the object the method was called on.
Within the class Restaurant block, and outside of any method, self is the Restaurant object which is a Class object. Everything is an object in Ruby. Everything.
You can also do this by declaring a block where the class is the instance.
class << self
def filter_by_city(restaurants, city)
restaurants.select { |restaurant| restaurant.city == city }
end
end
Normally you'd use this syntax if you have a lot of class methods.
See Self in Ruby: A Comprehensive Overview for more.
When defining a method in ruby you can optionally explicitly define that method's receiver using def <receiver>.<method> syntax instead of plain def <method>
object = Object.new
def object.foo
:foo
end
object.foo #=> foo
The receiver must either be a singular reference OR an expression (but it must be enclosed by brackets):
a = [Object.new, Object.new]
def (a.first).foo
:foo
end
def (a[1]).bar
:bar
end
a[0].foo #=> :foo
a.last.bar #=> :bar
a.first.bar #=> undefined method
When receiver is defined, the method is defined directly on the receiver's singleton class, ignoring the context in which the method is defined:
class A
o = Obejct.new
def o.foo
end
end
A.new.foo #=> undefined method
Even though method foo was defined in class A body, it is not available to its instances because of the explicit receiver
self is a ruby keyword returning the current "context". Inside the methods, self is (usually) a receiver of the call, and inside the module self returns that module. So:
module Wrapper
module SomeModule
puts self.name
end
end
will print Wrapper::SomeModule.
This means that:
class A
def self.foo
end
end
Is exactly the same as:
class A
def A.foo
end
end
So, the method is defined directly on A and can only be called directly on the class as A.foo, rather than on its instances.
Is there a way to implement monkey patching while an object is being instantiated?
When I call:
a = Foo.new
Prior to the instance being instantiated, I would like to extend the Foo class based on information which I will read from a data store. As such, each time I call Foo.new, the extension(s) that will be added to that instance of the class would change dynamically.
tl;dr: Adding methods to an instance is possible.
Answer: Adding methods to an instance is not possible. Instances in Ruby don't have methods. But each instance can have a singleton class, where one can add methods, which will then be only available on the single instance that this singleton class is made for.
class Foo
end
foo = Foo.new
def foo.bark
puts "Woof"
end
foo.bark
class << foo
def chew
puts "Crunch"
end
end
foo.chew
foo.define_singleton_method(:mark) do
puts "Widdle"
end
foo.mark
are just some of the ways to define a singleton method for an object.
module Happy
def cheer
puts "Wag"
end
end
foo.extend(Happy)
foo.cheer
This takes another approach, it will insert the module between the singleton class and the real class in the inheritance chain. This way, too, the module is available to the instance, but not on the whole class.
Sure you can!
method_name_only_known_at_runtime = 'hello'
string_only_known_at_runtime = 'Hello World!'
test = Object.new
test.define_singleton_method(method_name_only_known_at_runtime) do
puts(string_only_known_at_runtime)
end
test.hello
#> Hello World!
Prior to the instance being instantiated, I would like to extend
Given a class Foo which does something within its initialize method:
class Foo
attr_accessor :name
def initialize(name)
self.name = name
end
end
And a module FooExtension which wants to alter that behavior:
module FooExtension
def name=(value)
#name = value.reverse.upcase
end
end
You could patch it via prepend:
module FooPatcher
def initialize(*)
extend(FooExtension) if $do_extend # replace with actual logic
super
end
end
Foo.prepend(FooPatcher)
Or you could extend even before calling initialize by providing your own new class method:
class Foo
def self.new(*args)
obj = allocate
obj.extend(FooExtension) if $do_extend # replace with actual logic
obj.send(:initialize, *args)
obj
end
end
Both variants produce the same result:
$do_extend = false
Foo.new('hello')
#=> #<Foo:0x00007ff66582b640 #name="hello">
$do_extend = true
Foo.new('hello')
#=> #<Foo:0x00007ff66582b280 #name="OLLEH">
Note: This was the best title I could think of that wouldn't make this question seem like a dup. Please feel free to edit if it does not capture the point of the question
So I've been reading Advanced Rails and I had a question that wasn't fully answered by these posts:
When to use `self.foo` instead of `foo` in Ruby methods
In Ruby. How do I refer to the class from within class << self definition?
Why use "self" to access ActiveRecord/Rails model properties?
In my understanding, within a class definition, self refers to the object referenced by the pointer klass, so for the example of an eigenclass:
class A
class << self
def to.s
"Woot"
end
end
end
is the exact same as:
class << A
def to.s
"Woot"
end
end
This is because as you open up the class, ruby is creating a Class:A (virtual) class and assigning A's klass pointer to it. To my understanding this means that within the context of this class definition self == A
My question is (assuming my understanding as I've described it above is accurate) why then in an ActiveRecord::Base subclass in Rails does the use of self seem to be merely a disambiguation of the instance and not the class itself?
For example, if I have a class: A < ActiveRecord::Base with an attribute name, I cannot call A.name = "blah" outside the class definition. However, I CAN use ( and indeed MUST use ) self in assignment within the class definition like so: self.name = "blah".
If the use of self in Ruby refers to the Class object and not the instance, why does this work in ActiveRecord and how does Rails make the distinction?
Everything is a method
Anything you do with an object is a method. When you have a class like this:
class A
attr_accessor :foo
end
obj = A.new
obj.foo = 3
obj.foo #=> 3
It might feel weird, but you didn't make any assignment here. obj.foo = 3 is only a synthatic sugar for obj.send(:foo=, 3) so in fact, it executes foo= method. obj.foo is a method as well, it simply returns a value. This is because all the instance variables are private, and there is no other way to interact with them than inside the method. attr_accessor :foo is just a shortcut for:
def foo=(value)
#foo = value
end
def foo
#foo
end
Now, when ruby see any identifier like foo, it needs to find out what it is. Firstly it assumes it is a local variable, and if not, it tries to execute it as method on current self (see below). This means that:
class A
attr_accessor :foo
def bar
foo = 'bar'
end
end
a = A.new
a.bar #=> 'bar'
a.foo #=> nil
What happened? Interpretor first uses foo as an instance variable, it is not defined. But there is an assignment next to it, so it defines a new instance variable and make an assignment. If we want to use our setter we need to tell interpreter that it is not an instance varible:
class A
attr_accessor :foo
def bar
self.foo = 'bar'
end
end
a = A.new
a.bar #=> 'bar'
a.foo #=> 'bar'
I lied. Everything is an object
about self
self in ruby is very similar to this in javascript - it means different things in different context, and it can be easily changed at any point (obviously, not recommended).
First thing you need to know is that in Ruby every class is an object, namely it is an instance of class Class. When you do
class A
it is (almost, method below uses block and has access to external scope) equivalent to:
A = Class.new
self within a context of a class is always a class itself.
class A
self #=> A
def self.bar #=> this is class method
self #=> A
end
def foo #=> this is instance method
# We are no longer in context of class, but in context of the instance, hence
self #=> instance of A
end
end
Now, for any mutable object you can defined a singleton class. This is sort of weird concept - it is an extra subclass of the original class which only has a single instance. Since all the methods come from the class, this allows you to define extra methods on a particular instance of the method without affecting other object. In most of the cases, instance class is not needed and it is created when you access it for the first time. There are couple of ways to open it:
object.singleton_class do
self #=> instance class
end
class << object
self #=> instance class
end
Any method you defined within instance class is accessible only on that particular instance, hence this:
class A
self #=> A
class << A
# we are now in the instance class of A (which is an instance of Class class)
def foo
# So this is an instance method, however our instance is a class A
self #=> A
end
end
I have a class like so:
Railsapp/lib/five9_providers/record_provider.rb:
class Five9Providers::RecordProvider < Five9Providers::BaseProvider
def add_record_to_list
variable = 'test'
end
end
Then, in a controller I have this:
Railsapp/app/controllers/five9_controller.rb:
class Five9Controller < ApplicationController
def import
record_provider = Five9Providers::RecordProvider.new()
record_provider.add_record_to_list
puts Five9Providers::RecordProvider::variable
end
end
However, calling my controller method import just returns:
NoMethodError (undefined method 'variable' for Five9Providers::RecordProvider:Class)
How can I access variable from the recover_provider.rb class in my five9_controller.rb class?
EDIT:
Even when using ##variable in both my record_provider and my five9_controller, I still can't access that variable. I am calling it like so: puts ##variable.
As written, you cannot. variable is local to the instance method and can't be accessed by any Ruby expression from outside the method.
On a related point, the term "class variable" is typically used to refer to variables of the form ##variable.
Update: In response to your "Edit" statement, if you change variable to ##variable in your class, then there are techniques available to access that variable from outside the class, but a naked reference to ##variable isn't one of them. Carefully read the answers to the question you cited in your comment for more information.
Best way is to set and get the value using methods. Below is a sample code
class Planet
##planets_count = 0
def initialize(name)
#name = name
##planets_count += 1
end
def self.planets_count
##planets_count
end
def self.add_planet
##planets_count += 1
end
def add_planet_from_obj
##planets_count += 1
end
end
Planet.new("uranus")
Plant.add_planet
obj = Planet.new("earth")
obj.add_planet_from_obj
I am trying to learn Ruby by reading tutorials on Class Variables.
The code creates a "Worker" object, a class variable is created to keep track of which instance of Worker was created by the user last.
I have copied the code from the author but I get the error:
undefined method `latest' for Worker:Class (NoMethodError)
The code I have found is:
class Worker
attr_writer :number_of_jobs
def initialize(name, job)
#name = name
#job = job
##latest = #name
##job = #job
puts "Lets get started"
end
def new_job(job)
#job = job
puts "I moved to #{job}!"
self.fetch_info
end
def name_update(name_new)
#name = name_new
puts "My new name is #{name_new}."
self.fetch_info
end
def fetch_info
puts "I'm #{#name} in #{#location}."
end
def job_score
return "#{#number_of_jobs * 10}Fg"
end
protected
def are_you_worker?(guest_name)
puts "Yes #{guest_name}, I am a worker!"
return true
end
private
def text_a_message(message)
puts message
end
public
def tell_friend(where)
text_a_message("I just applied to #{where}")
end
end
#running the code
Worker1 = Worker.new("Steve", "Support")
Worker2 = Worker.new("Alan", "PA")
puts Worker.latest
Can anybody see why?
The Class variables are private inside this class which is causing a problem. Therefore accessing the Worker.latest variable will cause an error as it isn't visible from instances outside of the class (but it is created and set).
Additionally, attributes are part of the object not the class so you shouldn't have an attribute for this class . In native Ruby the class variables are not accessible from outside EXCEPT through a class method (there are extensions in Rails for them tho).
Hope that helps
IMHO, this is one of the more frustrating things about Ruby's class system. The privacy of class variables is true even for subclasses. In any case, the immediate solution to your problem is to add a class method like so:
class Worker
def self.latest
##latest
end
end