Ruby - NameError: uninitialized constant when accessing name of my derived class - ruby

I have class B derived from class A, like:
class A
..
end
class B < A
...
end
In another file I defined a variable M
M = B
Then I got the error
"NameError: uninitialized constant B".
I just found that if I write
M=A
M=B
Then it's OK. It seems I have to initialize A in some way.

Since the files have no relationship with rails lets assume that they are two seperate files on in any folder on your system.
Assuming they are in the same directory.
class_def.rb:
class A
def self.talk
puts 'hello'
end
end
class B < A
def self.talk
super()
puts 'world'
end
end
runner.rb:
require './class_def.rb'
M=B
M.talk
calling ruby runner.rb should produce the output
hello
world
However I noticed the same error using RubyFiddle
w/ a call to the method http://rubyfiddle.com/riddles/1d8e2
w/o a call to the method http://rubyfiddle.com/riddles/1d8e2/

Related

In Ruby, is it possible to have both Parent and child class in different Module

Is it possible to inherit a class which is in a separate module like Python allows? is there any other alternative that could be done without changing the structure drastically
File 1->
Module X
Class Y
end
end
File 2->
require relative 'path/to/File 1'
Module A
Class B
end
end
I want to inherit Y into B
It's possible like this,Parent class Hello is in module One and child class Hi is in module Two.
module X
class Y
def call_me
puts 'Hello Class'
end
end
end
module A
class B < X::Y
def call_me
puts 'Hi Class'
end
end
end
A::B.new.call_me
output
Hi Class
Is it possible to inherit a class which is in a separate module like Python allows?
Classes aren't "in modules", so the "module" part of your question is actually irrelevant. The rest of your question essentially boils down to "Is it possible to inherit a class", which is possible.
Constants can be namespaces within modules, but that does not create any relationship whatsoever between the class and the module. In your example, the class referenced by the constant Y is not in the module referenced by the constant X and the class referenced by the constant B is not in the module referenced by the constant A.
The constant Y is namespaced inside the module referenced by the constant X, but that only creates a relationship between the constant and the module, not between the class referenced by the constant and the module.
You can prove the fact that there is no relationship easily by simply assigning the class to a different variable:
foo = X::Y
class A::B < foo; end
Note: please don't let the default value of Class#name fool you. The default value of Class#name is indeed the full path of the first constant that the class is assigned to, even if you remove that constant again later:
module M
class C; end
end
module A
B = M::C
end
M::C = nil
M.send(:remove_const, :C)
M = nil
self.class.send(:remove_const, :M)
foo = A::B
A::B = nil
A.send(:remove_const, :B)
A = nil
self.class.send(:remove_const, :A)
# *None* of the constants exist anymore.
foo.name
#=> 'M::C'
# The `Class#name` property still stays the path of the very first constant

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.

Constant lookup

Scenario #1:
In the example below, puts Post::User.foo line prints foo. In other words Post::User returns a global User constant.
class User
def self.foo
"foo"
end
end
class Post
puts Post::User.foo
end
# => warning: toplevel constant User referenced by Post::User
# => foo
Scenario #2:
This second example raises an error because no constant is found.
module User
def self.foo
"foo"
end
end
module Post
puts Post::User.foo
end
# => uninitialized constant Post::User (NameError)
The result of scenario #2 is more intuitive. Why is the constant found in scenario #1? If User constant is returned in scenario #1, why isn't that happening in scenario #2? In scenario #2, Post is a module, so in that case, constant should be searched in Object.ancestors, which should also return User constant, but this is not happening.
The behaviour is caused by the lookup happening inside a module, instead of in a class. (the fact that you are looking up a module inside the module and a class inside the class is irrelevant).
module M
def self.foo; "moo" end
end
class C
def self.foo; "coo" end
end
class A
A::M.foo rescue puts $! # warning: toplevel constant M referenced by A::M
A::C.foo rescue puts $! # warning: toplevel constant M referenced by A::M
end
module B
B::M.foo rescue puts $! # error: uninitialized constant B::M
B::C.foo rescue puts $! # error: uninitialized constant B::C
end
If you look at the C code, both of these call into rb_const_get_0 with exclude=true, recurse=true, visibility=true.
In the case of A::M, this looks up:
tmp = A
tmp::M # (doesn't exist)
tmp = tmp.super # (tmp = Object)
tmp::M # (exists, but warns).
In the case of B::M, this looks up:
tmp = B
tmp::M # (doesn't exist)
tmp = tmp.super # (modules have no super so lookup stops here)
Because exclude is true, the usual edge case for modules jumping to (tmp = Object) is skipped. This means that B::M behaves differently to module B; M; end, which is arguably an inconsistency in ruby.
First of all, please consider that all top-level constants are defined in class Object, because Ruby is an object-oriented language and there can not be a variable or constant that does not belong to some class:
class A; end
module B; end
A == Object::A # => true
B == Object::B # => true
Second, class Object is by default an ancestor of any class, but not of a module:
class A; puts ancestors; end # => [A, Object, Kernel, BasicObject]
module B; puts ancestors; end # => []
At the same time, there's no Object in neither ofModule.nesting:
class A; puts Module.nesting; end # => [A]
module B; puts Module.nesting; end # => [B]
Then, chapter 7.9 of the Matz book mentioned above says that Ruby searches for any constant in Module.nesting and then in ancestors.
Therefore, in your example, it finds the constant User for class Post, because Ruby defines top-level constant User in class Object (as Object::User). And Object is an ancestor of Post:
Object::User == Post::User # => true
But there is no Object in ancestors or Module.nesting of your module Post. The constant Post is defined in class Object as Object::Post but it does not derive from Object because module Post is not an object. Therefore, it does not resolve constant User in module Post through it's ancestors.
At the same time, it will work if you keep User to be a module and turn Post into a class:
module User
def self.foo
"foo"
end
end
class Post
puts Post::User.foo
end
# => foo
That's because class Post can resolve any constant inside it's superclass Object and all top-level constants are defined in Object.
The problem i think is in the difference of inheritance level of Class and Module (ascii image). In fact Class object is inherited from Module object. ancestors that are looked up.
module A; end
p A.ancestors #=> [A]
class B; end
p B.ancestors #=> [B, Object, Kernel, BasicObject]
That means if lookup algorithm step in module A, it can not step out.
So in case of module Post lookup algorithm of Post::User is something like
find const Post (found module Post)
find const User in module Post (not found, go for ancestors of Post)
dead-end - error
and in case of class Post
find const Post (found class Post)
find const User in class Post (not found, go for ancestors of Post)
find const User (found class User with warning)
That's why you can chain classes in one level of namespace and ruby will still find them. If you try
class User
def self.foo
"foo"
end
end
class A1; end
class A2; end
class Foo
p Foo::A1::A2::User.foo
end
#... many of warnings
#> "foo"
is still good.
And
class User
def self.foo
"foo1"
end
end
class A1; end
module A2; end
class Foo
p Foo::A1::A2::User.foo
end
#>.. some warnings
#> uninitialized constant A2::User (NameError)
because lookup algorithm steps in module and got trapped.
TL;DR
class User
def self.foo
"foo"
end
end
class Post1
Post1.tap{|s| p s.ancestors}::User.foo #=> [Post1, Object, Kernel, BasicObject]
end
# ok
module Post2
Post2.tap{|s| p s.ancestors}::User.foo #=> [Post2]
end
# error
Updated
In this situation the place at what Post::User.foo was called does not play much role. It could be also outside of class/module and the behaviour will be the same.
module ModuleA; end
class ClassB; end
class ClassC; end
class E; ModuleA::ClassC; end # error
module F; ClassB::ClassC; end # ok
ClassB::ClassC # ok
ClassB::ModuleA # ok
ModuleA::ClassB # error
And as you pointed out
Sure, ancestors for a module or class differ, but in the case of
module, the additional constant lookup in Object.ancestors happens.
it happens only at moment of "initial" lookup. It means that in case of
module Post; Post::User.foo; end
const Post will firstly be looked in (here I can mistake something) Post.ancestors and because there are no Post::Post, continue to find in Object.ancestors (then it will go with algorithm that i described at top).
Summing up, the context at what you called const matter only for first (most left) const lookup. And then only what object is left considered.
A::B::C
A # consider context
::B # consider only A
::C # consider only B
** class keyword in ruby is actually a method name which accepts Constant and defines a class for it
In case of scenario 1, when puts Post::User.foo gets invoked. Ruby looks whether it has a class Post::User defined (Ruby searches it as a constant, because that's what it is). Once it finds it, foo method gets called.
But in scenario 2 you have defined it inside modules since when puts Post::User.foo gets invoked and there exist no such class as Post::User. Search fails and you get the obvious error message.
You may refer Class Names Are Constants Section in this link for more details.

Getting constant from a string

I got the following class structure:
class A
class B
class C
module M
MY_CONST = 1000
end
end
end
end
I want to get the constant MY_CONST from a string "MY_CONST". I tried M.const_get("MY_CONST") but it doesn't seem to work. Says NameError, wrong constant name. Am trying to do this within the module M. What am I missing?
If the module name was in fact M and not m, the following works in irb for Ruby 2.0:
irb(main):009:0> class A
irb(main):010:1> class B
irb(main):011:2> class C
irb(main):012:3> module M
irb(main):013:4> MY_CONST = 1000
irb(main):014:4> end
irb(main):015:3> end
irb(main):016:2> end
irb(main):017:1> end
=> 1000
irb(main):018:0> M.const_get("MY_CONST")
=> 1000
irb(main):019:0>
Ruby 1.8.7 does give the NameError: uninitialized constant M error, however, unless you fully qualify the module name:
A::B::C::M.const_get("MY_CONST")
=> 1000
So I was doing something really stupid. I was trying to get the name of the method from caller[] but was getting the entire path and didn't regex filter the name of the constant. There is nothing wrong with the way I was trying to derive the constant.

Difference between "class A; class B" and "class A::B"

What's the difference between:
class A
class B
end
end
and
class A
end
class A::B
end
Update: These 2 approaches are not exactly the same.
In the second approach, B doesn't have access to constants defined in A.
Also, as Matheus Moreira correctly stated, in the second approach, A must be defined before A::B can be defined.
What other differences are there?
In Ruby, modules and classes are instances of the Module and Class classes, respectively. They derive their names from the constant they are assigned to. When you write:
class A::B
# ...
end
You are effectively writing:
A::B ||= Class.new do
# ...
end
Which is valid constant assignment syntax, and assumes that the A constant has been properly initialized and that it refers to a Module or a Class.
For example, consider how classes are usually defined:
class A
# ...
end
What is effectively happening is this:
Object::A ||= Class.new do
# ...
end
Now, when you write:
class A
class B
# ...
end
end
What actually happens looks like this:
(Object::A ||= Class.new).class_eval do
(A::B ||= Class.new).class_eval do
# ...
end
end
Here's what is happening, in order:
A new Class instance is asssigned to the A constant of Object, unless it was already initialized.
A new Class instance is asssigned to the B constant of A, unless it was already initialized.
This ensures the existence of all outer classes before attempting to define any inner classes.
There is also a change in scope, which allows you to directly access A's constants. Compare:
class A
MESSAGE = "I'm here!"
end
# Scope of Object
class A::B
# Scope of B
puts MESSAGE # NameError: uninitialized constant A::B::MESSAGE
end
# Scope of Object
class A
# Scope of A
class B
# Scope of B
puts MESSAGE # I'm here!
end
end
According to this blog post, the Ruby core team calls the "current class" the cref. Unfortunately, the author does not elaborate, but as he notes, it is separate from the context of self.
As explained here, the cref is a linked list that represents the nesting of modules at some point in time.
The current cref is used for constant and class variable lookup and
for def, undef and alias.
As the others have stated, they are different ways of expressing the same thing.
There is, however, a subtle difference. When you write class A::B, you assume that the A class has already been defined. If it has not, you will get a NameError and B will not be defined at all.
Writing properly nested modules:
class A
class B
end
end
Ensures the A class exists before attempting to define B.
Two different ways to say the same thing. That thing is that class B is an inner or nested class and can only be accessed through the the A interface.
> class A
.. def say
.... "In A"
....end
..
.. class B
.... def say
...... "In B"
......end
....end
..end
=> nil
> A.new.say
=> "In A"
> B.new.say
=> #<NameError: uninitialized constant B>
> A::B.new.s­ay
=> "In B"
versus
> class A
.. def say
.... "In A"
....end
..end
=> nil
> class A::B
.. def say
.... "In B"
....end
..end
=> nil
> A.new.say
=> "In A"
> B.new.say
=> #<NameError: uninitialized constant B>
> A::B.new.s­ay
=> "In B"
>
They are the same. They are different ways of writing the same thing. The first one is the naive way of writing it, but often, it gets hard to keep track of the nesting once the class/module gets large. Using the second way, you can avoid nesting in the appearance.

Resources