Contextual Playground

Context

You are using a Moldable Tool, and you are ready to start exploring your software system.

Problem

Where do you start exploring?

Forces

— An editor for coding new methods typically provides no facilities for testing the code.

— When we code new behavior as methods, we must repeatedly change our context to incrementally develop the logic.

— Testing the code requires a separate setup.

— Setting up code to prototype and test logic can be cumbersome.

— Writing tests first for parts of the logic of a complex method may can be overkill.

Solution

Use a contextual playground of the tool to programmatically explore the entities of interest, and prototype new behavior.

Steps

A contextual playground provides a live programming interface to programmatically interact with the software entity managed by a Moldable Tool. The playground will be bound to the context of the object, so self and all slots ( i.e. , instance variables) can be accessed exactly as they would in a running method.

Use the contextual playground to write code snippets to extract data, test hypotheses, navigate to parts or other related objects, to explore the API, or to prototype new behavior. Evaluating the code will typically open a new instance of another moldable tool on the result returned by the code, with a new contextual playground. Code snippets that turn out to be useful or interesting can then be copy-pasted to existing methods, or extracted to new methods using an Extract method refactoring. The same process can be used whether the extracted code consists of accessors, assertions, examples, or new behavior.

Examples

The page Inspecting Python objects with custom inspectors shows how to obtain an inspector view of an instance of a Python MovieCollection class. The notebook page contain live code snippets, which function as contextual playgrounds within the context of the notebook page. From there we can navigate to a moldable object inspector, a moldable tool for exploring live instances. In this case we are exploring a live Python instance of a MovieCollection object, with several custom views showing the Directors, Years and Movies of the collection. At the bottom of the inspector we see a contextual playground bound to the context of the Python object.

Exploring Python objects within a contextual playground (bottom of middle pane).

In this contextual playground, we can experiment with Python code to perform a query over the collection and navigate to a particular Movie instance. Note that self in the code snippet is bound to the context of the MovieCollection instance. Once we have identified some useful code, we can extract it as a new method of the object. Similarly, if we identify an interesting instance, we can use the code to define an Example Object.

Evaluating this snippet opens another Python object inspector on the resulting Movie, seen in the third pane. The third pane also has a contextual playground, which can be opened by dragging up the handle at the bottom of the pane.

Consequences

By prototyping new behavior in a contextual playground, you obtain immediate feedback for experimental code.

You may need to evaluate multiple snippets in multiple playgrounds to get to interesting information.

Known uses

A REPL (Read-eval-print loop) or language shell is essentially a contextual playground for a programming language or an operating system.

The JavaScript console of a modern web browser also functions as a contextual playground: while inspecting a live web page, you can explore and programmatically interact with the live objects in the run-time environment of the page.

The moldable tools within GT all provide contextual playgrounds: notebook pages, object inspectors, code editors, and the debugger, in particular.

You can also see this process at work in the short video Exploring the GitHub REST API in 7'.

Related patterns

Use a Project Diary to start live coding. You can use a contextual playground to prototype custom tools (see Custom View, Custom Action and Custom Search) for a Moldable Object within a moldable inspector. A snippet within a contextual playground that yields an interesting object can be extracted as an Example Object. A contextual playground for an Example Object can also be used to explore and prototype assertions for that example.

GT implementation notes

The following steps illustrate how the Inspector Playground can be leveraged to explore an object's state, experiment with code, extract methods, extract (test) examples, and add custom views to a live object.

Suppose we are implementing a StackMachine class to simulate an RPN calculator. So far it just holds a stack slot initialized to an empty OrderedCollection SequenceableCollection subclass: #OrderedCollection instanceVariableNames: 'array firstIndex lastIndex' classVariableNames: '' package: 'Collections-Sequenceable-Ordered' , and nothing else. We want to implement operations to perform calculations with the stack machine.

Exploring the object's state

We start by exploring a live instance.

StackMachineV0 new 
  

We would like to push some values onto the stack but we need to explore the API of the underlying objects.

From the Inspector we dive into the stack slot and query its Meta view to see which add operations it has.

We find the addLast: selector, and experiment with it in the Inspector Playground.

We see in the stack's Items view that it has been updated.

Extracting a method

We generalize the code as follows.

We select the code and perform an Extract method refactoring, and name the new (binary) method !.

(For illustrative purposes we have multiple StackMachine classes to represent the different versions. In reality we would just have one class.)

We push two values onto the stack from the Inspector Playground.

Extending the API

It would be nice to inspect the stack and the top of the stack, so we test each of the following snippets in the Playground, and extract them as methods.

stack. stack isEmpty. stack last "top".

Our API now looks like this: StackMachineV1 Object subclass: #StackMachineV1 instanceVariableNames: 'stack' classVariableNames: '' package: 'GToolkit-Demo-StackMachine-Model'

Extracting an example method

We rewrite our scenario as an example:

example := self class new. example ! 3. example ! 4. example

And again we extract a method. We add a <gtExample> pragma, with some assertions. StackMachineV2>>#stackWith3and4 stackWith3and4 <gtExample> | example | example := self class new. self assert: example isEmpty. example ! 3. self assert: example isEmpty not. self assert: example top equals: 3. example ! 4. self assert: example top equals: 4. ^ example

We can generate an instance directly from this method ( Play and inspect ).

Adding a custom view

We'd like a custom view for the StackMachine that mirrors the stack slot's Items view.

From the live example, we navigate using the Raw view to the stack's Items view, Secondary-click on the tab and see that the method that implements this view is called gtItemsFor: (implemented in SequenceableCollection>>#gtItemsFor: gtItemsFor: aView <gtView> ^ aView columnedList title: 'Items'; priority: 50; items: [ self ]; actionUpdateButtonTooltip: 'Update item list'; column: 'Index' text: [ :eachItem :eachIndex | eachIndex asRopedText foreground: Color gray ] width: 45; column: 'Item' text: [ :eachItem | eachItem gtDisplayText ]. .

Now we can quickly implement a forwarding view as follows: StackMachineV3>>#gtStackFor: gtStackFor: aView <gtView> ^ aView forward title: 'Stack'; priority: 10; object: [ stack ]; view: #gtItemsFor:

It looks like this: