How to Create Instance of Modified Object with Added Methods? - ruby

I'm trying to dynamically add methods to a single instance of a class, and then be able to instantiate new objects of that modified class.
Below is a sketch of what I'm trying to accomplish.
class A
def add_new_method
define_singleton_method(:new_method) { puts("hi") }
end
end
a = A.new
a.add_new_method
B = a.singleton_class
b = B.new
Obviously, the above is reasonably impossible with those methods since you clearly shouldn't be able to create a new instance of a singleton. What is the right way to accomplish the same?

You can't instantiate singleton classes in Ruby but you can write some code to get the feeling you are doing it.
I would use a class method as the following example
class A
def add_new_method
define_singleton_method(:new_method) { puts("hi") }
self
end
def self.Enhanced
new.add_new_method
end
end
b = A.Enhanced
b.new_method
# hi

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

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)

Saving instance list in class variable

I want to store newly created Person instance inside the class variable objects, but not sure how to reference the current instance from the constructor.
class Person
##objects = {}
def initialize(key)
##objects[key] = something
end
Ideally, the result is to be able to access the dictionary of Person objects through Person.objects
Simply, in constructor function, self will refer to the current instance.
class Person
##objects = {}
def initialize(key)
##objects[key] = self
puts self # it will print the id of the current instance
end
end
Same way, if you write self in a class method, it will refer to the the class.
But from your question, you seem to be doing something like Person.objects, and it won't work, and will output the following line:
NoMethodError: undefined method `objects' for Person:Class
So, you need to write a class method for it to let the outside world access objects.
def self.objects
##objects
end
Well, there are other ways as well to access the class variables, please have a look at this question.

Does defining initialize for a singleton class make sense in Ruby?

I anticipate that I am not trying to do anything practical here, just trying to understand some deeper Ruby concepts.
Supppose I have the following code
class Bookshelf
#book_qty = 100 # class instance var
class << self
attr_accessor :books_qty
end
def initialize
#book = "This book is in every object as an object instance variable"
end
# so far so good, but what happens with...
def self.initialize # what would be this called on ?
puts " and at what step would this be printed, if ever?"
# I thought it would be printed when the class code is parsed first time,
# but no
end
# or also
class << self
def initialize
puts "same here"
end
end
end
I know it might not make sense or might be too intricately related on how Ruby internals work, but, if by chance anyone has been puzzled too by this and knows the answer... please share it :)
There is no purpose to defining initialize for the singleton class (whether you use def self. or class << self). initialize is only called by Class#new and...
Bookshelf.singleton_class.new
# TypeError: can't create instance of singleton class
that's not allowed.
If you want code to be executed the first time a class is parsed, just put it in the class
class Bookshelf
puts "class defined!"
end
In some cases, it does make sense to define a custom constructor, but I wouldn't call the method initialize, since the instance method you override to customise initialisation is also called initialize. That would be a little confusing.
def self.initialize # what would be this called on ?
If you define a method like this, you can invoke it by sending the method directly to the class:
Bookshelf.initialize
Same thing applies for methods defined inside class << self.
As mentioned, it does make sense to define custom constructors for a class. Sometimes just for readability's sake:
class Bookshelf
attr_accessor :book_qty
def self.with_quantity(quantity)
new(quantity)
end
def initialize(quantity)
self.book_qty = quantity
end
end
Now you could instantiate a Bookshelf like this:
bs = Bookshelf.with_quantity 100
bs.quantity # => 100
You actually call .new on the singleton class of Bookshelf. And #initialize is basically just a hook for the instance to tweak its initialisation.
How you could access class instance variables
class Bookshelf
#book_qty = 1000
class << self
attr_accessor :book_qty
end
end
Bookshelf.book_qty # => 1000
Bookshelf.book_qty = 2000
Bookshelf.book_qty # => 2000

What is created by Class.new.new

Class.new.new
# => #<#<Class:0x44f3a2>:0xd7244e>
I am curious to know what is created. Is it an object of object? Any technical explanation will be appreciated.
With Class.new you are creating a new class. In fact not only you can create classes via the common syntax:
class Bird
def is
"word"
end
end
but you can also use Class::new like this:
Bird = Class.new do
def is
"word"
end
end
In the above example you can run Bird.new.is and it will return "word" just like in the first example.
It is useful to create anonymous classes or classes that you can rename at your will.
In your case:
Class.new.new
By simply calling Class.new you are creating a new anonymous class with no custom methods or instance variables which is then later instantiated via the second new method.
You can follow it through in the console:
irb(main):011:0> c = Class.new
=> #<Class:0x000000028245e0>
c is a new class.
irb(main):012:0> c.new
=> #<#<Class:0x000000028245e0>:0x0000000282a170>
Calling c.new returns you a new instance of the new class you just created.
Class.new creates and returns Class instance (which is class). If you call on it new again, previously created class will be instantiated.
my_class = Class.new # makes a new class which is a subclass of Object
my_instance = my_class.new # makes a new instance object of the class

Resources