Definiční a vývojové prostředí - skriptování
Zde naleznete:
Při nasazení ABRA Gen u větších klientů je mnohdy nutné dodávaný produkt upravit dle specifických potřeb, které jdou nad rámec běžných uživatelských úprav a nastavení. Kromě možnosti definovat si definovatelné číselníky, možnosti různým způsobem do existujících objektů, agend a číselníků doplňovat další položky, možnost si pomocí definovatelných SQL skriptů doplňovat/rozšiřovat strukturu databáze, je mnohdy potřeba určité chování i doprogramovat. Proto je v systému k dispozici možnost skriptování – tedy možnost do existujících objektů (Business objektů, agend a nevizuálních číselníků) zařadit vlastní programový kód, který se bude vykonávat spolu s pevným kódem daným výrobcem. Tímto způsobem je tedy možné vytvořit nové chování případně upravit stávající přesně na míru uživateli. Jedná se o jednu z možností zákaznické modifikovatelnosti systému.
Uživatel/implementátor potřebuje upravit nebo rozšířit funkčnost stávajícího Business objektu Faktura vydaná tak, aby se při vytvoření nové faktury do popisu vyplnil nějaký text. Otevře si agendu Balíčky skriptů, zadefinuje nový balíček (nebo se rozhodne, že přidá svoje skripty do nějakého existujícího balíčku) a v rámci tohoto balíčku si vybere typ Business objekt. V rámci typu Business objekt si vybere třídu Business objektu Faktura vydaná. V ní pak zadefinuje vlastní skript pro událost AfterPrefill. V této události je mu k dispozici samotný Business objekt (předaný pomocí parametru) a případně další parametry. Zadefinovaný skript uloží – po uložení se skript zkompiluje pro pozdější použití (kompilace skriptu je časově nezanedbatelná, proto je lepší je mít už předkompilované).
Na balíčku, ve kterém je umístěný popisovaný skript, nastaví programátor příznak Používat. Pak už jen stačí spustit agendu Faktur vydaných nebo OLE aplikaci, která s Business objektem Faktura vydaná pracuje, a implementované řešení vyzkoušet.
Skriptování je také nezbytné pro některé jiné části systému. Viz příklady Využití skriptování v jiných modulech systému ABRA Gen.
POZOR!!! Skriptování značně rozšiřuje možnosti systému, jak plyne mj. i z několika základních příkladů Příklady možností využití skriptování. Nicméně z toho mohou plynout i možné negativní důsledky:
-
Může do značné míry změnit vzhled systému a chování systému a to bohužel i negativním způsobem - Např.:
- Skrytí či přejmenování některých funkčních tlačítek, položek apod., což může vést i ke zmatení uživatele (nebude odpovídat této příručce, která popisuje stav dodávaný defaultně výrobcem).
- Změna umístění některých prvků, která může vést ke kolizi (např. nechtěnému překrytí) s jinými položkami (např. nově dodanými výrobcem v dalších verzích apod.).
- Neočekávané chování (např. nabízení záznamů z číselníku v závislosti na hodnotě jiné položky řešené skriptováním) může vést třeba i k tomu, že se nenabídne nic, pokud daná položka není zadána (pokud to není ve skriptu řádně ošetřeno apod.).
- Systém je navržen tak, aby v případě, že nejsou žádné skripty definovány, nedocházelo k výraznějšímu zpomalení, nicméně nepatrné zpomalení je možné.
- Možný výskyt zavlečených chyb, zejména v případě použití nezkušených uživatelem.
Proto si v případě pochybností o nějaké funkčnosti systému nejdříve zjistěte, zda se vám nespouští nějaké skripty, které by daný stav mohly způsobit, viz Informace o spouštěných skriptech.
Jako skriptovací engine byl na základě testů rychlosti vybrán FastScript. Ačkoliv FastScript podporuje více skriptovacích jazyků, je ve skriptování ABRA Gen pro jednotnost použitý pouze PascalScript. Toto omezení souvisí mimo jiné i s tím, že ve skriptování jsou dostupné třídy, které jsou k dispozici pouze v Delphi/Pascalu – tudíž implementátor, který chce realizovat skriptování v ABRA Gen, musí být s Delphi/Pascalem tak jako tak obeznámen.
FastScript byl podstatně rozšířen, aby více vyhovoval potřebám ABRA Gen. Jedná se např. o podporu dokumentace na všech entitách, zprovoznění enumeračních typů a množin, podporu dokumentace pro události, podporu statických (class) metod. Nicméně i přesto má určitá omezení, se kterými nutno při tvorbě skriptů počítat. Viz Pokyny k tvorbě skriptů.
K dispozici je agenda Balíčky skriptů, pro vytváření, ladění a správu existujících skriptů.
Pojem "balíček" zavedený v agendě balíčky skriptů, umožňuje rozumnou organizaci (uspořádání) vytvořeného programového kódu včetně vzájemných závislostí. Neznamená snadnou distribuci všech součástí potřebných pro implementaci uživatelem požadované funkčnosti (skripty, definovatelné číselníky, definovatelné položky, definovatelné formuláře, definice reportů, definice exportů, definovatelné SQL skripty, …).
Aby bylo možné v definici skriptů (a i jinde) vědět, jaké BO, agendy a nevizuální číselníky podporují skriptování a jaké metody (události) nabízejí ke skriptování, je interně zajištěna registrace těchto objektů a v rámci objektu je k dispozici seznam podporovaných událostí včetně popisu (vysvětlivek). Skriptování je typicky dostupné pro Business objekty, které mají možnost definovat uživatelsky definovatelné položky (ale mohou nastat i výjimky) a pro většinu agend a nevizuálních číselníků. Možnost doplnit si vlastní skripty je pouze do vybraných míst (událostí) v Business objektech (BO), agendách a nevizuálních číselnících. Jaká místa pro skriptování, tzv. háčky (hooks), jsou pro daný objekt k dispozici, je dáno výrobcem a uživatel toto nemůže nijak měnit.
Seznam objektů/tříd a procedur/funkcí, které jsou dostupné ve skriptování, je k dispozici v agendě Balíčky skriptů v Class Exploreru.
Seznam dostupných háčků pro daný objekt je k dispozici v agendě Balíčky skriptů v záložce Zdrojový kód v položce Metoda.
Názvy metod (háčků) pokud možno co nejvíce vystihují místo, kde dochází k jejich volání. Dále jsou seznamy háčků rozděleny na základní a pokročilé. Pokročilé se liší od základních tím, že začínají znakem "_".
Dále může být odlišeno, zda se daný háček volá před nebo po určité akci. To je řešeno pomocí sufixů PreHook a PostHook v názvu. Pokud to nemá význam, končí název háčku pouze slovem Hook.
Existující háčky:
- _DeleteObject_PreHook
- _DeleteObject_PostHook
- AfterSiteOpen_Hook
- Validate_Hook(var AResult: Boolean)
- aj.
Nezbytnou podmínkou pro provoz skriptování je, aby byla tato část systému licencována. Skriptování je licencováno samostatně - viz Licencované celky (licencované moduly a vlastnosti). Licencována je tvorba a kompilace skriptů. Samotné vykonávání již zkompilovaných skriptů nijak licencováno není.
Zda je licence dostupná k zakoupení pro všechny produkty ABRA Gen, viz aktuální obchodní podmínky výrobce.
U skriptování však neplatí, že nepřítomnost licence skriptování "vypíná" jeho použití. Licence na modul Skriptování je potřebná pouze pro operaci Kompilace z agendy Balíčky skriptů. Tzn., že i v instalaci bez licence k tomuto modulu je možné naimportovat již zkompilovaný balíček bez zdrojových textů a je možné provádění zkompilovaných skriptů (např. vytvořených na zakázku výrobcem).
Pokud potřebujete skriptování vypnout (myšleno vypnout provádění skriptů), je třeba tak učinit jiným způsobem, viz Vypnutí skriptování.
Naopak pokud není skriptování nainstalováno, není k dispozici agenda Balíčky skriptů a nebude možné v systému plnohodnotně používat agendy a funkce, které jsou na skriptování navázány. Např. Funkce pro zajištění v SCM, Operace u Web. služeb, Kontrolní body u modulu E-maily a interní vzkazy apod.
Pokud chcete potlačit provádění skriptů z nějakého konkrétního balíčku skriptů, můžete v jeho položce Stav nastavit, že se nemá používat.
Pokud je třeba skriptování zcela vypnout (např. z důvodu, že je v nějakém skriptu chyba, která brání v provozu), můžete tak učinit spuštěním systému ABRA Gen bez skriptování. To lze provést spuštěním systému ABRA Gen s parametrem -noscripting. Toto vypnutí se provede pouze tehdy, pokud uživatel, který se do systému přihlásí, má současně nastaveno privilegium Obcházet skriptování. (Z toho důvodu, aby si běžný uživatel nemohl vypínat skriptování bezdůvodně).
Syntaxe viz popis parametru spuštění -noscripting.
Doporučujeme si prověřit, že se skriptování skutečně podařilo vypnout a že je vypnuté, např. tak, že se nezobrazuje volba Skriptování v menu Nápověda. Viz dále Informace o spouštěných skriptech.
Aktuálně přihlášenému uživateli se budou spouštět skripty, pokud jsou splněny následující podmínky:
- skript je z balíčku, který je zkompilován (nebo jen jeho část), viz příznak Zkompilováno
- skript je z balíčku, který má nastaveno Používat (viz příznak Stav) nebo má nastaveno Ladit a současně má daný uživatel privilegium Ladit skripty
- daný uživatel nemá skriptování zcela vypnuto
Dále pozor na to, zda se nejedná-li se o tzv. zastaralý skript (s hodnotou Zastaralá), u něhož je zamezeno jejich spouštění. Dále viz hodnota u daného skriptu v položce Verze kompilace skriptu a Verzování, Deprecated prvky.
Pokud ve skriptování implementujete nějakou požadovanou dynamickou změnu chování číselníku v nějaké vlastní číselníkové položce, je třeba pro ni použít prvek Číselník objektový (nikoli Číselník neobjektový). Prvek Číselník neobjektový není schopen implementovat dynamickou změnu chování danou skripty k danému objektu.
Jak bylo řečeno výše, skriptování značně rozšiřuje možnosti systému, nicméně mohou z něho plynout i možné negativní důsledky. Proto je potřeba mít možnost rychle uživatelsky (i bez existence práv pro přístup do agendy Balíčků skriptů) zjistit, zda aktuálně přihlášenému uživateli mohou být vykonány nějaké skripty.
Seznam všech skriptů, které si lze zobrazit, vytisknout, případně odeslat e-mailem (odeslání skriptů e-mailem je však již vázáno na patřičné právo):
-
v libovolné agendě z menu Nápověda volbou Skriptování (kdy je k dispozici a jak funguje, viz popis volby v menu Nápověda):
Příklad vyvolaného menu Nápověda s dostupnou položkou Skriptování
Ve skriptu je možné odkazovat se na jiné skripty. Pro odkazování na skripty mezi balíčky slouží druh skriptu Knihovna. Je tedy možné odkazovat se na skripty knihoven (nikoli Business objektů, agend a nevizuálních číselníků). Přehled závislých balíčků viz záložka Závislosti. Při volání funkcí nebo procedur z jiného skriptu je nutno používat frázi uses s následující konvencí:
uses
'nazev_balicku.nazev_knihovny','nazev_balicku.nazev_knihovny';
Pozor, volání přes název balíčku a knihovny není citlivé na velká/malá písmena.
Mějme balíček skriptů s názvem např. abra.cz.vyvoj.ukazka1, v němž je mj. zaveden skript druhu Knihovna s názvem Knihovna1. Chceme se na něj odkázat z jiného skriptu. Pak musíme použít syntaxi uses 'abra.cz.vyvoj.ukazka1.Knihovna1'.
Díky tomu, že je možné ve skriptování vytvářet vlastní instance (i vizuálních) objektů a těmto objektům nastavovat události, není možné po opravě zdrojových kódů skriptů a jejich překompilaci provést automaticky náhradu načtených skriptů v cache paměti. Tzn., že pokud editujete skript např. pro agendu a již jste ji měli alespoň jednou otevřenu v rámci daného spuštění ABRA Gen, pak se změna ve skriptu pro danou agendu projeví až po znovunačtení skriptu do paměti.
Možnosti, jak vyvolat znovunačtení skriptů do paměti:
- Zavřením a znovuspuštěním systému ABRA Gen.
- Pomocí funkce Znovu načíst skripty.
- Při uložení skriptu, je-li zatrženo Znovu načíst skripty.
Každý háček dostává minimálně jeden parametr "Self", ve kterém je mu předávána instance objektu, na kterém došlo k vyvolání háčku.
Aby bylo možné zjistit jména existujících vizuálních prvků (pro snazší zakomponování skriptů do vybraných objektů), je možno si spustit tzv. Show Inspector. Lze vyvolat z menu Nápověda volbou Zobrazit/Potlačit okno inspektoru nebo provést spuštěním systémuABRA Gen s parametrem -showinspector.
Syntaxe viz parametr spuštění -showinspector.
V obou případech se zobrazí speciální okno (Inspektor), které ukazuje za běhu ABRA Gen informace o komponentě/prvku, nad kterým se nachází kurzor myši. Zobrazená data o vybrané komponentě je možno zmrazit pomocí klávesy Pause.
Příklad zobrazení informací v Show Inspektoru pro prvek období.
FastScript neumožňuje vytváření (ve skriptu) vlastních tříd, typů a recordů.
Z této skutečnosti vyplývají i omezení skriptování v ABRA Gen. Na místech, kde je nutné pracovat s existujícími recordy (např. TRect a TPoint), se v obálkách objektů (tj. to, co je dostupné ve skriptování) tyto záležitosti převádějí na více hodnot (např. souřadnice x a y).
Dále FastScript nepodporuje práci s tzv. třídami tříd. Tudíž v případě potřeby této funkčnosti v obálkách, by bylo potřeba toto implementovat konkrétně pro jednotlivé třídy.
FastScript dále nepodporuje Interface. Tudíž na všech místech, kde se interně v ABRA Gen pracuje s rozhraními (proměnné typu interface s odkazy na příslušné objekty), se ve skriptování toto vždy musí převést na instance objektů. Z toho plyne ovšem ten důsledek, že pokud si uživatel pomocí nějaké metody vytvoří nové instance objektů, musí se postarat i o jejich zrušení (kdežto pokud je objekt "držen za interface", je zrušen automaticky po zrušení všech odkazů na daný objekt přes nějaký interface).
Od verze ABRA Gen 23.1. je ARC ve výchozím stavu vypnuté, proto je třeba ve skriptování zajistit správné uvolňování objektů, aby nedocházelo ke vzniku memory leaků. Zároveň se však nepřistupovalo k již uvolněným objektům, což může v nepředvídatelných chvílích způsobovat výjimku typu Access violation, jejíž příčina se obvykle velmi obtížně hledá.
S vypnutým ARC je třeba uvolňovat všechny objekty, které si uživatel ve skritpování sám vytvoří. To platí pro:
- Všechny neinterfacové objekty (typicky TStringList, TNxParameteters, atd).
- Některé interfacové objekty, které jsou získány konkrétními metodami. (Jedná se o metody, které si vytváří tzv. Holdery pro udržení reference na původní interfacové objekty.)
Ve skriptování je u daných metod v nápovědě napsáno: Upozornění - takto získaná objektová proměnná se musí uvolnit voláním <proměnná>Free. Uvolní pouze objekt dočasně vytvořený v rámci skriptovacího prostředí, skutečného objektu v ABRA Gen se nedotkne.
Týká se to i řady metod, které z logiky věci nevytváří pouze nový objekt, ale například vrátí objekt, který již existuje. Pokud si tedy Holder drží referenci, ať již na vytvořený nebo existující objekt, který se bez zavolání metodyFree neuvolní, vznikne tím Memory Leak.
Současně je třeba brát na zřetel, že samotné zavolání metody Free na interface objekt uvolní pouze výše uvedený Holder, ale vlastní interface objekt zanikne až v okamžiku, kdy na něj neexistuje žádná reference (nikdo se na něj neodkazuje).
Níže je uveden kompletní seznam metod (včetně jejich třídy), které vrátí objekt, jež musí uživatel následně sám ve skriptování uvolnit prostřednictvím metody Free, aby nedošlo ke vzniku memory leaku:
- funkce NxCreateDocumentImportManager
- funkce >NxCreateDepositUsage
- funkce >NxSetCustomBusinessObjectFromBOText_1
- funkce >NxCreateRoll
- funkce >NxCreateRoll_1
- funkce >NxCreateInitializedRoll
- funkce >NxCreateContext
- funkce >NxCreateContext_1
- funkce >NxCreateNotLoggedContext
- funkce >NxCreateNotLoggedContext_1
- CFxExchangeDifferences.CreateDiff
- TNxSelectionRequest.Create
- TSiteForm.GetFakeBusinessObject
- TDynSiteForm.CurrentObject
- TNxCustomObjectSpace.CreateObject
- TNxCustomObjectSpace.Load
- TNxCustomObjectSpace.LoadWhenExists
- TNxCustomObjectSpace.CreateClassObject
- TNxCustomBusinessMonikerCollection.FirstBusinessObject
- TNxCustomBusinessMonikerCollection.LastBusinessObject
- TNxConditionLink.GetContext
- TNxCustomObjectDataSet.CurrentObject
- TNxCustomObjectDataSet.ActiveObject
- TNxCustomObjectDataSet.CreateBusinessObject
- TNxCustomBusinessObject.CreateMoniker
- TNxCustomBusinessObject.Clone
- TNxCustomBusinessObject.GetMonikerForFieldCode
- TNxFirm.MajorCorrection
- TBusRollSiteForm
Příklady správného uvolňování ve skriptování
Při psaní skriptů je třeba se řídit stejnými zásadami, jako při běžném programování v Delphi.
V případě vypnutí ARC (Automatického uvolňování objektů) je třeba dbát především na uvolňování všech objektů, které si uživatel ve skriptování sám vytvoří. To platí pro všechny neinterfacové objekty (typicky TStringList, TNxParameteters). Ale to i pro některé interfacové objekty, které jsou vyjmenovány výše.
Následující příklady je třeba chápat pouze jako demonstrační ukázky, které ukazují jednotlivé problémy v co možná nejtriviálnějších konstrukcích. V reálných situacích jsou konstrukce samozřejmě často daleko komplikovanější, ale jejich přesné uvedení by zbytečně komplikovalo vysvětlení podstaty problému.
Nepoužívejte Globální proměnné, nelze zajistit jejich správnou inicializaci, protože sekce mezi begin end na konci unit se nevolá.
Příklad chybné konstrukce:
var
GlobalFlag: Boolean;
begin
GlobalFlag := True; //Toto se nikdy nezavolá
end.
Doporučujeme použít globální úložiště GlobParams, jehož použití můžete navíc zabalit do snadno použitelných funkcí:
//Funkce pro získání hodnoty
function GlobalFlag: Boolean;
begin
Result := GlobParams.ParamAsBoolean('GlobalFlag', True);
end;
//Procedura pro nastavení hodnoty
procedure SetGlobalFlag(AValue: Boolean);
var
mPar: TNxParameter;
begin
mPar:= GlobParams.GetOrCreateParam(dtBoolean, 'GlobalFlag');
mPar.AsBoolean := AValue;
end;
Příklad chybné konstrukce:
procedure DoSomething(AOS: TNxCustomObjectSpace; AValue: Integer);
var
mBO: TNxCustomBusinessObject;
begin
try
case aValue of
1: mBO:= AOS.CreateObject(Class_Firm);
2: mBO:= AOS.CreateObject(Class_Person);
end;
finally
mBO.Free; //Není nezaručeno, že mBO bude správně inicializovaný
end;
end;
Správná konstrukce je tedy následující:
procedure DoSomething(AOS: TNxCustomObjectSpace; AValue: Integer);
var
mBO: TNxCustomBusinessObject;
begin
try
case aValue of
1: mBO:= AOS.CreateObject(Class_Firm);
2: mBO:= AOS.CreateObject(Class_Person);
else
mBO := nil;
end;
finally
mBO.Free; //Je zaručeno, že mBO bude správně inicializovaný a volat .Free nad objektem, který je nil není problém
end;
end;
Případně lze použít tuto konstrukci:
procedure DoSomething(AOS: TNxCustomObjectSpace; AValue: Integer);
var
mBO: TNxCustomBusinessObject;
begin
mBO := nil;
try
case aValue of
1: mBO:= AOS.CreateObject(Class_Firm);
2: mBO:= AOS.CreateObject(Class_Person);
end;
finally
mBO.Free; //Je zaručeno, že mBO bude správně inicializovaný a volat .Free nad objektem, který je nil není problém
end;
end;
Je třeba destruovat všechny neinterfacové objekty.
Mezi typické neinterfacové objekty patří například TStringList, TNxParameteters.
Neuvolněním vytvořených objektů vznikají memory leaky, které při častém opakování mohou po delší době znamenat pád programu způsobený vyčerpáním zdrojů (USER objektů, GDI objektů nebo operační paměti).
Příklad chybné konstrukce:
procedure DoSomething;
var
mSL: TStringlist;
begin
mSL:= TStringlist.Create;
mSL.Add('Takto vznikne memory leak');
end;
Správná konstrukce je tedy následující:
procedure DoSomething;
var
mSL: TStringlist;
begin
mSL:= TStringlist.Create;
try
mSL.Add('Takto nevznikne memory leak');
finally
mSL.Free;
end;
end;
V případě potřeby současně inicializovat více objektů najednou lze použít i konstrukci uvedenou níže.
Tou lze obejít mnohonásobné zanoření try a finally do sebe, což pak značně znepřehledňuje kód.
Předpokladem je, že:
-
Všechny objekty musí být před try nainicializované na nil.
-
Destruktory všech uvolňovaných objektů jsou napsány tak, aby v jejich průběhu nedošlo k vzniku výjimky.
procedure DoSomething;
var
mSL1, mSL2: TStringlist;
begin
mSL1:= nil;
mSL2:= nil;
try
mSL1:= TStringlist.Create;
mSL1.Add('Takto nevznikne memory leak');
mSL2:= TStringlist.Create;
mSL2.Add('Takto nevznikne memory leak');
finally
mSL1.Free;
mSL2.Free;
end;
end;
Některé metody umožní získat odkaz nově vytvořený nebo již existující interface objekt. Zároveň si však při tomto požadavku vytvoří tzv. Holder, který si drží referenci na originální interface objekt.
Jedná se o relativně komplikovanou konstrukci, kterou je zbytečné zde celou popisovat. Podstatné je, že nad získaným odkazem na instancí objektu musíme zavolat metodu Free, která automaticky zajistí destrukci Holderu, a tím nastane i uvolnění reference na originální interface objekt.
Tím se originální objekt nemusí okamžitě uvolnit, pokud na něj existuje jinde v programu nějaká další reference, ale uvolní se automaticky v okamžiku, kdy počet referencí na něj klesne na nulu.
Pokud bychom nad získaným odkazem metodu Free nezavolali, držel by si neuvolněný Holder stále referenci na originální interface objekt a ten by se nikdy neuvolnil. Tím by vznikl memory leak. Je třeba brát v úvahu, že mezi nejběžnější interface objekty patří v ABRA Gen Business objekty, jejichž struktura bývá často členitá, a vzniklý memory leak tam může být relativně velký.
Existuje i řada funkcí a property objektů, které vrací odkaz na již existující interface objekt, a tak z jejich názvu není patrné, že je nutné pro ně volat metodu Free.
Seznam všech funkcí a property objektů pro které je třeba volat následně Free, je uvedená výše.
I když property CurrentObject nevytvoří žádný Business objekt, u kterého by se uživatel mohl domnívat, že ho musí na konci uvolnit, je i přesto voláním této property vytvořen výše zmíněný Holder, který si drží referenci na Business objekt DynSite. Pokud nezavoláme mBO.Free, nedojde k uvolnění Holderu, který si tak bude stále držet referenci na Business objekt DynSite, který tímto rovněž také nikdy neuvolní. Tím vznikne memory leak, a to jak na Holder, tak i na Business objekt DynSite.
Příklad chybné konstrukce:
procedure DoSomething(ASite: TDynSiteForm);
var
mBO: TNxCustomBusinessObject;
begin
mBO := ASite.CurrentObject;
mBO.SetFieldValueAsBoolean('MyField', True);
mBO.Save;
end;
Správná konstrukce je tedy následující:
procedure DoSomething(ASite: TDynSiteForm);
var
mBO: TNxCustomBusinessObject;
begin
mBO := ASite.CurrentObject;
try
mBO.SetFieldValueAsBoolean('MyField', True);
mBO.Save;
finally
mBO.Free;
end;
end;
Volání mOS.Free nic neudělá. Jen přistoupí k danému objektu, a protože ten nemá Holder, tak nic neprovede (neuvolní ho).
Příklad chybné konstrukce:
procedure DoSomething(ASite: TDynSiteForm);
var
mOS: TNxCustomObjectSpace;
begin
mOS := ASite.BaseObjectSpace;
try
...
finally
mOS.Free; //Nic neudělá
end;
end;
Správná konstrukce je tedy následující:
procedure DoSomething(ASite: TDynSiteForm);
var
mOS: TNxCustomObjectSpace;
begin
mOS := ASite.BaseObjectSpace;
...
end;
Jedná se o situaci, kdy například metodou ObjectSpace.CreateObject vytvoříme nový hlavní Business objekt a následně si v některé z jeho kolekcí vytvoříme novou položku.
V takovém případě je třeba provést destrukci pouze na hlavní Business objekt a nikoliv na vytvořenou položku v kolekci, protože o destrukci položky se postará sám hlavní objekt, resp. jeho Moniker dané kolekce.
Příklad chybné konstrukce:
procedure DoSomething(ASite: TDynSiteForm);
var
mObjectSpace: TNxCustomObjectSpace;
mBO: TNxCustomBusinessObject;
mRows: TNxCustomBusinessMonikerCollection;
mRow: TNxCustomBusinessObject;
begin
mObjectSpace := ASite.BaseObjectSpace;
mBO:= mObjectSpace.CreateObject(Class_PLMPieceList);
try
mBO.Load('1000000101');
mRows := mBO.GetLoadedCollectionMonikerForFieldCode(mBO.GetFieldCode('ROWS'));
mRow := mRows.AddNewObject;
finally
mBO.Free;
mRow.free; //Zde to spadne na výjimku, protože se pokusíte přistoupit k již zdestruovanému objektu
end;
end;
Správná konstrukce je tedy následující:
procedure DoSomething(ASite: TDynSiteForm);
var
mObjectSpace: TNxCustomObjectSpace;
mBO: TNxCustomBusinessObject;
mRows: TNxCustomBusinessMonikerCollection;
mRow: TNxCustomBusinessObject;
begin
mObjectSpace := ASite.BaseObjectSpace;
mBO:= mObjectSpace.CreateObject(Class_PLMPieceList);
try
mBO.Load('1000000101');
mRows := mBO.GetLoadedCollectionMonikerForFieldCode(mBO.GetFieldCode('ROWS'));
mRow := mRows.AddNewObject;
finally
mBO.Free;
end;
end;
Přípustná je i následující konstrukce, ovšem volání mRow.Free nic neudělá. Jen přistoupí k danému objektu, a protože ten nemá Holder, tak nic neprovede (neuvolní ho).
procedure DoSomething(ASite: TDynSiteForm);
var
mObjectSpace: TNxCustomObjectSpace;
mBO: TNxCustomBusinessObject;
mRows: TNxCustomBusinessMonikerCollection;
mRow: TNxCustomBusinessObject;
begin
mObjectSpace := ASite.BaseObjectSpace;
mBO:= mObjectSpace.CreateObject(Class_PLMPieceList);
try
mBO.Load('1000000101');
mRows := mBO.GetLoadedCollectionMonikerForFieldCode(mBO.GetFieldCode('ROWS'));
mRow := mRows.AddNewObject;
finally
mRow.Free; //Nic neudělá
mBO.Free;
end;
end;
Vždy je třeba dbát na to, aby vytvoření objektu bylo před try a destrukce objektu je ve finally, a to samozřejmě na stejné úrovni.
Zároveň upozorňujeme na častou chybu, kdy je objekt vytvářen v cyklu, ale destruován je pouze jednou, nebo je naopak vytvořen pouze jednou a destruován je v cyklu.
Příklad chybné konstrukce: Business objekt se vytváří n-krát v cyklu, ale uvolní se pouze ten poslední. Z tohoto důvodu může dojít ke vzniku memory leaků. Navíc pokud by parametr aSL neobsahoval žádnou položku, mohlo by dojít k uvolnění neinicializované proměnné mBO.
procedure DoSomething(ASite: TDynSiteForm);
var
mOS: TNxCustomObjectSpace;
begin
mOS := ASite.BaseObjectSpace;
try
...
finally
mOS.Free; //Nic neudělá
end;
end;
Další příklad chybné konstrukce: Business objekt se vytváří pouze jednou, ale destruuje se v cyklu.
Toto velmi pravděpodobně způsobí vznik výjimky při opakovaném volání metody Load, protože ta se bude aplikovat na již zdestruovaném objektu.
procedure DoSomething(ASite: TDynSiteForm; aSL: TStringList);
var
mObjectSpace: TNxCustomObjectSpace;
mBO: TNxCustomBusinessObject;
I: Integer;
begin
mObjectSpace := ASite.BaseObjectSpace;
mBO:= mObjectSpace.CreateObject(Class_PLMPieceList); //Business objket se vytváří pouze jednou
for I:=0 to aSL.Count-1 do
begin
try
mBO.Load(SL[I]); //Zde se při opakovaném průchodu zavolá Load nad již zdestruovaným Business objektem
...
finally
mBO.Free; //Ale destruuje se v cyklu
end;
end;
end;
Správná konstrukce je tedy následující:
procedure DoSomething(ASite: TDynSiteForm; aSL: TStringList);
var
mObjectSpace: TNxCustomObjectSpace;
mBO: TNxCustomBusinessObject;
I: Integer;
begin
mObjectSpace := ASite.BaseObjectSpace;
for I:=0 to aSL.Count-1 do
begin
mBO:= mObjectSpace.CreateObject(Class_PLMPieceList); //Business objket se vytváří v cyklu
try
mBO.Load(SL[I]); //Load se zavolá vždy nad novou instancí Business objektu
...
finally
mBO.Free; //Business objekt se rovněž destruuje v cyklu
end;
end;
end;
Přípustná je i konstrukce níže, avšak pouze za předpokladu, že se v cyklu po Load nevolá Save. ABRA Gen neumožňuje jeden a ten samý objekt používat ve výchozím stavu opakovaně a nahlásí chybu.
procedure DoSomething(ASite: TDynSiteForm; aSL: TStringList);
var
mObjectSpace: TNxCustomObjectSpace;
mBO: TNxCustomBusinessObject;
I: Integer;
begin
mObjectSpace := ASite.BaseObjectSpace;
mBO:= mObjectSpace.CreateObject(Class_PLMPieceList); //Business objket se vytváří pouze jednou
try
for I:=0 to aSL.Count-1 do
begin
mBO.Load(SL[I]); //Load se zavolá stále na nd stejnou instancí Business objektu
...
end;
finally
mBO.Free; //Business objekt se rovněž destruuje pouze jednou
end;
end;
Pokud nějaká námi vytvořená funkce vrací novou instanci objektu, pak ji musíme správně pojmenovat, aby bylo zřejmé, že vrácený objekt je třeba zdestruovat.
Například pokud naše funkce bude vytvářet novou instanci formuláře, pojmenujme ji CreateForm a výsledek vracejme v Resultu a ne var parametru.
Příklad chybné konstrukce: Z názvu funkce není na první pohled zřejmé, že vrací novou instanci formuláře, kterou je třeba následně zdestruovat.
function GetMyForm: TForm;
begin
Result := TForm.Create(nil);
...
end;
Příklad chybné konstrukce: Název procedury je již lepší, ale v Delphi není obvykle zvykem, aby procedura vracela novou instanci objektu ve var parametru.
procedure CreateMyForm(var AForm: TForm);
begin
AForm := TForm.Create(nil);
...
end;
Správná konstrukce:
function CreateMyForm: TForm;
begin
Result := TForm.Create(nil);
...
end;
Proto, aby bylo možné SQL dotazy připravit již na verzi 22.0 a přechod na Unicode verzi se co nejvíce urychlil, byla do ABRA Gen implementována technologie zástupných znaků, které dle verze ABRA Gen doplní nové nezbytné části SQL dotazů.
Od verze 22.0.0 ServicePack 26 respektive 22.0.1 ServicePack 24 jsou tyto zástupné znaky nahrazeny prázdným znakem.
Od verze 22.1.0 ServicePack 4 respektive 22.1.1. ServicePack 3 a výše jsou nahrazeny takto:
Firebird:
@{unicodeNvarcharCast(String what, int size, boolean withCollate)} vloží pouze zadané ID
Ostatní zástupné znaky, pokud budou použity, jsou nahrazeny prázdným znakem.
Praktické příklady využití skriptování naleznete v samostatné kapitole.
V průběhu skriptování může dojít (vinou špatného skriptu) nejen k chybám ve vlastním průběhu skriptování, ale i k chybám následným, a je proto důležité vědět, že prvotní příčina může být např. právě v chybně napsaném uživatelském skriptu. Proto chyby, které vznikly v průběhu skriptování, se uživateli zobrazují s uvedením informace, že k chybě došlo během skriptování.
K ladění lze využívat nástroj ScriptDebugger.exe.
Podpora ladění nesmí být vypnuta parametrem v konfiguračním souboru nexus.cfg. Více viz kap. Konfigurační soubor Nexus.cfg - oddíly a parametry, Sekce [ScriptDebugger].
Implementátor skriptování může pro ladění využít též funkci ShowMessage(), pomocí níž je možné zobrazovat hlášení. Text chyby se také posílá pomocí funkce OutputDebugStr(). Texty předané funkci OutputDebugStr je možné "chytat" pomocí nějakého externího "zobrazovače" logů, např. pomocí Sysinternals DebugView nástroje.
V rámci některých upgrade systému ABRA Gen může dojít k nějakým významným změnám (změny vývojového prostředí, související změny struktur, zásadní změny ve skriptování apod.), které vedou k tomu, že je třeba existující skripty upravit, příp. minimálně zkontrolovat. V takovém případě jsou takové skripty při procesu update automaticky nastaveny jako "zastaralé" (viz položka Verze kompilace skriptu na řádcích balíčků skriptů) a je zamezeno jejich spuštění.
Dále některé prvky skriptování mohou být v čase označeny atributem Deprecated Atribut označující nějaký prvek jako zastaralý. Typicky se jedná o prvek, který je v budoucnu určen ke zrušení (byl např. nahrazen nějakým jiným prvkem, jinou funkcionalitou).. Takto označené prvky lze zatím ve skriptování používat, ale v budoucnosti se s nimi nepočítá, protože byly nahrazeny alternativním kódem. Lze je skrýt nastavením parametru HideDeprecated.
Současně jsou dodávány *.adc definiční soubory skriptování, vygenerované k příslušným verzím, pro porovnání dalších případných rozdílů ve skriptování pomocí nástroje DefsComp.exe. Porovnává dva *.adc soubory, např. *.adc k aktuální verzi s *.adc souborem verze předchozí. Tyto soubory nelze vytvořit uživatelsky, ale jsou dodávány výrobcem. Lze je nalézt v instalačním adresáři ABRA Gen v podsložce ..\Doc\defscomp.
Pokud potřebujete logovat průběh nějaké funkcionality řešené pomocí skriptování, můžete využít k tomu určenou třídu TNxScriptingLog a její metody, především metodu WriteEvent. V konfiguračním souboru nexus.cfg v sekci logování, musí být nastaveno logování třídy Scripting.