Перейти к содержанию

Шаблоны

Работа с шаблонами генерации кода.

Расположение

internal/pkg/backend/
├── postgres/
│   └── tmpl/
│       ├── pkg/
│       │   ├── main.tmpl      # Основная структура
│       │   ├── select.tmpl    # Селекторы
│       │   ├── mutator.tmpl   # Мутаторы
│       │   └── ...
│       └── fixture/
│           └── ...            # Шаблоны фикстур
└── octopus/
    └── tmpl/
        └── ...

Формат шаблонов

Используется стандартный text/template:

package {{ .PackageName }}

type {{ .StructName }} struct {
    activerecord.BaseField
    Exists    bool
    IsReplica bool
    {{- range .Fields }}
    {{ .Name | lower }} {{ .GoType }}
    {{- end }}
}

Данные шаблона

Основная структура — *ds.RecordPackage:

type RecordPackage struct {
    Namespace       Namespace
    Server          Server
    Fields          []FieldDeclaration
    Indexes         []IndexDeclaration
    IndexParts      []IndexPartDeclaration
    Serializers     []SerializerDeclaration
    Mutators        []MutatorDeclaration
    Flags           []FlagDeclaration
    Triggers        []TriggerDeclaration
    FieldsObjectMap []FieldsObjectDeclaration
    // ...
}

Функции шаблонов

Определены в internal/pkg/generator/template.go:

Функция Описание Пример
lower Нижний регистр {{ .Name \| lower }}
upper Верхний регистр {{ .Name \| upper }}
title Title Case {{ .Name \| title }}
join Объединение слайса {{ join .Fields ", " }}
sub Вычитание {{ sub (len .Fields) 1 }}
add Сложение {{ add .Index 1 }}

Пример шаблона

Генерация геттеров

{{- range .Fields }}
// Get{{ .Name }} returns {{ .Name }} field value
func (obj *{{ $.StructName }}) Get{{ .Name }}() {{ .GoType }} {
    return obj.{{ .Name | lower }}
}
{{- end }}

Генерация сеттеров

{{- range .Fields }}
{{- if not .PrimaryKey }}
// Set{{ .Name }} sets {{ .Name }} field value
func (obj *{{ $.StructName }}) Set{{ .Name }}(v {{ .GoType }}) {
    obj.{{ .Name | lower }} = v
    obj.UpdateOps = append(obj.UpdateOps, OpSet{
        Field: "{{ .Name | lower }}",
        Value: v,
    })
}
{{- end }}
{{- end }}

Условная генерация

{{- if .HasSerializers }}
// Serializer imports
import (
    {{- range .Serializers }}
    "{{ .Package }}"
    {{- end }}
)
{{- end }}

Модификация шаблонов

1. Найдите нужный шаблон

# Поиск шаблона с Select
grep -r "SelectBy" internal/pkg/backend/postgres/tmpl/

2. Внесите изменения

// internal/pkg/backend/postgres/tmpl/pkg/select.tmpl

func SelectBy{{ .Selector }}(ctx context.Context, key {{ .KeyType }}) (*{{ .StructName }}, error) {
    // Ваши изменения
}

3. Протестируйте

make test

4. Проверьте генерацию

# Генерация тестовой модели
argen --path "test/model" --declaration "decl" --destination "gen"

# Проверка сгенерированного кода
cat test/model/gen/mymodel/postgres.go

Добавление нового шаблона

1. Создайте файл

internal/pkg/backend/postgres/tmpl/pkg/myfeature.tmpl

2. Добавьте в генератор

internal/pkg/backend/postgres/generator.go:

func (g *Generator) Generate(pkg *ds.RecordPackage) error {
    // ...
    if err := g.executeTemplate("myfeature.tmpl", pkg); err != nil {
        return err
    }
    // ...
}

3. Зарегистрируйте шаблон

//go:embed tmpl/pkg/myfeature.tmpl
var myfeatureTmpl string

func init() {
    templates["myfeature.tmpl"] = myfeatureTmpl
}

Отладка шаблонов

Вывод данных

{{/* Отладочный вывод */}}
// DEBUG: Fields count = {{ len .Fields }}
// DEBUG: First field = {{ (index .Fields 0).Name }}

Проверка ошибок

# Подробный вывод
argen --path "model" -v

# Проверка синтаксиса шаблона
go run ./cmd/argen --dry-run --path "model"

Best Practices

1. Комментируйте сгенерированный код

// {{ .StructName }} represents {{ .Namespace.ObjectName }} table
// Generated by argen. DO NOT EDIT.
type {{ .StructName }} struct {

2. Используйте whitespace control

{{- /* Убирает пробелы слева */ -}}
{{ .Name }}
{{- /* Убирает пробелы справа */ -}}

3. Группируйте связанный код

{{- /* ===== GETTERS ===== */ -}}
{{- range .Fields }}
func (obj *{{ $.StructName }}) Get{{ .Name }}() {{ .GoType }} {
    return obj.{{ .Name | lower }}
}
{{- end }}

{{- /* ===== SETTERS ===== */ -}}
{{- range .Fields }}
{{- if not .PrimaryKey }}
func (obj *{{ $.StructName }}) Set{{ .Name }}(v {{ .GoType }}) {
    // ...
}
{{- end }}
{{- end }}

Следующие шаги