Metacello Baselines

Metacello is Smalltalk's dependency manager: it describes which packages a project has, how they depend on each other, which external projects are required, and in which order to load them.

29.1 What Metacello does

Metacello is Smalltalk's dependency manager: it describes which packages a project has, how they depend on each other, which external projects are required, and in which order to load them. A baseline is a class named BaselineOf<Project> with a baseline: method.

29.2 Writing a baseline

Platform-specific sections use spec for: #pharo do: [...], spec for: #squeak do: [...], spec for: #gemstone do: [...], or version-specific attributes such as #'pharo12.x'.

BaselineOf << #BaselineOfBank
    slots: {};
    package: 'BaselineOfBank'

BaselineOfBank >> baseline: spec
    <baseline>
    spec for: #common do: [
        "External dependencies"
        spec
            baseline: 'NeoJSON'
            with: [ spec repository: 'github://svenvc/NeoJSON:master/repository' ].
        spec
            baseline: 'Teapot'
            with: [ spec repository: 'github://zeroflag/Teapot:master/source' ].

        "Our packages and their dependencies"
        spec
            package: 'Bank-Core' with: [ spec requires: #( 'NeoJSON' ) ];
            package: 'Bank-Tests' with: [ spec requires: #( 'Bank-Core' ) ];
            package: 'Bank-Web' with: [ spec requires: #( 'Bank-Core' 'Teapot' ) ].

        "Groups: named subsets to load"
        spec
            group: 'Core' with: #( 'Bank-Core' );
            group: 'Tests' with: #( 'Bank-Tests' );
            group: 'Web' with: #( 'Bank-Web' );
            group: 'default' with: #( 'Core' 'Tests' 'Web' ) ]

29.3 Loading a project

Repository URL forms: github://user/repo:branchOrTag/subdir, gitlab://…, bitbucket://…, gitlocal://path, filetree://path, tonel://path, and HTTP Monticello URLs.

Metacello new
    baseline: 'Bank';
    repository: 'github://myuser/my-bank:main/src';
    load.                                     "loads the 'default' group; use load: #( 'Core' ) for a group"

29.4 Handling conflicts and locks

Metacello new
    baseline: 'Bank';
    repository: 'github://myuser/my-bank:main/src';
    onConflictUseIncoming;               "or onConflictUseLoaded"
    load.
"Lock a dependency to a local working copy during development"
Metacello new
    baseline: 'NeoJSON';
    repository: 'gitlocal:///home/me/src/NeoJSON/repository';
    lock.

29.5 Pre/post load actions

Inside a package spec, spec preLoadDoIt: #selector and spec postLoadDoIt: #selector run baseline methods before or after loading (e.g., to initialize default settings).

29.6 ConfigurationOf (legacy)

Before Git, projects used ConfigurationOf<Project> classes with explicit numbered versions. You will still meet them in older projects; new projects should use baselines plus Git tags for versions.

🧪 Part II Exercises

Define a Fraction-like class Rational (named MyRational to avoid clashes) with numerator, denominator, normalization in the constructor, +, *, =, hash, <, and printOn:. Make it a subclass of Magnitude and check you get max: and between:and: for free.

Build a Shape hierarchy (Circle, Rect, Triangle) with an abstract area and perimeter; sort a collection of shapes by area with sorted: #area ascending.

Demonstrate the difference between a class variable and a class-side instance variable with a per-class instance counter in a three-level hierarchy.

Write a LoggingProxy (subclass of ProtoObject) that counts every message sent to the wrapped object, and use it to find how many messages sorted sends to an Array.

Explore metaclasses: write expressions showing the class, superclass, and metaclass chain for 3, Object, Object class, and Metaclass; draw the diagram.

Given a text file, compute the 10 most frequent words (case-insensitive) using a Bag, substrings, and sortedCounts; write the report to a new file with FileSystem.

Implement a Stack and Queue with TDD by starting from tests and creating all methods in the debugger.

Define an exception hierarchy for a library system (LibraryError, BookNotAvailable, MemberSuspended), signal them from domain methods, and test them with should:raise:.

Use retry to implement a function that re-attempts a flaky operation up to 5 times with an increasing Delay, then gives up with a custom error.

Put your Part II code in Git with Iceberg in Tonel format, write a BaselineOf with Core and Tests groups, and load it into a fresh image with Metacello.