Verifying Imported Objects before Merging

To gain more control when importing objects, you can import objects first into a “staging area” to verify the incoming objects before bringing them into the application proper. ZOM provides a staging area called the “Shadow Object Dictionary” which is a mirror image of the Zim Object Dictionary. Once in the Shadow Object Dictionary, you can request a report comparing the imported objects with the existing application objects. This enables you to review the incoming objects before actually merging them with the application. Use the following commands:

ZOMImport ;t SH

ZOMDiff ;d DiffRpt

Using Locks for Change Control

Combining ZOMImport and ZOMExport with keywording and object locking can give you a great deal of control over the management of the application as a whole. Let’s assume that the master is to receive Steve’s work, but needs to protect the application from any changes that Steve has made outside his defined task of user interface design. In the master environment, the following example unlocks all objects, then locks all objects except forms, displays and windows before importing objects from Steve.

ZOMSet * ;p l!

ZOMSet * -t Form,Disp,Win ;p l

ZOMImport

Usually a developer is not confined to specific types of objects, but rather to subsets of the application. If Steve were given the job to maintain the Inventory system, for example, we could lock everything except the Inventory system prior to import:

ZOMSet * ;p l!

ZOMSet * -k Inventory_System ;p l

ZOMImport

If you have the potential for conflicting updates from different developers, you should take steps to control or prevent conflict. One of the most straightforward methods is to keyword the objects when exporting from a development environment such that the keyword indicates the environment they were exported from, as shown in the following example:

ZOMExport +k $new,$changed ;k Steve_Env

This keyword is retained with the exported object descriptions, so when the objects are imported into the master, they retain this keyword.

As each developer’s work is imported, all previously imported work is locked as well by executing a lock against their keyword. Thus, when importing Carol’s work, execute

ZOMSet * ;p l!

ZOMSet * -t Ent,Rel,Role +k Steve_Env ;p l

These commands start by unlocking all objects. Then a lock is placed on all objects except for EntitySets, relationships, and roles but including objects keyworded with “Steve_Env”.

Master-Slave Development Approach

A typical configuration for projects involving multiple developers is separate development environments for each developer and a master environment that contains the combined results of the project team. This development approach is referred to as master-slave development. Each developer’s environment is called a “slave” to the “master” environment. From time to time, a developer merges his or her changes into the master environment and then resumes development, usually continuing with a copy of the updated master environment.

The master/slave approach has several benefits:

  • Developers can work at their own pace since each development environment is independent of every other slave development environment.

  • Catastrophic mistakes can be isolated to each developer’s environment and not affect other developments or the master.

  • Developers need not work on the same computer or even the same computer system or network. For example, development can take place on several PC’s without requiring a network. Or developers can work in a heterogeneous computing environment, with members of the development team working on a mixture of operating systems (e.g., UNIX, Windows, and so on.).

  • Object and data model modifications can be centrally controlled by controlling access to the master environment.

  • Version control can be centralized on the master environment.

As a simple example, assume there are two developers, Steve and Carol, developing parts of the same application. Steve is working on user interface design, while Carol is working on the data model. Each has a “slave” copy of the application, and there is a third environment representing the “master” version of the application. As discussed above, it is essential that each environment have its own unique environment code.

Sample Master/Slave Environment

Steve and Carol each work on the application, making various additions, deletions and changes. Steve changes two forms, fEmpl and fCorp, and creates a new one, fProj. Carol has revised the Employees EntitySet, and added a new relationship, WorkAt. They have both decided to merge their developments into the master environment.

Merging Changes

Merging changes from one development environment into another is done by exporting the changes from the first (i.e., source) environment using ZOMExport and then importing these changed objects into the second (i.e., target) environment using ZOMImport. The import process compares the incoming objects from the source environment with the objects in the target environment. New objects are created in the target environment. Changed objects are reflected in the target environment, taking into account any existing objects and dependencies with other objects.

Returning to the master-slave development example, assume that Steve is to merge his changes into the master environment first. Also, assume Steve has ZOM configured with the default new keyword, “$new”, and the default changed keyword, “$changed”. These two keywords then identify all new and changed objects in his development environment. These are the objects to be merged into the master environment.

Note: There are several variations of how ZOMExport and ZOMImport can work together. This is the most basic.

Note: ZOMExport and ZOMImport always save/load definitions to/from a directory called port beneath the DB directory.

To merge, Steve performs the following steps:

  1. Extract the new and changed objects from the slave environment using the ZOMExport command and the new keyword and changed keyword:

ZOMExport + k $new,$changed

The “+k $new” selects all new objects, while the “+k $changed” selects all changed objects. These objects are exported in the normal manner by ZOMExport. Thus, in Steve’s case, the two forms he changed, fEmpl and fCorp, and the new form he created, fProj, are exported.

Now, Steve should remove the new and changed keywords. In this way, the next time, Steve exports only objects created or changed since the last time he exported.

ZOMSet +k $new,$changed ;k$new! ;k$changed!

  1. Move the files containing the exported objects to the master environment.

ZOMExport creates a series of files whose names are suffixed with “.z50”. To move the objects from Steve’s environment to the master environment, first delete any files with the “.z50” suffix from the master environment, then copy the “.z50” files from Steve’s directory to the master environment.

For example, if Steve is working with Windows, Novell, or MS-Net and his development environment is stored in the directory C:ZimAPPSTEVE and the master environment is store in the directory C:ZimAPPMASTER, then Steve would execute the following commands:

del c:zimappmasterport*.z50

copy c:zimappsteveport*.z50 c:zimappmasterport

Under similar circumstances in a UNIX environment, Steve would execute:

rm /zimapp/master/port/*.z50

cp /zimapp/steve/port/*.z50 /zimapp/master

  1. Import the objects from the slave environment into the master environment by invoke the ZOMImport command in the master environment:

ZOMImport

ZOMImport imports the objects from the files produced by ZOMExport. These objects are loaded into the Object Dictionary and created.

The process can be repeated for Carol.

If Steve and Carol have been working on different parts of the system, their changes merge into the master environment without conflict. If, on the other hand, there are conflicting changes, then some of Carol’s updates can overwrite Steve’s. If there is the potential for conflict, there are a number of ways to control or prevent trouble using ZOM. The most straightforward methods involve use of keywords and locks.

Note: The master-slave development scenario includes the situation where the master environment represents the current “production” application and the slave environments represent ongoing developments. This scenario also applies to single person projects as well.

Configuring ZOM for Team Development

When working on a team development project, you should first adjust the configuration of ZOM for each development environment so that objects from the various environments can be easily identified. The three configuration options of importance here are the environment code, the new keyword, and the changed keyword. You configure ZOM using the ZOMConfig command.

Each development environment should be assigned a unique ZOM environment code. This guarantees all objects are registered with object keys (i.e., ObjectKey property) that are unique across all development environments. In other words, no two objects are ever assigned the same object key even though they might have been created in different development environments.

For example, you could assign environment codes based on your initials, followed by a serial number: “NICK01”. When a developer creates a new environment, the serial number assigned in the environment code could be incremented (e.g., “NICK02”, “NICK03”, and so on.).

If you do not assign an environment code, ZOM generates a random environment code for you. The randomization is designed to make duplicate environment codes unlikely.

There are two other ZOM configuration options that are useful in team development projects. The new keyword option specifies a keyword that is automatically assigned to all newly registered objects. The changed keyword option specifies a keyword that is automatically assigned to all objects you change (e.g., re-create, rename, move, erase, delete, compile, and so on).

The new keyword and changed keyword give you a convenient way to keep track of which objects are new or changed in your environment. You can reset the value of these keywords at any time using the ZOMConfig command and easily remove them from your objects using the standard ZOM key-wording services.

For example, you assign the new keyword and changed keyword to “NewObjs” and “ChangedObjs”, respectively. You are then able to retrieve all new and changed objects in your environment using the keyword selection criteria:

ZOMList +k NewObjs

ZOMList +k ChangedObjs

If you would like to reset your environment so that no objects are flagged as new or changed, use the standard ZOM keywording services:

ZOMSet +k NewObjs ;k NewObjs!

ZOMSet +k ChangedObjs ;k ChangedObjs!

By default, ZOM is configured with the new keyword as “$new” and the changed keyword as “$changed”.

Object Dependencies

Zim contains a number of fixed object types. The objects in any application depend on each other in various ways. These dependencies among objects form a network of interrelationships.

An EntitySet, for example, depends on its fields. If the definition of a field changes, then the definition of the EntitySet changes. A relationship object depends on the EntitySets that it is relating. A relationship object can also use constants, variables, and form fields, and so depend on those objects as well. A program can reference any object, including other programs. These references create dependencies between the program and the objects referenced.

ZOM tracks dependencies between objects. There are two basic kinds of dependencies:

  • Creation Dependency
  • Program Call Dependency

A Creation Dependency occurs when an object references another object in its definition (i.e., when the object is created).

A Program Call Dependency occurs when a document object references another object in its program code.

The diagram below shows the objects in a hypothetical application. The lines between objects illustrate dependencies between objects. This diagram is called the object dependency tree. Program that call dependencies are shown as solid black lines, while creation dependencies are shown as dotted lines.

The sample application represented here has four program objects: pMainMenu, pEditCust, pFindOrder, and pEditOrder. pEditCust and pFindOrder are called from pMainMenu. pEditOrder is called from pFindOrder. Each of these programs uses a different display object (whose names all start with “d”). The display objects, in turn, depend on form objects (whose names all start with “f”). As well, there are three EntitySets, Customers, Orders and Inventory, and two relationships, Place and Require, the latter with fields.

If we look at the object dOrder, we can see that dOrder depends on (i.e., uses) both fOrder and fFrame (via creation references). On the other hand, dOrder is depended on (i.e., used) by the program pFindOrder (via a program reference). The object fOrder depends on no other objects, but is depended on by dOrder and pFindOrder.

Example Object Dependency Tree

ZOM enables you to query the dependencies between objects and use this information to process dependent objects. You can also use this information to detect unreferenced objects that are not dependent on any other object.

There are two ways to query object dependencies:

  1. A Dependency Explosion finds the objects that depend on (i.e., use) a given object. Dependency explosion begins with an object and works up the dependency tree finding objects that use the given object. You can limit the explosion to one level up the tree (i.e., immediate ancestors) or recursively retrieve dependent objects all the way back to the root of the tree.
  2. A Dependency Implosion finds the objects that are depended on (i.e., used) by a given object. Dependency implosion begins with an object and works down the dependency tree finding objects that are used by the given object. You can limit the explosion to one level down the tree (i.e., immediate descendants) or recursively retrieve dependent objects all the way to the leaves of the tree.

The dependency information tracked by ZOM leads to some of the most powerful uses of the ZOM services in developing, maintaining, and analyzing your applications.

Unreferenced Objects

The object dependency information can also be used to select un-referenced objects that are not used by any other object. In our example, there are three un-referenced objects: pMainMenu, PlacedBy and vJunkVar. Un-referenced objects can be selected using the “u” selection criteria, as shown in the following example:

ZOMList +p u

Objects Found by Unreferenced Criteria “+p u”

Often, objects that are not referenced are no longer in use by the application and can be destroyed. This is not always the case as shown above where the unreferenced pMainMenu, which is the root of the dependency tree, is a very significant object to this application. Another object, the role PlacedBy, could have been created in the application as a convenience to users entering ad hoc queries.

Dependency Implosion

Dependency implosion is the process of finding the objects which depended on (i.e., used) by a given object, and so on down the dependency tree. There are three different kinds of implosion: immediate, creation, and program.

As an example, to list all of objects that are used by pMainMenu, enter

ZOMList pMainMenu +i

The result is the list of objects that are used by pMainMenu directly, regardless of whether the dependency is creation or program. The diagram below highlights the selected objects:

Objects Selected by “pMainMenu +i”

This selects only objects that are used by pMainMenu explicitly by means of a creation dependency or a program dependency. The objects that use these depended on objects are ignored.

Similar to dependency explosions, you can implode by either creation dependencies (using the ‘t’ option) or program dependencies (using the ‘p’ option). Now suppose we want to list only the objects that are used by pMainMenu by means of a program dependency:

ZOMList pMainMenu +i p

The diagram below highlights the selected objects:

Objects Selected by “pMainMenu +i p”

This selects only objects that are used by pMainMenu by means of a program dependency, as well as any objects that have program dependencies on these dependent objects, and so on all the way down to the leaves of the tree. Creation dependencies are ignored.

 Next suppose we want to list only the objects that are used by pMainMenu by means of either creation or program dependencies and recursively search all the way down to the leaves of the tree:

ZOMList Customers +i pt

The diagram below highlights the selected objects:

Objects Selected by “pMainMenu +i pt”

This selects objects that are used by Customers by means of any dependency, as well as any objects that have a dependency on these dependent objects, and so on all the way down to the leaves of the tree.

To list only the objects that are used by pMainMenu by means of either creation or program dependencies and recursively search all the way forward to the leaves, but not include the pMainMenu object, enter

ZOMList pMainMenu +i pth

The diagram below highlights the selected objects:

Objects Selected by “pMainMenu +i pth”

This selects only objects that are used by pMainMenu by means of any dependency, as well as any objects that are used by these dependent objects, and so on all the way down to the leaves of the tree, but not the set of objects that were used as the starting point of the implosion. This is called a hollow implosion.

In a hollow implosion, the selected object set does not contain the original objects that fed the implosion algorithm in the first place.

Dependency Explosion

Dependency Explosion is the process of finding the objects which depend on (i.e., uses) a given object, and so on up the dependency tree. There are three different kinds of explosion: immediate, creation, and program.

As an example, let’s suppose we want list all of the objects that use Customers:

ZOMList Customers +x

The result is the set of objects which use Customers directly, regardless of whether the dependency is a creation dependency or a program dependency. The diagram below highlights the selected objects:

 

Objects Selected by “Customers +x”

This selects only objects that use Customers explicitly by means of a creation dependency or a program dependency. The objects that use these dependent objects are ignored.

Now suppose we want to list only the objects that use Customers by means of a creation dependency:

ZOMList Customers +x t

The diagram below highlights the selected objects:

Objects Selected by “Customers +x t”

This selects only objects that use Customers by means of a creation dependency, as well as any objects that have creation dependencies on these dependent objects, and so on all the way up to the root of the tree. Program dependencies are ignored.

 Next suppose we want to list only the objects that use Customers by  means of a program dependency:

ZOMList Customers +x p

The diagram below highlights the selected objects:

Objects Selected by “Customers +x p”

This selects only objects that use Customers by means of a program dependency, as well as any objects that have program dependencies on these dependent objects, and so on all the way up to the root of the tree. Creation dependencies are ignored.

 Next suppose we want to list only the objects that use Customers by means of either creation or program dependencies and recursively search all the way back to the root:

ZOMList Customers +x pt

The diagram below highlights the selected objects:

Objects Found by “Customers +x pt”

This selects only objects that use Customers by means of any dependency, as well as any objects that have a dependency on these dependent objects, and so on all the way up to the root of the tree.

 Now suppose we want to list only the objects that use Customers by means of either creation or program dependencies and recursively search all the way back to the root, but not include the Customers object:

ZOMList Customers +x pth

The diagram below highlights the selected objects:

Objects Selected by “Customers +x pth”

This selects objects that only use Customers by means of any dependency, as well as any objects that have a dependency on these dependent objects, and so on all the way up to the root of the tree. However, this does not select the set of objects that were used as the starting point of the explosion. This is called a hollow explosion.

In a hollow explosion, the selected object set does not contain the original objects which fed the explosion algorithm in the first place.

Importing Objects

Objects are imported using the ZOMImport command. ZOMImport imports all objects described in the ASCII files produced by ZOMExport. Therefore, before you can use ZOMImport, you must first place the ZOMExport files in your database directory. Then, to invoke ZOMImport, enter

ZOMImport

You can also import Z41 files that are compatible with older versions of ZOM as shown in the following example:

ZOMImport ;f Z41

ZOMImport can also be used with the standard ZimSAVE object description files. If you want to import objects from ZimSAVE format files, enter

ZOMImport ;f DT4

en_CAEnglish