Thursday, October 29, 2009

Recipe 9.5. Avoiding Naming Collisions with Namespaces










Recipe 9.5. Avoiding Naming Collisions with Namespaces





Problem


You want to define a class or module whose name conflicts with an existing class or module, or you want to prevent someone else from coming along later and defining a class whose name conflicts with yours.




Solution


A Ruby module can contain classes and other modules, which means you can use it as a namespace.


Here's some code from a physics library that defines a class called String within the StringTheory module. The real name of this class is its fully-qualified name: StringTheory::String. It's a totally different class from Ruby's built-in String class.



module StringTheory
class String
def initialize(length=10**-33)
@length = length
end
end
end

String.new # => ""

StringTheory::String.new
# => #<StringTheory::String:0xb7c343b8 @length=1.0e-33>





Discussion


If you've read Recipe 8.17, you've already seen
namespaces in action. The constants defined in a module are qualified with the module's name. This lets Math::PI have a different value from Greek::PI.


You can qualify the name of any Ruby object this way: a variable, a class, or even another module.
Namespaces let you organize your libraries, and make it possible for them to coexist alongside others.


Ruby's standard library uses namespaces heavily as an organizing principle. An excellent example is REXML, the standard XML library. It defines a REXML namespace that includes lots of XML-related classes like REXML::Comment and REXML::Instruction. Naming those classes Comment and Instruction would be a disaster: they'd get overwritten by other librarys' Comment and Instruction classes. Since nothing about the genericsounding names relates them to the REXML library, you might look at someone else's code for a long time before realizing that the Comment objects have to do with XML.


Namespaces can be nested: see for instance rexml's REXML::Parsers module, which contains classes like REXML::Parsers::StreamParser. Namespaces group similar classes in one place so you can find what you're looking for; nested namespaces do the same for namespaces.


In Ruby, you should name your top-level module after your software project (SAX), or after the task it performs (XML::Parser). If you're writing Yet Another implementation of something that already exists, you should make sure your namespace includes your project name (XML::Parser::SAX). This is in contrast to Java's namespaces: they exist in its package structure, which follows a naming convention that includes a domain name, like org.xml.sax.


All code within a module is implicitly qualified with the name of the module. This can cause problems for a module like StringTheory, if it needs to use Ruby's built-in String class for something. This should be fixed in Ruby 2.0, but you can also fix it by setting the built-in String class to a variable before defining your StringTheory::String class. Here's a version of the StringTheory module that can use Ruby's builtin String class:



module StringTheory2
RubyString = String
class String
def initialize(length=10**-33)
@length = length
end
end

RubyString.new("This is a built-in string, not a StringTheory2::String")
end
# => "This is a built-in string, not a StringTheory2::String"





See Also


  • qRecipe 8.17, "Declaring Constants"

  • Recipe 9.7, "Including
    Namespaces"













No comments: