CuriousMind
CuriousMind

Reputation: 34135

What this Ruby does with Class.new to create class,

Please consider the following code:

module MyClass
 def foo
  "method"
 end
end

now, I can instantiate a new class of this by usual

@my_new_class = MyClass.new

or, I can do some meta-programming magic

@my_new_class = Class.new { include MyClass }.send :new 

Questions is what is the difference between the two?

Upvotes: 0

Views: 4497

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110675

Your naming of the module is akin to sue = Boy.new, so I've changed it to befit its heritage. I trust you won't mind.

module MyModule
 def foo
  "method"
 end
end

Let's first gather some basic information:

Module.class                       #=> Class
Module.ancestors                   #=> [Module, Object, Kernel, BasicObject]
Module.methods.include? :new       #=> true
Module.new                         #=> #<Module:0x000001022050c8>

As you see, Module is an instance of Class and has a method :new. Now let's look at the module you created:

MyModule.class                     #=> Module
MyModule.ancestors                 #=> [MyModule]
MyModule.instance_methods          #=> [:foo]
MyModule.methods.include? :new     #=> false

It is an instance of Class and has just one instance method (:foo), which you created. It has no ancestors, so it does not inherit any methods. Importantly, instances of Module do not have a method :new. If we try to invoke it, therefore, the results are predictable:

my_new_module = MyModule.new
  #=> NoMethodError: undefined method `new' for MyModule:Module

End of story for approach #1`.

Now gather information related to your second approach:

my_new_instance = Class.new { include MyModule }.send :new

I've changed the name of the variable my_new_class to my_new_instance, for reasons that will soon become apparent. We can write this in two steps, like this:

Class1 = Class.new { include MyModule }
Class1.instance_methods.include? :foo #=> true
Class1.methods.include? :new          #=> true
Class1.method(:new).owner             #=> Class

So we confirm Class1 was constructed properly, has the instance method :foo and inherits the class method :new from Class.

my_new_instance = Class1.new          #=> #<Class1:0x00000101a1edc8>
my_new_instance = Class1.send :new    #=> #<Class1:0x0000010204eab8>
my_new_instance.class                 #=> Class1
my_new_instance.is_a? Class           #=> false
my_new_instance.foo                   # (prints) "method"
my_new_instance.send :foo             # (prints) "method"

We see that my_new_instance is indeed an instance of Class1 (which can be created by either of the methods shown), and invokes :foo in either of the two ways shown.

Upvotes: 1

Uri Agassi
Uri Agassi

Reputation: 37409

The code above is (almost) equivalent to:

MyNewClass = Class.new { include MyClass }
@my_new_class = MyNewClass.new

Which is like

class MyNewClass
  include MyClass
end
@my_new_class = MyNewClass.new

using Class.new declares an anonymous new class on the fly:

Creates a new anonymous (unnamed) class with the given superclass (or Object if no parameter is given). You can give a class a name by assigning the class object to a constant.

If a block is given, it is passed the class object, and the block is evaluated in the context of this class using class_eval.

Upvotes: 5

Related Questions