Constants in singleton classes - ruby

I have this code:
class A
def print
puts CONSTANT
end
end
module B
CONSTANT = "Don't Panic!"
end
Suppose a is an instance of class A.
So I need a definition of CONSTANT that should be able to be found to make a.print available.
I considered to include the module B into a's singleton class like:
a.singleton_class.send :include, B
a.print # error: CONSTANT isn't found
I assumed it should be okay now to call the method but actually not.
The constant should be successfully imported as the following code works as expectation:
a.singleton_class.constants # => [.., :CONSTANT, ..]
However, by including the constant into the class instead of singleton class, it works:
a.class.send :include, B
a.print # prints: Don't Panic!
I thought it is strange that I can't refer a constant that is defined in a singleton class. If this is reasonable, I want to know why.

The class returned by singleton_class is an anonymous class which was inherited from class of the object a.
So a.class and a.singleton_class refer to different objects.
puts a.class.object_id # => 79495930
puts a.singleton_class.object_id # => 79495830
And also different classes: a.singleton_class is a child class of A.
a.singleton_class.superclass # => A
a.singleton_class.ancestors # => [B, A, Object, Kernel, BasicObject]
a.class.ancestors # => [A, Object, Kernel, BasicObject]
And because of this, a.singleton_class doesn't share its own constants to parent just like any other child class do.
puts a.class.constants # => []
puts a.singleton_class.constants # => [:CONSTANT]

Related

How to set a class variable from a class-level method when using Class.new?

I recall reading somewhere that the following two pieces of code are identical in Ruby.
# Number 1
class A
# body
end
# Number 2
A = Class.new
# body
end
However I noticed that when I try to set a class variable from a class-level method, the two syntaxes produce different results.
With the first syntax, the variable is set on the class itself as expected.
irb(main):001:0> class A
irb(main):002:1> def self.foo
irb(main):003:2> ##a = :banana
irb(main):004:2> end
irb(main):005:1> end
=> :foo
irb(main):006:0> A.class_variables
=> []
irb(main):007:0> A.foo
=> :banana
irb(main):008:0> A.class_variables
=> [:##a]
irb(main):009:0> A.class_variable_get :##a
=> :banana
irb(main):010:0> Class.class_variable_get :##a
NameError: uninitialized class variable ##a in Class
from (irb):10:in `class_variable_get'
from (irb):10
from /usr/local/bin/irb:11:in `<main>'
irb(main):011:0>
With the second, the variable gets set on Class itself!
irb(main):015:0> K = Class.new do
irb(main):016:1* def self.foo
irb(main):017:2> ##k = :banana
irb(main):018:2> end
irb(main):019:1> end
=> K
irb(main):020:0> K.class_variables
=> []
irb(main):021:0> K.foo
(irb):17: warning: class variable access from toplevel
=> :banana
irb(main):022:0> K.class_variables
=> [:##k]
irb(main):023:0> K.class_variable_get :##k
=> :banana
irb(main):024:0> Class.class_variable_get :##k
=> :banana
Why does this happen?
In what I am working on, I have to generate a class dynamically, so I have no choice but to use the second syntax. As such, how do I ensure that the variable ##k gets set on the new Class object being defined, and not on Class itself?
The two pieces are not identical, there are differences in lexical scoping of the declarations.
In the first definition, the more common class declaration, definitions are lexically scoped to class A, so the class variable is set on A. Only A and classes that inherit from A will have that class variable. Since Class does not inherit from A, it doesn't get the class variable.
In the second definition, the dynamic class assignment, the definitions are lexically scoped to top level object, which is object. So a class variable will be set on Object, the class of the top level object.
Now, this has huge implications. Every object inherits from Object and every class is an instance of the class Class, you're defining a class variable on each and every class in your object space. Hence, A gets the class variable, and Class gets it as well. Try defining a new class F, and it will have it as well. This is why Ruby screams the warning:
class variable access from toplevel
This is one of reasons why class variables are typically avoided.
There are multiple ways to solve this. My favourite is, using the attr_accessor on the class instance:
K = Class.new do
class << self
attr_accessor :fruit
end
self.fruit = :banana
end
K.fruit
# => :banana
# The value isn't shared between inherited classes,
# but the variable is:
class L < K
end
L.fruit
# => nil
L.fruit = :mango
# => :mango
K.fruit
# => :banana
Edit:
Keep in mind, that these variables are still not thread-safe, and are shared by all threads. You will need thread-local variables for ensuring thread-safety.

Why module included outside the class adds instance methods to class's objects

I am experimenting with the Ruby include keyword as shown below:
module A
def show
puts "working"
end
end
include A
class E
end
class D
end
e = E.new
d = D.new
e.show
d.show
o = Object.new
puts o.respond_to?("show")
******************************output****************
working
working
true
I was expecting output to be undefined method but it's giving me the proper output. I have also observed that the show method defined in module A is becoming an instance method of Object.
Why are these methods becoming instance methods of the class Object?
Please help in understanding this concept.
Because instances of class Class inherit from Object.
Thus, modules, included into Object are available to instances of Class's instances (instances of your E and D classes).
class A
end
module B
def test; :hi end
end
#=> test
include B
#=> Object
A.new.test
#=> :hi
Object.new.test
#=> :hi
Having include B written in the top-level means include'ing B into Object.
include B is the outermost context is equivalent to:
class Object
include B
end
The only class, whose instances do not share module B's methods is BasicObject.

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.

How to define a const on the singleton class?

I want to define a constant for a single instance of an object, not for all instances of an object.
class MyTest
def call
Foo
end
end
t = MyTest.new
t.call # => NameError (as expected)
t.singleton_class.class_eval do
const_set 'Foo', 'foo'
end
t.singleton_class::Foo # => 'foo'
t.call # => NameError
Why does the const lookup not include the const defined in the objects singleton class?
Here is another attempt:
Dog = Class.new { def call; Bark; end }
d = Dog.new
d.call # => NameError (expected)
d.singleton_class.const_set('Bark', 'woof')
d.call # => NameError (not expected)
The constant has been defined in the instance's singleton class but Foo doesn't include (singleton_class)::Foo in one of its possible evaluations so specify it explicitly:
class MyTest
def call
singleton_class::Foo
end
end
I believe it is not possible:
const_set is defined on Module
Ruby looks for constant in nesting and ancestors
There is no place where to define the constant for a MyTest instance
class MyTest
def call
puts (Module.nesting + Module.ancestors).uniq.inspect
end
end
MyTest.new.call
# => [MyTest, Module, Object, Kernel, BasicObject]
I think this comment is correct - t.singleton_class.class_eval first gets the singleton_class of t and then evals something on that singleton classes' class.
Instead what you want is to define the constant on the instance of that singleton class, like so:
t.singleton_class.instance_eval do
Foo = 'foo'
end
After that, t.call returns "foo".
Start be creating an arbitrary set and an instance of that set.
class A; end
a = A.new
#=> #<A:0x00007ff0bbaa9f98>
For convenience, assign a's singleton class1 to a variable.
as = a.singleton_class
#=> #<Class:#<A:0x00007ff0bbaa9f98>>
Create a constant in as whose value equals 3.
as.const_set(:A, 3)
#=> 3
Check that the constant was defined.
as.constants
#=> [:A]
Let's check its value also.
as.const_get(:A)
#=> 3
Now let's create a method in as that references the constant we've created.
as.define_method(:cat) { puts "#{as.const_get(:A)} cats"}
#=> :cat
Try it.
a.cat
#=> "3 cats"
See Module#const_set and Module#const_get.
1. Every Ruby object has a singleton class. Singleton classes are like regular classes except they cannot be subclassed and one cannot create instances of subclasses.

Understanding ruby .class and .ancestors methods

I have a class defined as below
class Order
end
puts Order.class #-> Class
puts Order.ancestors #-> [Order, Object, Kernel, BasicObject]
puts Order.class.ancestors #->[Class, Module, Object, Kernel, BasicObject]
My question is why is it that Order.ancestors doesn't show 'Class' or 'Module' in ancestors chain? Since Order is an object of the class Class, shouldn't Order show all the ancestors of Class?
For that you need to see how the Ruby object model looks.
That means the classes created using keyword class will always be the subclass of Object by default. Class is not the superclass of your class Order, rather it is an instance of class Class.Module#ancestors will include list of modules included in mod (including mod itself) and the superclass of your class Order.
class Order;end
Order.superclass # => Object
Order.superclass.superclass # => BasicObject
Order.superclass.included_modules # => [Kernel]
So if you look at the output and understand the above code,then the below should now be clear to you:
Order.ancestors #-> [Order, Object, Kernel, BasicObject]
Now see,
class Order;end
Order.class # => Class
Order.instance_of? Class # => true
Order.class.superclass # => Module
Order.class.superclass.superclass # => Object
Order.class.superclass.superclass.included_modules # => [Kernel]
So if you look at the output and understand the above code, then the below should now be clear to you:
Order.class.ancestors #->[Class, Module, Object, Kernel, BasicObject]
That said Order.ancestors is giving you the ancestors of the class Order,whereas Order.class.ancestors is giving you the ancestors of the Class.

Resources