goat gen: implement i implement:overwrite
Subterminal goat gen udostępnia dwa polecenia służące do rejestrowania implementacji generowania:
implementimplement:overwrite
Implementacje mogą być deklarowane zarówno w głównym skrypcie generującym, jak i w dowolnym załadowanym module.
Po zakończeniu fazy rejestracji wszystkie finalnie wybrane implementacje są wykonywane współbieżnie.
implement
Polecenie implement rejestruje implementację pod unikalną nazwą.
Składnia
implement --name <nazwa> --body=<<EOF
...
EOFPrzykład
implement --name assets --body=<<IMPLEOF
gen:files:onchange \
--from="assets" \
--cache="cache/assets-mtimes.json" \
--foreach=<<EOF
gen:force \
--tmpl="asset.txt.tmpl" \
--out="generated/{{.Data.Name.SnakeCase}}.txt"
EOF \
--index=<<EOF
gen:force \
--tmpl="index.txt.tmpl" \
--out="generated/index.txt"
EOF
IMPLEOFWartość przekazana w parametrze --name identyfikuje implementację.
Dla każdej nazwy może istnieć dokładnie jedna definicja implement.
Przykładowo, po zarejestrowaniu:
implement --name assets --body=<<EOF
...
EOFponowna rejestracja implementacji o tej samej nazwie:
implement --name assets --body=<<EOF
...
EOFmusi zakończyć się błędem.
implement:overwrite
Polecenie implement:overwrite zastępuje ciało implementacji podczas finalnego rozwiązywania rejestru implementacji.
Składnia
implement:overwrite --name <nazwa> --body=<<EOF
...
EOFPrzykład
implement:overwrite --name assets --body=<<IMPLEOF
gen:files:onchange \
--from="assets" \
--cache="cache/assets-mtimes.json" \
--foreach=<<EOF
gen:force \
--tmpl="other_asset.txt.tmpl" \
--out="generated/{{.Data.Name.SnakeCase}}.txt"
EOF \
--index=<<EOF
gen:force \
--tmpl="other_index.txt.tmpl" \
--out="generated/index.txt"
EOF
IMPLEOFDla każdej nazwy implementacji może istnieć maksymalnie jedna definicja implement:overwrite.
Przykładowo:
implement:overwrite --name assets --body=<<EOF
...
EOFa następnie kolejne:
implement:overwrite --name assets --body=<<EOF
...
EOFmusi zakończyć się błędem.
Kolejność rejestracji nie ma znaczenia
Moduły generujące mogą być ładowane współbieżnie.
Z tego powodu kolejność, w której napotykane są polecenia implement i implement:overwrite, nie jest gwarantowana.
implement:overwrite nie wymaga więc, aby podstawowa implementacja została wcześniej zarejestrowana.
Poprawny jest na przykład taki przypadek:
implement:overwrite --name assets --body=<<EOF
# nadpisana implementacja
EOFnawet jeśli podstawowa implementacja zostanie napotkana dopiero później:
implement --name assets --body=<<EOF
# podstawowa implementacja
EOFFinalna implementacja jest ustalana dopiero po zakończeniu rejestracji przez główny skrypt generujący oraz wszystkie załadowane moduły.
Dla każdej nazwy obowiązują następujące zasady:
| Rejestracja | Dozwolona |
Jedno implement | Tak |
Dwa lub więcej implement | Nie |
Jedno implement + jedno implement:overwrite | Tak |
Jedno implement + wiele implement:overwrite | Nie |
Wiele implement + jedno implement:overwrite | Nie |
Jeżeli istnieje implement:overwrite, jego ciało zastępuje ciało zarejestrowane przez implement.
Implementacje nie mogą być zagnieżdżane
implement i implement:overwrite są poleceniami rejestracyjnymi.
Mogą być używane wyłącznie podczas fazy rejestracji implementacji.
Nie mogą być wywoływane z wnętrza ciała innej implementacji.
Niepoprawne: implement wewnątrz implement
implement --name assets --body=<<EOF
implement --name nested --body=<<INNER
...
INNER
EOFNiepoprawne: implement:overwrite wewnątrz implement
implement --name assets --body=<<EOF
implement:overwrite --name nested --body=<<INNER
...
INNER
EOFNiepoprawne: implement wewnątrz implement:overwrite
implement:overwrite --name assets --body=<<EOF
implement --name nested --body=<<INNER
...
INNER
EOFNiepoprawne: implement:overwrite wewnątrz implement:overwrite
implement:overwrite --name assets --body=<<EOF
implement:overwrite --name nested --body=<<INNER
...
INNER
EOFWszystkie powyższe przypadki muszą zakończyć się błędem.
Cykl działania
Obsługa implementacji w goat gen przebiega w dwóch oddzielnych fazach.
1. Rejestracja
Główny skrypt generujący oraz wszystkie załadowane moduły rejestrują implementacje przy użyciu:
implement
implement:overwriteW tej fazie ciała implementacji są zapisywane, ale nie są jeszcze wykonywane.
Po zakończeniu rejestracji rejestr implementacji jest walidowany, a następnie wyznaczane są finalne implementacje.
Dla każdej nazwy:
- musi istnieć dokładnie jedna definicja
implement; - może istnieć zero lub jedna definicja
implement:overwrite; - zduplikowane definicje
implementpowodują błąd; - zduplikowane definicje
implement:overwritepowodują błąd; - jeśli istnieje
implement:overwrite, jego ciało staje się finalnym ciałem implementacji.
2. Wykonanie
Po zakończeniu rejestracji wszystkie finalne implementacje są wykonywane współbieżnie.
goat gen czeka na zakończenie wszystkich implementacji przed zakończeniem działania.
W fazie wykonania rejestracja jest zamknięta.
Jeżeli ciało implementacji spróbuje wywołać:
implementlub:
implement:overwritewywołanie musi zakończyć się błędem.
Błędy zwracane podczas wykonywania implementacji muszą zostać poprawnie przekazane do goat gen.
Używanie implementacji pomiędzy modułami
Moduł może dostarczyć domyślną implementację:
# moduł: assets
implement --name assets --body=<<EOF
gen:files:onchange \
--from="assets" \
--cache="cache/assets-mtimes.json" \
--foreach=<<FILE
gen:force \
--tmpl="asset.txt.tmpl" \
--out="generated/{{.Data.Name.SnakeCase}}.txt"
FILE
EOFInny moduł albo główny skrypt generujący może tę implementację zastąpić:
implement:overwrite --name assets --body=<<EOF
gen:files:onchange \
--from="assets" \
--cache="cache/assets-mtimes.json" \
--foreach=<<FILE
gen:force \
--tmpl="custom-asset.txt.tmpl" \
--out="generated/{{.Data.Name.SnakeCase}}.txt"
FILE
EOFNie ma znaczenia, która deklaracja zostanie napotkana jako pierwsza.
Po zakończeniu rejestracji finalną implementacją dla assets będzie ciało przekazane do implement:overwrite.
Oryginalne ciało nie zostanie wykonane.
Wiele niezależnych implementacji
Różne implementacje mogą być rejestrowane przez różne moduły.
Przykład:
implement --name assets --body=<<EOF
# generowanie assetów
EOFimplement --name routes --body=<<EOF
# generowanie routingu
EOFimplement --name models --body=<<EOF
# generowanie modeli
EOFPo zakończeniu rejestracji wszystkie trzy implementacje zostaną wykonane współbieżnie.
Schematycznie:
rejestracja
|
+-- assets
+-- routes
+-- models
|
v
rozwiązanie rejestru
|
+--> wykonaj assets ----+
+--> wykonaj routes ----+--> czekaj na wszystkie --> zakończ
+--> wykonaj models ----+Nie istnieje gwarantowana kolejność wykonywania niezależnych implementacji.
Implementacja nie powinna więc zakładać, że inna implementacja zostanie wykonana przed nią albo po niej.
Przykład nadpisania
Załóżmy, że istnieją dwa moduły.
Pierwszy dostarcza domyślną implementację:
implement --name assets --body=<<EOF
echo "default assets implementation"
EOFDrugi ją zastępuje:
implement:overwrite --name assets --body=<<EOF
echo "custom assets implementation"
EOFFinalną implementacją będzie:
echo "custom assets implementation"Wykonane zostanie wyłącznie ciało z implement:overwrite.
Poniższa kolejność jest równoważna i również poprawna:
implement:overwrite --name assets --body=<<EOF
echo "custom assets implementation"
EOF
implement --name assets --body=<<EOF
echo "default assets implementation"
EOFKolejność deklaracji nie wpływa na wynik.
Niepoprawne duplikaty
Zduplikowane implement
implement --name assets --body=<<EOF
echo "first"
EOF
implement --name assets --body=<<EOF
echo "second"
EOFTen przypadek musi zakończyć się błędem, ponieważ dla assets istnieje więcej niż jedna podstawowa implementacja.
Zduplikowane implement:overwrite
implement --name assets --body=<<EOF
echo "default"
EOF
implement:overwrite --name assets --body=<<EOF
echo "first overwrite"
EOF
implement:overwrite --name assets --body=<<EOF
echo "second overwrite"
EOFTen przypadek musi zakończyć się błędem, ponieważ dla assets istnieje więcej niż jedno nadpisanie.
Najważniejsze zasady
Podczas korzystania z implementacji obowiązują następujące reguły:
implementdefiniuje podstawową implementację;- każda nazwa musi mieć dokładnie jedną podstawową definicję;
implement:overwritemoże zastąpić podstawową definicję;- dla jednej nazwy może istnieć maksymalnie jedno
implement:overwrite; implement:overwritemoże zostać zarejestrowane przed odpowiadającym muimplement;- nie należy polegać na kolejności rejestracji pomiędzy modułami;
- ciała implementacji nie są wykonywane podczas rejestracji;
implementiimplement:overwritenie mogą być wywoływane z ciał implementacji;- finalne implementacje są wykonywane współbieżnie;
- kolejność wykonywania implementacji nie jest gwarantowana;
goat genczeka na zakończenie wszystkich implementacji;- błędy powstałe podczas wykonania implementacji są propagowane do
goat gen.