2. Style guide voor M binnen Brocade

2.1. Inleiding

Dit document is geschreven voor software ontwikkelaars, die meewerken aan Brocade. Het omvat richtlijnen voor dat gedeelte van Brocade, dat in Mumps is geschreven. Mumps of M is een ISO standaard databank systeem met een eigen programmeertaal aan boord.

2.1.1. Waartoe dient een M style guide ?

Een behoorlijk deel van de software ontwikkelingen binnen Brocade spelen zich af binnen de M omgeving.

We moeten dan ook erover waken dat :

  • De code permanent leesbaar is, en dit niet alleen voor de persoon, die deze software heeft geschreven, maar ook voor andere ontwikkelaars, en nog lang nadien ook.

  • Het gedrag van de code voorspelbaar is.

  • Het duidelijk is, hoe deze code moet worden ingezet.

  • De code eenvoudig bereikbaar is binnen de software bronbestanden.

  • De code onderhoudbaar blijft.

  • De code foutvrij draait. Hierbij denken we niet alleen aan syntactische fouten - die eenvoudig opspoorbaar zijn bij het compileren van de code, maar ook zoveel mogelijk logische fouten, en liefst nog ook detecteerbaar voor de runtime. M is een programmeertaal, die van nature zich als een interpreter gedraagt, en bijgevolg de kans op runtime fouten in zich draagt.

  • De code inzetbaar is in andere code . Dit betekent ook, dat de invloed van code op de oproepende code minimaal en voorspelbaar moet zijn.

Dit alles is niet mogelijk zonder richtlijnen. Het is mede dankzij het rigoureuze toepassen van deze Style Guide, dat Brocade na meer dan tien jaar niet is geworden tot

  • een onleesbare code, waarvan het gedrag onvoorspelbaar is,

  • de code onbruikbaar is, eenmaal je ze wil inzetten in meerdere toepassingen,

  • de code gedurig faalt bij uitvoering,

  • elke aanpassing van functionaliteit een verschrikking is geworden,

  • het onderhoud van de code tot een labeur is uitgegroeid

en daar zijn we heel blij mee. Niettemin moet het onze grootste bekommernis blijven om deze richtlijnen permanent te blijven volgen.

2.1.2. Wat omvat deze style guide niet ?

Dit document is geen overzicht van alle tools, die een Brocade ontwikkelaar kan gebruiken. Er zijn andere documenten, die hier dieper op ingaan. Voorbeelden, die hier niet worden behandeld :

  • x files die definities bevatten van formaten van webformulieren

  • l files, die verwoordingen van de dialogen bevatten in verschillende talen

  • een opsomming van alle macro's, die algemeen inzetbaar zijn

2.2. M routines ? Nee, M files en macro's

Binnen de Brocade software bevindt de M code zich in zgn. m-files . Deze bestanden (herkenbaar aan het suffix .m) kan je met elke tekstverwerker lezen en bewerken. Zij bevatten - naast M code - ook constructies, die verwijzen naar andere M code. Bij het inchecken van zulke m file worden deze constructies vertaald, en het resultaat is een M routine, die dezelfde naam draagt als de m file.

Enkele voorbeelden

  • gbln.m

  • ukswmnd.m

De naam van een m file, en bijgevold ook de naam van een M routine, is steeds in kleine letters.

Een macro definitie is een van de belangrijkste bouwstenen van de M architectuur binnen Brocade. Macro definities worden aangemaakt in files met suffix .d. Een voorbeeld verduidelijkt:

macro parseIsoDate($hdate, $iso, $base="d"):
    '''
    $synopsis: Bereken de datun aan de hand van een gegeven iso formaat
    $hdate: Te berekenen datum. In $H format
    $iso: Datum in een ISO formaat (zie displayIsoDate)
    $base: d | m | y  : geeft de nauwkeurigheid voor parsing aan:
           staat er "m" in dan is er nauwkeurigheid op maand niveau. De dag wordt ingevuld met "1"
           staat er "y" in dan is er nauwkeurigheid op jaar niveau. De dag en maand worden ingevuld met "1"
    $example: m4_parseIsoDate(RDh, "2008-12-23")
    '''
    «s $hdate=$$%InpIso^udtcode($iso,$base)»

Indien U de code m4_parseIsoDate(doh,"20140417120300","m") gebruikt in uw m file, dan zal deze na inchecken vertaald worden in de M code:

s doh=$$%InpIso^udtcode("20140417120300","m")

Macro definities zijn meer dan placeholders van M code :

  • Zij vormen de interface tussen de verschillende stukken M code binnen Brocade.

  • Zij documenteren deze interface

  • Zij zorgen voor een eenvormige aanpak van problematieken.

2.3. Categorieen van m files

  • m files van type g houden zich bezig met het schrijven en lezen van permanente globals Ze zijn uniek in hun soort : dat wil zeggen : geen enkele andere m file spreekt permanente globals aan. Ze zijn herkenbaar aan hun beginletter g, gevolgd door een verwijzing naar de global, die ze beschrijven. Voorbeeld :

    gukaspf.m beheert de global ^UKASPF .

  • m files van type w houden zich bezig met het verwerken van web acties, en het produceren van (dynamische) web pagina's. De vierde letter van hun naam is steeds een w. Voorbeeld :

    bdmwmeta.m

    Ze zijn zo goed als steeds vergezeld van een x file met dezelfde naam. Voorbeeld :

    bdmwmeta.x

  • m files van type u bevatten de code, die uitgevoerd wordt bij zgn. gebruikersprocessen.

Er is een aparte documentatie beschikbaar hierover. Hun vierde letter is steeds een u. Gebruikersprocessen behandelen acties. die door Brocade gebruikers kunnen worden uitgevoerd. Zij komen met aangepaste, gecontroleerde interfaces, eigen aan hun problematiek. Voorbeeld :

bksunje

  • m files van type t bevatten unittesten voor de verschillende applicaties.

Hun vierde letter is steeds een t. Unittesten controleren de basiswerking van de software. Fouten worden op output geschreven. Voorbeeld:

^uostst

  • m files van type z ofte scratch files bevatten experimentele code, die niet kan beschouwd worden als deel van Brocade. Ze vormen de zandbak voor ontwikkelaars. Zij moeten zodanig gemaakt zijn, dat het schrappen ervan geen enkele invloed heeft op de werking van Brocade.

Hun naam begint steeds met de letter z. Voorbeeld:

zadbad

2.4. Labels in M routines

In Brocade gaan de namen van labels je bepalen, welke bereik ze hebben, en hoe ze worden opgeroepen door andere M software. Dit zijn afspraken, die het analyseren van M code heel wat vereenvoudigen.

in Brocade maak je nooit gebruik van de M constructie met offset, zoals :

do Mylabel+7

De M lijnen, startend aan een Label, en eindigend net voor de volgende Label of op het einde van een m file , vormen een module.

  • Externe Labels : deze zijn labels, die kunnen worden opgeroepen vanuit een andere toepassing. Ze zijn herkenbaar, omdat het eerste karakter van hun naam is een %. Voorbeeld :

    %Entry ;
    %Calc ;
    
  • Interne scratchlabels : deze zijn labels, waarvan de logica niet alleen staat binnen de m file . Ze vormen een deel van de logica van andere software, resortend onder externe of interne labels. Hun naam begint met een kleine letter. Gebruik enkel scratch labels in beperkte mate, waar het niet of moeilijk anders kan. Voorbeeld :

    cin ;
    calcmin ;
    
  • Interne hoofdlabels : dit zijn alle andere labels. Hun naam begint met een hoofdletter, en zij leiden een aantal M lijnen in, die een logisch geheel vormen. Voorbeeld :

    Form
    Zip
    

2.4.1. Grootte van M files en M lijnen

M routines bevatten - volgens ISO 1995 aanbeveling - max. 10.000 karakters, en elke lijn bevat ten hoogste 255 karakters. Voor het MDC comite was transporteerbaarheid de belangrijkste aanleiding van deze aanbeveling. Ondertussen overschrijden de limieten, opgelegd door de verschillende M providers, ver deze aanbevelingen. Dat maakt, dat andere factoren een hoofdrol spelen: de leesbaarheid, de onderhoudbaarheid en de terugvindbaarheid. Vandaar het volgende advies:

  • Begin met de m-code, die logisch bij elkaar hoort, in dezelfde m file te stoppen.

  • Als een deel van die m code een eigen leven kan leiden, aarzel niet om ze in een aparte m file te stoppen. Zelfs als dit uit enkele lijnen bestaat.

  • Lange M lijnen zijn minder leesbaar : splits dan ook gretig je code in verschillende M lijnen, waar het kan. Leesbaarheid en onderhoudbaarheid primeren op (amper) snelheidswinst. Voorbeeld:

    • Zeg niet:

      k RAexecs s RAexecs(1,"exec")="d %job^ustscol",stattit=$g(RAstat("ti")) s:stattit="" stattit="("_RDstloi_")",RAexecs(1,"label")=stattit,RAexecs(1,"gabriel")=$g(RAstat("mailman")),RXcallb="s RDstloi="""_RDstloi_""" d %Callbac^ustscol"
      
    • Maar wel:

      k RAexecs
      s RAexecs(1,"exec")="d %job^ustscol"
      s stattit=$g(RAstat("ti")) s:stattit="" stattit="("_RDstloi_")"
      s RAexecs(1,"label")=stattit
      s RAexecs(1,"gabriel")=$g(RAstat("mailman"))
      s RXcallb="s RDstloi="""_RDstloi_""" d %Callbac^ustscol"
      

2.4.2. Schrijfwijze van labels in M files

Binnen een m file schrijft men nooit een label rechtstreeks. Schrijf dus niet:

Label s a=77

maar wel:

def Label:
 s a=77

Dit wordt - na parsing - vertaald in :

Label ;def
 s a=77

Om een expliciet een onderscheid te maken tussen functies en procedures, kan je ook gebruik maken van tags fn en sub: Voorbeeld:

fn Label(PDvar):
 q PDvar*2

sub Label(PDreturn,PDvar):
 s PDreturn=PDvar*2
 q

2.5. Vaste labels

Bepaalde logica komt herhaaldelijk voor in vele verscheidene m files. Daarom bestaan er ook vaste afspraken rond labels, die een vast betekenis hebben.

  • %Entry is de label, waar een webroutine start.

  • %EntryZ is de label, waar een zoekroutine start in een webformulier.

  • %Action is de label, opgeroepen bij een webroutine nadat een gebruiker een actie heeft ondernomen, die het doorsturen van een form teweegbracht.

  • Form: bouwt een webformulier op. Ze transformeert o.a. - waar nodig - de form variabelen van databank formaat naar een presentatievorm.

  • Trans: transformeert de form variabelen naar een databank formaat.

  • Test: test de inhoud van (getransformeerde) form variabelen.

2.6. Globals in Brocade

  • Globals worden steeds genoteerd met de 'eenvoudige' notatie, zonder namespace verwijzingen. Ze zijn steeds in hoofdletters.

Zeg dus niet :

^|vol|GLOBAL(index,index2)

Maar wel :

`^GLOBAL(index1,index2)

We onderscheiden volgende groepen globals :

Niet-Permanente globals

Hoewel globals van nature vrij 'permanent' zijn, toch is het wel handig, als je globals kan gebruiken voor tijdelijke informatie. Binnen Brocade bestaan er mechanismen, die bepaalde globals geregeld automatisch schrappen. We onderscheiden volgende subcategorieen:

Z globals (patroon Z*)

Deze worden dagelijks geschrapt. Zij bevatten kleine of grote hoeveelheden data, die tijdelijk nodig zijn voor bepaalde processen, of over verschillende processen heen.

K globals (patroon K*)

K globals worden dagelijks geschrapt. Zij bevatten kleinere hoeveelheden data. Voorbeelden :

  • Kopijen van gebruikte meta data, gespijkerd in permanente globals .

  • Tussenresultaten van berekeningen.

Voor K globals is het optimaal dat de systeen ingenieur een databank segment voorzien, dat niet groot is, doch heel snel adresseerbaar. (Ram, SSD disk, bvb.)

global SDEAD

Deze laat je toe, om tijdelijke informatie te bewaren gedurende langer dan een dag. De enige wijze, om hiervan gebruik te maken, gaat via macro m4_getDead. Bij het aanmaken van de global leg je ineens ook zijn sterfdatum vast.

C globals (patroon C*)

Deze globals bevatten tijdelijke data in het kader van een conversie. Zij worden niet automatisch geschrapt. Het is de taak van de ontwikkelaar, om toe te zien, dat deze globals ten gepasten tijde worden geschrapt.

Permanente globals

Deze zijn de globals die de persistente gegevens van de databank bevatten. Men onderscheidt:

B globals (patroon B*)

bevatten de primaire Brocade data

X globals (patroon X*)

bevatten indexen op de primaire data

U globals (patroon U*)

bevatten gegevens, die in meer of mindere mate onveranderd teruggevonden worden op alle Brocade servers. Voorbeeld : global UMENU bevat de menu structuren.

2.6.1. Beschrijving van global structuren

De data structuur van permanente globals wordt, eigenaardig genoeg, nergens beschreven. In alle gevallen ga je ook niet rechtstreeks met de global werken, maar maak je gebruik van een m4 constructie, die, zoals voorgeschreven, de nodige documentatie bevat. je kan dus stellen :

In Brocade worden geen globals gedocumenteerd : in de plaats daarvan worden de interfaces met de globals gedocumenteerd. Voorbeeld :

Zeg niet:

s transco=$P($G(^BLN("rsv",$P(rsvloi,":",2),$P(rsvloi,":",3))),"^")

Maar wel:

m4_getLoanReservationTransaction(transco,"r:UA:28913")

Hierbij hoort een gedocumenteerde macro definitie: (die zich bevindt in een file van type d)

macro getLoanReservationTransaction($return, $rsvloi):
    '''
    $synopsis: Bepaalt het transactienummer geassocieerd met een reservatie
    $return: Transactienr
    $rsvloi: Reservatieloi
    $example: m4_getLoanReservationTransaction(transco,"r:UA:28913")
    '''
    «s $return=$P($G(^BLN("rsv",$P($rsvloi,":",2),$P($rsvloi,":",3))),"^")»

2.7. Lokale variabelen in Brocade

De naam van een lokale variabele in Brocade is beperkt tot 11 karakters.

Namen van lokale variabelen komen in Brocade in twee schrijfwijzen:

  • scratch variabelen worden in kleine letters geschreven. Hun bestaan reikt nooit verder dan een module . Zij dienen dan ook steeds genewed te worden binnen de module waarin ze worden gebruikt.

  • alle andere variabelen zijn steevast van de vorm :

    [hoofdletter1][hoofdletter2][0 of meer kleine letters]

    of

    [hoofdletter1][hoofdletter2][1 of meer kleine letters][0 of meer kleine en/of grote letters en cijfers]

    Voorbeelden :

    PDform
    FDnew
    RXmember
    RArray
    

Hierbij bepaalt :

  • [hoofdletter1] de omgeving, waarin de variabele wordt gebruikt.

  • [hoofdletter2] de inhoud van de variabele.

We gaan hier even dieper op in :

2.7.1. Hoofdletter1 : de omgeving, waarin de variabele wordt gebruikt.

[hoofdletter1] kan volgende waarden aannemen :

  • Fform variabele

    de variabele is afkomstig van een webformulier. Zij wordt aangemaakt vanuit (steeds dezelfde) software, die een webformulier analyseert. De betekenis van zulke variabelen hangen nauw samen met het laatste webformulier. Zij kunnen in die contekst dan ook worden gewijzigd. Voorbeeld :

    FDname
    FDidnr
    
  • Pformele parameter variabele

    de variabele is gebruikt als een formele parameter. Zij dienen dan ook vermeld te worden in de label, die de module inleidt.

  • C: cache variabele

    de variabele wordt gebruikt als een Cache variabele. Zij dient om bepaalde data in een lokale variabele tijdelijk te stockeren, om bijvoorbeeld de access naar de schijf te beperken. Zij bevat dan ook dikwijls copies van delen van globals. C variabelen worden niet of nauwelijks genewed. Je kiest dan ook een naam voor een C variabele, die uniek is over de hele Brocade software. Voorbeeld :

    CArelat
    CDschnxt
    
  • Uuniversele variabele

    de variabele heeft een universele betekenis. De inhoud van de variabele bevat een quasi onveranderde waarde doorheen een proces. Voorbeeld :

    UDuser
    

    bevat de identiteit van de Brocade gebruiker.

    De inhoud van deze variabelen dienen te worden gerespecteerd en enkel gewijzigd door de de software, die zich daarop heeft toegelegd.

  • Mmacro variabele

    de variabele wordt gebruikt gedurende de definitie van een macro . Haar leven is slechts kortstondig, en zij dient nooit te worden genewed. De waarde van zulke variabele is - buiten de definitie van de macro - onvoorspelbaar. Voorbeeld :

    MDx
    
  • Z: scratch variabele

    de variabel wordt gebruikt als scratch variabele. Dit is de tweede mogelijke schrijfwijze voor een scratch variabele. Voorbeeld:

    ZAtmp
    
  • R: routine variabele

    in alle andere gevallen. Het bereik van deze variabelen is steeds beperkt tot de module waarin zij gebruikt worden. Zij dienen dan ook te worden genewed ter hoogte hiervan. Enige uitzondering : indien een module een commentaarlijn bevat van de vorm :

    ;pragma fos RDmyvar,RDmyvar2
    

    dan betekent dit, dat zij het bereik van deze module overstijgen. Zij overstijgen echter nooit het bereik van een gehele routine.

2.7.2. Hoofdletter2 : de inhoud van de variabale.

[hoofdletter2] kan volgende waarden aannemen :

  • G:

    de variabele bevat een global referentie, m.a.w. @[variabele] is een geldige expressie. Voorbeeld :

    s RGtemp="^ZAD(""temp"")"
    s RG="^ZMYGLOB("_$j_")"
    
  • X:

    de variabele bevat een M executable, m.a.w. xecute [variabele] is syntactisch juist. Voorbeeld:

    s RXfile="s @RG@(RDlevel)=RDvalue"
    s RXwrite="w !,RDvalue"
    
  • D:

    de variabele bevat een eenvoudige en enkelvoudige data, m.a.w. $d([variabele])=1 en de inhoud van ervan bevat een enkel data element. Voorbeeld:

    s RDname="Descamps"
    s RDstreet="Prinsstraat"
    
  • R:

    de variabele bevat een reeks van data elementen, gescheiden door een bepaald scheidingsteken. Hier ga je er ook van uit, dat $d([variabele])=1. Voorbeeld :

    s RRadres="Prinsstraat^9^^BE^2000^Antwerpen"
    
  • A: de variabele is een 'array' : in M termen : $data([variabele])>1. Voorbeeld :

    s RAtable(1)="table value1"
    f x="dut","eng","fre" s RAlanguage(x)=""
    

2.7.3. Omvang van de lokale tabel

Hoewel met recente M variaties de totale mogelijke omvang van de lokale variabelen vrij behoorlijk is geworden, toch mogen we niet uit het oog verliezen dat ze beperkt is. Men volge hier twee richtlijnen :

  • Voor lokale variabelen, waarvan de omvang niet voorspelbaar is, gebruikt men global SDEAD .

  • Voor lokale variabelen, waarbij de grootte wel eens 5Mb overschrijdt, gebruikt men eveneens global SDEAD .

  • In alle andere gevallen gebruikt men lokale variabelen.

2.8. Brave routines

Een routine in M geschreven gedraagt zich braaf als de invloed van haar op de oproepende software minimaal en voorspelbaar is. Enkele eenvoudige tegenvoorbeelden tonen aan dat niet elke M routine zomaar een brave routine kan genoemd worden . We sommen enkele M constructies op:

  • Lock ^MYGLOBAL : hier worden alle locks, gelegd door de bovenliggende software, teniet gedaan. Beter en braver is :

  • Lock +^MYGLOBAL

  • s RDname=".." zonder dat de variabele RDname wordt genewed. Indien de oproepende software dezelfde naam voor de variabele gebruikt, dan wordt de waarde ervan overschreven. Een voorafgaandelijke new RDname kan hiervan een brave constructie maken.

Het is de bedoeling, dat alle Brocade software zich gedraagt als een brave routine, of zo gedocumenteerd is, dat haar invloed op de oproepende software volledig voorspelbaar is.

2.9. M instructies in Brocade

Niet alle M instructies mogen zomaar in elke M routine gebruikt worden : dit is ten dele een gevolg van de afgesproken opdeling van functionaliteiten naar aparte m file types, maar ook een advies om leesbare en onderhoudbare dingen te schrijven. Daarom deze bespreking van M instructies.

Omwille van de leesbaarheid spreken wij af alle instructies in kleine letters te zetten.

2.9.1. Break

Is een instructie, die de uitvoering van M code kan onderbreken. Bijgevolg is dit verboden in code, die in een productieomgeving wordt gebruikt. De mparser verwittigt als er breaks in je M routine voorkomen.

Macro m4_break vormt het enige juiste alternatief om alsnog een break instructie te inbedden. Deze macro dient evenwel de laatste instructie te zijn van een M lijn:

Zeg dus niet :

m4_break s a=1

Maar wel:

m4_break
s a=1

En dit is ook goed:

s a=1 m4_break

Deze macro lost twee vervelende problemen automatisch op :

  • De break instructie wordt enkel uitgevoerd als M variabele UDdebug=1. De uitvoering van je code wordt dus enkel onderbroken, als je dit uitdrukkelijk wil.

  • De mparser geeft geen foutmelding.

Een alternatieve manier vind je onder de pragma constructies.

2.9.2. Close

Is een instructie die devices controleert. Deze wordt nooit rechtstreeks gebruikt, maar via een van volgende macro's:

m4_openFile
m4_openXmlFile
m4_readFile
m4_writeFile
m4_createCSVFile

Opmerking : in Brocade gebruik je binnen de M omgeving nauwelijks device controle : meestal wordt gewoon op de principal device geschreven, en heeft de bovenliggende software de controle over de output device.

2.9.3. Do

  • Do ABC is toegelaten.

  • Do ABC+offset wordt nooit gebruikt.

  • Labels, die vanuit andere M code worden opgeroepen, herken je aan de beginletter %. Dus :

    Zeg dus niet:

    Do Label^routine
    

    Maar wel:

    Do %Label^routine
    
  • Het verdient aanbeveling, om externe calls steeds in te bedden in macro constructies. Is dit om bepaalde redenen niet mogelijk of ligt dit niet voor de hand, schrijf dan :

    m4_routine(%Label^routine)
    

    Hierdoor worden deze entries traceerbaar.

  • Argumentloze do constructies in combinatie met punt notaties zijn toegelaten, mits elk niveau expliciet eindigt met een quit instructie. Voor eenvormigheid laat je ook een spatie tussen de punt notatie en de M code. Voorbeeld :

    Zeg dus niet:

    d
    .f a=1,2 d
    ..s b=55
    

    Maar wel:

    d
    . f a=1,2 d
    .. s b=55
    .. q
    . q
    

2.9.4. Else

Dit commando heeft de - spijtige - betekenis van if '$t `` . Aangezien de systeemvariabele ``$t nogal wat side effecten te verwerken krijgt, wordt gebruik van else afgeraden. Uitzondering: plaats op het einde van je vorige lijn een i 1. Voorbeeld:

i a>1000 s comment="big" i 1
e  s comment="small"

2.9.5. For

Geen beperkingen ivm dit commando.

2.9.6. Goto

  • Go label is toegelaten. Het verdient aanbevling om dit enkel te gebruiken in uitzonderlijke omstandigheden.

  • Go label+offset is zonder meer verboden.

  • Go label^routine is enkel toegelaten, als de label begint met een % teken.

2.9.7. Hang en Halt

Halt is verboden, want kan de hele opbouw van server processen schaden. Op zeer uitzonderlijke plaatsen (error handling, job handling) kan dit evenwel voorkomen.

Hang + time-out is verboden boven 1 seconde, want het enige effect voor de gebruiker is een zandloper. Wij raden bovendien deze instructie af, tenzij men echt niet anders kan. Tot op heden zijn geen zulke gevallen bekend.

2.9.8. If

Geen beperkingen ivm dit commando.

2.9.9. Job

Gebruik je nooit rechtstreeks . Laat het hele opstarten van jobs doen door de m4 constructie:

m4_launchMJob

2.9.10. Kill

Killen van locale variabelen mag enkel, als deze 'genewed' zijn in dezelfde module .

Killen van niet permanente globals mag. Het is de verantwoordelijkheid van de omtwikkelaar om dit in goede banen te leiden.

Killen van permanente globals mag enkel in de daartoe bestemde macro constructies, en in m files van type g .

Killen van (delen van) global SDEAD mag je niet : dit gebeurt m.b.v. software, specifiek daarvoor geschreven.

2.9.11. Lock

Lock zonder argumenten is nooit toegelaten.

Lock ^global is nooit toegelaten.

Lock +^global is enkel toegelaten in m files van type g .

Lock +^global:time-out is enkel toegelaten in m files van type g

Lock -^global is enkel toegelaten in m files van type g

Uiteraard moeten alle m files ervoor zorgen dat, bij het verlaten van de M routine, alle gezette locks ook zijn unlocked.

De periodes waarbinnen locks actief zijn, dienen zo kort mogelijk te worden gehouden.

Er wordt verrassend weinig gelocked in de Brocade software : er liggen een aantal alternatieven voor je klaar :

  • Wil je een unieke teller maken, grijp dan terug naar (voorbeeld):

    s x=$i(^BMYGLO(counterid))
    

    Aangezien de $i functie een atomaire functie is, zal hier geen conflict ontstaan tussen verschillende processen.

  • Beschouw ook volgende uitgebreide macro, die o.a. ook een node in de databank kan initialiseren: (voorbeeld)

    m4_incSubscript(x, ^UKASPF(PDhub,"fj"), "")
    

2.9.12. Merge

Merge local1=local2 is toegelaten.

Merge local1=^global2 is enkel toegelaten in m files van type g of in macro definities .

Merge ^global1=^local2 is enkel toegelaten in m files van type g of in macro definities .

Zie ook de notities bij de set instructie.

2.9.13. New

New zonder argumenten is niet toegelaten.

New (var1,var2,....) is niet aanbevolen.

New var1,var2,.... is toegelaten, ja zelfs aanbevolen : zie ook brave routines.

2.9.14. Open

Is een instructie die devices controleert.

Opmerking : in Brocade gebruik je binnen de M omgeving nauwelijks device controle : meestal wordt gewoon op de principal device geschreven, en het is dan ook meestal niet aan de M routine om het openen te initialiseren.

Wat betreft het openen van files, waarin een M routine heeft geschreven, maak gebruik van :

  • m4_readFile

  • of m4_writeFile

  • of eventueel m4_openFile

  • of m4_openXmlFile

2.9.15. Print

Is geen standaard M commando. Niet toegelaten.

2.9.16. Quit

Het gebruik van het commando quit heeft volgende beperkingen:

  • Je mag een quit argument gebruiken met of zonder argumenten.

  • Binnen dezelfde M blok gebruik je ofwel steeds quit met argument, ofwel steeds zonder argument. Gemengde vormen zijn niet toegestaan.

  • Om het onderscheid te maken tussen een functie en een subroutine, maak je gebruik van de schrijfwijze van labels in m files .

2.9.17. Read

Is een instructie die devices controleert. In de praktijk situeren deze zich enkel in het project www, dat zich bezighoudt met de interactie tussen de M omgeving en de webserver.

Dezelfde opmerking als open en close geldt hier : om de inhoud van een file te lezen, gebruik steeds m4_readFile . Maak gebruik van macro definities, die hiervoor zijn uitgebouwd.

2.9.18. Set

Set commando's mogen in alle vormen worden gebruikt, met als enige beperking : alles wat met permanente globals te maken heeft, hoort thuis in m files van type g of macro definities.

Elke lokale variabele, die je gebruikt, moet geïnitialiseerd worden via een set of merge instructie. Uitzonderingen hierop :

;pragma set RDact,RDnew
s RDx=$$%Fn^routine(RDact) s:RDnew RDx=""

2.9.19. Tstart, Tcommit en Trollback ofte Transacties.

Tstart, Tcommit en andere transactionele commando's worden nooit rechtstreeks aangesproken.

Hoewel het op het eerste zicht contradictorisch lijkt, is het niet de m file van type g die mag bepalen wanneer de transactie begint en eindigt : immers, het is soms nodig dat verschillende combinaties van global updates in dezelfde transactie plaatsvinden.

Transactioneel programmeren doe je in Brocade met behulp van de m4_tpExec macro. Een voorbeeld :

m4_tpExec("d %File^bcawfile")

2.9.20. Use

  • Is een instructie die devices controleert. Toegelaten constructies:

  • Voor het lezen/schrijven van files , gebruik je m4_readFile en m4_writeFile.

  • Kan je dit niet waar maken, dan kan je gebruik maken van volgende opzet:

    m4_openFile(iofile,"/anet/test.txt","W")
    u iofile w !,"Hello World"
    m4_closeFile(iofile)
    q
    
  • Interactie met de web omgeving wordt uitsluitend in het daartoe bestemde project behandeld.

  • Andere device handling gebeurt buiten de M omgeving. De M routine is zich van geen andere devices bewust, en schrijft gewoon op de principal device. Deze output wordt dan door software (buiten M) naar de goede devices geleid.

2.9.21. View

Niet standaard M. Deze zijn in Brocade ingebed binnen macro constructies. Voorbeeld :

Zeg niet:

s x=$v("JNLACTIVE","") s err=$s(x=2:0,1:1)

Maar wel:

m4_getStatusMJournalling(err)

2.9.22. Write

Is een instructie die devices controleert.

Toepassingen, die webformulieren willen opbouwen, gebruiken zelf ook niet rechtsreeks de write instructie, maar maken gretig gebruik van de m4 constructies m4_showScreen, m4_initRow, m4_addParameter

Toepassingen, die andere dingen willen opbouwen, mogen commando w gebruiken, op voorwaarde dat hun output device onafhankelijk is.

Dezelfde opmerking als bij open en close geldt hier : om de inhoud van een file te schrijven, gebruik steeds:

m4_writeFile

of (voorbeeld)

m4_openFile(iofile,"#test.txt","W")
u iofile w "test",!

2.9.23. eXecute

Maak gebruik van execute, als het moeilijk zonder kan.

Execute heeft een vertragend effect, en bovendien is het niet mogelijk de syntax op voorhand te controleren. Anderzijds is dit een krachtig instrument, en wordt bijvoorbeeld gretig ingezet bij het parametriseren en configureren.

Maar het kan een achterpoort zijn voor Command injection. Stel je hebt in een x-file <input name="FDact" type="hidden" value="bcawedit">, zodat je in Mumps volgende code kan uitvoeren: x "d %Run^"_FDact. Echter een hacker kan de de waarde van FDact overschrijven. Bijvoorbeeld FDact="bcawedit k ^ABC en zodoende een hele databank om zeep helpen. Dus voer een eXecute enkel uit op strings die je volledig onder controle hebt en als het niet anders kan. Bovenstaande situatie kan voorkomen worden op volgende manieren:

  • door i.p.v. de routine in FDact te steken, deze in een Z global te steken op basis van bijvoorbeeld UDses,

  • eerst te testen op $t(@("%Run^"_FDact)),

  • de execute gewoon wegwerken m.b.v d @("%Run^"_FDact).

Het laatste geniet de voorkeur. Een ander voorbeeld van het wegwerken van eXecute, s statex="s stateok=$t(%Run^"_FDact_")" x statex is gevoelig aan Command Injection, maar s @("stateok=$t(%Run^"_FDact_")") niet.

2.9.24. Z* commando's

Alle instructies, beginnend met een Z, zijn per definitie niet-standaard M code, en horen bijgevolg thuis in geisoleerde software, die wordt opgeroepen via macro definities. Zodoende vraagt het aanpassen van de omgeving voor andere M implementaties een beperkte inspanning.

Voorbeeld :

Zeg niet:

zwrite

Maar wel:

m4_writeMLocals

2.10. M parser

Om de ontwikkelaar te helpen met het maken van goede code, die voldoet aan deze Style Guide, bestaat een uitgebreide parser, die meer doet dan alleen de Mumps syntax van je code controleren.

Meer nog, het is zelfs dankzij deze richtlijnen, dat deze parser diep op de software kan ingaan, en de ontwikkelaar kan helpen met goede code , waar pro-actief runtime errors en ongewenst gedrag in vele gevallen worden vermeden.

De mparser controleert niet de inhoud van de m file , maar wel de M routine, die daaruit wordt opgebouwd.

Hoe de M parser oproepen ?

Je kan dit op verschillende manieren:

  • Impliciet, telkens bij het inchecken van een m file .

  • Expliciet, van bestaande M routines, via de Unix prompt: (ook wildcards zijn toegelaten)

    mutil -rc 'myroutine'
    mutil -rc 'ust*'
    
  • Expliciet, via de Mumps prompt:

    m4_writeMParserr("myroutine")
    
Mag de M parser fouten rapporteren van mijn m file ?

Neen. Je m code dient te worden aangepast, totdat de parser geen foutberichten meldt.

Is mijn m file ingecheckt, indien de parser fouten rapporteert ?

Ja, de m file wordt steeds in een M routine omgezet, en deze wordt in de actieve namespace opgeladen. Dit om het mogelijk te maken om code te debuggen, of uit te testen.

Voldoet mijn m file aan de Stijl definities als de parser geen fouten oplevert ?

Neen, helaas. Er zijn stijl elementen, die men niet kan controleren. Het is bovendien de opdracht en verantwoordelijkheid van de ontwikkelaar om goede code te schrijven. De M parser vormt enkel een helpende hand.

Welke **pragma* constructies zijn er eigenlijk allemaal mogelijk, om de parser aan te sturen ?*

  • Om aan te duiden, dat een variabele reeds een waarde van buiten af heeft bekomen, of op een indirecte wijze wordt gezet:

    ;pragma set RDmyvar
    
  • Om aan te duiden, dat de scope van een variabele de scope van de label overstijgt:

    ;pragma fos RDmyvar
    
  • Om aan te duiden, dat een bepaalde module een of meerdere andere modules oproept:

    ;pragma calls LabelB
    

    Ook wildcards, eindigend op een '*' zijn toegelaten:

    ;pragma calls Label*
    
  • Om aan te duiden dat bepaalde code niet correct is:

    ;pragma bad
    ;pragma bad code is incompatible with previous version
    

    In dat geval wordt de boodschap (code is incompatible with previous version) getoond bij het parsen, of de standaard boodschap bad code .

  • Om aan te duiden dat bepaalde code verouderd is:

    ;pragma deprecated use routine abcnew instead
    

    In dat geval wordt de boodschap (use routine abcnew instead) getoond bij het parsen, of de standaard boodschap deprecated .

  • Om een breakpoint in je code aan te duiden:

    ;pragma break
    

    De parser zal op zich hiermee niets melden, doch de debugger (^debug) zal hier een breakpoint genereren bij het uitvoeren van de code in debug mode. Deze manier van werken is veiliger dan het expliciet plaatsen van een break instructie. Zie ook verder onder het break commando.

  • Om aan te duiden, dat men (desgewenst) geen gedetailleerd overzicht van de parser wenst.

    ;pragma skip
    

    Deze lijn hoeft slechts een maal in een m file te worden geplaatst, en telt voor de gehele inhoud. Hoewel de plaats in de m file niet relevant is, verdient het aanbeveling om dit in een van de eerste lijnen te plaatsen. Een eventueel (te) lange lijst van foutboodschappen wordt dan vervangen door een enkele lijn (-I- code contains errors, details skipped) . Ze is enkel bedoeld voor oudere m files.

Kan ik **pragma* constructies ook gebruiken in een Macro definitie ?*

  • Het is ongepast om macro's te voorzien van kommentaarlijnen. Daarom zijn volgende constructies ook mogelijk :

    • s MDcall="Label*"
      

      heeft hetzelfde effect als

      ;pragma calls Label*
      
    • s MDpragma="fos RDmyvar"
      

      of

      s MDcom="pragma fos RDmyvar"
      

      heeft hetzelfde effect als

      ;pragma fos RDmyvar*
      
  • Om aan te duiden of een bepaalde macro deprecated is :

    s MDpragma="deprecated",..