Namespaces

SketchUp's plugins are loaded by its built-in Ruby interpreter. So, it is quite possible for plugins to redefine each others' names (eg, constants, globals). SketchApps prevents many types of namespace conflicts, using some fairly exotic techniques.

Background

Ruby Namespaces

Ruby supports several namespaces, ranging in scope from local to global. Assuming that we aren't talking about "qualified" names (eg, Foo.bar, Foo::Bar), the following rules apply:

  • After the first underscore or letter, a name may contain underscores,
    upper- or lower-case letters, or numbers.

  • Local variables may be defined within a method or one of its blocks.
    They are (generally) undefined once the program exits the defining scope.
    Their names (eg, foo, _foo) begin with a lower-case letter or underscore.

  • Methods are defined for a "receiver" object, defaulting to self.
    Their names (eg, foo, _foo) follow the rules for local variables.

  • A method call may or may not enclose its arguments in parentheses.
    If parentheses are not used, a local variable will take precedence
    over a method of the same name.

  • Instance variables are defined within an instance.
    Their names (eg, @foo, @_foo) begin with a single at-sign (@),
    followed by a lower-case letter or underscore.

  • Class variables are defined within a class.
    Their names (eg, @@foo, @@_foo) begin with a double at-sign (@@),
    followed by a lower-case letter or underscore.

  • Constants (including class and module names) are defined
    within a class or module.
    Their names (eg, Foo) begin with an upper-case letter.

  • Global variables are visible throughout the program.
    Their names (eg, $foo, $foo) generally begin with a dollar sign ($),
    followed by a letter or underscore (though dashes also work).

    A number of global variables are predefined by the interpreter.
    Most of these have names (eg, $:) that consist of a dollar sign
    and a single special character.

Note: The material above was adapted (with possible errors :-) from "The Ruby Programming Language" (Flanagan and Matsumoto), the definitive reference on Ruby. Get a copy!

In conventional Ruby programs, keeping these names unique is part of the normal development process. So, for example, developers need to make sure that a global name isn't used simultaneously for conflicting purposes.

Plugin Namespaces

The situation is quite a bit more complicated (and risky) in the world of SketchUp plugins:

  • Plugins are commonly written by inexperienced developers.

  • "Best Practices" are not well documented or commonly followed.

  • There is no formal coordination for plugin development.

  • A user's plugins folder may contain plugins from many authors.

Although the user or operating system may notice naming conflicts between files in the plugins folder, other conflicts are more insidious. For example, two plugins that contain different versions of the same code are very likely to have conflicting class definitions.

Given that none of these issues is likely to change, there is a real (and growing) risk of naming conflicts. Barring fundamental changes in Ruby and/or SketchUp, we need a convenient and effective way to contain this risk.

Approach

SketchApps handles a number of these issues, using a multifaceted approach:

  • It defines very few global variables; for distinctiveness,
    all defined globals have names of the form "sketchapps...".

  • It defines only one top-level constant (::Load_SketchApps).

  • It only installs a single file (load_sketchapps.rb) in the plugins directory.

  • Each author has a single directory (eg, CFCL, Igloo) in the SketchApps tree.

  • The author's directory contains independent copies of the framework's
    _External and _Framework libraries.

Collectively, these techniques minimize the chance of destructive collisions.

Implementation

SketchApps generally uses Kernel#load's "wrap" mode to load libraries and plugins. This evaluates the code in an anonymous module, minimizing contamination. To provide access to needed constants (eg, classes, modules), each loaded file defines or modifies a global, as:

class PF_Main
  ...
end
$sketchapps_refs[:pf_common].merge!( :PF_Main_c => PF_Main )

Remaining Issues

Some issues are still being addressed:

  • load_sketchapps.rb creates ::Load_SketchApps (a top-level module).

  • erb.rb is loaded directly, creating some top-level constants (eg, ::ERB).


This wiki page is maintained by Rich Morin, an independent consultant specializing in software design, development, and documentation. Please feel free to email comments, inquiries, suggestions, etc!

Topic revision: r5 - 07 Mar 2012, RichMorin
This site is powered by Foswiki Copyright © by the contributing authors. All material on this wiki is the property of the contributing authors.
Foswiki version v2.1.6, Release Foswiki-2.1.6, Plugin API version 2.4
Ideas, requests, problems regarding CFCL Wiki? Send us email