How to register a new operation

You will find here how to register a new operation.

After you’ve seen it is possible to make addition, subtraction and other operations using context, you can also create your own operation in the platform you want:

The registration of an operation on iOS is through OperationsProviderprotocol, see below:

public protocol OperationsProvider {
    func register(operationId: String, handler: @escaping OperationHandler)
    func evaluate(with operation: Operation, in view: UIView) -> DynamicObject
}

To register your customized operation, you have to do two things:

  1. Provide an id for this operation;
  2. Provide to it an action that it will happen through a closure of the type OperationHandler.

The OperationHandler is a typealias of a code block that returns DynamicObject through a parameter of [DynamicObject] type.

public typealias OperationHandler = (_ parameters: [DynamicObject]) -> DynamicObject

Now, to register your new operation you have to use BeagleDependencies, where you can access the OperationsProvider, which has the register function.

let dependencies = BeagleDependencies()

dependencies.operationsProvider.register(operationId: "isValidCpf") { parameters in
    let anyParameters = parameters.map { $0.asAny() }
    if let intParameters = anyParameters.first as? Int {
        let stringParameters = String(intParameters)
        return .bool(stringParameters.isValidCPF)
    } else if let stringParameters = anyParameters.first as? String {
        return .bool(stringParameters.isValidCPF)
    }
    return nil
}

Done! Your operation can be used now!

You must the Operation interface in order to register a new operation on Android. The example below shows this interface signature

interface Operation {
    fun execute(vararg params: OperationType?): OperationType
}

The following class shows the OperationType class details. This class holds the return types supported on Beagle.

sealed class OperationType(open val value: Any?) {
    data class TypeString(override val value: String) : OperationType(value)
    data class TypeBoolean(override val value: Boolean) : OperationType(value)
    data class TypeNumber(override val value: Number) : OperationType(value)
    data class TypeJsonArray(override val value: JSONArray) : OperationType(value)
    data class TypeJsonObject(override val value: JSONObject) : OperationType(value)
    object Null : OperationType(null)
}

To register a custom operation you must:

  1. Create a class and enter the name you want.
  2. Place an annotation @RegisterOperation (name =" name-your-operation ") over the class name.
  3. Implement the Operation interface.

The following example shows a custom operation that validates a CPF register number

@RegisterOperation(name = "isValidCpf")
class IsValidCPFOperation : Operation {
    override fun execute(vararg params: OperationType?): OperationType {
        val cpf = params[0]?.value as String
        return OperationType.TypeBoolean(isCPF(cpf))
    }

    @Suppress("ReturnCount")
    private fun isCPF(document: String): Boolean {
        if (TextUtils.isEmpty(document)) return false

        val numbers = arrayListOf<Int>()

        document.filter { it.isDigit() }.forEach {
            numbers.add(it.toString().toInt())
        }

        if (numbers.size != 11) return false

        for (n in 0..9) {
            val digits = arrayListOf<Int>()
            repeat((0..10).count()) { _ -> digits.add(n) }
            if (numbers == digits) return false
        }

        val dv1 = ((0..8).sumBy { (it + 1) * numbers[it] }).rem(11).let {
            if (it >= 10) 0 else it
        }

        val dv2 = ((0..8).sumBy { it * numbers[it] }.let { (it + (dv1 * 9)).rem(11) }).let {
            if (it >= 10) 0 else it
        }

        return numbers[9] == dv1 && numbers[10] == dv2
    }
}

Done! Your operation can be used now!

Example

See below an example using the isvalidCpf operation that was created above, where the text component Text will vary according the verification result, if the CPF is valid or not:

fun screen() = Screen(
    navigationBar = NavigationBar(title = "Custom operation", showBackButton = true),
    child = Container(
        context = ContextData("cpf", "00000000000"),
        children = listOf(
            Button("CPF atual: @{cpf}", onPress = listOf(
                SetContext(
                    contextId = "cpf",
                    value = "42249625000"
                )
            )),
            Text(text = "@{condition(isValidCpf(cpf), 'cpf is valid', 'cpf is not valid')}")
        )
    )
)


Last modified February 12, 2021: Fix/migrate images to aws (#299) (a7bb5457)