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.
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
When using the Module.nesting method, I can return a list of nested Modules from the point of call. The only examples I saw in documentation and elsewhere show the method call being placed and run from within the nested module definition:
module M1
module M2
Module.nesting #=> [M1::M2, M1]
end
end
In the following example:
module A
module B; end
module C
module D
# I want to access the name-space chain from this point
end
end
end
# But how can I reference it from our here?
# A::C::D .... ?
is there any way to check the name-space chain within the module once it has been defined?
The only examples I can see create a variable within the definition to hold the resulting call to be referenced again outside of the definition:
module A
module B; end
module C
module D
$chain = Module.nesting
end
end
end
p $chain
#=> [A::C::D, A::C, A]
Is there a method to looking this name-spacing chain up, without creating a variable in the definition to be referenced later?
In Ruby, the way to "remember" the value of an expression is to assign that value to a variable.
Also, the value of a module definition is the value of the last expression that was evaluated inside the body.
If you put the two together, you get something like this:
chain = module A
module B; end
module C
module D
Module.nesting
end
end
end
p chain
# [A::C::D, A::C, A]
Consider the following nested modules mentioned in the question.
module A
module B; end
module C
module D
Module.nesting
end
end
end
#=> [A::C::D, A::C, A]
Now try this:
module A::C::D
Module.nesting
end
#=> [A::C::D]
Module::nesting resolves the nesting of modules lexicographically, meaning that modules are ordered by their point of inclusion, beginning at A::C::D and working outward. Module::nesting does not "remember" the nesting from where the module was defined; it merely examines the code locally. This is in contrast to subclassing.
Perhaps you can obtain the information you need by using the method ObjectSpace::each_method.
outer_module = A
ObjectSpace.each_object(Module).select { |m| m.to_s.match?(/\A#{outer_module}::/) }
#=> [A::B, A::C::D, A::C]
In Ruby, constant lookup is affected by nesting and methods retain their nesting.
For example, if I have these modules:
module A
X = 1
end
module B
X = 2
end
module Foo
end
I can define a method Foo.a that has a nesting of [A, Foo]:
module Foo
module ::A
Module.nesting #=> [A, Foo]
def Foo.a
X
end
end
end
Foo.a #=> 1
And a method Foo.b that has a nesting of [Foo, B]:
module B
module ::Foo
Module.nesting #=> [Foo, B]
def Foo.b
X
end
end
end
Foo.b #=> 2
The difference becomes apparent if I define Foo::X:
Foo::X = 3
Foo.a #=> 1 <- still resolves to A::X
Foo.b #=> 3 <- now resolves to Foo::X
But how do I determine the nesting of a given method?
This would work:
Foo.method(:a).to_proc.binding.eval('Module.nesting')
#=> [A, Foo]
Foo.method(:b).to_proc.binding.eval('Module.nesting')
#=> [Foo, B]
Tested with Ruby 2.2.1 and 2.3.1.
It doesn't work with Ruby 2.1.5.
You are thinking about it the wrong way. There is no such thing as "nesting of methods". Constants are nested somewhere. Nesting has path resolution connected to names of modules and classes. Methods are contained within a module/class (be it "normal" or singleton).
Where a method is placed is semantical. There is a concept, similar to self, which determines where a method will be defined, called default definee. There is no keyword for it, but it's roughly equivalent to:
kind_of?(Module) ? name : self.class.name
Where a constant is placed/searched for is purely syntactical. When you refer to X, it does not care the least bit if you placed it a method or not:
DEEP_MIND = Object.new
module Foo
X = 42
end
module Foo
module Bar
def DEEP_MIND.talk
p X
end
end
end
DEEP_MIND.talk # => 42
module Foo::Bar
def DEEP_MIND.talk
p X
end
end
DEEP_MIND.talk # => uninitialized constant
All it cares about is what the "current nesting" on the line of the code where you tried to reference it.
Now, if you actually wanted to find "the current nesting inside the body of the method", then you need some way to pretend you are actually there.
Sadly, I don't think there is any other way other than the one showed in #Eric's answer. Using block with instance_eval/instance_exec will give you the nesting of where the block was defined.
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.