I think you hit the utility of the documented approach. To not accidentally delete the databases.

If you forgot to delete databases as part of namespace removal, they can be manually tidied up (System Administration->Configuration->System Configuration->Databases).

Questions I would also be asking when directed by Customer from a Vanilla "ENSEMBLE" name, to a purposeful named Namespace:

  • Are IRIS.DAT(s) located in the correct disk volume / Drive name. ie: Sufficient capacity for expected use. (Is it supposed to be on the same volume / location as where services are installed).
  • Is there a backup strategy. AND did I retest it yet?
  • Are the databases journal enabled.
  • Check Users don't still have "ENSEMBLE" as default namespace location
  • Check Scheduled Tasks are not expecting "ENSEMBLE" namespace
  • Update documented standard build script

One more thought. If you are using Unix / Linux for hosting IRIS, a more recent option is mergeCPF, to update both Namespace, Database and Application settings. Suitable for:

  • migration activity as a testable automated script
  • configuration and setup from vanilla docker images.

https://docs.intersystems.com/iris20231/csp/docbook/Doc.View.cls?KEY=ACMF

This allows applying updates with a simple text file AND it does both traditional CPF settings AND the [csp web] Application settings in one go.

Your question encouraged me to share a little web calculator for CreateApplication settings.

So less time needed to figure out Application setting names and values.

https://openexchange.intersystems.com/package/MergeCPF-Application-Settings

Am going to make some assumptions that you have been given an IRIS.DAT file and you want to mount that Database file on a new instance for some evaluation activity.

This is likely to not be exactly the situation but can serve as an example to help understand what you need to achieve.

A Namespace is a CODE and DATA context that database processes normally operate in.

To use the Database file it is "mounted" as a Database configuration.

The Database configuration is used by a Namespace, either as the DEFAULT for code and routines OR the data is MAPPED for use by a namespace.

As an example I create a copy of an empty database at: C:\InterSystems\somewhere\db\empty2
  This directory contains the IRIS.DAT.
In System Management Portal I can "Create a Database" (System Administration->Configuration->System Configuration->LocalDatabases)
  Use the path of new IRIS DAT file: "C:\InterSystems\somewhere\db\empty2"
  There will be a warning:  "Database file, IRIS.DAT, already exists in directory."

If you then navigate with System Explorer -> Globals

  And use "Lookin : Database" for the example is "EMPTY2"
  Here I added the Global Test

If the IRIS.DAT has both code (eg: SQL Table definitions) and Data, possibly you might want to create a new namespace and make the mounted database from earlier as the default database for routine and code for that namespace.
  So "Namespaces -> Create New Namespace

Alternatively, you may want to apply the new Data with code in another existing database for example SQL Table definitions. Here you could decide what data you want to map from the new database file to an existing namespace. (See: Explorer for Globals above)
  Navigate to Namespaces (System Administration->Configuration->System Configuration->Namespaces)
  Select "Global Mappings" of the namespace you want to "access data from the new Dat file".

Example to map the whole of global "Test" to existing namespace. Note this is will "hide" any data in "Test" that might exist in the default database of the pre-existing namespace.

Hint: Be sure to have database files unmounted or instance shut down when first learning to copy / swap database files around.

There are advanced ways of doing things. Occasionally you might get an IRIS database file from an operating system with a different Endian composition. The tool cvendian can help translate this to a target operating system endian mode. When moving big DAT files about it can be useful to run an SHA-1 or MD5 checksum hash on the database file before and after copying to ensure there is no corruption.

Hope this gave some ideas and helps progress the direction needed for your specific situation.

I am utilizing properties on class methods to good effect.

I would not use a classmethod only approach for normal development.

There is nothing stopping a community parallel UnitTest.Manager re-implementation that follows a ClassMethod pattern.

Some have reimplemented UnitTest.Manager:

1) Without Deleteing the test classes at end of run (With Run instead of DebugRun)

2) Not needed ^UnitTestRoot to be defined.

If for Interoperability productions it may also be appropriate to weigh up existing capabilities available for Production deployment. See: https://docs.intersystems.com/irisforhealth20232/csp/docbook/DocBook.UI....

It provides functionality for detecting implementation used by a production.

Have created idea for Production exports to generate IPM modules as well as the usual XML export deployment. https://ideas.intersystems.com/ideas/DPI-I-382

An example idea for Grand Prix entry:
Demonstration of a "Language Interface" (chat interface) to replace the functionality of a traditional online shop ( HTML UI )

  •   Use IRIS to hold a product catalogue (Images, Description, Cost, Stock)
  •   Use IRIS to present the "Language Interface". Can support via Embedded Python
  •   User Experience:
    •   Describe what you want instead of search
    •   Filter products based on preferences identified in conversation
    •   Automatically locate products via catalogue meta data from conversation content
  •   CSP or other web Session for current Shopping basket (Item, Quantity, Total)
  •   Persist and reuse preferences identified from previous shopping chats

Cloning a particular task based on persistent class implementing %ConstructClone.

Can be good to have distinct names

ZN "%SYS"
set taskToCopyId=1000
set source=##class(%SYSTEM.Task).%OpenId(taskToCopyId)
for ns="FTP","WSDL","X12" {set copy=source.%ConstructClone()  set copy.JobGUID=""  set copy.Name=source.Name_" "_ns  set copy.NameSpace=ns  write !,ns," result ",copy.%Save()}

Can see other properties might want to reset via OBJ.Dump

set taskToCopyId=1000
set source=##class(%SYSTEM.Task).%OpenId(taskToCopyId)
do $SYSTEM.OBJ.Dump(source)

Thanks for sharing answer.

Adding a snippet for %ZSTART in case fellow community person wished find / refer to later.

 #include %occStatus
  quit 0
SYSTEM() PUBLIC {
    Try {
    // Some test to determine whether to start or not
        if ##class(%File).Exists("c:\tmp\stop.txt") {
          Do INT^SHUTDOWN
        }  
    Catch { }
quit 0
}

A thought about moving from *.xml source controlled files to *.CLS (UDL) file content with VSCode.

  • The exported source content is different from existing in source control
  • The export name is different and may be seen as an unrelated resource

A while back I had shared community application ompare

I mention as this utility allows the comparison of what is installed in a namespace, regardless if the external format was XML or UDL. Some may find useful to see the real differences between their own historic product releases and a current release. At least until have a few releases / baseline in their new source repository.

Have notice other community apps like xml-to-udl that might be appropriate for need.

Hi Stella,

Thinking could this use the "List Of" syntax to achieve a collection for XML projection.

Service:

Class TEST.SOAPCOL.MyService Extends EnsLib.SOAP.Service
{ 
/// Name of the WebService.
Parameter SERVICENAME = "MyService"; /// TODO: change this to actual SOAP namespace.
/// SOAP Namespace for the WebService
Parameter NAMESPACE = "http://tempuri.org"; Property Target As Ens.DataType.ConfigName; Parameter SETTINGS = "Target:Basic";
Method SendToTarget(pRqst As TEST.SOAPCOL.Request) As TEST.SOAPCOL.Response [ SoapAction = "http://tempuri.org/Sample.MyService.SendToTarget", WebMethod ]
{
    set tResponse=##class(TEST.SOAPCOL.Response).%New()
    Set tSC= ..SendRequestAsync(..Target, pRqst)
    return tResponse
}
}

Request (Note "List Of" )

Class TEST.SOAPCOL.Request Extends Ens.Request
{
  Property MyContainer As list Of TEST.SOAPCOL.ListItem;
} 

Inner Items

Class TEST.SOAPCOL.ListItem Extends (%Persistent, %XML.Adaptor)
{
Property PropertyA As %String;
Property PropertyB As %String; 
}

Response:

Class TEST.SOAPCOL.Response Extends Ens.Response
{
}  

Projection seen in Message Trace:

<?xml version="1.0" ?>
<!-- type: TEST.SOAPCOL.Request  id: 1 -->
<Request>
  <MyContainer>
    <ListItem>
      <PropertyA>AAA
      </PropertyA>
      <PropertyB>BBB
      </PropertyB>
    </ListItem>
  </MyContainer>
</Request>

Trying out client code in terminal:

set o=##class(MyService.MyServiceSoap).%New()
set o.Location="http://localhost:1980/TEST.SOAPCOL.MyService.CLS"
set pRqst=##class(MyService.Request).%New()
set item=##class(MyService.ListItem).%New()
set item.PropertyA="AAA"
set item.PropertyB="BBB"
do pRqst.MyCOntainer.Insert(item)
set response=o.SendToTarget(pRqst)

A handy way to call UnitTest Case method in a terminal

Do ##class(%UnitTest.Manager).DebugRunTestCase("", "[ClassName]", "", "[MethodName]")

Run all Test methods for a TestCase:

Do ##class(%UnitTest.Manager).DebugRunTestCase("", "[ClassName]", "", "")

Placing a "break" line within a method can be useful when iterating creating the test. See the variables. Run other code and then type "g"+ [Enter] to continue.

The instance gives context to current test run, when raising assertions and other functionality.

Hi Christine,

Please consider if the following snippet may be helpful.

Includes some common checks to prevent invalid lookup

Class Test.Fun Extends Ens.Rule.FunctionSet
{

ClassMethod ExistsAny(pTable As %String = "", pValue As %String = "", pDelimiter As %String = "<>") As %Boolean [ Final ]
{
  Quit:""=pTable 0
  Quit:""=pValue 0
  Quit:""=pDelimiter 0
  Set result=0
  // Use Length Function to count delimited fields. Always one or more
  Set len=$Length(pValue,pDelimiter)
  For i=1:1:len {
    Set key=$Piece(pValue,pDelimiter,i)
    // strip white space at front and end
    Set key=$ZSTRIP(key,"<>W")
    // Avoid empty string for key lookup
    Continue:""=key
    // make uppercase for case insensitive search
    Set key=$ZCVT(key,"U")
    If $Data(^Ens.LookupTable(pTable,key)) {
      Set result=1
      Quit
    }
  }
  Return result
}
} 

I approached in similar way.

Difference was to use the built in "$Piece" function on original string, instead of using intermediary lists.

Also using the dynamic property names.

   ClassMethod AsJSON(value = "")
{
set dyn=[]
set len=$L(value,"|")
if len>1 {
for i=1:2:len {
set q=$P(value,"|",i)
set a=$P(value,"|",i+1)
set item={}
set item.number="number "_(i\2+1)
set item.squad=q
set item.answer=[]
set answer1={}
set answer1.initialLetter=a
do item.answer.%Push(answer1)
do dyn.%Push(item)
}
}
quit dyn.%ToJSON()
}