Skip to content

Commit

Permalink
feat: imrpoves serialization, splitting out a utility namespace, and …
Browse files Browse the repository at this point in the history
…making it simple to serialize classes and nodes
  • Loading branch information
georgejecook committed Oct 14, 2021
1 parent 0558e18 commit 1e50081
Show file tree
Hide file tree
Showing 13 changed files with 645 additions and 63 deletions.
8 changes: 5 additions & 3 deletions docs/2. Utils/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
priority: 0
title: Overview
---

## Utils

Maestro comes with a bunch of common Utilities (namespaced functions and classes) that will simplify your adoption of roku programming.

- [mc (maestro core utils)](mc.html)
- [mc.Tasks](mc.tasks.html)

- [mc (maestro core utils)](mc.html)
- [mc.Tasks](mc.tasks.html)
- [mc.Serialization](mc.utils.Serialization.html)
- [mc.Collections](mc.utils.Collections.html)
2 changes: 1 addition & 1 deletion docs/2. Utils/mc.tasks.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
priority: 0
priority: 1
title: mc.tasks (maestro task utils)
---

Expand Down
80 changes: 80 additions & 0 deletions docs/2. Utils/mc.utils.Collections.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
priority: 3
title: mc.utils.Collections
---

# Overview

Maestro contains utility functions to aid with working with collections, and node wrappers for certain types of common collections.

## Functions

### mapArray

Takes a raw array, and runs it through the mc.Collections.BaseMapper subclass (i.e. CallFuncMapper, FuncMapper, FieldMapper, your own Mapper), returning the result.

## getSortedArrayByKey

Sorts the given array by key name.

## getItemAtOffset

Ascertains the index of the given item, in the passed in array, and returns whatever item is found offset from that index. By default, will use `id` field to compare objects; but the key can be specified.

## getArrayIndex

Ascertains the index of the given item, in the passed in array. By default, will use `id` field to compare objects; but the key can be specified.

## filterArray

Returns the filtered array using a filterPredicate. The filterPredicate can be either a function (taking one item as the argument), or a mc.Collections.AbstractPredicate subclass (i.e. ValuePredicate, FuncPredicate, CallFuncPredicate, or your own predicate)

## arrayContains

Returns true if the array contains the passed in value. By default, will use `id` field to compare objects; but the key can be specified.

# Wrapped types

Associative arrays and arrays are copied whenever they are assigned to nodes. This can be expensive computationally, and undesirable. Maestro offers a solution to this with the following nodes:

- mc_Map - a node wrapper for AssociativeArray
- mc_Array - a node wrapper for Array

## mc_Map

Has the following api
- clear()
- remove(key)
- set(key, value)
- append({}) - appends a dictionary
- get(key)
- hasKey(key)
- getValues() - returns a raw dictionary copy

### Serialization support

The mc_Map supports maestro serialization, and can therefore be used in the registry's `writeSerializable` and `readSerializable` as well as `mc.utils.Serializable` methods.

### Debugging

You can see the stored values of the collection in RALE by checking the `_debug` value, which will dump the current values to the `__contents` field

## mc_Array

Has the following api
- clear()
- remove(item)
- push(item)
- append([]) - appends an array
- get(index)
- getIndex(item, key = invalid) - gets index of item, the string key can be used to direct the comparison
- hasItem(index, key = invalid) - returns true if the item is present, the string key can be used to direct the comparison
- getValues() - returns a raw array copy

### Serialization support

The mc_Array supports maestro serialization, and can therefore be used in the registry's `writeSerializable` and `readSerializable` as well as `mc.utils.Serializable` methods.

### Debugging

You can see the stored values of the collection in RALE by checking the `_debug` value, which will dump the current values to the `__contents` field
52 changes: 52 additions & 0 deletions docs/2. Utils/mc.utils.Serialization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
priority: 2
title: mc.utils.Serialization
---

# Overview

Maestro supports serialization apis to make it easy to write objects to registry, or to json for other purposes.

Objects that can be serialized are classes, or nodes, which implement the Serializable interface

## Implementing serialization

Your node or class must adhere to the Serializable interface

- have isSerializable field set to true
- have public functions (i.e. for nodes, a func interface) for:
- public function serialize() as mc.types.assocarray - returns an aa of the values to serialize the node
- public function deserialize(data as mc.types.assocarray) - takes the values in data, and assigns them to itself

## Serializing and Deserializing

### Using mc.utils.Serialization

#### Serializing

Objects are serialized with:

`mc.utils.Serialization.serialize(serializable as object) as mc.types.assocarray`

Which returns an associative array, if successful, invalid otherwise
If the passed in object is a node, then the field `_serializationType` with the type of the node will be appended to the serialized data

#### Deserializing

Objects can be deserialized with:
`mc.utils.Serialization.deSerialize(data as mc.types.assocarray, serializable = invalid as object) as object`

note, one can pass in a target serializable object to serialize into. If an object is not passed in the function will assume that the serialize object is node, and will use the `data._serializationType` to create an instance of that node, and serialize it.

#### Checking if an object can be deserialized
`mc.utils.Serialization.isSerializable(item as object) as boolean`


### Using mc.Registry

You can easily work with serializable objects by using the registry functions:

- `readSerializable(section as string, key as string, serializable as object) as boolean`
- if the data was successfully read from the registry, returns the serialized object. A target serializable can be passed in to receive the data.
- `writeSerializable(section as string, key as string, serializable as object) as boolean`
- returns true if the object was successfully deserialized and written to the registry
75 changes: 62 additions & 13 deletions src/source/core/Array.bs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import "pkg:/source/core/Collections.bs"
import "Serialization.bs"
import "pkg:/source/roku_modules/log/LogMixin.brs"

' /**
' * @member Array
Expand All @@ -8,17 +10,30 @@ import "pkg:/source/core/Collections.bs"
' * @description light node wrapper around a Set to stop copies
' */
namespace mc.collections
@node("Array", "Node")
@node("mc_Array", "Node")
class Array

protected data = []
public length = 0

public isSerializable = true

@observer("onDebugChange")
public _debug = false

public __contents as mc.types.Array


protected log

function new()
m.log = new log.Logger("mc_Map")
end function

'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'++ Public Methods
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function sortByKey(keyName as string, ascending = true as boolean)
data = mc.collections.getSortedArrayByKey(m.data, keyName, ascending)
m.clear()
Expand All @@ -41,35 +56,69 @@ namespace mc.collections
m.top.length = m.data.count()
end function

public function get(key as string)
return m.data[key]
public function get(index as integer)
return m.data[index]
end function

public function remove(key as string)
m.data.delete(key)
public function remove(index as integer)
m.data.delete(index)
m.top.length = m.data.count()
end function

public function doesExist(value as dynamic)
public function hasItem(value as dynamic, key = invalid as string)
return m.getIndex(value) <> -1
end function

public function getValues()
return m.data
end function

public function getIndex(value as dynamic)
for i = 0 to m.data.count() - 1
if m.data[i] = value
return i
end if
end for
return -1
public function getIndex(value as dynamic, key = invalid as string)
return mc.collections.getArrayIndex(m.data, value, key)
end function

'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'++ Private Methods
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

private function onDebugChange(value as dynamic)
m.top.contents = m.data
end function


'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'++ Serializable support
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

public function serialize() as mc.types.assocarray
serialized = {
items: []
}
for each item in m.data
data = mc.utils.Serialization.serialize(item)
if data <> invalid
serialized.items.push(data)
else
serialized.items.push(item)
end if
end for
return serialized
end function

public function deserialize(data as mc.types.assocarray)
m.clear()
if mc.isAACompatible(data) and mc.isArray(data.items)
for each item in data.items
if mc.utils.Serialization.isDeSerializableToNode(item)
deSerializedItem = mc.utils.Serialization.deSerialize(item)
if deSerializedItem <> invalid
m.data.push(deSerializedItem)
end if
else
m.data.push(item)
end if
end for
end if
end function
end class
end namespace
2 changes: 1 addition & 1 deletion src/source/core/Array.spec.bs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace tests

protected override function setup()
super.setup()
m.array = createObject("roSGNode", "Array")
m.array = createObject("roSGNode", "mc_Array")
end function

'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Expand Down
1 change: 1 addition & 0 deletions src/source/core/BaseClass.bs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "pkg:/source/core/Tasks.bs"
import "pkg:/source/core/Utils.bs"
import "Types-Polyfill.bs"
import "pkg:/source/roku_modules/log/LogMixin.brs"

namespace mc
@strict
Expand Down
11 changes: 1 addition & 10 deletions src/source/core/Collections.bs
Original file line number Diff line number Diff line change
Expand Up @@ -262,17 +262,8 @@ namespace mc.collections
end function
end class

'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
'++ basic utils
'+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function arrayContains(array as mc.types.array, value as dynamic) as boolean
for each item in array
if item = value
return true
end if
end for
return false
return mc.collections.getArrayIndex(array, value) <> -1
end function

end namespace
1 change: 0 additions & 1 deletion src/source/core/JsonCombiner.spec.bs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
namespace tests

@nocatch
@suite("JsonCombinerTests")
class JsonCombinerTests extends rooibos.BaseTestSuite

Expand Down

0 comments on commit 1e50081

Please sign in to comment.