Article
· Mars 7 3m de lecture

Forcer une variable à s'auto-observer

Je me suis lancé un défi : trouver un moyen de faire en sorte qu'une variable se surveille elle-même pour une certaine valeur et fasse quelque chose lorsqu'elle atteint cette valeur sans avoir à la vérifier à chaque fois que quelque chose la touche. En gros, un moyen de dire "à un moment donné pendant l'exécution de ce code, si x = 0 (ou quelle que soit la condition) faire ceci". La classe avec laquelle j'ai fini par surveiller un %Status :

Class User.WatchedStatus Extends %RegisteredObject
{
Property sc As %Status [ InitialExpression = 1, SqlComputeCode = {set {*} = ##class(User.WatchedStatus).Reset({sc},{resetSelf})}, SqlComputed, SqlComputeOnChange = isErr ];
Property isErr As %Boolean [ InitialExpression = 0, SqlComputeCode = {set {*} = ##class(User.WatchedStatus).ErrCheck({sc},{writeSelf},{logSelf},{throwSelf})}, SqlComputed, SqlComputeOnChange = sc ];
Property throwSelf As %Boolean [ InitialExpression = 0 ];
Property logSelf As %Boolean [ InitialExpression = 0 ];
Property writeSelf As %Boolean [ InitialExpression = 0 ];
Property resetSelf As %Boolean [ InitialExpression = 0 ];
/// Handles status according to flags set, then sets isErr.
ClassMethod ErrCheck(sc, writeSelf, logSelf, throwSelf) As %Boolean [ Internal ]
{
	if $$$ISERR(sc){
		if writeSelf{
			write $SYSTEM.Status.GetErrorText(sc)
		}
		if logSelf = 1{
			do ##class(%Exception.StatusException).CreateFromStatus(sc).Log()
		}
		if throwSelf = 1{
			$$$ThrowStatus(sc)
		}
		quit 1
	}
	else{
		quit 0
	}
}

/// If resetSelf is true, resets the status code after error handling occurs.
ClassMethod Reset(sc, resetSelf) As %Status [ Internal ]
{
	return:resetSelf $$$OK
	return sc
}

/// flags is a string which determines status behavior when an error occurs
/// T = throw the status
/// L = log the status as an exception
/// W = write the status error text
/// R = reset status after error handling; if set, isErr goes back to 0 and sc goes back to 1
ClassMethod New(flags As %String) As User.WatchedStatus
{
	set status = ##class(User.WatchedStatus).%New()
	set flags = $ZCVT(flags,"U")
	set:(flags [ "T") status.throwSelf = 1
	set:(flags [ "L") status.logSelf = 1
	set:(flags [ "W") status.writeSelf = 1
	set:(flags [ "R") status.resetSelf = 1
	return status
}

}

Si je crée une nouvelle instance de cette classe en utilisant set status = ##class(User.WatchedStatus).New("wr"), alors lorsque j'appelle des méthodes qui renvoient un %Status, je peux utiliser set status.sc = (quelle que soit la méthode ici) et si elle renvoie un statut d'erreur, elle écrira ce statut sans que j'aie à vérifier s'il s'agit d'une erreur et lui dire de le faire à chaque fois, puis se réinitialisera pour sa prochaine utilisation. J'ai également un indicateur t ou l pour savoir si je veux que le statut se déclenche ou s'enregistre comme une exception dans les journaux système. (Notez que si je n'incluais pas l'indicateur r, il ne se réinitialiserait pas, de sorte que vous pourriez toujours vérifier le code de statut par la suite.)

Cela utilise SqlComputeCode d'une manière qui n'est probablement pas censée être utilisée, et vous devez être prudent en l'écrivant de cette façon. Étant donné que isErr se calcule lui-même lors d'un changement de sc et que sc se calcule lui-même lors d'un changement d'isErr, vous pourriez vous retrouver dans une boucle. Cela n'arrivera pas ici, car l'ensemble du système finira par se stabiliser à nouveau.

Je ne sais pas exactement à quel point tout le monde tirera parti de cet exemple de %Status, mais je documente ici comment je l'ai fait au cas où quelqu'un d'autre essaierait de comprendre cela pour un autre type d'objet.

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer