How to call another classes methods from another class? - ruby

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.

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 handle optional parameter for Class.new

I create a dynamic class using the Class.new method. But sometimes I call the method with a parameter to create an inherited class - sometimes without.
Option 1:
newclass = Class.new do
...
end
Option 2:
newClass = Class.new(p) do
...
end
The body of the new class is identical. But I cannot call Class.new(p) with undefined p. So I have to create an if statement and then either call Class.new with parameter or without which means I have the duplicate the code for creating the class which is not ideal since every change in my code I have to make twice. Any way how I can get around this?
You can just abstract the Class.new call and leave the block in one place. Something like this:
def create_me_a_class(superklass = Object, &block)
Class.new(superklass, &block)
end
newclass = create_me_a_class(p) do
def my_method
# whatever
end
end

Get a reference to an object while instantiating it

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)

ruby class problem

I have the following ruby code:
class Mp
def initialize
Test.new.mytest
Work.new.mywork
ha
address
end
def ha
puts "message from ha"
end
def address
a='see'
end
end
class Test
def mytest
m=Mp.new
puts "Message from test do you #{m.address}"
end
end
class Work
def mywork
puts "message from work"
end
end
Mp.new
This works fine except the part in def mytest where I'm trying to put out the m.address. Thanks for your help in advance.
Actually the reason it doesn't work has nothing to do with printing the address. It's one line before that:
m = Mp.new this creates a new Mp object. However inside Mp's initialize method a new Test object is created and its mytest method is called. The mytest method then again creates a new Mp object and so on. In other words: Test#mytest and Mp#initialize are mutually and infinitely recursive.
Edit in response to your comment:
I'm not quite sure I understood the question. If you mean "How do I access the variable a which was set in the address method, after address has been called": you don't. a is a local variable that goes out of scope once the method has returned. If you want to set an instance variable use #a = 'see'. # denotes instance variables in ruby. If you want to be able to access that variable from outside the object, use attr_accessor :a to define accessor methods for #a.
An example:
class Mp
attr_accessor :c
def initialize
initialize_variables
puts #c
puts #b # I can access #c and #b here because it's an instance variable
# and I'm within the same object
# puts a # This does not work because a is a local variable from the
# initialize_variables method and no longer in scope
end
def initialize_variables
a = "a"
#b = "b"
#c = "c"
puts a # I can access a here because I'm still inside the method
# where a was defined
end
end
m = Mp.new
# puts m.a
# puts m.b # These don't work because there are no methods a or b
puts m.c # This works because attr_accessor defined a method c which
# returns the content of m's #c variable
You've got an infinite loop. You create a new object of class Mp, which in turn creates a new object of class Test and then calls its mytest method, which in turn creates another object of class Mp, which in turn...

What is "main" in Ruby?

If I run this file as "ruby x.rb":
class X
end
x = X.new
What is the thing that is calling "X.new"?
Is it an object/process/etc?
Everything in Ruby occurs in the context of some object. The object at the top level is called "main". It's basically an instance of Object with the special property that any methods defined there are added as instance methods of Object (so they're available everywhere).
So we can make a script consisting entirely of:
puts object_id
#a = 'Look, I have instance variables!'
puts #a
and it will print "105640" and "Look, I have instance variables!".
It's not something you generally need to concern yourself with, but it is there.
The top-level caller is an object main, which is of class Object.
Try this ruby program:
p self
p self.class
It's the X class. You're invoking the method "new" that creates an object of class X. So, if you run this text as a script, Ruby:
creates a new class X which is a subclass of Object, and which automatically (as a subclass of Object) inherits some methods, of which new is one.
sets up a name x
calls the new method on that new class X, creating an X instance object; x gets a reference to that object.
It's the ruby interpreter running the line
x = X.new
As with many scripting languages, the script is interpreted from top to bottom rather than having a standard entry point method like most compiled languages.
As Charlie Martin said, X.new is a call to the constructor on the X class, which returns an object of type X, stored in variable x.
Based on your title, I think you're looking for a bit more. Ruby has no need for a main, it executes code in the order that it sees it. So dependencies must be included before they are called.
So your main is any procedural-style code that is written outside of a class or module definition.
main is the object in the context of which the top level code is executed. Which means that self at the top level refers to the main object:
$ ruby -e 'p self'
main
And that ruby follows the main's method lookup chain to determine which method to call:
$ ruby -e 'p singleton_class.ancestors'
[#<Class:#<Object:0x00007f9e9fdee230>>, Object, Kernel, BasicObject]
There could be more, but that's what you get from the get-go.
main itself is an instance of Object:
$ ruby -e 'p self.class'
Object
It has a singleton class with 2 methods (a method and an alias to be more precise):
$ ruby -e 'p singleton_class.instance_methods(false)'
[:inspect, :to_s]
$ ruby -e 'p singleton_methods'
[:inspect, :to_s]
It's defined here.
As you can see its to_s method returns "main" (overrides the Object's behavior), which is what you get when you do p self.
You can think that the code you execute is put into a main's method, after which the method is called. Along the lines of:
main = Object.new
class Object
def main.go
<your code here>
end
end
main.go
That is a rough idea. Let me justify it in a couple of steps.
In Ruby you can actually nest methods, but every time you call the outer method, the inner one gets defined/redefined. More importantly, it's defined as an instance method of the enclosing class:
class A
def m
def m2; end
end
end
A.new.m
p A.instance_methods(false) # [:m2, :m]
The same happens here, but the enclosing class in this case is the singleton class of A:
class A
class << self
def m
def m2; end
end
end
end
A.m
p A.singleton_class.instance_methods(false) # [:m2, :m]
And what if we use the def self.<name> notation?
class A
def self.m
def m2; end
end
end
A.m
p A.singleton_class.instance_methods(false) # [:m]
p A.instance_methods(false) # [:m2]
So, self. affects only m, m2 becomes an instance method of A.
Actually, instead of self there can be some random object:
o = Object.new
A = Class.new do
def o.m
def m2; end
end
end
o.m
p o.singleton_class.instance_methods(false) # [:m]
p A.instance_methods(false) # [:m2]
I had to use Class.new because with class o wouldn't be visible inside the class definition.
Or actually I hadn't:
class A
o = Object.new
def o.m
def m2; end
end
o.m
p o.singleton_class.instance_methods(false) # [:m]
p A.instance_methods(false) # [:m2]
end
But let's ignore this branch of thought.
A couple of changes and you get this:
main = Object.new
Object.class_eval do
def main.go
#a = 1
def m2
puts #a
end
m2 # 1
end
end
main.go
p Object.instance_methods(false) # [:m2]
p main.instance_variables # [:#a]
I had to use class_eval for it to not complain that I'm trying to redefine the Object constant.
You can also add:
def main.to_s
"main"
end
main.instance_eval { alias inspect to_s }
for completeness.
Another way is to use global variables:
$main = Object.new
class Object
def $main.go
#a = 1
def m2
puts #a
end
m2 # 1
end
end
$main.go
p Object.instance_methods(false) # [:m2]
p $main.instance_variables # [:#a]
Of course variables main/$main and the go method don't exist. But no more flaws come to mind when I think about this idea. The idea that it works as if your code is put into a main's method and executed by running the method.
Also this kind of explains why methods defined at the top level are visible everywhere:
a.rb:
f
$ ruby -e 'def f; puts "f"; end; require "./a"'
f
Because they become instance methods of Object.
And you can use instance variables, which are instance variables of the main object.
UPD I noticed that you can't define constants (in the usualy way), classes and modules in main.go. So the abstraction appears to be leaky. I might try to amend it:
Object.class_eval do
<your constants, classes, modules, methods>
def main.go
<the rest of the code>
end
end
But at this point I'd rather say, that at the top level self points to the main object, and the current class reference to the Object class. More on class references here.

Resources