I thought there were no differences between methods declared within a class << self
block and those declared with a self.
prefix, but there are:
module A
VAR = 'some_constant'
end
class B
extend A
class << self
def m1
puts VAR
end
end
def self.m2
puts VAR
end
end
B.m1 # => OK
B.m2 # => uninitialized constant B::VAR
Why are constants of A
available in m1
but not in m2
?
In Ruby, constant lookup is not the same as method lookup. For method lookup, calling foo
is always the same as calling self.foo
(assuming it isn't private). Calling a constant FOO
is very different from self::FOO
or singleton_class::FOO
.
Using an unqualified constant (e.g. FOO
) will do a lookup in the currently opened modules. A module is opened with module Mod
, class Klass
, class << obj
, or module_eval
and variants. When defining m1
, these are B
, and then B.singleton_class
. When defining m2
, only B
is opened.
module Foo
X = 42
class Bar
def self.hello
X
end
end
end
In this code, Foo::Bar.hello
will return 42, even though X
is not a constant of Bar
, its singleton class or ancestor. Also, if you later add a constant X
to Bar
, then that value will be returned. Finally, the following definition is not equivalent:
module Foo
X = 42
end
class Foo::Bar
def self.hello
X
end
end
Foo::Bar.hello # => uninitialized constant Foo::Bar::X
Indeed, when hello
is defined, only the class Foo::Bar
is opened, while in the previous example, both Foo
and Foo::Bar
where opened.
A last example, to show the difference an explicit scope can have with inheritance:
class Base
X = 42
def self.foo
X
end
def self.bar
self::X
end
end
class Parent < Base
X = :other
end
Parent.foo # => 42
Parent.bar # => :other
In your case, you probably want to include
your module, instead of extending
it, no?
Otherwise, you could use singleton_class::VAR
, your code will work as you expect it.
module A
VAR = 'some_constant'
end
class B
extend A
class << self
def m1
puts singleton_class::VAR # not necessary here, as singleton_class is opened
end
end
def self.m2
puts singleton_class::VAR # necessary here!
end
end
B.m1 # => OK
B.m2 # => OK
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With