Get a reference to an object while instantiating it - ruby

I'd like to reference an object while instantiating it in order to pass it to another object I'm instantiating. What I mean:
A.new(B.new(self))
In this case, self would refer to the scope in which I'm actually calling A.new. What I want is for self (or whatever other keyword) to refer to the newly instantiated A object, so that B would have a reference to A. Is there a way to do this?

The way you have written it (A.new(B.new(self))) is impossible, due to a circular reference.
In order to create an instance of A, you need an instance of B; in order to create the instance of B, you need the instance of A.
There are a few ways you tweak the implementation to make this possible, but you must first resolve this chicken-and-egg problem between the A and B. For example:
class A
def initialize
#b = yield(self)
end
end
class B
def initialize(a)
#a = a
end
end
A.new { |a| B.new(a) }
Note that in the above code, a is being initialized first. It is only being yielded in the scope after the object has been created.
Or, here's another way:
class A
def initialize
#b = B.new(self)
end
end
class B
def initialize(a)
#a = a
end
end
A.new
Like above, the instance of A is being created first. But this time, I've done all the initialization in one go rather than building it within the new() methed call.
One final example:
class A
attr_writer :b
def initialize
end
end
class B
def initialize(a)
#a = a
end
end
A.new.tap { |a| a.b = B.new(a) }
In this example, I have fully initialized a before defining its attribute of b. This could just as easily have been written in two lines of code, with a regular variable instead of the closure:
a = A.new
a.b = B.new(a)

Related

Ruby Inheritance Puzzle: How to Properly Call self.class.new in Parent Method

I have a library that I would use in an app by using a class that wraps the library object in a new class by inheriting from it and adding a few instance variables. I can change the library code if need be. Here is the problem:
class A
def process_it
# Make a new instance
aa = self.class.new
do_something_to(aa)
end
def do_something_to(item)
item
end
end
class B < A
def initialize(extra = "Default extra")
#extra = extra
super()
end
def extra
#extra
end
end
# I want B to inherit A's methods, like #process_it but:
b = B.new("Non-default extra")
puts b.process_it.extra => Default extra
The output should have been "Non-default extra" and the problem is that, in the parent class I call self.class.new but can pass no parameter to it to set #extra. In the call, self.class is B, the inherited class, but when I write the parent class library, A, I cannot predict what, if any parameters, should be passed to self.class.new. Indeed, I might have class C < A with different parameters for initialize.
Is there a proper way to write the code in library A to instantiate a new instance of the self class that takes possible parameters into account?
Will #dup work for you? Instead of aa = self.class.new, change it to aa = self.dup

How to call another classes methods from another class?

I have the following structure
class A
def method1
end
end
class B
#my = A.new
def classATest
#myT.method1
end
def newTest
classATest
end
end
class C
newB = B.new
newB.newTest
end
When I run class C, it gives me the error that it cannot find method1 of Class A (method newtest, calls method classATest, which calls the method1 using a global variable. What am I doing wrong? Is this not allowed?
Your line that says #my = A.new is not doing anything useful. It's making a new object and assigning it as an instance variable of class B, but that kind of variable cannot be used by instances of B without extra effort. You should replace that line with:
def initialize
#myT = A.new
end
Also, you had a typo: you wrote #my in one place and #myT in another.
Alternatively, keep the code the way you have it and replace #my and #myT with the name of a constant, such as MyA. Constants in Ruby start with capital letters, and can be used the way you are trying to use this variable.

Where do methods defined at the top-level exist?

I am learning some metaprogramming and I was stuck trying to find a method. Let's say I have the following class:
class MyClass
def self.my_method
end
def your_method
end
end
With the following code I can search for each method in the object space:
type = Class
name = /^my_method$/
result = ObjectSpace.each_object(type).select do |o|
o.instance_methods(false).grep(name).size > 0 || o.methods.grep(name).size > 0
end
p result
And it finds it showing the following output:
[MyClass]
As the searcher code also searches for instance methods, it shows the same output when looking for your_method.
Even with if I add a singleton method to an object:
mc = MyClass.new
def mc.our_method
end
Just changing the searcher like this:
type = Object
name = /^our_method$/
result = ObjectSpace.each_object(type).select do |o|
o.methods.grep(name).size > 0
end
p result
It also finds it:
[#<MyClass:0x8f86760>]
The question is, how do I find a method defined in the top level object? This method:
def hidden
end
Besides, which is the current class when defining a method like this?
Which is the current class when defining a method like this?
We can easily figure out what object we’re in by inspecting self in this top level scope:
self #=> main
self.class #=> Object
So we’re not in a Class, but an instance of Object which is dubbed “main”.
How do I find a method defined in the top level object?
This is where it gets interesting. The top-level scope object in Ruby behaves specially, but it’s relatively easy to discover where a method here defined lives:
def foo; :bar; end
method(:foo).owner #=> Object
Object.new.foo #=> NoMethodError: private method `foo' called
Object.new.send(:foo) #=> :bar
So methods defined at the top-level are made (private*) instance methods of Object. The reason your ”searcher” cannot find it is because these methods are private, and neither methods nor instance_methods include private methods, instead you need private_methods and private_instance_methods:
Object.instance_methods.include?(:foo) #=> false
Object.private_instance_methods.include?(:foo) #=> true
* Note that Pry (at least v0.10.1) alters this to make methods defined at top-level in its REPL public.
If you had this:
def my_method() end
class A
def self.my_method() end
end
class B < A
def my_method() end
end
class C
def my_method() end
end
and wanted to find methods named 'my_method' that you've created, you could do this:
ObjectSpace.each_object(Class).select do |o|
o.instance_methods(false).include?(:my_method)
end
#=> [C, B]
ObjectSpace.each_object(Class).select do |o|
o.methods(false).include?(:my_method)
end
#=> [A]
ObjectSpace.each_object(Class).select do |o|
o.private_instance_methods(false).include?(:my_method)
end
#=> [Object]

Pass in self when I initiate the class

When I call A.new inside of B, is there a way to automatically get certain objects without specifying it? I want to pass self from B into A automatically without specifying it.
class A
initialize object=target-self
end
end
class B
A.new
end
Then, I don't have to type this every time I want self to be passed in default unless I specify another class.
A.new self
This is straightforward. All you have to do is write:
class A
def initialize(object = self)
# work with object
end
end
There is always a value for self in Ruby. In the example you provided, it will evaluate to the B class. To get it to be an instance of the class, just call the method during initialization:
class B
def initialize
A.new # self is a B instance here
end
end

Ruby- read the value of a variable in another class?

I have something like the following
class A
def initialize
#var = 0
end
def dosomething
#var+=1
end
end
class B < A
def initialize
super
end
def func
puts #var
end
end
The problem is when I call
a = A.new
a.dosomething
b = B.new
the value which #var returns is 0 how would I change my code so it would return the "new" value of var (1)?
Quick answer, for if you actually understand Classes, Inheritance and Objects : replace #var (an instance variable, and therefore different in a and b) with ##var (a class variable, and therefore the same in all instances of class A).
Otherwise, your question indicates you have a fundamental misunderstanding of what's going on with classes, objects and inheritance.
Your code does the following:
Defines a class, called A. This is essentially a blueprint from which you can create objects.
Declares that when an object of type A is created, that object should be given it's own private copy of an attribute, called var, which is set to 0.
Declares that objects of type A can be asked to dosomething, which increases the value of that object's var by 1.
Defines a class called B, which is a special case of an A
Therefore, in your second snippet, you create an object a, which is an A. It has its own attribute called var, which is set to 0 and then incremented. You then create b, which is a B (and is therefore also an A). b has its own attribute called var, separate from a's var, which is set to 0.
Variables like #var are called instance variables because they are unique to every instance of a class. You've created two separate instances, a and b, and they have their own instance variables.
You can use class variables if you want to see them in your subclass:
class A
##var = 0
def dosomething
##var += 1
end
end
class B < A
def func
puts ##var
end
end
You can also use class-level accessor methods:
class A
class << self
attr_accessor :var
def dosomething(n)
self.var = n
end
end
end
class B < A
def func
puts A.var
end
end
irb(main):031:0> A.dosomething(5)
=> 5
irb(main):032:0> b = B.new
=> #<B:0x2b349d>
irb(main):033:0> b.func
5
Note that inheritance is not needed for this to work.
You could use class variables:
class A
##var = 0
def dosomething
##var += 1
end
end
class B < A
def func
puts ##var
end
end
a = A.new
a.dosomething
a.dosomething
b = B.new
b.func # => 2
#var is an instance variable, each instance of this class has its own value of this variable. So it is correct that b does return 0, since it is a different instance compared to a.
To update the value in b use:
b = B.new
b.dosomething
Here is more information on instance variables.
Or if you want a class variable (ie a variable which is the same in all instances of that class), use ##var. Then your given example works.
Here is some more information on class variables.
Which solution you need, depends on your application.
You need to use class variables, not instance variables. Try something like this:
class A
##var = 0
def dosomething
##var+=1
end
end
class B < A
def func
puts ##var
end
end

Resources