Working with anonymous modules in Ruby
Suppose I make a module as follows:
m = Module.new do
class C
end
end
Three questions:
Other than a referen开发者_StackOverflow中文版ce to
m
, is there a way I can accessC
and other things insidem
?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 makesc
refer to your classC
without referencingm
: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
, thenm.name
will return"M"
instead ofnil
, and references likeM::C
will work. Actually, when I do this and typeM::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
orC
, andm
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.
精彩评论