externalizing Magritte descriptions and partial forms

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

externalizing Magritte descriptions and partial forms

Peter Uhnak
Hi,

I'm trying to wrap my head around Magritte but I am failing hard...

Imagine I have object with three attributes.

~~~~~~~~~~~~~~~~~~~
Object subclass: #Person
instanceVariableNames: 'name age email'
~~~~~~~~~~~~~~~~~~~

Now the normal way would be to add magritte descriptions...

~~~~~~~~~~~~~~~~~~~
Person>>descriptionEmail
<magritteDescription>
    ^ MAStringDescription new
        label: 'Email';
        accessor: #email;
        beRequired;
        yourself
~~~~~~~~~~~~~~~~~~~
etc for the rest...


And now if I want to manipulate it I would do...
~~~~~~~~~~~~~~~~~~~
|p|
p := Person new name: 'Someone'; email: '[hidden email]'; age: 20.
p asMagritteMorph addButtons; openInWindow.
~~~~~~~~~~~~~~~~~~~

So far so simple... however what I don't understand:

1. What if I want to have different descriptions for the same attribute?
For example in some views the email is required and in others it is not.

2. Must the description be part of the object?
Maybe I am still not fully committed to the having massive protocols
with tons of extension methods instead of externalizing the code to
separate adapters and whatnot.

3. What if I want only partial rendering?
For example in one view I want to be able to edit only name and age,
and in another one all three.
Since #asMagritteMorph collects everything I don't see how I can customize this.

Now I do expect that all these things are possible, however as I'm
just starting with Magritte it's a lot of information and concepts to
absorb... so any help is appreciated.

Also, is there maintained Magritte-Spec? I've seen something by Sean,
but it's from 2013 with a single-ish commit.

Thanks,
Peter

Reply | Threaded
Open this post in threaded view
|

Re: externalizing Magritte descriptions and partial forms

Stephan Eggermont-3
On 21-09-15 02:46, Peter Uhnák wrote:
> So far so simple... however what I don't understand:
>
> 1. What if I want to have different descriptions for the same attribute?
> For example in some views the email is required and in others it is not.

asMagritteMorph is just the quick and dirty variant for straightforward
situations.

> 2. Must the description be part of the object?

Yes. Single responsibility principle applied.

> Maybe I am still not fully committed to the having massive protocols
> with tons of extension methods instead of externalizing the code to
> separate adapters and whatnot.

Ahum, that is exactly what Magritte does, while reusing the smalltalk
tools. For very large domain models (more than several hundred domain
classes), you'll be able to do better using a dynamic object model, and
then you'll have to develop browsers, editors and inspectors for that.

> 3. What if I want only partial rendering?
> For example in one view I want to be able to edit only name and age,
> and in another one all three.
> Since #asMagritteMorph collects everything I don't see how I can customize this.

asMagritteMorph provides the default. On Object it is defined as

Object>>asMagritteMorph
        self magritteDescription  asMorphOn: self

Object>>magritteDescription
        ^ self basicMagritteDescription

Object>>basicMagritteDescription
        ^ MAPragmaBuilder for: self

which collects all descriptions and puts them in the container described by

Object>descriptionContainer
        "Return the default description container."
        <magritteContainer>
        ^ MAPriorityContainer new
                label: self class label;
                yourself.

The nice thing about a MA(Priority)Container is that it supports a
large part of the collection protocol, so you can just do

Person>>asNameMagritteMorph
        ^(self magritteDescription select: [:each | each label = 'Name'])
asMorphOn: self

So you'd  define all possible descriptions you want on the domain object
itself, possibly using extension methods, and override #asMagritteMorph
to only return the default descriptions. For complex dependencies with
extensions, use a smarter MAPragmaBuilder subclass that makes decisions
based on which extensions are loaded.

You also want to take a look at reference. In the Seaside generation
that is used like

MAInternalEditorComponent>>buildComponent
        ^ self value isNil ifFalse: [
                (self magritteDescription reference asComponentOn: self value)
                        setParent: self;
                        yourself ]

so that provides an extension point for nested objects. I don't see it
used in Magritte-Morph

> Also, is there maintained Magritte-Spec? I've seen something by Sean,
> but it's from 2013 with a single-ish commit.

I don't know if it needs more. Nothing changed in the spec interface
since then, did there?

Stephan


Reply | Threaded
Open this post in threaded view
|

Re: externalizing Magritte descriptions and partial forms

Peter Uhnak
Thank you Stephan, I think the image is becoming much clearer.

However I still don't understand how SRP applies here:

> Person>>asNameMagritteMorph
>         ^(self magritteDescription select: [:each | each label = 'Name'])
> asMorphOn: self

Surely this is responsibility of the tool that needs it, no?
Otherwise the Person class would have to describe every single
possible scenario.

Or maybe the tool would puts this into an extension protocol, which
while visually places the code to Person class is actually separated
(because it's part of another repository).

>
>> Also, is there maintained Magritte-Spec? I've seen something by Sean,
>> but it's from 2013 with a single-ish commit.
>
>
> I don't know if it needs more. Nothing changed in the spec interface since
> then, did there?

The Magritte-Spec seems to be just an experiment (with three classes)
and it breaks Magritte-Morphic.
Also I know I have changed Spec interface in the last year alone and
as far as I can tell there was a lot work done in before.

In any case I can always use asMagritteMorph morph asSpecAdapter in
the meantime and maybe stitch together a Spec version once I am
comfortable with Magritte.

Thanks!

Peter

Reply | Threaded
Open this post in threaded view
|

Re: externalizing Magritte descriptions and partial forms

Stephan Eggermont-3
On 21-09-15 12:09, Peter Uhnák wrote:
> Thank you Stephan, I think the image is becoming much clearer.
>
> However I still don't understand how SRP applies here:
>
>> Person>>asNameMagritteMorph
>>          ^(self magritteDescription select: [:each | each label = 'Name'])
>> asMorphOn: self
>
> Surely this is responsibility of the tool that needs it, no?

No. It is a domain model. The person class is an information
holder/structurer (Object Design, Roles, Responsibilities, and
Collaborations), describing all possible uses, the user selects the one
it needs.

If the morph you need depends on the context in which it is shown,
you might want to add a

Person>>asMagritteMorphFrom: aDomainContext
        ^(aDomainContext magritteDescriptionFor: self magritteDescription)
asMorphOn: self

and do the selection on the other side

> Otherwise the Person class would have to describe every single
> possible scenario.

Not the scenario, but the possible combinations yes.

> Or maybe the tool would puts this into an extension protocol, which
> while visually places the code to Person class is actually separated
> (because it's part of another repository).

For optional parts that works well, especially using MAObject subclasses
with the dictionary based properties.

> In any case I can always use asMagritteMorph morph asSpecAdapter in
> the meantime and maybe stitch together a Spec version once I am
> comfortable with Magritte.

That sounds like the right order.





Reply | Threaded
Open this post in threaded view
|

Re: externalizing Magritte descriptions and partial forms

Damien Cassou-2
In reply to this post by Peter Uhnak

Peter Uhnák <[hidden email]> writes:

> ~~~~~~~~~~~~~~~~~~~
> Person>>descriptionEmail
> <magritteDescription>
>     ^ MAStringDescription new
>         label: 'Email';
>         accessor: #email;
>         beRequired;
>         yourself
> ~~~~~~~~~~~~~~~~~~~

to complement Stephan's answer, you can add properties to your
description that you use to filter them when building the morphs. For
example:

Person>>descriptionEmail
<magritteDescription>
    ^ MAStringDescription new
        label: 'Email';
        accessor: #email;
        beRequired;
        shouldBeShownInTheBasicView: true;
        yourself

Then, when building the morph, you can #select: on
'shouldBeShownInTheBasicView' value.


--
Damien Cassou
http://damiencassou.seasidehosting.st

"Success is the ability to go from one failure to another without
losing enthusiasm." --Winston Churchill