Submitted by muir (Wed Dec 29 14:12:20 UTC 2004)
The primary problem in having the library implementer establish the namespace for a certain project is guaranteeing the uniqueness of that namespace, i.e. ensuring that no other library is using the same designator. For this reason simple namespaces like 'math' are not practical and must be enclosed in a unique one like 'foosoftware'. This is not only cumbersome but also not guaranteed to be unique without having to go through some central authority (e.g. nothing in Java prevents me from using org.apache as my namespace even though I'm in no way affiliated with the organization).
The second problem, for Ruby particularly, is the conceptual muddling of 'module' that namespaces cause, one of the few instances where Ruby doesn't seem to be following the Principle of the Least Surprise. The implication of the word 'module' is of a functional/logical grouping ('module AllMathFunctions') instead of an associative grouping ('module AllFooSoftwareLibraries').
# Library code # foomath.rb module FooSoft module Math ... end end
# Client code # test.rb require 'foomath' ... x = FooSoft::Math.do_math(y)
Code would be written:
# Library code # foomath.rb module Math ... end
# Library code # barphys.rb module Phys ... end
# Client code # test.rb import 'foomath', as :Extern import 'barphys' # Phys doesn't clash ... x = Extern::Math.do_math(y) z = Phys.physicate()
This change is, to a degree, possible to implement using current Ruby constructs (see Implementation). This, however, is not quite straightforward and involves some considerably slow code.
Additionally, since this change is the conceptual inverse of the traditional notion of a namespace as a library-writer's responsibility, it would be beneficial to include it at a language level to signal that this is the Ruby Way of doing namespaces.
There are certain (surpassable) problems with this approach (see: ), mainly the evaluation of 'top-level' code within the wrapped module--i.e., what happens if a given file changes the behaviour of one of the top-level modules or classes, String for example. If the entire file is simply wrapped in Extern, any modification of String would create a new class Extern::String. To avoid this problem, the loaded file should be transformed by escaping all of these top-level classes and modules before evaluating the file (unless, of course, the library IS implementing its own String class inside its namespace). Then a source file like this would have the following transformation:
# source.rb
# Add a method to String class String def my_method ... end end
# Some math library module Math # An internal String class (don't ask..) class String ... end end
# result.rb # (not an actual file)
#New namespace as given by the import()er module Extern
# Add a method to String (escaped) class ::String ... end
# Some math library module Math # Internal String class (not escaped) class String ... end end
end # module Extern
However, this sort of processing being slow, it would work best as a builtin feature.
# A pseudo-implementation
# Add methods at top level
def as mod # Create the module in case it didn't exist Kernel.module_eval("module #{mod}; end") return Kernel.module_eval(mod.id2name) end
def import file, mod fdata = IO.readlines file # Scan the file for top-level classes/modules fdata.each do |line| if line =~ /(class|module)\s+([A-Z].*?)/ if $TOP_LEVEL_CLASSES_AND_MODULES.has_key? $2 unless inside_a_module # Escape the module end else inside_a_module = true end end # Check for ending a library module here end
# All scanned mod.module_eval(fdata) end
Alternatively, one could directly wrap any class/module that's not at top-level and leave the rest untouched. This does still leave the problem of detecting nested modules and classes.
Comments | Current voting | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
RCRchive copyright © David Alan Black, 2003-2005.
Powered by .
wraps "foo.rb" in an anonymous module and it seem to me that it always returns true, or raises an exception. Maybe it could be possible to make it return the module itself, then the module would be named by just assigning it to a constant, which is what current ruby does. IMO this would be consistent with ruby, avoid a new method and still let what the author wants possible. As for the automatic renaming, I think it is a bad idea. An user could just use the module as usual if he wants it to be merged in the top level. -- GabrieleRenzi
oh, and remove the reference to POLS, thou should not name that in an RCR according to matz ;) -- GabrieleRenzi