I've came across an issue with instance_eval
and module inclusion.
Please take a look at the following code:
module B
class C
def initialize
puts 'This is C'
end
end
def hello
puts 'hello'
end
end
class A
include B
def initialize(&block)
hello
C.new
instance_eval(&block)
end
end
A.new do
hello
C.new
end
When I run this code I get
hello
This is C
hello
-:25:in `block in ': uninitialized constant C (NameError)
from -:19:in `instance_eval'
from -:19:in `initialize'
from -:23:in `new'
from -:23:in `'
I understand that it has to do with bindings and how methods and objects are binded to class. What I cannot understand is how come I have access to C
within A
, but not when I eval the block
. I would expect them to be in the same scope.
Thanks!
In the below code
A.new do
hello
C.new
end
You are are trying to create the object of C
, as if it is defined in the scope of the class Object
. No, it is not. class C
is defined inside the scope of module B
. And you need to tell that explicitly as B:C.new
.
Above explanation is for the error -:25:in 'block in ': uninitialized constant C (NameError).
What I cannot understand is how come I have access to C within A ?
Module#constants
has the answer for you :-
Returns an array of the names of the constants accessible in mod. This includes the names of constants in any included modules (example at start of section), unless the inherit parameter is set to
false
.
Look at the example :
module M
X = 10
class D; Y = 10 ;end
end
class C
include M
def initialize
p self.class.constants
end
end
C.new
# >> [:X, :D]
Now applying to your example :
module B
class C
def initialize
'This is C'
end
end
def hello
'hello'
end
end
class A
include B
def initialize(&block)
p self.class.constants
C.new # it will work
#instance_eval(&block)
end
end
A.new do
hello
C.new # but this will throw error.
end
# it confirms that C is accessible, within the method.
# That is the same reason you got the output "This is C"
# >> [:C]
Hope that helps.