How to implement a moldable exception

Overview

The basic steps are the following:

(1) Define a new subclass of Exception Object subclass: #Exception instanceVariableNames: 'messageText tag signaler signalContext handlerContext outerContext' classVariableNames: '' package: 'Kernel-Exceptions' that encapsulates all the data you need your dubugger views.

(2) Add view methods to your exception class with a <gtExceptionView> pragma.

(3) Catch exceptions when they occur, and signal instead your new exception.

Example — Moldable Ludo exceptions

Let's look an example where we want to catch errors in playing a GtLudoRecordingGame GtLudoGame subclass: #GtLudoRecordingGame instanceVariableNames: 'moves' classVariableNames: '' package: 'GToolkit-Demo-Ludo-Model' .

The game already makes use of assertions to detect errors in the game play:

#assert:description: gtSenders 
	& GtLudoMove gtMethodsInClass.
  

We define our own exception LudoMoveAssertionFailure Error subclass: #LudoMoveAssertionFailure instanceVariableNames: 'move' classVariableNames: '' package: 'GToolkit-Demo-Ludo-Exceptions' which encapsulates the problematic move, and we signal it in GtLudoMove>>#assert:description: assert: aBlock description: aStringOrBlock "Demo of moldable exceptions." <debuggerCompleteToSender> aBlock value ifFalse: [ (LudoMoveAssertionFailure forMove: self) signal: aStringOrBlock value ] , which now overrides Object>>#assert:description: assert: aBlock description: aStringOrBlock "Throw an assertion error if aBlock does not evaluates to true." <debuggerCompleteToSender> "we do the == true check to avoid NonBooleanReceiver error for nonBoolean aBlock values" aBlock value == true ifFalse: [ AssertionFailure signal: aStringOrBlock value ] .

We just add a few simple, forwarding debugger views, namely:

LudoMoveAssertionFailure>>#gtGameViewFor: gtGameViewFor: aView <gtView> <gtExceptionView> ^ aView forward title: 'Game'; priority: 10; object: [ move game ]; view: #gtPositionsFor: , and

LudoMoveAssertionFailure>>#gtMovesViewFor: gtMovesViewFor: aView <gtView> <gtExceptionView> ^ aView forward title: 'Moves'; priority: 20; object: [ move game ]; view: #gtMovesFor:

Each of these views forwards to a view we already have for the Ludo game.

Now we can see the moldable exception in action by provoking exceptions.

Here we try to move without rolling the die first:

GtLudoRecordingGameExamples new bEntersAndPlaysWithAahead
	moveTokenNamed: 'B';
	yourself.
  

Here we try to move the wrong player:

GtLudoRecordingGameExamples new bEntersAndPlaysWithAahead
	roll: 6;
	moveTokenNamed: 'B';
	yourself.
  

In both cases we now get a Debugger with our three debugging views. We can switch back to the regular debugger by clicking the GT Debugger button at the top.