Article
· 2 hr ago 2m read

Temp Files and Singletons: Cleaning Up After Yourself

There's a pattern I've encountered several times where I need to use a temp file/folder and have it cleaned up at some point later.

The natural thing to do here is to follow the patterns from "Robust Error Handling and Cleanup in ObjectScript" with a try/catch/pseudo-finally or a registered object to manage cleanup in the destructor. %Stream.File* also has a "RemoveOnClose" property that you can set - but use with care, as you could accidentally remove an important file, and this flag gets reset by calls to %Save() so you'll need to set it back to 1 after doing that.

There's one tricky case, though - suppose you need the temp file to survive in an enclosing stack level. e.g.:

ClassMethod MethodA()
{
    Do ..MethodB(.filename)
    // Do something else with the filename
}

ClassMethod MethodB(Output filename)
{
    // Create a temp file and set filename to the file's name
    Set filename = ##class(%Library.File).TempFilename()
    
    //... and probably do some other stuff
}

You could always pass around %Stream.File* objects with RemoveOnClose set to 1, but we're really just talking about temp files here.

This is where the concept of a "Singleton" comes in. We have a base implementation of this in IPM in %IPM.General.Singleton which you can extend to meet different use cases. The general behavior and use pattern is:

  • In a higher stack level, call %Get() on that class and get the one instance that will also be obtainable by calls to %Get() at lower stack levels.
  • When the object goes out of scope in highest stack level that uses it, the cleanup code runs.

This is a bit better than a % variable because you don't need to go checking if it's defined, and it also survives argumentless NEW at lower stack levels through some deep object trickery.

On to temp files, IPM also has a temp file manager singleton. Applying to this problem, the solution is:

ClassMethod MethodA()
{
    Set tempFileManager = ##class(%IPM.Utils.TempFileManager).%Get()
    Do ..MethodB(.filename)
    // Do something else with the filename
    
    // The temp file is cleaned up automatically when tempFileManager goes out of scope
}

ClassMethod MethodB(Output filename)
{
    Set tempFileManager = ##class(%IPM.Utils.TempFileManager).%Get()
    // Create a temp file and set filename to the file's name
    Set filename = tempFileManager.GetTempFileName(".md")
    
    //... and probably do some other stuff
}
Discussion (0)2
Log in or sign up to continue