I got the following class structure:
class A
class B
class C
module M
MY_CONST = 1000
end
end
end
end
I want to get the constant MY_CONST from a string "MY_CONST". I tried M.const_get("MY_CONST") but it doesn't seem to work. Says NameError, wrong constant name. Am trying to do this within the module M. What am I missing?
If the module name was in fact M and not m, the following works in irb for Ruby 2.0:
irb(main):009:0> class A
irb(main):010:1> class B
irb(main):011:2> class C
irb(main):012:3> module M
irb(main):013:4> MY_CONST = 1000
irb(main):014:4> end
irb(main):015:3> end
irb(main):016:2> end
irb(main):017:1> end
=> 1000
irb(main):018:0> M.const_get("MY_CONST")
=> 1000
irb(main):019:0>
Ruby 1.8.7 does give the NameError: uninitialized constant M error, however, unless you fully qualify the module name:
A::B::C::M.const_get("MY_CONST")
=> 1000
So I was doing something really stupid. I was trying to get the name of the method from caller[] but was getting the entire path and didn't regex filter the name of the constant. There is nothing wrong with the way I was trying to derive the constant.
Related
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.
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/
Starting in Ruby 1.9.3, we can create private constants:
module M
class C; end
private_constant :C
end
Is there a good documentation about what this does? Is there a way to get the names of only private constants similar to calling constants
There is no such thing as private constants until Ruby 1.9.3. To get a list of all the constants though, you can simply use constants.
module Mod
CONST = "value"
end
Mod.constants #=> [:CONST]
From 1.9.3, private_constant was added, but as nothing is really private, you can do...
module Mod
CONST = "value"
private_constant :CONST
end
Mod.const_get(:CONST) #=> "value"
I don't think there is a way of getting a list of all the private constants but you can still test the presence of a particular name.
Mod.const_defined?(:CONST) #=> true
As of Ruby 2.1, while Module#constants includes only public constants, if you set inherit=false, you will get private constants as well. So if you find a constant in constants(false) but not in constants (and you don't care about inherited constants), that might be a more or less reliable way to tell if it's private.
class Module
def private_constants
constants(false) - constants
end
end
module Foo
X = 1
Y = 2
private_constant :Y
end
puts "Foo.constants = #{Foo.constants}"
puts "Foo.constants(false) = #{Foo.constants(false)}"
puts "Foo.private_constants = #{Foo.private_constants}"
# => Foo.constants = [:X]
# => Foo.constants(false) = [:X, :Y]
# => Foo.private_constants = [:Y]
This is undocumented, and I'm not sure if it's intentional, but empirically it works. I would back it up with unit tests.
Update: It looks like this is a bug in Ruby, and may disappear in a future version.
Suppose I make a module as follows:
m = Module.new do
class C
end
end
Three questions:
Other than a reference to m, is there a way I can access C and other things inside m?
Can I give a name to the anonymous module after I've created it (just as if I'd typed "module ...")?
How do I delete the anonymous module when I'm done with it, such that the constants it defines are no longer present?
Three answers:
Yes, using ObjectSpace. This code makes c refer to your class C without referencing m:
c = nil
ObjectSpace.each_object { |obj|
c = obj if (Class === obj and obj.name =~ /::C$/)
}
Of course this depends on there being no other classes named C anywhere in the program, but you get the idea.
Yes, sort of. If you just assign it to a constant, like M = m, then m.name will return "M" instead of nil, and references like M::C will work. Actually, when I do this and type M::C in irb, I get #<Module:0x9ed509c>::C, but maybe that's a bug.
I think it should be garbage collected once there are no references to it, i.e. when there are no instances or subtypes of m or C, and m is set to a different value or goes out of scope. If you assigned it to a constant as in the above point, you would need to change it to a different value too (though changing constants is generally ill-advised).
Define a NamedModule
Once way to handle this is to define your own kind of module that can be initialized with a name.
class NamedModule < Module
attr_accessor :name
def initialize(name, &block)
super(&block)
self.name = name
end
def to_s
[self.class.name, name, object_id].join(':')
end
end
Then you can do this:
piracy = NamedModule.new("Piracy") do
def berate
puts "Yer a #{adjectives.sample} #{nouns.sample}!"
end
private
def adjectives
%w[yella-bellied landlubbing]
end
def nouns
%w[scallywag bilge-drinker]
end
end
Sailor = Class.new
Sailor.send(:include, piracy)
Sailor.new.berate #=> "Yer a yella-bellied scallywag!"
Defining to_s gives you nice output in ancestors:
Sailor.ancestors
#=> [Sailor, NamedModule:Piracy:70169997844420, Object, Kernel, BasicObject]
Update - use the Named gem
After my colleague and I had experimented with this, he wrote a small gem implementation. Check out the Named gem - Rubygems and Github.
I tried wdebeaum's second answert in Ruby 1.9.3-p0 and it didn't work.
M::C returned NameError: uninitialized constant M::C and M.constants returns []
So you should try the approach suggested here
m = Module.new do
class self::C
end
end
And you can use m::C as usual.
Say, I have the following 2 classes:
class A
def a_method
end
end
class B < A
end
Is it possible to detect from within (an instance of) class B that method a_method is only defined in the superclass, thus not being overridden in B?
Update: the solution
While I have marked the answer of Chuck as "accepted", later Paolo Perrota made me realize that the solution can apparently be simpler, and it will probably work with earlier versions of Ruby, too.
Detecting if "a_method" is overridden in B:
B.instance_methods(false).include?("a_method")
And for class methods we use singleton_methods similarly:
B.singleton_methods(false).include?("a_class_method")
If you're using Ruby 1.8.7 or above, it's easy with Method#owner/UnboundMethod#owner.
class Module
def implements_instance_method(method_name)
instance_method(method_name).owner == self
rescue NameError
false
end
end
class A
def m1; end
def m2; end
end
class B < A
def m1; end
def m3; end
end
obj = B.new
methods_in_class = obj.class.instance_methods(false) # => ["m1", "m3"]
methods_in_superclass = obj.class.superclass.instance_methods(false) # => ["m2", "m1"]
methods_in_superclass - methods_in_class # => ["m2"]
you can always to the following and see if its defined there:
a = A.new
a.methods.include?(:method)
Given an object b which is an instance of B, you can test to see whether b's immediate superclass has a_method:
b.class.superclass.instance_methods.include? 'a_method'
Notice that the test is against the method name, not a symbol or a method object.
"thus not being overridden in B" - Just knowing that the method is only defined in A is difficult because you can define the method on an individual instances of A and B... so I think it's going to be difficult to test that a_method is only defined on A, because you'd have to round up all the subclasses and subinstances in the system and test them...