The PR superseeds the existing deepMerge algorithm used to merge
settings of parent and child models with a new algorithm that allows
to specify the way each setting is merged or mixed-in.
This configuration of this algorithm uses a merge policy specification.
The `getMergePolicy()` helper of BaseModelClass can be used to ease
model merge configuration.
Next is presented the expected merge behaviour for each option.
NOTE: This applies to top-level settings properties
- Any
- `{replace: true}` (default): child replaces the value from parent
- assignin `null` on child setting deletes the inherited setting
- Arrays
- `{replace: false}`: unique elements of parent and child cumulate
- `{rank: true}` adds the model inheritance rank to array
elements of type Object {} as internal property `__rank`
- Object {}:
- `{replace: false}`: deep merges parent and child objects
- `{patch: true}`: child replaces inner properties from parent
The recommended merge policy is returned by getMergePolicy()
when calling the method with option `{configureModelMerge: true}`.
The legacy built-in merge policy is returned by `getMergePolicy()`
when avoiding option `configureModelMerge`.
NOTE: it also delivers ACLs ranking in addition to the legacy
behaviour as well as fixes for settings `description` and `relations`
`getMergePolicy()` can be customized using model's setting
`configureModelMerge` as follows:
```
{
// ..
options: {
configureModelMerge: {
// merge options
}
}
// ..
}
```
`getMergePolicy()` method can also be extended programmatically as
follows:
```
myModel.getMergePolicy = function(options) {
const origin = myModel.base.getMergePolicy(options);
return Object.assign({}, origin, {
// new/overriding options
});
};
```
The setting controls the strict mode used for embedded property types,
for example the type of "address" property in this model definition:
modelBuilder.define('TestEmbedded', {
name: 'string',
address: {
street: 'string',
},
});
Sub models sometimes need to customize the properties from the base model.
This change allows each sub model has its own copy of the base property
definition to avoid potential conflicts across multiple sub models of the
same base.
When the setting "persistUndefinedAsNull" is true,
the model will use `null` instead of `undefined` in
all property values.
- Known optional model properties are set to `null` when no value
was provided.
- When setting model properties, `undefined` is always converted
to `null`. This applies to both known (model-defined) properties
and additional (custom, dynamic) properties.
- The instance method `toObject()` converts `undefined` to `null` too.
Because `toJSON()` calls `toObject()` under the hood, the change
applies to `toJSON()` too.
Fix the bug in `ModelClass.extend` where the `base` option used
in the new class was inherited from ModelClass. As a result
the extended model was incorrectly based on ModelClass's parent.
Modify `modelBuilder.define` to normalize the property name storing
the name of the base model to `settings.base`.
Add a shortcut for registering a new value type.
The current implementation registers the type in the singleton registry
`ModelBuilder.schemaTypes`.
The API should allow us to to change the implementation to register
the type in the scope of ModelBuilder instance only.