Using Caché Objects
Package Options
[Back] [Next]
   
Server:docs2
Instance:LATEST
User:UnknownUser
 
-
Go to:
Search:    

This chapter discusses packages in more detail. Topics include:

When viewing this book online, use the preface of this book to quickly find other topics.
For persistent classes, the package is represented in SQL as an SQL schema. For details, see Projection of Packages to Schemas,” later in this book.
Important:
When Caché encounters a reference to a class that does not include a package name and where the class name starts with “%”, Caché assumes the class is in the “%Library” package.
Overview of Packages
Caché supports packages, which group related classes within a specific database. Packages provide the following benefits:
A package is simply a way to group related classes under a common name. For example, an application could have an “Accounting” system and an “Inventory” system. The classes that make up these applications could be organized into an “Accounting” package and an “Inventory” package:
Any of these classes can be referred to using their full name (which consists of package and class name):
 Do ##class(Accounting.Invoice).Method()
 Do ##class(Inventory.Item).Method()
If the package name can be determined from context (see below), then the package name can be omitted:
 Do ##class(Invoice).Method()
As with classes, a package definition exists within a Caché database. For information on mapping a package from a database to a namespace, see the section Package Mapping.”
Package Names
A package name is a string. It may contain “.” (period) characters, but no other punctuation. Each period-delimited piece of the package name is a subpackage, and there can be multiple subpackages. If you give a class the name Test.Subtest.TestClass, then this indicates that the name of the package is Test, the name of the subpackage is Subtest, and the name of the class is TestClass.
There are several limitations on the length and usage of package names:
For general information on identifiers, see the section Naming Conventions in the chapter Defining and Compiling Classes.”
Defining Packages
Packages are implied by the name of the classes. When you create a class, the package is automatically defined. Similarly, when the last class in a package is deleted, the package is also automatically deleted.
The following shows an example in which the package name is Accounting, the class name is Invoice, and the fully qualified class name is Accounting.Invoice:
Class Accounting.Invoice 
{
}
Package Mapping
By definition, each package is part of a particular database. Frequently, each database is associated with a namespace, where the database and the namespace share a common name. This is the case for various system-supplied databases and namespaces, such as SAMPLES and USER. To make a package definition in a database available to a namespace not associated with that database, use package mapping.
The following procedure refers to the database containing the package as the “source database” and the namespace into which the package is being mapped as the “viewing namespace.” To map a package, the procedure is:
  1. From the Management Portal home page, go to the Namespaces page ([Home] > [Configuration] > [Namespaces]).
  2. On the Namespaces page, select the viewing namespace by clicking Package Mappings on that corresponding row in the table. This displays the Package Mappings page for the viewing namespace.
  3. On the Package Mappings page, click New Package Mapping. This displays a dialog for setting up the mapping.
  4. In this dialog, complete the fields as follows:
    Click OK to use these values and dismiss the dialog.
  5. The Package Mappings page should now display the mapping. Click Save Changes to save the mapping.
Mapping a package across namespace maps the package definition, not its data. Hence, mapping the Sample package from the SAMPLES namespace to the USER namespace does not make the instances of the Sample.Person from the SAMPLES namespace available in the USER namespace.
Important:
When you map a package, be sure to identify any include files used by the classes in that package. If the names of these include files do not start with %, they are not available in all namespaces, and you must map them as well (via the Routine Mappings page) so that the classes can find them. Similarly, make sure that all routines and classes used by the mapped classes are available in the target namespace.
When you map a package, the mapping applies to the class definitions in that package and to the generated routines, which are in the same package.
Mapping a Package Across Multiple Namespaces
Caché also provides functionality to make a source package available in multiple target namespaces through a single action. Such a mapping makes the package available in all namespaces except DOCBOOK and SAMPLES.
To make a package available to multiple namespaces, the procedure is:
  1. Create a namespace called %ALL according the instructions in the Create a Namespace section of the “Configuring Caché” chapter of the Caché System Administration Guide.
  2. Create a package mapping as described in this section and save it.
The classes in the mapped package are then visible and usable in the %SYS namespace, the USER namespace, and any user-defined namespaces.
Note:
Deleting the %ALL namespace removes its mappings.
Package Use When Referring to Classes
There are two ways to refer to classes:
Importing Packages
When you import packages, Caché looks for any short class names in those packages. In a class definition, you can import a package via the class Import directive or the Caché ObjectScript #IMPORT directive. This section explains these directives, discusses the effect on the User package and the effect on subclasses, and presents some tips.
Class Import Directive
You can include the class Import directive at the top of a class definition, before the Class line. The syntax for this directive is as follows:
Import packages

Class name {}
Where packages is either a single package or a comma-separated list of packages, enclosed in parentheses. The word Import is not case-sensitive, but is usually capitalized as shown here.
Remember that in a class context, the current package is always implicitly imported.
Caché ObjectScript #IMPORT Directive
In Caché ObjectScript method, an #IMPORT directive imports a package so that you can use short class names to refer to classes in it. The syntax for this directive is as follows:
#import packagename
Where packagename is the name of the package. The word #import is not case-sensitive. For example:
#import Lab
 // Next line will use %New method of Lab.Patient, if that exists
 Set patient = ##class(Patient).%New()
You can have multiple #IMPORT directives:
#import Lab
#import Accounting

 // Look for "Patient" within Lab & Accounting packages.
 Set pat = ##class(Patient).%New()

 // Look for "Invoice" within Lab & Accounting packages.
 Set inv = ##class(Invoice).%New()
The order of #IMPORT directives has no effect on how the compiler resolves short class names.
Explicit Package Import Affects Access to User Package
Once your code imports any packages explicitly, the User package is not automatically imported. If you need that package, you must import it explicitly as well. For example:
#import MyPackage
#import User
The reason for this logic is because there are cases where you may not want the User package to be imported.
Package Import and Inheritance
A class inherits any explicitly imported packages from the superclasses.
The name of a class is resolved in the context where it was first used and not with the current class name. For example, suppose you define in User.MyClass a method MyMethod() and then you create a MyPackage.MyClass class that inherits from User.MyClass and compile this. Caché compiles the inherited MyMethod() method in MyPackage.MyClass — but resolves any class names in this method in the context of User.MyClass (because this is where this method was defined).
Tips for Importing Packages
By importing packages, you can make more adaptable code. For example, you can create code such as:
#import Customer1
 Do ##class(Application).Run()
Now change App.MAC to:
#import Customer2
 Do ##class(Application).Run()
When you recompile App.MAC, you will be using the Customer2.Application class. Such code requires planning: you have to consider code compatibility as well as the effects on your storage structures.