-
Notifications
You must be signed in to change notification settings - Fork 564
[Cookbook] Interfaces
Camilo edited this page Sep 9, 2021
·
4 revisions
Interfaces are not supported natively, until then a small workaround can be used to validate if a class implements some methods. Using Attributes we can create a small method to validate an interface.
class Implements {
static validate(obj, interfaces) {
if (!(obj is Class)) {
obj = obj.type
}
if (!(interfaces is List)) {
interfaces = [interfaces]
}
for (interface in interfaces) {
for (method in interface.attributes.methods) {
var i = method.key.indexOf("(")
var key = method.key[0...i]
if (method.value[null] && method.value[null].containsKey(key)) {
var found = false
for (item in obj.attributes.methods) {
if (item.key == method.key) {
found = true
break
}
}
if (found) {
continue
} else {
Fiber.abort("%(obj.name) does not implement %(interface.name).%(method.key)")
}
}
}
return true
}
}
static check(obj, interfaces) {
var fn = Fiber.new{
return validate(obj, interfaces)
}
fn.try()
return fn.error == null
}
}
class IPrint {
#!print(message)
print(message) {}
#!print
print(){}
#!somethingElse
#!json
somethingElse(msg) {}
#!ignore
nonMatchingName() {}
}
class Printer {
#!somethingElse
somethingElse(msg) {}
#!print(message)
print(message){
System.print(message)
}
#!print
print(){
System.print("Hello")
}
}
class Writer {
#!print(message)
print(message){
System.print(message)
}
}
System.print(Implements.check(Printer, IPrint))
System.print(Implements.validate(Printer, IPrint))
System.print(Implements.check(Writer, IPrint))
System.print(Implements.validate(Writer, IPrint))
Here is an example using the attributes.methods
property.
#!protocol
class JsonEncodingProtocol {
#!required
toJson{}
#!required
toJSON{}
static methods {
var methods = this.attributes.methods
var required = []
var optional = []
for (method in methods) {
for (attributes in method.value) {
for (attribute in attributes.value) {
if (attribute.key == "required") {
required.add(method.key)
}
if (attribute.key == "optional") {
optional.add(method.key)
}
}
}
}
return {
"required": required,
"optional": optional
}
}
static implements(Entity) {
if (!Entity.attributes) {
return false
}
// quick hack to get the protocol name
// must be changed if multiple protocols are present
var protocol = Entity.attributes.self.keys.toList[0]
var candidates = []
for(method in Entity.attributes.self.values) {
for (attribute in method.keys) {
// We have to check if the entity
// implements this protocol
if (protocol == this.name) {
candidates.add(attribute)
}
}
}
for (method in this.methods["required"]) {
if (!candidates.contains(method)) {
// Fiber.abort("required method %(method) not found")
return false
}
}
for (method in this.methods["optional"]) {
if (!candidates.contains(method)) {
// System.print("optional method %(method) not implemented")
}
}
return true
}
}
// We specify which methods of the protocol
// we are implementing
#!JsonEncodingProtocol(
toJson,
toJSON)
class MyObject {
construct new(){}
// does nothing, just for tell its the protocol implementation
#region = JsonEncodingProtocol
toJson {"Hello"}
toJSON {toJson}
}
var myobj = MyObject.new()
// Must be true
System.print(JsonEncodingProtocol.implements(myobj.type))