Object Library
Stof's standard object library ("Object").
Common Value Functions
Object.toString(object: obj): str
The default implementation for turning an object into a string is to use the "json" format. If not loaded, it will fall back to the "toml" format. If both are not loaded in the document, this function will return a debug implementation, dumping the entire contents of the object as a string (just like "std.dbg(obj)").
Object.or(object: obj, ...): unknown
Returns the first non-empty (null or void) argument, just like the Standard Library "or" function.
Fields & Functions
Object.len(object: obj): int
Because the default iteration of an object is over its fields, the length of an object is defined by the number of fields it has.
Object.at(object: obj, index: str | int): unknown
Will get a field or function on this object according to an index.
If the index is a string, this function will return the first field or function with a name that matches the index, or null if not found.
If the index is an integer, this function will return the field at the desired index in relation to all other fields on the object as a Tuple (name, value). This is only useful for iterating over an object's fields (without using the preferred "Object.fields(object)" function).
Object.fields(object: obj): map
Creates a map out of all fields on this object.
This function differs from the Standard Library map initializer std.map(obj)
with an object argument. This function will not create maps out of sub-objects like the standard library function will.
Object.keys(object: obj): vec
Returns a vector of all field keys on this object.
Object.values(object: obj): vec
Returns a vector of all field values on this object.
Object.set(object: obj, name: str, value: unknown): bool
Sets a field value on/from this object. Will create a new field if one does not already exist.
The field name is really a field path, starting at this object - Stof will create new objects as needed to make the path valid.
Object.box(object: obj, name: str, value?: unknown): bool
Box a field on this object without an additional assignment statement, optionally accepting a new value to box and set.
This function can be used like "set", but it makes sure that the value is boxed. If a field is not present, it will box a null value in preparation for an assignment.
Object.unbox(object: obj, name: str, value?: unknown): bool
Unbox a field on this object without an additional assignment statement, optionally accepting a new value to unbox and set.
Unlike the function "box", if the field is not present and a value is not given as an argument, this function will not set a null value and will return false.
This function can be used like "set", but it makes sure that the value is unboxed.
Object.removeField(object: obj, name: str, dropIfObject?: bool): bool
Remove a field by name/path, starting from this object. Path behavior is the same as for "set".
The parameter "dropIfObject" is only considered when the field being removed is an object. Objects in Stof are just references and can exist in multiple objects at once. This parameter gives more control over when those objects are removed from the document entirely. The default value is false, not dropping objects from the document. Unless you know what you're doing, it is recommended to leave it as false.
Returns whether or not the field was found and removed.
Object.moveField(object: obj, from: str, to: str): bool
Alias function for "renameField". Both functions have the same implementation.
Object.renameField(object: obj, from: str, to: str): bool
Renames a field and/or moves it to a new location, starting from this object.
Both "from" and "to" are paths, which makes this function very useful.
Object.mapFields(object: obj, mapping: map): map
Calls "moveField"/"renameField" for each key/value pair in a mapping, returning a map of all key/value pairs that were successfully moved/renamed.
Take note that the paths start at the calling object. So if you wanted to call this function instead from "source" (self.source.mapFields(mapping)
), the mapping from field "a" to field "first" would instead be ("a", "super.destination.first")
.
Object.attributes(object: obj, name: str): map
Returns a map of attributes on a field or function that exists at the name/path, starting at this object, or null if a field or function cannot be found.
Object.functions(object: obj): map
Returns a map of all functions that exist on this object. Keys are function names and values are function pointers that can be called.
Object.removeFunc(object: obj, name: str): bool
Remove a function from the document by name/path, starting at this object.
Object.reference(object: obj, path: str): bool
Add a reference to a field or function on this object from a path.
In Stof's terms, this function merely attaches the field or function to this object in addition to any other objects it is already attached to - nothing is created, copied, or moved.
This is a cool functionality in Stof, but use it wisely as it can make some interfaces hard to trace and use.
Object.search(object: obj, field: str, parentChildren: bool = true, ignore: vec = []): (unknown, int)
Search this object for a field by name. Will search both up (parents) and down (children), returning the closest found field. If a field was found in a parent and also found in a child and the distances are the same, this function will favor the child field (downwards).
Parameters:
field - the name of the field to search for.
parentChildren - allow searching through the children of parent nodes?
ignore - a list of objects to skip searching through (will still search parents & children of these objects).
Object.searchUp(object: obj, field: str, parentChildren: bool = true, ignore: vec = []): (unknown, int)
Search this object for a field by name. Behaves just like "search", however, will only allow searching upwards, through this object's parents.
Object.searchDown(object: obj, field: str, currDist: int = 0, ignore: vec = []): (unknown, int)
Search this object for a field by name. Behaves just like "search", however, will only allow searching downwards, through this object's children.
Schemafy
Every object in Stof can be treated as a schema, capable of being applied to another object. We call this application "schemafy".
This is one of the most powerful and complex "standard" operations that Stof can perform, able to create new fields, remove lots of fields that are not defined, validate, control access, etc.
You might think that the interface is complex as well, but "schemafy" only looks at one singular attribute, #[schema(...)]
and returns a boolean, reflecting the validity of the object in question (we call this the "target" object) with respect to this "schema" object.
See REST and Restructure for a real-life example of "schemafy" (at the bottom).
Object.schemafy(schema: obj, target: obj, removeInvalidFields: bool = true, removeUndefinedFields: bool = false): bool
schema - The first argument (or caller) of this function is considered the "schema" object. Fields defined on this object that have a #[schema] attribute will have that attribute applied to the "target" object's corresponding fields (by name).
target - The object that will be manipulated/validated using this "schema".
removeInvalidFields - If true, when a field is determined to be invalid (using the #[schema(..)] attribute), that field will be removed from the target object (including object fields). If false, invalid fields will be left on the target object (but the return value will still be false if an invalid field is found).
removeUndefinedFields - If true, after each schema field is applied to the target object, all fields on the target that are not defined on the schema object (#[schema] attribute or not) will be removed from the target object. Ex. the target object should only contain these few fields.
Schema Attribute Values
The #[schema(...)]
attribute for "schemafy" is extremely powerful, enabling one to create new fields, conditionally validate values, etc. If you can think it, you can do it.
Objects
If an object value is given in the schema attribute, it will be treated as an additional schema to apply to the current field (like a proxy).
Null
A null attribute value will not do anything (just returns valid) unless the field is an object and the corresponding field on the target is also an object. In that case, the schema field object will be applied as the schema to the corresponding field object on the target.
Functions
The most common value contained within the attribute is a function. Schemafy functions can have up to 4 parameters and can either have a boolean return value (indicating validity), a null value (resulting in nothing being done), or anything other value, set as the new field value.
Parameters can be specified in any order, but are associated with arguments via name (and value type) at runtime.
target: obj - if found in the function's parameters, the target object that the schema is being applied to will be given as this argument.
schema: obj - if found in the function's parameters, the schema object being applied to the target will be given as this argument.
field: str - if found in the function's parameters, the name of the field that is currently being validated/manipulated will be given as this argument. If the type is a Box<str>, this will enable one to rename this field by setting this value in the function.
value: unknown - if found in the function's parameters, the current value associated with this field on the target will be given as this argument. Any type will work here as long as the name is "value". If the type is a box type (Ex. Box<unknown> instead of just unknown), this will box the field value and enable one to modify the field value by setting this value in the function (this is an alternative to returning a non-null and non-boolean value to be set as the new field value).
Arrays
An array of objects and functions can be given as the schema attribute value, indicating that each function and additional schema should be evaluated in order, ceasing execution if the field is evaluated to be invalid.
Object Interface
Object.name(object: obj): str
Returns the name of this object. Typically, but not always the name of fields that reference this object.
Object.id(object: obj): str
Returns this object's ID. Helpful for some debugging instances.
Object.parent(object: obj): obj
Returns this object's parent object, or null if this object is a root. It is preferred to use the "super" keyword when working with paths (if able).
Object.root(object: obj): obj
Returns this object's root object in the document.
Object.isRoot(object: obj): bool
Returns true if this object is a root object in the document.
Object.path(object: obj): str
Returns the dot-separated path to this object from a root of this document.
Object.children(object: obj): vec
Returns a vector containing this object's child objects, or an empty vector if this object does not have any children.
Object.typename(object: obj): str
Returns the name of this object's type. If this object does not have a prototype, this will return "obj", the same as "typeof".
Object.typestack(object: obj): vec
Returns a vector containing the stack of type names associated with this object.
Object.instanceOf(object: obj, type: str): bool
Returns true if this object has the type name "type" in its type stack.
Object.prototype(object: obj): obj
Return this object's prototype object if it has one. The prototype object will most likely be referenced by other objects, so use this function carefully.
Object.setPrototype(object: obj, prototype: obj): void
Set this object's prototype to be the object "prototype".
Object.upcast(object: obj): bool
Changes this object's prototype to be the prototype its current prototype extends.
Object.removePrototype(object: obj): bool
Removes this object's prototype completely (regardless of whether it extends another), returning true if this object had a prototype that was removed.
Object.shallowCopy(object: obj, other: obj): void
Make "object" a shallow copy of "other" by attaching all of the data on "other" to it. Essentially, a "reference" call to each one of the fields on "other".
This function does not just shallow copy fields, but all data (functions, prototype, etc...).
Object.deepCopyFields(object: obj, other: obj): void
Copies all fields from "other" and places them on this object. This function is also called for every field on "other" that is an object, recursively deep copying all referenced (by field) sub-objects.
Unlike shallow copy, this function only operates on fields, not functions or any other type of data.
The object "other" is unmodified by this function.
Last updated
Was this helpful?