Given:
# typed: true
module X
class Y
end
end
module X
class X
def y
X::Y
end
end
end
Sorbet gives error :
editor.rb:6: Unable to resolve constant Y https://srb.help/5002
6 | X::Y
Why sorbet given error even though X::Y is defined?
Playground link
Because this is how constant lookup works in ruby. Roughly, it tries to resolve the name starting from innermost nesting. Therefore, in a your X::Y it resolves X to class X, which doesn't have Y.
Use ::X::Y instead, to force lookup from top level.
Related
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
module Api
module V1
class ABC
end
end
end
class Api::V1::ABC
end
Any different between these to declare a class?
Any pros and cons?
Yes, there is a difference while you will be doing constant lookups. Module::nesting will be helpful here to things get cleared for you.
module Api
module V1
class ABC
p Module.nesting
end
end
end
# >> [Api::V1::ABC, Api::V1, Api]
module Api
module V1
end
end
class Api::V1::ABC
p Module.nesting
end
# >> [Api::V1::ABC]
Module.nesting returns the lexically enclosing classes/modules which are searched before Ruby walks up the class/module hierarchy to find the constant.
It means, with the following code :
module Api
module V1
X = 12
end
end
X = 10
class Api::V1::ABC
p X
end
Api::V1::ABC.superclass # => Object
# >> 10
While constant looks-up will be happening, it will first search to the array of constants, which has been given by Module.nesting, if not found then, up to the ancestor chains/included modules. It means following to the Api::V1::ABC.ancestors outputs.
Now in the above example, you can see that value of X printed, which is defined in the Object, not inside the API::V1. The reason as I said above.
Now coming to another example below :-
X = 10
module Api
module V1
X = 12
class ABC
p X
end
end
end
# >> 12
Here the constant look up will follow through the array [Api::V1::ABC, Api::V1, Api]. You can see the output found as 12, as Api::V1 has a constant X defined into it.
Thus we can say - Yes there is a difference between the 2 declarations.
For example we have:
module X
def test
"X"
end
end
module Y
def test
"Y"
end
end
class L
include X
end
L.new.test #=> "X"
class L
include Y
end
L.new.test #=> "Y"
class L
include X
end
L.new.test #=> "Y"
Here's the strange thing. Why the last include didn't change the method to X's method?
Ruby does not allow to include one module twice, attempt to do it second time is just ignored.
From documentation of Module#append_features which is used by Module#include under the covers:
Ruby's default implementation is to add the constants, methods, and module variables of this module to mod if this module has not already been added to mod or one of its ancestors.
I cannot figure out how come the object is so smart to disregard the following block of code even if performed for the first time.
The method it is searching for is on each module
class A
include B,C,D,B
end
Does ruby keeps an array of module names on the side (as it obviously calls D)?
I'm not 100% I understand your question but I try my best...
Ruby actually remembers which modules are included into a class and merges these modules into the lookup path for methods. You can ask a class for its included methods my using A.included_modules
Methods from included modules are put on top of the modules defined in the current class. Please see this example:
class X
def foo
puts 'Hi from X'
end
end
module AnotherFoo
def foo
puts "Hi from AnotherFoo"
super
end
end
class Y < X
include AnotherFoo
end
y = Y.new
y.foo
# Hi from another foo
# Hi from X
class Y < X
include AnotherFoo
def foo
puts "Hi from Y"
super
end
end
y.foo
# Hi from Y
# Hi from another foo
# Hi from X
You can see the method resolution order: the child class -> the included modules -> the parent class. You can also see that modules are always included only once. So if a class already includes a module, it will not be re-included again.
I'm trying to write a program that dynamically defines ruby classes based on configuration read from a file. I know I can use Class.new to do this. Here's an example program:
x = [1,2,3]
Test = Class.new do
##mylist = x
def foo
puts ##mylist
end
end
Test.new.foo
When I run this I get the following output (running with ruby 1.9.3p0):
c:/utils/test.rb:4: warning: class variable access from toplevel
c:/utils/test.rb:7: warning: class variable access from toplevel
1
2
3
Does anyone know what causes these warnings and how I can get rid of them?
I've tried replacing the line tjhat does
##mylist = x
with this
class_variable_set(:##mylist, x)
But when I do that I get this error instead:
c:/utils/test.rb:7: warning: class variable access from toplevel
c:/utils/test.rb:7:in `foo': uninitialized class variable ##mylist in Object (NameError)
from c:/utils/test.rb:11:in `'
Thanks in advance!
This isn't doing what you think it's doing. Since you're not creating a class with the class keyword, your class variable is being set on Object, not Test. The implications of this are pretty huge, which is why Ruby is warning you. Class variables are shared between ancestors, and objects usually inherit from Object.
Just to remove this warning, you should use class_variable_set method:
x = [1,2,3]
Test = Class.new do
class_variable_set(:##mylist, x)
def foo
puts ##mylist
end
end
Rather than defining your "mylist" class variable on the class when declaring the class, you can declare class level variables on it later on as below. Two different methods are shown. The former only works in 1.9, the latter works in both versions, but is less idiomatic.
x = [1,2,3]
Test = Class.new do
def foo
puts ##mylist
end
end
# ruby 1.9.2
Test.class_variable_set(:##mylist, x)
# ruby 1.8.7
Test.class_eval {
##mylist = x
}
Test.new.foo
if you want only to suppress this warnings you can use
$VERBOSE = nil
in top of you code