According to the documentation mod.const_get(sym) "Returns the value of the named constant in mod."
I also know that const_get by default may look up the inheritance chain of the receiver. So the following works:
class A; HELLO = :hello; end
class B < A; end
B.const_get(:HELLO) #=> :hello
I also know that classes in Ruby subclass Object, so that you can use const_get to look up 'global' constants even though the receiver is a normal class:
class C; end
C.const_get(:Array) #=> Array
However, and this is where i'm confused -- modules do not subclass Object. So why can I still look up 'global' constants from a module using const_get? Why does the following work?
module M; end
M.const_get(:Array) #=> Array
If the documentation is correct - const_get simply looks up the constant defined under the receiver or its superclasses. But in the code immediately above, Object is not a superclass of M, so why is it possible to look up Array ?
Thanks
You are correct to be confused... The doc didn't state that Ruby makes a special case for lookup of constants in Modules and has been modified to state this explicitly. If the constant has not been found in the normal hierarchy, Ruby restarts the lookup from Object, as can be found in the source.
Constant lookup by itself can be bit confusing. Take the following example:
module M
Foo = :bar
module N
# Accessing Foo here is fine:
p Foo # => bar
end
end
module M::N
# Accessing Foo here isn't
p Foo # => uninitialized constant M::N::Foo
end
p M::N.const_get :Foo # => uninitialized constant M::N::Foo
In both places, though, accessing Object level constants like Array is fine (thank god!). What's going on is that Ruby maintains a list of "opened Module definitions". If a constant has an explicit scope, say LookHereOnly::Foo, then only LookHereOnly and its included modules will be searched. If no scope is specified (like Foo in the example above), Ruby will look through the opened module definitions to find the constant Foo: M::N, then M and finally Object. The topmost opened module definition is always Object.
So M::N.const_get :Foo is equivalent to accessing Foo when the opened classes are only M::N and Object, like in the last part of my example.
I hope I got this right, coz I'm still confused by constant lookups myself :-)
I came up with the following script to load name spaced constants:
def load_constant(name)
parts = name.split('::')
klass = Module.const_get(parts.shift)
klass = klass.const_get(parts.shift) until parts.empty?
klass
end
As long as we are not checking for errors you can:
def load_constant(name)
name.split('::').inject(Module) do |mod_path, mod_to_find|
mod_path.const_get(mod_to_find)
end
end
Related
I have recently discovered that Ruby (2.2.1) has some "interesting" behavior.
module Foo
class Foo
end
class Bar
end
end
Foo.const_get('Foo') #=> Foo::Foo
Foo.const_get('Bar') #=> Foo::Bar
Foo.const_get('Foo::Foo') #=> Foo
Foo.const_get('Foo::Bar') #=> NameError: uninitialized constant Foo::Foo::Bar
Foo.const_get('Foo::Foo::Bar') #=> Foo::Bar
Foo.const_get('Foo::Foo::Foo::Bar') #=> NameError: uninitialized constant Foo::Foo::Bar
Foo.const_get('Foo::Foo::Foo::Foo::Bar') #=> Foo::Bar
Foo.const_get('Foo::Foo::Foo') #=> Foo::Foo
Foo.const_get('Foo::Foo::Foo::Foo') #=> Foo
Foo.const_get('Foo::Foo::Foo::Foo::Foo') #=> Foo::Foo
Foo.const_get('Foo::Foo::Foo::Foo::Foo::Foo') #=> Foo
This is a bit surprising. My understanding has been that const_get first looks for a constant in the receiver's collection of constants and then looks at Object's constants. OK, fine. Why then does the fourth Foo#const_get above fail and the third one doesn't?
I'm also curious as to why calling Foo#const_get alternates between the module and class depending on how many ::Foos you add.
The docs say:
This method will recursively look up constant names if a namespaced class name is provided.
So Foo.const_get('Foo::Bar') is basically the same as Foo.const_get('Foo').const_get('Bar'). Using this interpretation your results make sense.
Your third example:
Foo.const_get('Foo::Foo')
is the same as
Foo.const_get('Foo').const_get('Foo')
The first const_get looks at constants defined within the top level Foo (the module), and finds the nested class. So the whole thing effectively becomes:
Foo::Foo.const_get('Foo')
The second call then looks at the class, first looking the any contained constants (finding none) and then looking in its ancestors. Object is an ancestor, and has the top level Foo module as a constant, so this is found and returned.
This also explains the alternation when adding extra ::Foos. The alternation is between const_get on the top level module finding the nested class, and on the nested class looking up the inheritance chain and finding the top level module.
The fourth example, Foo.const_get('Foo::Bar'), which raises an exception, can also be explained. It is equivalent to
Foo.const_get('Foo').const_get('Bar')
The first part, Foo.const_get('Foo') is the same as the case above, evaluating to Foo::Foo, so the whole thing now effectively becomes:
Foo::Foo.const_get('Bar')
Now the nested Foo class doesn’t contain a Bar constant, and looking up the inheritance chain, neither does Object, so the result is a NameError.
I made a class with a constant & a method:
class A
FOO = 'hello'
def bar
puts FOO
end
end
A.new.bar
=> 'hello'
And everything works as expected. However when I do this:
A.class_eval do
def bar
puts FOO
end
end
A.new.bar
NameError: uninitialized constant FOO
Weirdness... To get around this I'm doing:
A.class_eval do
def bar
puts self.class::FOO
end
end
Any good explanation on why this is so?
Constants are looked up
outwards in the lexically enclosing module declarations
upwards in the inheritance chain of the current module declaration
So, let's just do what Ruby does:
look outwards in the lexically enclosing module declarations: easy – there are no module declarations
look upwards from the current module declaration: again, there is no current module declaration … or is there? Well, if there is no module declaration, it is implicitly assumed to be class Object – but Object doesn't have a constant named FOO and neither do its ancestors Kernel and BasicObject.
Ergo: Ruby is right. There is no constant named FOO within the constant lookup path. It didn't remove FOO from the scope, it was never in scope to begin with.
[Ruby's reflection API actually gives you access to anything you need: #1 is exactly the same as Module.nesting and #2 is (almost) the same as Module.nesting.first.ancestors.]
You might think: wait, isn't module_eval a module declaration? No, it isn't! It's just a method like any other method taking a block that's just like any other block. It doesn't alter the constant lookup rules in any way.
Note that that's not quite true: after all, e.g. instance_eval does change method lookup rules, for example, so it would not be inconceivable that module_eval changes constant lookup rules. And even more confusingly, when called with a String instead of a block, it actually does change Module.nesting and thus the constant lookup rules!
In a tutorial, there are examples querying constants using const_get:
class This
end
p Module.const_get('This').new
and
A_CONSTANT = 42
p Module.const_get('A_CONSTANT')
I can't see any module definition. Why is Module used? Why isn't const_get('This') or self.const_get('This') enough? The class where all this is written in is Object, ancestor of BasicObject.
Does the fact that This is a constant mean that it holds the class definition inside of it? Like as it just could hold the number 42? Or to be clearer, is it the same as:
This = class
end
like an unnamed method so I can write p This and get the class definition?
Some nice knowledge you have acquired. But I don't know why are you mentioning metaprogramming in your title, when your question is all about constants. Metaprogramming is something else.
In any case, #const_get is an instance method of Module class, so it won't work at the top level, where the implicit receiver is of Object class. When you write
class Foo; end
Constant Foo gets added to Object:
Object::Foo #=> Foo
Object::Bar #=> error (we didn't define constant Bar)
This constant is available also in other classes, such as
Module::Foo #=> Foo, but with a warning
Array::Foo #=> same Foo with the same warning
Fixnum::Foo #=> ditto
In other words, your Foo defined at toplevel is visible from almost every module. But accessing toplevel constants like this is frowned upon, as this is often not what the programmer intended. You can indeed see that the constant is defined on Object:
Object.constants( false ).include? :Foo #=> true
and not on eg. Module:
Module.constants( false ).include? :Foo #=> false
However, if you use #const_get, the warning message is suppressed and you may come to think that Module::Foo actually exists:
Module.const_get( :Foo ) #=> Foo
It doesn't.
Documentation I've read tells me to use Module.method to access methods in a module. However, I can use Module::method as well. Is this syntactic sugar, or am I confused?
module Cat
FURRY_LEVEL = 4
def self.sound
%w{meow purr hiss zzzz}.sample
end
end
puts Cat.sound # This works.
puts Cat::sound # This also works. Why?!
puts Cat.FURRY_LEVEL # Expected error occurs here.
puts Cat::FURRY_LEVEL # This works.
Constant resolution always requires that you use ::.
Method invocation is idiomatically and usually a period (.), but :: is also legal. This is not just true for so-called module methods, but for invoking any method on any object:
class Foo
def bar
puts "hi"
end
end
Foo.new::bar
#=> hi
It's not so much "syntax sugar" as it is simply alternative syntax, such as the ability to write if or case statements with either a newline, then and newline, or just then.
It is specifically allowed because Ruby allows methods with the same name as a constant, and sometimes it makes sense to think that they are the same item:
class Foo
class Bar
attr_accessor :x
def initialize( x )
self.x = x
end
end
def self.Bar( size )
Foo::Bar.new( size )
end
end
p Foo::Bar #=> Foo::Bar (the class)
p Foo::Bar(42) #=> #<Foo::Bar:0x2d54fc0 #x=42> (instance result from method call)
You see this commonly in Ruby in the Nokogiri library, which has (for example) the Nokogiri::XML module as well as the Nokogiri.XML method. When creating an XML document, many people choose to write
#doc = Nokogiri::XML( my_xml )
You see this also in the Sequel library, where you can write either:
class User < Sequel::Model # Simple class inheritance
class User < Sequel::Model(DB[:regular_users]) # Set which table to use
Again, we have a method (Sequel.Model) named the same as a constant (Sequel::Model). The second line could also be written as
class User < Sequel.Model(DB[:regular_users])
…but it doesn't look quite as nice.
The :: is called scope resolution operator, which is used to find out under what scope the method, class or constant is defined.
In the following example, we use :: to access class Base which is defined under module ActiveRecord
ActiveRecord::Base.connection_config
# => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"}
We use :: to access constants defined in module
> Cat::FURRY_LEVEL
=> 4
> Cat.FURRY_LEVEL
=> undefined method `FURRY_LEVEL' for Cat:Module (NoMethodError)
The . operator is used to call a module method(defined with self.) of a module.
Summary: Even though both :: and . does the same job here, it is used for different purpose. You can read more from here.
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.