This sample class will add parameter DSINTERVAL to each class containing the parameter DSTIME
To execute it from a Terminal :
SAMPLES>d ##class(adm.param).add(,,1)
[SAMPLES] parameter DSINTERVAL=5 added to class DeepSee.Study.CityRainfall (via adm.param)
[SAMPLES] parameter DSINTERVAL=5 added to class HoleFoods.Transaction (via adm.param)
[SAMPLES] parameter DSINTERVAL=5 added to class News.DeepSee.NewsArticle (via adm.param)
DSINTERVAL parameter was added to 3 classes
Class adm.param [ Abstract ]
{
/// this class will add DSINTERVAL parameter to each class containing DSTIME
/// in order to synchronize a cube that is based on a source table on a mirror system
/// see: http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=D2IMP_ch_current#D2IMP_current_dstime_enabling
ClassMethod add(name As %String = "DSINTERVAL", value As %String = "5", verbose As %Boolean = 0) As %Status
{
set sc = $$$OK
try {
set statement = ##class(%SQL.Statement).%New()
// %Dictionary classes are useful to select classes based on their content
// here we use %Dictionary.ParameterDefinition class which contains all the parameters defined in all classes
// then we will open the parent class to add the missing parameter
set query="SELECT a.parent "
_"FROM %Dictionary.ParameterDefinition a "
_"WHERE a.name='DSTIME' "
_"AND substr(a.parent,1,1)<>'%' "
_"AND NOT EXISTS (SELECT null "
_"FROM %Dictionary.ParameterDefinition b "
_"WHERE b.name='"_name_"' and b.parent=a.parent)"
set sc = statement.%Prepare(query)
quit:$$$ISERR(sc)
set classes="",nb=0
set rs = statement.%Execute()
while (rs.%Next(.sc)) {
quit:$$$ISERR(sc)
if (rs.parent '="") {
// opening of the parent class
set class = ##class(%Dictionary.ClassDefinition).%OpenId(rs.parent)
if $IsObject(class) {
// creating the new parameter
set param=##class(%Dictionary.ParameterDefinition).%New()
set param.Name=name
set param.Default=value
// adding the parameter to the parent class
do class.Parameters.Insert(param)
set sc=class.%Save()
// if parent class is successfully saved set the following message
set:sc msg="["_$namespace_"] parameter "_name_"="_value_" added to class "_rs.parent_" (via "_..%ClassName(1)_")",severity=0
// if parent class is not saved set the following message
set:'sc msg="["_$namespace_"] ERROR while saving "_rs.parent_" after having added the parameter "_name_"="_value_" (via "_..%ClassName(1)_") :"_$system.Status.GetErrorText(sc),severity=1
// if verbose=1 write the message to the terminal
write:verbose msg,!
// write the message to cconsole.log
do ##class(%SYS.System).WriteToConsoleLog(msg,verbose,severity)
// add the class name to compile
set classes(rs.parent)="",nb=$increment(nb)
}
}
}
set:nb=0 msg=name_" parameter was not added to any class"
set:nb=1 msg=name_" parameter was added to "_nb_" class"
set:nb>1 msg=name_" parameter was added to "_nb_" classes"
write:verbose msg,!
// compile all modified classes
if nb>0 {
set sc=$system.OBJ.Compile(.classes,"ck"_$select(verbose:"d",1:"-d"))
set:sc msg="["_$namespace_"] modified "_$select(nb=1:"class",1:"classes")_" successfully compiled",severity=0
set:'sc msg="["_$namespace_"] ERROR while compiling classes : "_$system.Status.GetErrorText(sc),severity=1
do ##class(%SYS.System).WriteToConsoleLog(msg,verbose,severity)
write:verbose msg,!
}
}
catch(ex) {
set sc = ex.AsStatus()
set msg="["_$namespace_"] error while executing "_..%ClassName(1)_":add("_name_","_value_") : "_ex.DisplayString(),severity=1
write:verbose msg,!
do ##class(%SYS.System).WriteToConsoleLog(msg,1,severity)
}
return sc
}
}
You can also check the results in the cconsole.log file:
10/05/16-19:39:11:250 (5744) 0 [SAMPLES] parameter DSINTERVAL=5 added to class DeepSee.Study.CityRainfall (via adm.param)
10/05/16-19:39:11:250 (5744) 0 [SAMPLES] parameter DSINTERVAL=5 added to class HoleFoods.Transaction (via adm.param)
10/05/16-19:39:11:250 (5744) 0 [SAMPLES] parameter DSINTERVAL=5 added to class News.DeepSee.NewsArticle (via adm.param)
10/05/16-19:39:11:515 (5744) 0 [SAMPLES] modified classes successfully compiled
I think compile should be called once after the loop. While in the loop you can build a local array of affected classes and pass it to the compiler after the loop.
Done. Thanks for this comment Eduard !
Could you add a little bit of documentation to the code? While I think it's a useful example, documenting the code will make it easier to understand. Not everyone is knows about the %Dictionary classes. And it's generally a good idea to add explaining comments. Thanks!
I've added some comments in the code ; thank you Fabian.
Hi, Sylvian! Thanks for the sippet! What is the reason to add DSINTERVAL to all the classes?
Hi Evgeny,
this code was written while upgrading to an async mirrored a DeepSee remote instance (originally based on a shadow server configuration + ECP access to ^OBJ.DSTIME global from DeepSee instance to production. It was before DSINTERVAL was created).
Of course this sample can be modified to add/remove/modify any other parameter by modifying the query on %Dictionary.ParameterDefinition to filter any other parameter you are trying to add/remove/modify.