Skip to main content
Engineering LibreTexts

6.4: Advanced Topics

  • Page ID
    43822
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Now we will have a look at several advanced topics, including history, managing dependencies, making configuration, and class initialization.

    History

    By action-clicking on a package, you can select the item History. It opens a version history viewer that displays the comments committed along with each version of the selected package (see Figure \(\PageIndex{1}\)). The versions of the package, in this case Perfect, are listed on the left, while information about the selected version is displayed on the right.

    \(\bigstar\) Select the Perfect package, right click and select the History item.

    The version history viewer.
    Figure \(\PageIndex{1}\): The version history viewer provides informaiton about the various versions of a package.

    By action-clicking on a particular version, you can explore the changes with respect to the current working copy of the package loaded in the image, or spawn a new history browser relative to the selected version.

    Dependencies

    Most applications cannot live on their own and typically require the presence of other packages in order to work properly. For example, let us have a look at Pier1, a meta-described content management system. Pier is a large piece of software with many facets (tools, documentations, blog, catch strategies, security, etc). Each facet is implemented by a separate package. Most Pier packages cannot be used in isolation since they refer to methods and classes defined in other packages. Monticello provides a dependency mechanism for declaring the required packages of a given package to ensure that it will be correctly loaded.

    Essentially, the dependency mechanism ensures that all required packages of a package are loaded before the package is loaded itself. Since required packages may themselves require other packages, the process is applied recursively to a tree of dependencies, ensuring that the leaves of the tree are loaded before any branches that depend on them. Whenever new versions of required packages are checked in, then new versions of the packages that depend on them will automatically depend on the new versions.

    Dependencies cannot be expressed across repositories. All requiring and required packages must live in the same repository.

    Figure \(\PageIndex{2}\) illustrates how this works in Pier. Package Pier-All is an empty package that acts as a kind of umbrella. It requires Pier-Blog, Pier-Caching and all the other Pier packages.

    Pier dependencies diagram.
    Figure \(\PageIndex{2}\): Dependencies in Pier.

    Because of these dependencies, installing Pier-All causes all the other Pier packages to be installed. Furthermore, when developing, the only package that needs to be saved is Pier-All; all dependent dirty packages are saved automatically.

    Let us see how this works in practice. Our Perfect package currently bundles the tests together with the implementation. Suppose we would like instead to separate these into separate packages, so that the implementation can be loaded without the tests. By default, however, we would like to load everything.

    \(\bigstar\) Take the following steps:

    • Load version 4 of the Perfect package from the package cache
    • Create a new package in the browser called NewPerfect-Tests and drag the class PerfectTest to this package
    • Rename the *perfect protocol of the Integer class to *newperfect-extensions (action-click to rename it)
    • In the Monticello browser, add the packages NewPerfect-All and NewPerfect-Extensions.
    • Add NewPerfect-Extensions and NewPerfect-Tests as required packages to NewPerfect-All (action-click on NewPerfect-All)
    • Save package NewPerfect-All in the package-cache repository. Note that Monticello prompts for comments to save the required packages too.
    • Check that all three packages have been saved in the package cache.
    • Monticello thinks that Perfect is still loaded. Unload it and then load NewPerfect-All from the repository inspector. This will cause NewPerfect-Extensions and NewPerfect-Tests to be loaded as well as required packages.
    • Check that all tests run.

    Note that when NewPerfect-All is selected in the Monticello browser, the dependent packages are displayed in bold (see Figure \(\PageIndex{3}\)).

    Monticello Browser showing dependencies.
    Figure \(\PageIndex{3}\): NewPerfect-All requires NewPerfect-Extensions and NewPerfect-Tests.

    If you further develop the Perfect package, you should only load or save NewPerfect-All, not its required packages.

    Here is the reason why:

    • If you load NewPerfect-All from a repository (package-cache, or anywhere else), this will cause NewPerfect-Extensions and NewPerfect-Tests to be loaded from the same repository.
    • If you modify the PerfectTest class, this will cause the NewPerfect-Tests and NewPerfect-All packages to both become dirty (but not NewPerfect-Extensions).
    • To commit the change, you should save NewPerfect-All. This will commit a new version of NewPerfect-All which then requires the new version of NewPerfect-Tests. (It will also depend on the existing, unmodified version of NewPerfect-Extensions.) Loading the latest version of NewPerfect-All will also load the latest version of the required packages.
    • If instead you save NewPerfect-Tests, this will not cause NewPerfect-All to be saved. This is bad because you effectively break the dependency. If you then load the latest version of NewPerfect-All you will not get the latest versions of the required packages. Don’t do it!

    Do not name your top level package with a suffix (e.g., Perfect) that could match your subpackages. Do not define Perfect as a required package of Perfect-Extensions or PerfectTest. You would run into trouble as Monticello would save all the classes for three packages, though you only want two packages and an empty one at the top level.

    To build more flexible dependencies between packages, we recommend using a Metacello configuration (see Chapter 9). The +Config button creates a kind of configuration structure. The only thing to do is to add the dependencies.

    Class initialization

    When Monticello loads a package into the image, any class that defines an initialize method on the class side will be sent the initialize message. The message is sent only to classes that define this method on the class side. A class that does not define this method will not be initialized, even if initialize is defined by one of its superclasses. NB: the initialize method is not invoked when you merely reload a package!

    Class initialization can be used to perform any number of checks or special actions. A particularly useful application is to add new instance variables to a class.

    Class extensions are strictly limited to adding new methods to a class. Sometimes, however, extension methods may need new instance variables to exist.

    Suppose, for example, that we want to extend the TestCase class of SUnit with methods to keep track of the history of the last time the test was red. We would need to store that information somewhere, but unfortunately we cannot define instance variables as part of our extension.

    A solution would be to define an initialize method on the class side of one of the classes:

    TestCaseExtension class>>initialize
        (TestCase instVarNames includes: 'lastRedRun')
            ifFalse: [TestCase addInstVarName: 'lastRedRun']
    

    When our package is loaded, this code will be evaluated and the instance variable will be added, if it does not already exist. Note that if you change a class that is not in your package, the other package will become dirty. In the previous example, the package SUnit contains TestCase. After installing TestCaseExtension, the package SUnit will become dirty.


    1. http://source.lukas-renggli.ch/pier

    This page titled 6.4: Advanced Topics is shared under a CC BY-SA 3.0 license and was authored, remixed, and/or curated by Alexandre Bergel, Damien Cassou, Stéphane Ducasse, Jannik Laval (Square Bracket Associates) via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.