I have classes A and B, each of which needs some constant from the other. When I do this:
class A
puts B::CONSTANT
CONSTANT = "A constant"
end
class B
puts A::CONSTANT
CONSTANT = "B constant"
end
then I get this error:
NameError: uninitialized constant B::CONSTANT
A circular dependency exists, as each class needs the other class to be loaded first. What is the solution for such case?
Edit
In my original case there was no circular dependency, but I had a class A that uses a constant from another class B, however A was loaded before B so an exception was thrown, then I forced the load of B before A, but then I thought: what will I do if I have circular dependency?
I don't understand why you have to have only one class body for each class, and have the contents in that order. Simply do:
class A
CONSTANT = "A constant"
end
class B
CONSTANT = "B constant"
end
class A
puts B::CONSTANT
end
class B
puts A::CONSTANT
end
Related
I am trying to create a module to define some methods and constants for some classes.
This means the same method or constant can be defined in both the module and the class that includes the module.
In that scenario, I want to have control (preferably in the module definition, or maybe from the caller, without changing the class code) over which one takes precedence.
Often, I want the method or constant defined in the module to be used.
However, it looks like those defined in the class always get used unless I delete them from the class.
module A
def self.included(clazz)
const_set(:CONST, clazz.name == "B" ? "constant from module A for B" : "constant from module A for C")
define_method(:a) do
"from module A"
end
end
end
class B
include A
CONST = "constant from class B"
def a
"from class B"
end
end
class C < B
include A
CONST = "constant from class C"
def a
"from class C"
end
end
b = B.new
c = C.new
p b.a, c.a
p B::CONST, C::CONST
Running this on this REPL prints
main.rb:3: warning: already initialized constant A::CONST
main.rb:3: warning: previous definition of CONST was here
"from class B"
"from class C"
"constant from class B"
"constant from class C"
Is there a way I can make this output
"from class A"
"from class A"
"constant from module A for B"
"constant from module A for C"
without deleting constant CONST or method a from class B or C?
First, Ruby interpret code line by line from top to bottom, so in your case, Ruby read module A before the line define CONST =, as a result, the CONST defined in class B or C will always the output.
-> move include A below CONST=
Second, inside def self.included(clazz) the self is the module A not the class B or C, as a result, the const_set method be called by the module A itself so there's no B::CONST or C::CONST is defined here.
-> use clazz.const_set inside def self.included(clazz)
try this
module A
def self.included(base)
base.send :remove_const, :CONST if base.const_defined?(:CONST)
base.const_set(:CONST, base.name == "B" ? "constant from module A for B" : "constant from module A for C")
end
end
class B
CONST = "constant from class B"
include A
end
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
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/
I have a dynamically generated nested namespace like this:
class A
class B
class C
...
end
end
end
I can access class C manually by A::B::C, so it exists, so my generator works. However Object.const_get "A::B::C" raises a "wrong constant name" error. Can I somehow access it dynamically without cumbersome loops?
without loops? not sure.
here is my solution to this, but it does loop through your names:
class A
class B
class C
end
end
end
c = "A::B::C".split("::").inject(Object) { |n,c| n.const_get c }
p c
A::B::C
here you can see it in action
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.say
=> "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.say
=> "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.