I finally got around to writing some documentation (reprinted below from
[1]), so I figured this was a good time to announce. Load "Magritte-Merging"
from my GH fork [2]
Motivation
Let's say you have a Person object with a Name. Maybe you are in the
marketing domain, and info comes in through various lists (say csv or Excel
files). Some of these may contain missing, incomplete, or incorrect
information. When new info comes in referring to the same person, you'd like
to take the most correct parts of both the existing and incoming data.
API
Main entry point
The well-known #patchRelativeToBase:, which we borrow from Monticello.
newObject patchRelativeToBase: existingObject returns an MAPatchMacro which
contains a MAPatchOperation for each description of interest. "Interesting"
here means that the description:
1. The field has not been excluded from merging
2. The new value is interesting
3. Excluding a field from merging
Just do anElementDescription shouldMerge: false. Why would you want to do
this? Continuing our Person example, let's say we have a virtual #fullName
field which concatenates #firstName, #lastName, etc into a pretty printed
string. The real data lies not in the generated string, but in the
components and will be merged there. NB: maybe a simpler way would be to
exclude #readonly fields?
Is a New Value "Interesting"
The default rules are:
1. The value has changed - duh!
2. The new value is not nil
3. The new value is not the Magritte default value (This should be
implemented. See Issue #19)
These can be changed by overriding #is:anInterestingReplacementFor:,
currently on the domain object level e.g. Name. For example, aName might
want to merge a new value which is a full first name like "James", when the
previous value was an abbreviation e.g. "J.". Obviously, overriding once per
domain object only works when its fields which are all similar. Probably in
the future when the need arises, this should be moved to the element
description.
Merging in Place
By default, field values are replaced with the new values. If instead you
want to keep the existing field value object and recursively update its
fields, in your field type (Name in our example), override as follows:
patchRelativeToBase: oldValue as: description of: receiver
"Edit me in place instead of full replacement like a value object"
^ self patchRelativeToBase: oldValue
This would allow us to consider each update to aPerson's name individually
and not as an atomic operation. We may want to accept the new first name,
but reject the new middle name.
1. https://github.com/seandenigris/Magritte3/wiki/Merging
2. https://github.com/seandenigris/Magritte3/
-----
Cheers,
Sean
--
Sent from: http://forum.world.st/Magritte-Pier-and-Related-Tools-f115649.html
This is something I've been dreaming about for a long time, and finally had
some time to implement. Next step: integrate with browser, and possible
offer as a refactoring…
Motivation: Define a descripton on a class, along with instVar, getter and
setter, in three easy steps.
Example Usage:
```smalltalk
MyClass maAddField: #employeeNumber.
```
1. A list of MADescription subclasses will appear; select one.
2. Next fill out a Morphic Magritte form on a prototype of your selection
from #1
3. An annotated description-creation method will be added to MyClass. If you
gave the description an MASelectorAccessor, the instVar, getter, and setter
will be created if needed.
-----
Cheers,
Sean
--
Sent from: http://forum.world.st/Magritte-Pier-and-Related-Tools-f115649.html
I'm wondering whether conditions could be fleshed out some more or if I'm
missing some intention/design…
I was employing a technique described by Ramon Leon [1] for conditions that
affect multiple descriptions, namely:
```
descriptionContainer
^(super descriptionContainer)
addCondition: [:memento |
(memento cache at: self descriptionEndDate) > (memento cache at:
self descriptionStartDate)]
labelled: 'End date must be after start date';
yourself
```
I noticed a few seeming limitations of this technique (and conditions in
general):
1. The example above only works when using a cached memento. It fails due to
DNU #cache for other Memento types, as well as when validating objects
directly (e.g. `myDomainObject magritteDescription validate: self.`)
2. When the above fails, the exception bubbles up on its own instead of
being grouped together with failed conditions from individual descriptions
because `MAValidatorVisitor>>#visitContainer:` handles the container
validation separately (via `super visitContainer: aDescription.`) before
looping through the element descriptions to validate them.
3. In both the container and element validations, once one condition
per-[container | element] fails, others are not processed (for the same
[container | element]). I experimented with expanding the error handling
block, but couldn't see a great way to overcome this without a more
significant rewrite because MAValidationErrors are not resumable.
My quick fix idea would be to make MAValidationErrors resumable and expand
the error block to include the container along with the element validations.
Would that be the best way to address the issues above?
1. http://onsmalltalk.com/multiple-field-validation-rules-in-magritte
-----
Cheers,
Sean
--
Sent from: http://forum.world.st/Magritte-Pier-and-Related-Tools-f115649.html
In cases where there is an MASelectorAccessor, I'd like to make the default
label = `the accessor capitalized unCamelCased`. It seems the overwhelmingly
common use case is `... #accessor: #dateCreated; #label: 'Date Created'`.
The current behavior is
MADescription>>#label
^ self propertyAt: #label ifAbsent: [ self class defaultLabel ]
Is anyone using (or can state a plausible use case for) this
subclass-customizable class-side #defaultLabel?
If no one is relying on the existing behavior, I'll make the change...
-----
Cheers,
Sean
--
View this message in context: http://forum.world.st/Default-labels-tp4827879.html
Sent from the Magritte, Pier and Related Tools mailing list archive at Nabble.com.
Rereading "Magritte – A Meta-Driven Approach to Empower Developers and End
Users", I found the following very interesting passage on page 5:
> Magritte does not provide specific functionality to describe behavioral
> aspects, such as operations, their parameters and return values [17,9].
> This is not necessary, as methods in Smalltalk are objects that can be
> described as any other object. Then using the reflective facilities it is
> possible to retrieve a list of invokable method sends (first class method
> invocations) that are available on a particular class. On request these
> methods can be invoked with arguments provided by end users.
Can anyone provide or point to an example of the above in actual use? It is
very intriguing but I'm not clear how one would go about implementing it!
-----
Cheers,
Sean
--
Sent from: http://forum.world.st/Magritte-Pier-and-Related-Tools-f115649.html
Hi,
is there any photo gallery available for Pier? I mean a widget where I
could upload pictures and they are rendered in a nice way. I started
one 4 years ago but I abandoned it.
Bye
--
Damien Cassou
http://damiencassou.seasidehosting.st
Hi all
I have just uploaded a new config, and called it version 3.5.2.
Essential change:
Split out Trait extensions FileSystem/Directory detection and load only when supported.
- Traits are supporten in Pharo and Squek (but only when loaded).
- FileSystem is in all newer Pharo but could be loaded in Pharo 1.0 and Squeak, GemStone and Squeak have FileDriectory by default'
This makes it possible to properly load in Squeak and neatly have the dependencies more "capability"-based.
This is because Squeak can live with and without Traits or FileSystem.
Another goal was to Un-break GemStone a bit more, but not too much progress here.
The effect is that the tests for the external file model are now centralized, and the implementation in
Magritte-FileDirectory (for Squeak/GemStone), Magritte-FileSystem (for squeak, pharo-1 and pharo-2), and (traditional way) Magritte-Pharo3-Model).
If this is not to your liking, please tell and I'll reverse this.
=-=-=
Also, I found a problem with using DateAndTime as timestamp 'backend' in Pharo.
The problem is, that we loose information, should ever a DateAndTime be used that uses a local offset, and, sadly, that is almost every one.
Here's a snipped to illustrate:
|d t tz text |
tz := DateAndTime localTimeZone.
[d := MATimeStampDescription new
accessor: MANullAccessor new;
yourself.
t := GRPlatform current magritteTimeStampClass current.
text := d toString: t.
DateAndTime localTimeZone: (TimeZone offset: -8 hours name: 'Pacific Standard Time' abbreviation: 'PST').
(d fromString: text) = t
] ensure: [ DateAndTime localTimeZone: tz ].
You will get false, but we want true.
Sadly, this also holds for Squeak.
We could force a timeszone designator at the end of the to-string-converted version (like this
MAStringWriter>>visitTimeStampDescription: aDescription
self stream print: self object asDate; space.
self stream print: self object asTime; space.
(self object offset ~= Duration zero)
ifTrue: [self stream
nextPut: (self object offset positive ifTrue: [$+] ifFalse: [$-]);
nextPutAll: (self object offset hours abs asString padLeftTo: 2 with: $0);
nextPutAll: (self object offset minutes abs asString padLeftTo: 2 with: $0)]
ifFalse: [self stream nextPut: $Z]
) which would fix it for pharo.
But i really think that it would be better to force an ISO format, which should have explicit timezone (or 'Z' for that matter).
=-=-=-=
Best regards
-Tobias