GMX Script¶
GMX Script est un langage inspiré de TypeScript qui se transpile en Go. Il permet d'écrire la logique métier de manière concise avec gestion d'erreurs automatique et méthodes ORM intégrées.
Syntaxe de Base¶
<script>
func toggleTask(id: uuid) error {
let task = try Task.find(id)
task.done = !task.done
try task.save()
return render(task)
}
</script>
Transpilé en Go :
func toggleTask(ctx *GMXContext, id string) error {
task, err := TaskFind(ctx.DB, id)
if err != nil {
return err
}
task.Done = !task.Done
if err := TaskSave(ctx.DB, task); err != nil {
return err
}
return renderFragment(ctx.Writer, "task", task)
}
Déclarations de Variables¶
let — Variable Mutable¶
Transpilé :
const — Variable Immutable¶
Transpilé :
Différence avec TypeScript
En GMX, const génère simplement un := en Go. L'immutabilité n'est pas forcée par le compilateur Go.
Types¶
| GMX Type | Go Type | Usage |
|---|---|---|
string |
string |
Texte |
int |
int |
Nombres entiers |
float |
float64 |
Nombres décimaux |
bool |
bool |
Vrai/faux |
uuid |
string |
Identifiants (transpilé) |
error |
error |
Type de retour obligatoire |
Types de Modèles¶
Les modèles GMX sont utilisés comme types :
Transpilé :
Gestion des Erreurs¶
try — Unwrap ou Return¶
Transpilé en :
Le try unwrap automatiquement et return l'erreur si elle existe.
error() — Créer une Erreur¶
Transpilé :
Toutes les Fonctions Retournent error¶
func deleteTask(id: uuid) error {
let task = try Task.find(id)
try task.delete()
return nil // ✅ Success
}
IMPORTANT : Le type de retour error est obligatoire pour toutes les fonctions GMX Script.
Méthodes ORM¶
Model.find(id)¶
Trouve une entité par son ID :
Transpilé :
Model.all()¶
Récupère toutes les entités :
Transpilé :
instance.save()¶
Crée ou met à jour une entité :
Transpilé :
task := &Task{Title: "New task", Done: false}
if err := TaskSave(ctx.DB, task); err != nil {
return err
}
instance.delete()¶
Supprime une entité :
Transpilé :
task, err := TaskFind(ctx.DB, id)
if err != nil {
return err
}
if err := TaskDelete(ctx.DB, task); err != nil {
return err
}
Rendu de Templates¶
render(data)¶
Rend un fragment de template avec des données :
Transpilé :
render() Multiple¶
Vous pouvez passer plusieurs arguments :
Transpilé :
data := map[string]interface{}{
"task": task,
"user": user,
"posts": posts,
}
return renderFragment(ctx.Writer, "combined", data)
Structures de Contrôle¶
if / else¶
Transpilé :
Conditions Complexes¶
Opérateurs supportés :
- Comparaison : ==, !=, <, >, <=, >=
- Logique : &&, ||, !
- Arithmétique : +, -, *, /, %
Expressions¶
Opérateurs Binaires¶
Opérateurs Unaires¶
Accès aux Membres¶
Transpilé en PascalCase :
Appels de Fonctions¶
Littéraux de Structures¶
Transpilé :
Interpolation de Chaînes¶
Transpilé :
Expressions supportées :
Limitation Actuelle
L'interpolation avec accès aux membres ({task.title}) a des bugs connus. Préférez :
Contexte Implicite¶
ctx — Contexte de Requête¶
Le contexte HTTP est toujours disponible :
Champs disponibles :
type GMXContext struct {
DB *gorm.DB
Tenant string
User string
Writer http.ResponseWriter
Request *http.Request
}
Exemples Complets¶
CRUD Simple¶
<script>
func createTask(title: string) error {
if title == "" {
return error("Title cannot be empty")
}
const task = Task{title: title, done: false}
try task.save()
return render(task)
}
func listTasks() error {
let tasks = try Task.all()
return render(tasks)
}
func toggleTask(id: uuid) error {
let task = try Task.find(id)
task.done = !task.done
try task.save()
return render(task)
}
func deleteTask(id: uuid) error {
let task = try Task.find(id)
try task.delete()
return nil
}
</script>
Validation Métier¶
<script>
func createPost(title: string, content: string) error {
if title == "" {
return error("Title is required")
}
if len(content) < 50 {
return error("Content must be at least 50 characters")
}
const post = Post{
title: title,
content: content,
authorId: ctx.User
}
try post.save()
return render(post)
}
</script>
Relations¶
<script>
func getUserWithPosts(userId: uuid) error {
let user = try User.find(userId)
let posts = try Post.all() // TODO: filter by userId
return render(user, posts)
}
</script>
Filtrage Personnalisé
Actuellement, Model.all() récupère tout. Pour filtrer, utilisez GORM directement dans le code généré (limitation temporaire).
Transpilation Détaillée¶
Fonction Minimale¶
GMX :
Go généré :
Avec ORM¶
GMX :
Go généré :
func getTask(ctx *GMXContext, id string) error {
task, err := TaskFind(ctx.DB, id)
if err != nil {
return err
}
return renderFragment(ctx.Writer, "task", task)
}
Helpers Générés¶
Le transpiler génère automatiquement ces helpers :
// ORM Helpers
func TaskFind(db *gorm.DB, id string) (*Task, error) {
var obj Task
if err := db.First(&obj, "id = ?", id).Error; err != nil {
return nil, err
}
return &obj, nil
}
func TaskAll(db *gorm.DB) ([]Task, error) {
var objs []Task
if err := db.Find(&objs).Error; err != nil {
return nil, err
}
return objs, nil
}
func TaskSave(db *gorm.DB, obj *Task) error {
if err := obj.Validate(); err != nil {
return err
}
return db.Save(obj).Error
}
func TaskDelete(db *gorm.DB, obj *Task) error {
return db.Delete(obj).Error
}
// GMXContext
type GMXContext struct {
DB *gorm.DB
Tenant string
User string
Writer http.ResponseWriter
Request *http.Request
}
// Render Helper
func renderFragment(w http.ResponseWriter, name string, data interface{}) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
return tmpl.ExecuteTemplate(w, name, data)
}
Limitations Actuelles¶
| Fonctionnalité | Status |
|---|---|
| Variables (let/const) | ✅ Implémenté |
| try/error | ✅ Implémenté |
| if/else | ✅ Implémenté |
| Opérateurs (==, !=, &&, etc.) | ✅ Implémenté |
| ORM methods (find, all, save, delete) | ✅ Implémenté |
| render() | ✅ Implémenté |
| Interpolation simple | ✅ Implémenté |
| Interpolation avec membres | 🟡 Buggy |
| for loops | ❌ Non implémenté |
| switch/case | ❌ Non implémenté |
| Fonctions anonymes | ❌ Non implémenté |
| async/await | ❌ Non implémenté |
Bonnes Pratiques¶
✅ Do¶
- Toujours retourner
error - Utiliser
trypour les appels ORM - Valider les inputs avant
save() - Nommer les fonctions en camelCase
- Garder les fonctions courtes (< 20 lignes)
❌ Don't¶
- Ne pas oublier
trysur les méthodes ORM - Ne pas ignorer les erreurs
- Ne pas faire de logique complexe (utiliser Go pur dans le code généré)
- Ne pas utiliser des boucles (pas encore supporté)
Debugging¶
Voir le Code Go Généré¶
Erreurs de Transpilation¶
Si le transpiler échoue, le compiler affiche :
Solutions courantes :
- Vérifier que toutes les fonctions retournent
error - Vérifier la syntaxe des
trystatements - S'assurer que les types de modèles existent
Comparaison GMX ↔ Go¶
Variable Declaration¶
| GMX | Go |
|---|---|
let x = 5 |
x := 5 |
const x = 5 |
x := 5 |
Error Handling¶
| GMX | Go |
|---|---|
let x = try f() |
x, err := f()if err != nil { return err } |
return error("msg") |
return fmt.Errorf("msg") |
ORM Methods¶
| GMX | Go |
|---|---|
Task.find(id) |
TaskFind(ctx.DB, id) |
Task.all() |
TaskAll(ctx.DB) |
task.save() |
TaskSave(ctx.DB, task) |
task.delete() |
TaskDelete(ctx.DB, task) |
Rendering¶
| GMX | Go |
|---|---|
render(task) |
renderFragment(ctx.Writer, "task", task) |
Prochaines Étapes¶
- Templates — Connecter le script aux templates HTMX
- Security — Validation et sécurité dans le script
- Contributing — Architecture du transpiler