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.
Related
I'm trying to understand Ruby singletons and class inheritance better. I read everywhere that
def self.method_name; end`
is equivalent to
class << self
def method_name; end
end
But if that were true, then I would expect print_constant_fails to work, but it doesn't. What is going on here?
class SuperExample
A_CONSTANT = "super example constant"
end
class SubExample < SuperExample
def self.print_constant_works_1
puts A_CONSTANT
end
class << self
def print_constant_works_2
puts self::A_CONSTANT
end
def print_constant_fails
puts A_CONSTANT
end
end
end
pry(main)> SubExample.print_constant_works_1
super example constant
pry(main)> SubExample.print_constant_works_2
super example constant
pry(main)> SubExample.print_constant_fails
NameError: uninitialized constant #<Class:SubExample>::A_CONSTANT
from (pry):13:in `print_constant_fails'
You have encountered a common Ruby gotcha - constant lookup.
The most important concept in constant lookup is Module.nesting (unlike in method lookup, where the primary starting point is self). This method gives you the current module nesting which is directly used by the Ruby interpreter when resolving the constant token. The only way to modify the nesting is to use keywords class and module and it only includes modules and classes for which you used that keyword:
class A
Module.nesting #=> [A]
class B
Module.nesting #=> [A::B, A]
end
end
class A::B
Module.nesting #=> [A::B] sic! no A
end
In meta programming, a module or class can be defined dynamically using Class.new or Module.new - this does not affect nesting and is an extremely common cause of bugs (ah, also worth mentioning - constants are defined on the first module of Module.nesting):
module A
B = Class.new do
VALUE = 1
end
C = Class.new do
VALUE = 2
end
end
A::B::VALUE #=> uninitialized constant A::B::VALUE
A::VALUE #=> 2
The above code will generate two warnings: one for double initialization of constant A::VALUE and a second for reassigning the constant.
If it looks like "I'd never do that" - this also applies to all the constants defined within RSpec.describe (which internally calls Class.new), so if you define a constant within your rspec tests, they are most certainly global (unless you explicitly stated the module it is to be defined in with self::)
Now let's get back to your code:
class SubExample < SuperExample
puts Module.nesting.inspect #=> [SubExample]
class << self
puts Module.nesting.inspect #=> [#<Class:SubExample>, SubExample]
end
end
When resolving the constant, the interpreter first iterates over all the modules in Module.nesting and searches this constant within that module. So if nesting is [A::B, A] and we're looking for the constant with token C, the interpreter will look for A::B::C first and then A::C.
However, in your example, that will fail in both cases :). Then the interpreter starts searching ancestors of the first (and only first) module in Module.nesting. SubrExample.singleton_class.ancestors gives you:
[
#<Class:SubExample>,
#<Class:SuperExample>,
#<Class:Object>,
#<Class:BasicObject>,
Class,
Module,
Object,
Kernel,
BasicObject
]
As you can see - there is no SuperExample module, only its singleton class - which is why constant lookup within class << self fails (print_constant_fails).
The ancestors of Subclass are:
[
SubExample,
SuperExample,
Object,
Kernel,
BasicObject
]
We have SuperExample there, so the interpreter will manage to find SuperExample::A_CONSTANT within this nesting.
We're left with print_constant_works_2. This is an instance method on a singleton class, so self within this method is just SubExample. So, we're looking for SubExample::A_CONSTANT - constant lookup firstly searches on SubExample and, when that fails, on all its ancestors, including SuperExample.
It has to do with scope. When you are inside class << self, the scope is different than when you are inside class Something. Thus, inside class << self there is actually no constant called A_CONSTANT.
In Ruby, every Ruby's constant has its own path, start from the main (root) with the sign :: (default, we don't need to declare this sign). And class should not be considered a keyword (kind of static), but a method (kind of dynamic) take responsibility for creating a class object and a class name constant which point to that class object, and all Constants are defined inside a class without a path (P::Q::...) will automatically be considered belongs to the created class with path :: ClassName::A_CONSTANT.
GLOBAL = 1
class SuperExample
A_CONSTANT = "super constant" # <-- ::SuperExample::A_CONSTANT
end
puts ::GLOBAL # 1
puts ::SuperExample # SuperExample
puts ::SuperExample::A_CONSTANT # "super constant"
It looks like constants paths in children classes has same level with parent
class SubExample < SuperExample
end
puts ::SubExample::A_CONSTANT # "super constant"
As I noticed, all constants (without ::) inside the class block will be set path under the classpath, so when you get them, either you get with the explicitly constant path or under the class that constants belong to:
class SubExample < SuperExample
def self.print_constant_works_1
puts A_CONSTANT # ::SubExample::A_CONSTANT
end
def another
puts A_CONSTANT # ::SubExample::A_CONSTANT
end
def yet_another
puts SubExample::A_CONSTANT # ::SubExample::A_CONSTANT
end
end
Now check class << self
class SubExample < SuperExample
class << self
puts self # <Class:SubExample>
def print_constant_works_2
puts self::A_CONSTANT # declare explicitly constant path
end
def print_constant_fails
puts A_CONSTANT # not declare explicitly <-- Class:SubExample::A_CONSTANT
end
end
end
As you can see, the class inside class << self is different, so the path of constant A_CONSTANT inside method print_constant_fails is pointing to Class:SubExample which does not define any constant A_CONSTANT, so an error uninitialized constant #<Class:SubExample>::A_CONSTANT be raised.
Meanwhile print_constant_works_2 will work since we declare explicitly constant path, and self in this case is actually SubExample(call SubExample.print_constant_works_2).
Now let try with an explicit path ::A_CONSTANT inside print_constant_fails
def print_constant_fails
puts ::A_CONSTANT
end
The error be raised is uninitialized constant A_CONSTANT, ::A_CONSTANT is considered a global constant (main).
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
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.
class A
def set(v)
##v = v
end
def put
puts ##v
end
end
class B < A
end
class C < A
end
B.new.set 'b'
B.new.put # => b
C.new.set 'c'
C.new.put # => c
B.new.put # => c
Why? And how should I write this to have 'b' in last B.new.put?
Here is a nice article on the subject - Class and Instance Variables In Ruby.
Basically, what you can do is:
class A
class << self
attr_accessor :class_var
end
def set_class_var(value)
self.class.class_var = value
end
def get_class_var
self.class.class_var
end
end
class B < A; end
A.class_var = 'a'
B.class_var = 'b'
puts A.class_var # => a
puts B.class_var # => b
A.new.set_class_var 'aa'
B.new.set_class_var 'bb'
puts A.new.get_class_var # => aa
puts B.new.get_class_var # => bb
To understand it you should think about A as an instance of Class class (and that's how it is in Ruby). But every object in Ruby has its own singleton class that stores object-specific stuff like methods defined on object itself:
a = A.new
def a.foo
puts 'foo'
end
In that case foo is method defined only for a object and not for every instance of A class. And another way to define method in object's singleton class is like that:
class << a # open a's singleton class
def bar # define method that will be available only on 'a' object
puts 'bar'
end
end
In the first code snippet we use that approach to define class_var attribute accessor in the context of singleton class of our A class (it's a bit tricky, so you need to think about it). As the result class itself has class_var variable as well as its descendant class B. The difference is that every one of them has its own class_var variable that do not interfere.
Another option is to pull out class_inheritable_accessor code from Rails and include its behavior in your classes. See here for a good discussion and the guts of the code.
Perhaps you don't really want a class variable, though.
Assigning a value to a class variable (an ## variable) sets it for EVERY instance of the class. It even "sets" it for instances that "aren't created yet." So, consider this...
B.new.set 'b' # OK, that set ##v for that particular instance of B
B.new.put # Hey, you just created *another* new instance of B!
How can ##v have a value in that one? The second object's value of ##v would be unset, except for the fact that ##v is a class variable, so it has the same value for every instance of the class.
def class A
def a
raise "hi" #can't be reached
end
class B
def b
a() #doesn't find method a.
end
end
end
I want to invoke a from b and raise the exception. How can I?
Ruby doesn't have nested classes.
The only way to inherit behavior is, well, via inheritance.
If you want your code to work, you need to use a language which supports nested classes. While this is an incredibly neat and powerful feature, I unfortunately know of only two languages that have nested classes:
BETA, the language which introduced nested classes (and its successor gbeta)
Newspeak
I don't know of any other.
Java has a construct called nested classes, but they have some unfortunate design limitations.
In your example above, it's not the class B that is nested inside A, it is the constant B that is nested inside A. Think about this:
C = A::B
Now, the class is available under two names: A::B and C. It should be immediately obvious that C is global and not nested inside A. (Well, actually, C is nested inside Object, because there aren't really global constants either, but that's beside the point.) But since C and A::B are the same class, it obviously cannot be both nested and not nested. The only logical conclusion is that the class itself isn't nested.
The defining feature of nested classes is that method lookup goes along two dimensions: up the inheritance chain, and outwards through the nesting. Ruby, like 99.9% of all OO languages, only supports the former. (In some sense, nested classes inherit not only the features of their superclass, but also the features of their surrounding class.)
This is just for the lulz:
class A
def a
puts "hello from a"
end
class B
def b
Module.nesting[1].new.a()
end
end
end
I typically do something like this:
class A
def a
puts "hi"
end
def createB
B.new self
end
class B
def initialize(parent)
#parent=parent
end
def b
#parent.a
end
end
end
A.new.createB.b
If you want then nested class to extend the outer class, then do so:
class Outer
class Inner < Outer
def use_outer_method
outer_method("hi mom!")
end
end
def outer_method(foo)
puts foo
end
end
foo = Outer::Inner.new
foo.use_outer_method #<= "hi mom"
foo.outer_method("hi dad!") #<= "hi dad"
Well depending on your circumstances there is actually a solution, a pretty easy one at that. Ruby allows the catching of method calls that aren't captured by the object. So for your example you could do:
def class A
def a
raise "hi" #can't be reached
end
class B
def initialize()
#parent = A.new
end
def b
a() #does find method a.
end
def method_missing(*args)
if #parent.respond_to?(method)
#parent.send(*args)
else
super
end
end
end
end
So then if you do this:
A::B.new().b
you get:
!! #<RuntimeError: hi>
It is probably an easier way to make something like a SubController that only handles certain activities, but can easily call basic controller methods (You would want to send in the parent controller as an argument in the initializer though).
Obviously this should be used sparingly, and it can really get confusing if you use it all over the place, but it can be really great to simplify your code.
You can use methods like module_parent, module_parent_name, module_parents from ActiveSupport to get outer modules, eg.:
class A
def self.a; puts 'a!' end
class B
def self.b; module_parent.a end # use `parent` if rails < 6.0
end
end
A::B.b #=> a!
Was a supposed to be a class method for class A?
class A
def self.a
raise "hi"
end
class B
def b
A::a
end
end
end
A::B.new.b
If you want to keep it as an instance method, you'll obviously have call to it on an instance, like for example A.new.a.