Creating objects
Table of Contents
Introduction
When collecting user input, we often define the kind of input we expect using schemas. These schemas can use certain Python base types, but they can also use custom types, called objects, that we define. For example, consider this schema:
{
"type": "dict",
"properties": [
{
"name": "length",
"schema": {
"type": "custom"
"obj_type": "Real",
}
}
]
}
The schema would match a dictionary like this:
{"length": 34.5}
This schema describes a dictionary with a length key whose value is of custom type Real, and the Real type is one of our objects. We define these objects as classes in extensions/objects/models/objects.py and components in extensions/objects/templates.
How objects are defined
Python classes
The Python classes for objects are defined in objects.py. For example, here’s the class for the int type:
class Int(BaseObject):
"""Integer class."""
description = 'An integer.'
default_value = 0
@classmethod
def get_schema(cls):
"""Returns the object schema.
Returns:
dict. The object schema.
"""
return {
'type': 'int'
}
Object classes all meet the following criteria:
They inherit from either
BaseObjector another object class.Their
descriptionclass variables are set to a string describing the object.Their
default_valueclass variables are set to the value that should be the default whenever this type is used in a rule. Note that this variable is technically not required if the object will never be used in a rule.They provide a
normalize()class method that accepts a raw Python object. The method should first validate the raw object to check whether it is malformed. If the validation checks pass, then the method should return a normalized form of the object. Note that in this context, “normalized” means a primitive Python type or multiple primitive types combined with lists and/or dicts.The
BaseObjectclass provides anormalizeclass method that can normalize any object so long as the object’s class provides aget_schemaclass method that returns a schema describing the object.BaseObject.normalizepasses the schema toschema_utils.normalize_against_schema, so make sure thatnormalize_against_schemawon’t try to normalize the object using the object class. Otherwise, there will be an endless loop betweennormalize_against_schemaandBaseObject.normalize.
Angular components
Every object is accompanied by a component defined in extensions/objects/templates and imported in extensions/objects/object-components.module.ts. Each component provides the HTML and frontend code needed to create a form for the user to provide input in the form of the object. These components rely heavily on schema-based forms. For example, the int-editor component’s HTML is just this:
<schema-based-editor [schema]="getSchema.bind(this)"
[localValue]="value"
(localValueChange)="updateValue($event)">
</schema-based-editor>
The getSchema function is defined in int-editor.component.ts and returns this schema:
{
type: "int",
validators: [{
id: "is_integer"
}]
}
The schema-based-editor component already knows how to construct a form for this schema.
Create a new object
Let’s suppose you’re creating a new object called MyObject (in practice you should use a more descriptive name). You’ll need to follow these steps:
Create a new object class named
MyObject. It must satisfy the criteria documented above.Add tests for your class in
extensions/objects/models/objects_test.py. You should add normalization tests to theObjectNormalizationUnitTestsclass. You can also create a new test class to hold additional test cases.Add a new Angular component to accept input in the form of your object. Your component files should be named
my-object-editor.component.*, and your component class should be namedMyObjectEditorComponent.Import your component in
extensions/objects/object-components.module.tsand add it to the module there.
Using objects
You will usually use objects by referencing them as you create interactions, define rules, or otherwise use schemas.