Go Classes
Create Go classes that can be instantiated and used from Scriptling.
Basic Class
Define a Class
import (
"context"
"fmt"
"github.com/paularlott/scriptling/object"
)
var PersonClass = &object.Class{
Name: "Person",
Methods: map[string]object.Object{
"__init__": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
// args[0] is always 'self' (the instance)
instance := args[0].(*object.Instance)
name, _ := args[1].AsString()
age, _ := args[2].AsInt()
instance.Fields["name"] = &object.String{Value: name}
instance.Fields["age"] = object.NewInteger(age)
return object.None
},
HelpText: "__init__(name, age) - Initialize Person",
},
"greet": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
instance := args[0].(*object.Instance)
name, _ := instance.Fields["name"].AsString()
return &object.String{Value: "Hello, " + name + "!"}
},
HelpText: "greet() - Return greeting with person's name",
},
"birthday": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
instance := args[0].(*object.Instance)
age, _ := instance.Fields["age"].AsInt()
instance.Fields["age"] = object.NewInteger(age + 1)
return &object.String{Value: fmt.Sprintf("Happy birthday! You're now %d", age+1)}
},
HelpText: "birthday() - Increment age and return birthday message",
},
"get_info": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
instance := args[0].(*object.Instance)
name, _ := instance.Fields["name"].AsString()
age, _ := instance.Fields["age"].AsInt()
return &object.Dict{Pairs: map[string]object.Object{
"name": &object.String{Value: name},
"age": object.NewInteger(age),
}}
},
HelpText: "get_info() - Return person info as dict",
},
},
}Register and Use
func main() {
p := scriptling.New()
// Register class
p.SetVar("Person", PersonClass)
// Use from Scriptling
p.Eval(`
person = Person("Alice", 30)
print(person.greet()) # "Hello, Alice!"
print(person.birthday()) # "Happy birthday! You're now 31"
info = person.get_info()
`)
}Creating Instances from Go
func main() {
p := scriptling.New()
p.SetVar("Person", PersonClass)
// Create instance from Go
instance, err := p.CreateInstance("Person", "Bob", 25)
if err != nil {
log.Fatal(err)
}
// Store instance in variable
p.SetObjectVar("bob", instance)
// Call methods
greeting, _ := p.CallMethod(instance, "greet")
fmt.Println(greeting.Inspect()) // "Hello, Bob!"
// Use from script
p.Eval(`
print(bob.greet())
bob.birthday()
`)
}Inheritance
Base Class
var AnimalClass = &object.Class{
Name: "Animal",
Methods: map[string]object.Object{
"__init__": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
instance := args[0].(*object.Instance)
name, _ := args[1].AsString()
instance.Fields["name"] = &object.String{Value: name}
return object.None
},
HelpText: "__init__(name) - Initialize Animal",
},
"speak": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
return &object.String{Value: "Generic animal sound"}
},
HelpText: "speak() - Make animal sound",
},
},
}Derived Class
var DogClass = &object.Class{
Name: "Dog",
BaseClass: AnimalClass, // Inherit from Animal
Methods: map[string]object.Object{
"__init__": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
instance := args[0].(*object.Instance)
name, _ := args[1].AsString()
breed, _ := args[2].AsString()
instance.Fields["name"] = &object.String{Value: name}
instance.Fields["breed"] = &object.String{Value: breed}
return object.None
},
HelpText: "__init__(name, breed) - Initialize Dog",
},
"speak": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
return &object.String{Value: "Woof!"}
},
HelpText: "speak() - Bark",
},
"fetch": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
instance := args[0].(*object.Instance)
name, _ := instance.Fields["name"].AsString()
return &object.String{Value: name + " fetches the ball!"}
},
HelpText: "fetch() - Fetch something",
},
},
}Using Inheritance
func main() {
p := scriptling.New()
p.SetVar("Animal", AnimalClass)
p.SetVar("Dog", DogClass)
p.Eval(`
dog = Dog("Rex", "German Shepherd")
print(dog.speak()) # "Woof!" (overridden method)
print(dog.fetch()) # "Rex fetches the ball!"
`)
}Class with Static Methods
var UtilityClass = &object.Class{
Name: "Utility",
Methods: map[string]object.Object{
"format_currency": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
// Static method - self is still passed but ignored
amount, _ := args[1].AsFloat()
return &object.String{Value: fmt.Sprintf("$%.2f", amount)}
},
HelpText: "format_currency(amount) - Format as currency",
},
"now": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
return &object.String{Value: time.Now().Format(time.RFC3339)}
},
HelpText: "now() - Get current timestamp",
},
},
}Complete Example: HTTP Client Class
package main
import (
"context"
"fmt"
"io"
"net/http"
"time"
"github.com/paularlott/scriptling"
"github.com/paularlott/scriptling/object"
)
var HTTPClientClass = &object.Class{
Name: "HTTPClient",
Methods: map[string]object.Object{
"__init__": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
instance := args[0].(*object.Instance)
// Get optional parameters
baseURL, _ := kwargs.GetString("base_url", "")
timeout, _ := kwargs.GetInt("timeout", 30)
instance.Fields["base_url"] = &object.String{Value: baseURL}
instance.Fields["timeout"] = object.NewInteger(int64(timeout))
instance.Fields["headers"] = &object.Dict{Pairs: map[string]object.Object{}}
return object.None
},
HelpText: "__init__(base_url='', timeout=30) - Create HTTP client",
},
"set_header": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
instance := args[0].(*object.Instance)
key, _ := args[1].AsString()
value, _ := args[2].AsString()
headers := instance.Fields["headers"].(*object.Dict)
headers.Pairs[key] = &object.String{Value: value}
return object.None
},
HelpText: "set_header(key, value) - Set default header",
},
"get": &object.Builtin{
Fn: func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
instance := args[0].(*object.Instance)
path, _ := args[1].AsString()
// Build URL
baseURL, _ := instance.Fields["base_url"].AsString()
url := baseURL + path
// Get timeout
timeoutSec, _ := instance.Fields["timeout"].AsInt()
// Create client with timeout
client := &http.Client{
Timeout: time.Duration(timeoutSec) * time.Second,
}
// Create request
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return &object.Error{Message: err.Error()}
}
// Add headers
headers := instance.Fields["headers"].(*object.Dict)
for key, val := range headers.Pairs {
valStr, _ := val.AsString()
req.Header.Set(key, valStr)
}
// Execute
resp, err := client.Do(req)
if err != nil {
return &object.Error{Message: err.Error()}
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
return &object.Dict{Pairs: map[string]object.Object{
"status": object.NewInteger(int64(resp.StatusCode)),
"body": &object.String{Value: string(body)},
"headers": &object.Dict{Pairs: map[string]object.Object{}},
}}
},
HelpText: "get(path) - Make GET request",
},
},
}
func main() {
p := scriptling.New()
p.SetVar("HTTPClient", HTTPClientClass)
p.Eval(`
client = HTTPClient(base_url="https://api.example.com", timeout=10)
client.set_header("Authorization", "Bearer token123")
client.set_header("Content-Type", "application/json")
response = client.get("/users")
if response["status"] == 200:
print("Success!")
print(response["body"])
else:
print("Error:", response["status"])
`)
}Best Practices
- Always handle
self- First argument is always the instance - Use
instance.Fieldsfor state - Store instance data in the Fields map - Return
object.Nonefor void methods - Methods without return values - Provide help text - Document all methods
- Use type assertions safely - Check types before casting
// Safe type handling
func safeMethod(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object {
if len(args) < 2 {
return &object.Error{Message: "method requires at least 1 argument"}
}
instance, ok := args[0].(*object.Instance)
if !ok {
return &object.Error{Message: "invalid instance"}
}
value, err := args[1].AsString()
if err != nil {
return &object.Error{Message: "argument must be a string"}
}
// Safe to use instance and value
instance.Fields["data"] = &object.String{Value: value}
return object.None
}