Loading and managing code configurations with Metacello

Metacello is the code configuration management engine for Pharo. It's like NPM for JavaScript or Maven for Java. It essentially has two abilities: (1) define what packages a project consists of in a so called Metacello baseline, and (2) offer a way to load a given baseline from a remote repository.

Quick start

Consider the following script:

Metacello new
   baseline: 'GToolkit4P3';
   repository: 'github://feenkcom/gt4p3';
   load.
  

Executing the script will load the GTooklit4P3 project from https://github.com/feenkcom/gt4p3. This will load the package GToolkit4P3 and the baseline called BaselineOfP3ForGToolkit. Go ahead and try. And then browse BaselineOfGToolkit4P3.

The anatomy of a Metacello baseline

A baseline is defined in plain Pharo code through a fluent API.

Consider BaselineOfDeepTraverser BaselineOf subclass: #BaselineOfDeepTraverser instanceVariableNames: '' classVariableNames: '' package: 'BaselineOfDeepTraverser' . It is a subclass of BaselineOf ConfigurationOf subclass: #BaselineOf instanceVariableNames: '' classVariableNames: '' package: 'Metacello-Base' defines a BaselineOfDeepTraverser>>#baseline: baseline: spec <baseline> spec for: #'common' do: [ spec baseline: 'GToolkitExamplesEngine' with: [ spec repository: 'github://feenkcom/gtoolkit-examples:main/src' ]. spec package: 'DeepTraverser'; package: 'DeepTraverser-MemoryTraversal' with: [ spec requires: #('DeepTraverser' ). ]; package: 'DeepTraverser-Examples' with: [ spec requires: #('DeepTraverser' 'GToolkitExamplesEngine' ). ]; package: 'DeepTraverser-Tests' with: [ spec requires: #('DeepTraverser' ). ]. ]. method that is annotated with a <baseline> pragma. The method defines a dependency to BaselineOfGToolkitExamplesEngine BaselineOf subclass: #BaselineOfGToolkitExamplesEngine instanceVariableNames: '' classVariableNames: '' package: 'BaselineOfGToolkitExamplesEngine' and it specifies that there are several package in the project, such as DeepTraverser.

Simple package definition

A contained package is defined like this:

Defining a package with load ordering

Sometimes, we want to specify that a package is to be loaded after another package. For example, DeepTraverser-MemoryTraversal depends on DeepTraverser, so we want it loaded afterwards. This is achieved with:

Defining a baseline dependency

A baseline dependency is defined similarly:

Visualizing baseline and package dependencies

Evaluating the dependencies between packages and projects is easier when seen through a visualization. You can see it in Coder when browsing a baseline. For example, for the baseline of BaselineOfDeepTraverser BaselineOf subclass: #BaselineOfDeepTraverser instanceVariableNames: '' classVariableNames: '' package: 'BaselineOfDeepTraverser' we can see this view showing how packages depend on each other and on external projects: