18.5.7 Magritte presentation
Magritte is a framework for meta-describing data, and one of its applications is the automatic generation of user interfaces for handling data. An extensive description of the concepts of Magritte is provided at: http://book.seaside.st/book/advanced/magritte.
All that is required by the Magritte engine is a description for each of the handled entities. Thus, the MagrittePresentation expects the evaluation of the display:
block to return an object that can be used for the model behind the description:
. Once the description obtained, it is simply passed to Magritte and the engine takes care of the rendering.
As an example, let us set our mind to create a small tool to help us review source code. More specifically, we want for each class we inspect in the code browser to have a small form that enables us to write a comment and to mark the class as problematic.
We first need the class that will hold the review information. Thus, we create TMBReview
class that holds a comment and problematic instance variables:
Object subclass: #TMBReview
instanceVariableNames: 'comment problematic'
classVariableNames: ''
poolDictionaries: ''
category: 'TheMooseBook-Examples'
TMBReview>>comment
^ comment
TMBReview>>comment: aString
comment := aString
TMBReview>>problematic
^ problematic
TMBReview>>problematic: aBoolean
problematic := aBoolean
The next step is to make this class Magritte aware:
TMBReview class>>descriptionComment
^ MAStringDescription new
accessor: #comment;
label: 'Comment';
priority: 100;
yourself
TMBReview class>>descriptionProblematic
^ MABooleanDescription new
accessor: #problematic;
label: 'Problematic';
priority: 200;
yourself
Once we have that, we can just go ahead and build our browser:
| browser |
browser := GLMTabulator new.
browser title: 'Reviewer'.
browser
row: [ :r | r column: #namespaces; column: #classes; column: #methods ];
row: [:r | r column: #details span: 2; column: #review ].
browser transmit to: #namespaces; andShow: [ :a |
a tree
display: [ :model | model allNamespaces select: [ :each | each isRoot ] ];
children: [ :namespace | namespace childScopes ];
format: [ :namespace | namespace stubFormattedName ] ].
browser transmit from: #namespaces; to: #classes; andShow: [ :a |
a list
display: [ :namespace | namespace classes ];
format: [ :class | class stubFormattedName ] ].
browser transmit from: #classes; to: #methods; andShow: [ :a |
a list
display: [ :class | class methods ];
format: [ :method | method stubFormattedName ] ].
browser transmit from: #classes; from: #methods; to: #details; andShow: [ :a |
a text
title: [ :class | class name, ' source'];
display: [ :class | class sourceText ];
allowNil.
a text
title: [ :class :method | method name, ' source'];
display: [ :class :method | method sourceText ] ].
browser transmit from: #classes; to: #review; andShow: [:a |
a magritte
title: 'Review';
display: [ :class |
| review |
review := class propertyNamed: #theReview ifAbsentPut: [TMBReview new].
review ];
description: #description].
For obtaining the review information for each class, we make use of the generic propertyNamed:ifAbsentPut:
method present in any Moose entities. Using this, we lazily create a review object if none exists for the current class. We then pass the description
of this object to be rendered.
Executing the script leads to a browser like in the screenshot below: