Daniel Becker Bighelini

SPEL API: Lessons

Blog Post created by Daniel Becker Bighelini on Jun 13, 2016

Workshop A

A simple application

 

 

Table of Contents

Overview                                                                                                 

Documents needed                                                                                                                                        

Introduction                                                                                                                                                   

Files                                                                                                          

Domsrvr Files                                                                                                                                                

The Database Tables                                                                                                                                    

The VR files                                                                                                                                                    

Discussion                                                                                               

Starting up the Application                                                                                                                         

Here’s what is vbop does to start the application                                                                                 

Explorations                                                                                                                                                

 

 

Overview

Documents needed

  • Bop Components       (bop_component_list.doc)
  • Majic syntax (majic.doc)
  • Domsrvr Methods      (pub_msgs.doc)

 

    All document files are located in source/bp/eng-notes

 

Introduction

 

This tutorial shows you how to set up the development environment and create a simple application. The goal is to make sure your environment is set up properly, and to get you ‘over the hump’ of your first application.  By the end of this session you should be able to create business objects and make a simple application  using them.  You will be able to examine the objects in the domsrvr and to look at the messages passing between the domsrvr and the GUI application, and you should have a better sense of how the pieces fit together.

 

Before you begin you should install a recent version of the Paradigm system.  The system should be configured to use the proprietary database.  Enter in a license key and bring up Call Manager to make sure everything is installed correctly.  Make sure that the NX_ROOT/site/mods directories are all empty (They should contain directories, but no files).  We will be doing our customization in these directories.  You will be bringing up various executables in the Bop environment as part of these lessons.  In order to do this easily, you should set your NX_ROOT environment variable to the correct directory and put NX_ROOT/bin in your path.

 

Our first application will be a very simple employee information tracking system.  We will enhance this application in the first few workshops.  Let’s start by examining the business requirements for the application:

 

Business Requirement: Provide a system that tracks our employees. For each employee track the name (first, last, and middle), salary, employee id number, phone number, and hire date.  We should be able to search for employees and to view, create, etc information about the employee.

 

How to proceed: The first step is to define the ensemble of objects we need for this starter application.  We will define an employee business object, and employee factory. Additionally we will define a database schema file to define how the data is stored in the database, and we will define some screen constructs to display our data.

 

The files which define our application are in source/bp/training with the prefix wkshop01.  You may use those or enter them in by hand.

 

 

Files

Domsrvr Files

 

This file defines the emp business object and factory.  It should be copied into $NX_ROOT/site/mods/majic. Let’s take a look at it.

 

/////////////////////////////////////////////////////////////////

// Module: wkshop01.maj

// Author: John Chenault

// Created: 11/5/96

/////////////////////////////////////////////////////////////////

// Defines a simple employee object.

/////////////////////////////////////////////////////////////////

OBJECT emp {

                 ATTRIBUTES Employee {

last_name     STRING;

first_name    STRING;

middle_name   STRING;

salary        INTEGER;

emp_id        STRING;

phone_num     STRING;

hire_date     DATE;

};

};

 

So what’s going on here?  The keyword OBJECT is used to define a dob, factory, and producer that all work together.

 

  • The dob is named emp. It’s attributes are listed with data types.  The emp dob is associated with an SQL table which is listed in the ATTRIBUTE section.  In this case the table is Employee.  The default is to assume a lexical match between the attribute name and the column name in the SQL table (this can be overridden).
  • The factory is also named emp.  This particular example does nothing interesting or special with the factory except to create one. The emp factory is accessible as an attribute of the TOP object
  • The producer is also named emp.  The producer is the object that actually handles the interface to the sql table. An application developer rarely has to be concerned with the producer.  The producer is accessible as an attribute of the factory although there is no compelling reason for you to access it.

 

We will look at these items defined once we start up the domsrvr.

 

 

The Database Tables

 

This file defines the SQL table our emp object will use to store it’s data into.  Copy the wkshop01.sch file to the $NX_ROOT/site/mods directory.

 

/////////////////////////////////////////////////////////////////

// Module: wkshop01.sch

// Author: John Chenault

// Created: 11/5/96

/////////////////////////////////////////////////////////////////

// Defines the database storage for our employee workshop.

/////////////////////////////////////////////////////////////////

 

#include “../schema.mac”

 

TABLE Employee LARGE LOW_VOLATILE {

id              INTEGER uniq KEY ;     // key id

last_name       ENT_NAME;

first_name      ENT_NAME;

middle_name     ENT_NAME;

salary          INTEGER;

emp_id          ENT_NAME;

phone_num       PHONENUM

hire_date       LOCAL_TIME;

}

p1 Employee -> CURR_PROV empl;

 

This table defines the database schema for us.  The syntax for defining this file is found in existing Paradigm documentation.  For now we have defined no indexes on the file.  It will get slow when it gets big, but this is a demo application.  Note that the column names match the attribute names in the ATTRIBUTES section of the .maj file

 

The VR files

 

For now just copy wkshop01.vr  to $NX_ROOT/site/mods/gvr.  We will look at it in a bit.

 

Discussion

Starting up the Application

 

Run pdm_configure to rebuild the database.  If you have any errors in the .sch file you will need to fix them and re-run the pdm_configure.  If you have errors from the domsrvr you may ignore them for now.  Once pdm_configure has been successfully run,  do a pdm_init.  This will start up the paradigm daemons including the domsrvr. If you have any errors in your .maj files the domsrvr will print to standard error and will exit.  The line number of the error will be printed, and you will have to fix the error before the domsrvr comes up.  You can re-start the domsrvr by hand. (no arguments are needed). Starting by hand is faster than doing a pdm_init and we will be stopping and restarting the domsrvr often.

 

Lets take a moment to look at what we have added to the domsrvr.  Run the program ‘bop_sinfo’  (Documented in Bop Components)  There are various flags here.  Try ‘bop_sinfo -f -d emp’ as a good starting place.  bop_sinfo gives you information about the domsrvr schema.  Remember that the domsrvr also has the objects used in Call manager  defined so there is more to look at than just your employee objects.  Explore what you can see with bop_sinfo. Note: bop_sinfo may not be in the standard release.  It can be found with the sample files.  You want to copy bop_sinfo* to your $NX_ROOT/bin directory.

 

What we have done is added to the domsrvr two new static objects.  The emp factory (as an attribute to TOP) and the emp producer (as an attribute to the emp factory).  We will ignore the producer for now and look at the factory. The factory is the object that will give us our emp dobs, and will create our domsets of emp dobs for us. We added an Employee database table to the SQL database, and have bound the attributes of the dob to the columns in the database table.  We have created new entries in the lower two layers of our environment (Database and Object Repository).  Now let’s add an application (The GUI app) to the mix.

 

The GUI app is driven by two file types.  .wnd files and .vr files.  The .wnd files define the DISPOBS, and the .vr files define the form dialogs. When vbop starts up, it will build default DISPOBs in ram.  You can add to these DISPOBs or override them as you desire. By default we create an ‘emp’ and ‘emp_list’ DISPOB, and create managers for each ‘emp_manager’ and emp_list_manager.  The form dialogs looked for in the .vr file are emp and emp_list.  (We tried to keep the naming conventions simple).  Our first few workshops will use the default wand entries.

 

We define our dialogs using the gfe tool.  In this tool, you define the layout of your dialogs on the screen. For any widget in the dialog, you can associate a tag field.  For a button or menu entry  the tag name is the METHOD name defined in wand you want to execute (for example ‘close’ to call the builtin close method). For a widget that displays data from the domsrvr, the tag name is the dispob name followed by the attribute name you want to display (for example emp_list.last_name to display the last name in the emp_list).

 

Start up the GUI application and look at what is going on.  Enter the command ‘vbop  emp_list_manager’.  This invokes the vbop application (the viewer) and tells it to start up with the emp_list_manager DISPOB. Take a moment to explore the application defined and to understand the functionality.

 

 

  • Note that you can change data either on the list form or the detail form.  If you change data on the detail form, the data on the list changes as well.
  • Start up a second vbop application using ‘vbop -s vbop2 emp_list_manager’.  If you change the value in app one, when does it show up in app two?
  • Start a modification of an object in app one.  Then try to modify the same object in app 2.  When is the conflict detected?
  • Bring up gfe and see how the widgets are defined for the detail form.  Can you add another field?
  • Some of the fields on the detail form cannot be changed.  Why is this?
  • Try doing some searches.  Using gfe - try and see how the search is defined.  Can you change what you are searching on?

 

Here’s what is vbop does to start the application

 

  • Requests schema information from the domsrvr.  It does this by sending messages to the factory objects. Once it has the schema information it builds default DISPOBS based on the domsrvr schema.
  • Reads in any .wnd files which extend and change the default DISPOBS it has defined.
  • Creates the manager objects defined in the DISPOB definitions.
  • Looks on the command line for a manager object to invoke (in this case the emp_list_manager). It finds the emp_list_manager DISPOB and calls a method on it which brings up the emp_list DISPOB.  This DISPOB will put up a form.
  • The emp_list DISPOB gets a domset from the domsrvr which can contain emp objects. In our example the domset begins with no data in it.  The emp_list DISPOB has defined as it’s form to display one named ‘emp_list’.
  • Looks at the .vr files, and finds the appropriate form. It paints the form, and then looks for tag names to be filled in. For the list form, we only show the length, so we show the current length.
  • When the user presses a search button,  vbop sends a fetch command to the domset in the domsrvr.  As the domset is filled in, vbop updates the screen appropriately.
  • If the user double clicks on an element in the list, the default wand code brings up a detail of the cursed row.  This creates an emp DISPOB based on a particular dob.  As in the list form, the widgets on the form are updated based on the tag field of the widget.
  • When you make a change to a field, vbop attempts to checkout the object against it’s group leader.  If the change is successful,  we allow you to make the change.  When you press the save button, the changes are checked in.

 

 

Let’s look at the messages that are doing the communication between vbop and the domsrvr.  The ‘bop_logging’ command can be sent to any bop executable that uses the distributed object layer.  It sends a message to the process telling it to log any bop messages to a file.  In order to do this, you need to specify the slump process name for the application you want to send the message to. You can find this name by running slstat..  Let’s look at the messages to and from the vbop executable.  Do a “slstat | grep vbop’ to get the correct application name to send the message to.  Then do a ‘bop_logging vbop... -f/filename on’.  Then do a tail -f on your file.  If you execute all of this immediately after you start vbop you may catch some of the initialization messages.

 

An example of a message is found in Bop Components

 

Here are some things to note about the bop messages:

 

  • There is a standard form for all messages.  This makes reading easier.
  • The messages are async.  Since async messaging systems are by their nature hard to code, Bop provides a threaded layer in the interpreter.
  • Note the addressing scheme.  See how the object address is specified.
  • Note the concept of reply object and reply method.
  • Observe the messages generated on a Checkout or Checkin.
  • Do a search for various subsets of the emp list and look at the messages that occur.

 

Many of the messages may not make sense to you at this time.  You may want to refer to the pub_msgs.doc document to see what messages are being sent.  The important thing here is to get a general idea of what is going on, not to necessarily understand all of the gory details

 

Explorations

 

  • Modify form (using gfe) to display other attributes
  • Compare messages in vbop and domsrvr

 

 

 

Workshop 2

Adding to the domsrvr

 

 

Table of Contents

Introduction                                                                                          

File Locations                                                                                       

Workshop Example                                                                               

A Business Need                                                                                                                                             

1a. Capitalizion  - Initial version                                                                                                               

  1. emp.mod                                                                                                                                                      
  2. emp.spl                                                                                                                                                         

Debugging                                                                                                                                                    

1b. Capitalization - Version 2                                                                                                                    

  1. emp.mod                                                                                                                                                      
  2. emp.spl                                                                                                                                                         
  3. Make id required.                                                                                                                                     
  4. Sorting on lists.                                                                                                                                          
  5. A combined name field.                                                                                                                         
  6. emp.mod                                                                                                                                                    
  7. emp.mod                                                                                                                                                    
  8. emp.spl                                                                                                                                                       

Discussion                                                                                             

Some un-obvious points here:                                                                                                                  

Additional explorations                                                                                                                             

 

 

Introduction

 

Documents Needed:

  • Bop Components (bop_component.doc)
  • Majic Syntax        (majic.doc)
  • Majic Triggers     (triggers.doc)
  • Interpretive Language     (INTERP-LANG)

 

This tutorial shows you how to make simple additions and modifications to the domsrvr.  By the end of this session you should be able to modify the behavior of the domsrvr to begin customization of your applications.

 

 

File Locations

 

This section introduces domsrvr customization. To avoid later confusion, let’s talk about the various file types used by the domsrvr.  Definitions for the objects in the domsrvr can be defined in multiple files.  This allows you or the customer to modify the system as shipped from the CA without modifying files which could be changed in the next release. The files that are considered part of the core application and should not be customized in the field are located under $NX_ROOT/bopcfg.  Customization is done in the $NX_ROOT/site/mod tree.  When the domsrvr comes up it first reads in .maj files from $NX_ROOT/bopcfg.  It then reads .maj files from site/mods/majic.  It then reads in .mod files from site/mods/majic.  Then it reads in .spl files from bopcfg then mods.

 

Type

Product Files

bopcfg/majic

Customer Files site/mods/majic

Majic

            *.maj

Initial object specification of product

Bug fixes from CA

Define new object classes and new attributes in existing object classes.

Majic modification            *.mod

Bug Fixes from CA

Customer modifications to existing object and attributes.

Spel     *.spl

Either

1) compiled product spel libraries or

2) ptf0***.spl replacements for one or more spell methods in product spel libraries.

Either

1) customer ascii Spel methods

2) optional customer replacements for one or more spell methods in product Spel libraries.

 

.maj and .mod files define the object schema or metadata.  .maj files define new information about the schema (such as defining new attributes, new classes, etc).  .mod files are a way to change parameters of existing attributes, factories, etc.

 

.spl files contain the procedural code for triggers and methods.  Note that .spl files come in two flavors, precompiled and ascii.  The precompiled .spl files are already compiled into p-code and are not readable by humans.  Ascii files always take precidence over functions defined in the precompiled .spl files.

 

 

Workshop Example

A Business Need

Let’s say you have shown your employee application to your customer. He likes it but wants ‘some simple changes’.  To whit:

  1. The name fields should all have a capital letter in the first position of each name.
  2. The employee id number is required.
  3. He wants to be able to sort my employees in the employee list screen.
  4. The use of multiple name fields is awkward.  Can you combine them into a single field for display.

 

Oh - and he needs it done by this afternoon.

 

We can make these changes entirely in the domsrvr without any changes to the database layer.  #4 will require a minor change to the detail form to display the name field.  Let’s add these features one at a time and look at what’s going on.

 

1a. Capitalizion - Initial version

 

We will add a trigger to make sure the first letter is capitalized.  If it isn’t we will fail the edit.  We put the code to do this in two files. emp.mod and emp.spl  Here is the code to make the changes.

emp.mod

/////////////////////////////////////////////////////////////////

// Module: emp.mod

// Author: John Chenault

// Created: 11/5/96

/////////////////////////////////////////////////////////////////

// Adds triggers to check on format of names field for workshop

/////////////////////////////////////////////////////////////////

MODIFY emp last_name   ON_PRE_VAL check_first_char() 5;

MODIFY emp first_name  ON_PRE_VAL check_first_char() 5;

MODIFY emp middle_name ON_PRE_VAL check_first_char() 5;

 

emp.spl

//////////////////////////////////////////////////////////////

// Module: emp.spl

// Author: John Chenault

// Created: 11/5/96

/////////////////////////////////////////////////////////////

// Perform edit checks for name fields.

// Insurers that the first character is capatilized.

/////////////////////////////////////////////////////////////

void emp.last_name::check_first_char( ... )

{

string name;

name = argv[3];

string first_char;

string upcase_first;

 

if ( name = "" return);

 

first_char = substr( name, 0, 1);

upcase_first = upcase(first_char);

 

if ( first_char == upcase_first) { // Passes ok

return (0);

} else {

set_error(-1);

set_return_data(

         "Invalid entry - Capitalization Error");

return;

}

}

 

//////////////////////////////////////////////////////////////

void emp.first_name::check_first_char( ... )

{

   // Duplicate code omitted

}

//////////////////////////////////////////////////////////////

void emp.middle_name::check_first_char( ... )

{

   // Duplicate code omitted

}

 

We have defined a pre_validate trigger on each of the name fields so that when one of them is modified, we will call an interpretive function.  The function, written in interpretive C will test the entry and make sure the first letter is capitalized.  Add these changes to the system , re-start the domsrver, and see the results.

 

Debugging

If there is a problem with the syntax of your spl code, or the syntax in your majic files, the domsrvr will complain and won’t come up.  Other errors in the run-time C code may not show up until you actually execute the code.

 

If the code you write doesn’t do what you expect, you have three ways to figure out what is wrong.

  1. Look in the NX_ROOT/log directory for errors.  If you don’t understand how Paradigm logging works, refer to the Paradigm user manuals. If an error occurs at runtime in the interpreter, it will log the fact of the error into the logfile with the line number of the error.
  2. You can turn on bop_logging on the domsrvr.  This shows you the internal messages that the domsrvr is producing for the spel code you have written.
  3. Put printf statements in your interpreted code.

 

Let’s look at what is going on here in detail.  In the emp.mod file we have defined a pre-val trigger for the name fields.  Whenever any user attempts to set the name field (Send a set_val to the attribute) this trigger will be called.  This applies whether the change is from the GUI, Web, or a Command Line Interface.  Attribute triggers can be called on a PRE_VAL (Called before we do validate) and POST_VAL (Called after we validate).  Each of these calls will ‘suspend’ the set_val  until the trigger returns. We also  have a POST_VAL_ASYNC which just calls the trigger and continues. With the trigger, we provide an ordinal value as to when it should fire.  A lower number trigger will be called before a higher number.  If the trigger doesn’t return, the trigger will time out and the change will be aborted.  Try putting in a sleep in the function call and see what happens.  To see details about the syntax and about triggers in general refer to Triggers.doc and to majic.doc

 

When the trigger is invoked, it calls a function.  Functions that you are writing are written in interpreted C (.spl). Let’s look at one of our functions in detail.  When the function is called we pass various parameters which can be accessed either in an argv[] style or by enumerating as part of the function definition.  In order to know what arguments are there for a specific trigger, you need to refer to the documentation.  Turning on bop_logging is also useful for better understanding.  (Note - if you turn on bop_logging the first three arguments you see are ‘private context’ and are not passed as part of the functions arguments).  We start by getting what the user has entered.  We then do some string manipulation to see if the first character is a capital letter.

 

If the first character is okay, we want to simply return with our error code set to 0 (the default).    If there is a problem, we need  to do some more work.  Just what are these set_error() and set_return_data()functions? Remember that BOP is based on messaging not just function calls.  When the function is complete, the interpreter will build up a reply message and send it back to the C++ code in the domsrvr.  These functions allow you to set up the reply message. set_error() sets the error code for the message to be returned. set_return_data() sets up the arguments in the reply message.  To signal failure, set the error_code to a non 0 value, and put a string in the first argument field.

 

Note the name of the function the trigger calls. It is in the form of ‘obj_name.attr_name::function_name’  This naming convention is intended to make it clearer what functions belong to what objects. An alternative naming convention is available if you want to share methods between attributes and objects. If you pre-pend a ‘::’ to the function name, it will be considered a global name and will not have object or attribute information added to it.  Be careful about using this syntax.  If you remove the idea of what object the functions belongs to, some syntax checking may be disabled, additionally you may have problems with name scoping.

 

The notification on bad input is also clumsy for the end user.  Let’s make a single new function to coerce the string to the proper value instead of just throwing a warning.

 

 

1b. Capitalization - Version 2

emp.mod

//////////////////////////////////////////////////////////////

// Adds triggers to check on format of names field for workshop

//////////////////////////////////////////////////////////////

MODIFY emp last_name   ON_PRE_VAL

"::emp::upcase_first_char"() 5 ;

MODIFY emp last_name   ON_PRE_VAL

"::emp::upcase_first_char"() 5 ;

MODIFY emp middle_name ON_PRE_VAL

"::emp::upcase_first_char"() 5 ;

emp.spl

//////////////////////////////////////////////////////////////

// Coerce the field to have the first letter caps.

//////////////////////////////////////////////////////////////

void emp::upcase_first_char( ... )

{

string name;

name = argv[3];

 

if ( strlen(name)) {

string first_char;

string upcase_first;

string new_name;

 

first_char = substr( name, 0, 1);

upcase_first = upcase(first_char);

 

if ( first_char != upcase_first) {

// We need to coerce it

         new_name = upcase_first + substring(name, 1);

         set_return_data(new_name);

}

}

return;

}

 

We have chosen to use a single function in the emp class instead of a purely global function.  We have to put the function name in quotes to make the parser understand what is going on. The function itself is pretty simple, On a PRE_VAL we can change the field that the user is working on by putting the new value in the first argument of the message, and returning an error code of 0.  This is what we have done.

 

2. Make id required.

 

We want to change the object schema so this field is required.  A required field displays differently in the GUI and must not be null when the object is saved (checked in).  Here is the code to make the employee id field required.  It should be put in the emp.mod file in $NX_ROOT/site/mod/majic.

 

MODIFY emp emp_id REQUIRED;

 

Before the dob  is saved we will verify that the emp_id attribute is not null.  If it is null we will not allow the object to be saved. If you look at your majic syntax document you will see the things you can add via the MODIFY statement.  They include simple defaulting, making fields required, adding triggers.  You might want to add some additional types of MODIFY statements to your code to see how they work.

 

3. Sorting on lists.

 

In order to provide sorting, we first need to talk about lists a bit. Our list construct is a domset.  A domset can best be thought of as an array of rows, each row contains multiple sort criteria based on attributes you have defined, and a reference to a dob that has those attributes.  The domsets you can ask for in your application are pre-defined in the .maj file for the domsrvr.  Each factory has some standard domsets which are ‘guaranteed’ to be there, and can have additional domsets which the you can defined as needed.  The default DISPOB’s use the standard domsets to display what the user sees.

 

When you define a domset, you define if it should be shared among multiple users, whether it is static or dynamic, and what where clause fragment should be added to the domset before a fetch is started.  You also specify what columns in the database we should sort on. For reasons of efficiency, you should not put every possible sort in a domset, neither should you define huge ones unless you really need them.  Domsets are being re-designed to be more efficient, and to share themselves among multiple users where appropriate..

 

Let’s look briefly at how to set up the standard domsets for sorting.  The list helper uses the standard domset by default.  So to provide sorting, we need to define some sort criteria in our standard lists. Let’s sort on last_name and, employee_id.  The domset will supports either sort, and the GUI will let the user choose which sort to use.

 

Here’s some code to start our list example with.  Typically this goes in the emp.maj file, although it can go into the .mod file with a different format.

 

OBJECT emp {

   ...  Attribute information

    FACTORY {

    STANDARD_LISTS {

      SORT_BY "last_name, emp_id";

      };

   };

};

 

When you re-start the domsrvr and vbop you will notice that the list fills in immediately. Why is this?  By defining a SORT_BY in the  STANDARD_LISTS you told the Domsrvr to create a Master List (or MLIST) for the factory. This is a list of all emp objects that exist.  When the selector helper comes up it displays this list by default.  Why would you not always define an MLIST?  System resources.  If you have 100,000 employees, you may not want to take the time at system initialization to load them all in.  You may not want to devote enough memory to store all of the sort criteria, etc  The domsrvr gets clogged down if a list contains more than 3 to 5 thousand entries. If it is a ‘big’ list, don’t turn on MLISTS.  Let’s turn off MLISTS to understand what happens.  Change the STANDARD_LISTS statement as follows:

 

STANDARD_LISTS {

   SORT_BY "last_name, emp_id";

   MLIST OFF;

};

         

This defines sort criteria for the standard lists, but tells the factory not to make an MLIST for the factory.  When you re-start the domset now - you will notice that we don’t default any list, but the sort criteria are still active.

 

What if you want to sort not by just last name, but last . first, middle. So that all Smiths are ordered by first name.  Here’s what you do.

 

SORT_BY "last_name = last_name + first_name + middle_name, emp_id"

 

This defines a composite key.  The key consists of last_name, first_name, middle_name and is called for notational convenience ‘last_name’.  By naming the composite key last_name we tell the GUI that if the .vr file contains a column with the tag of ‘last_name’ that it should treat that column as a sort column.

 

Try different sort criteria, duplicate record names, etc.

 

4. A combined name field.

 

Experience has shown that life for the developer is simpler if we have separate fields for first last middle names.  But users often like to see them mashed together.  We choose to implement the combined name field as a ‘local’ field. One that is not stored in the persistent repository.  We need to define a local field in the object, and set up triggers to create the field when the object is instanced, and update the field whenever any of the name fields are changed.  Since this is a ‘new’ attribute it must be in a .maj file.  It doesn’t have to be in the same .maj file that our original employee object is defined in.  For now we will show it as a separate file, later we will merge it in.  We also have code to create the combined name field from the first last middle names and to update it whenever they are changed.

 

emp.mod

//////////////////////////////////////////////////////////////

// Adds a local attribute to the emp object.

//    This is a .maj file since it defines a new attribute

//    The information about the emp object will be merged with

//    other emp information at run time.

//////////////////////////////////////////////////////////////

OBJECT emp {

   ATTRIBUTES Employee {

      combo_name         LOCAL STRING ;

   };

   TRIGGERS {

      DB_INIT init_combo_name(

                   first_name, middle_name, last_name) 50 ;

   };

};

 

emp.mod

//////////////////////////////////////////////////////////////

// Adds triggers to Create the combo_name field

//  This is in a .mod file because it adds triggers to

//  existing attributes

//////////////////////////////////////////////////////////////

MODIFY emp last_name

   ON_POST_VAL "::emp::reset_combo_name"() 5 ;

MODIFY emp first_name

   ON_POST_VAL "::emp::reset_combo_name"() 5 ;

MODIFY emp middle_name

   ON_POST_VAL "::emp::reset_combo_name"() 5 ;

 

 

emp.spl

//////////////////////////////////////////////////////////////

// Code to deal with the combo name field

// This is a .spl file because it defines procedural behavior.

//

// Called on db_init with first, last, middle name

//////////////////////////////////////////////////////////////

emp::init_combo_name(...)

{

   string first_name, middle_name, last_name;

   first_name = argv[2];

   middle_name = argv[5];

   last_name = argv[8];

 

   string combo_name;

   combo_name =

        emp::build_combo_name(first_name,middle_name,last_name);

 

   // Set name/value pairs in the return message

   set_return_data(1);

   set_return_data("combo_name");

   set_return_data(combo_name);

   return;

}

//////////////////////////////////////////////////////////////

// Called when a name attribute changes

//////////////////////////////////////////////////////////////

emp::reset_combo_name(...)

{

   string temp

   temp = emp::build_combo_name( first_name, middle_name, last_name);

   combo_name = temp;

}

 

//////////////////////////////////////////////////////////////

// Builds a new combined name as a string and returns it

//////////////////////////////////////////////////////////////

string emp::build_combo_name(

   string first_name, string middle_name, string last_name  )

{

   string temp_name;

   if ( ! is_null( last_name)) {

      temp_name = last_name;

   }

   temp_name += ", ";

   if ( ! is_null( first_name)){

      temp_name += first_name;

      temp_name += " ";

   }

   if ( ! is_null( middle_name)) {

      temp_name += middle_name;

   }

   return temp_name;

}

 

Discussion

 

Let’s talk about what is happening here.  First we have defined an attribute which is not back-stored.  This is where we will store the combined name. We add a Trigger to be called on instantiation of the object from the database ( DB_INIT ) .    In this call we name the attribute information to be passed to the function when it is called (In this case first, middle, and last names). When the function is called, we get the arguments from argv[] and call a helper function to build the combined name string.  The helper function returns the new string which we stuff into the reply message per the Trigger documentation.

 

The second set of trigger code is more interesting.  reset_combo_name is called with no arguments.  So where does the variable ‘last_name’ come from.  It comes from the emp object that made the call. Most triggers have as context within the trigger the dob we are working on.  You can access the attributes of the object directly, so in this case ‘last_name’ referrers to the attribute in the object making the call. The combo_name attribute of the current object is set to the return of build_combo_name() in a similar manner.  It is instructive to turn on bop_logging and watch the messages that these calls create. The interpreter is actually generating calls to get_val and set_val.

 

Some un-obvious points here:

 

  • Functions can stuff replies in a message, or they can be treated from within the interpretive layer as standard C functions that return a string, integer, etc.
  • Functions when called from other functions can return simple variables in the manner of C.
  • Most functions called from a trigger are passed with context of the dob that the function is called from.  This means that (from within spel) you can refer to the attributes simply by the name of the attribute.  No need to code a get_val, etc..  The access of the object is not as cheap as a normal variable access. You may want to use a temporary. Also if you try to change the value of an attribute while the object is not checked out, you will get an error.
  • To see the combined name field - you will need to add a field to the detail form to view the field.  Add a text field,  set the tag to emp.combo_name.  Start up the application and have a look.
  • If you type into the combined name field, you don’t get what you expect.  Writing code to update the last, first, middle names from entering into the combo name is left as an exercise for the student.
  • Turn on bop_logging and see the arguments that get generated when the trigger is called.
  • Try accessing an attribute that does not exist or misspell an attribute name.  This is a common bug.  You have been warned.

 

 

Workshop 3

More domsrvr additions

 

 

Table of Contents

Introduction                                                                                          

Documents Needed:                                                                                                                                   

The Workshop Business Need                                                            

Solution                                                                                                  

  1. Requirement - Employee Types                                                                                                             

What do these files do.                                                                                                                                 

e_type object                                                                                                                                               

e_type SREL                                                                                                                                                

  1. Salary Range Checking Trigger                                                  
  2. Other Requirements                                                                      

Inactive Employees                                                                                                                                  

The last modified attribute                                                                                                                     

Install the files & test                                                                                                                               

Additional explorations                                                                  

 

 

Introduction

 

Documents Needed

  • Bop Components (bop_components.doc)
  • Majic Syntax        (majic.doc)
  • Majic Triggers     (triggers.doc)
  • Interpretive Language     (INTERP-LANG)

 

This tutorial shows you how to add more complex behavior to the domsrvr.  By the end of the workshop, you should be able to make most common changes to the domsrvr.

 

 

The Workshop Business Need

 

Our customer, after having used the employee system wants some additional changes.  Here’s what he says.

  1. I need the idea of employee types, such as Cleaning, Admin, etc.  And I need to know how many employees in each type.
  2. The salary range for an employee should be based on the type of employee.  When an employee object is created, default the salary to the minimum $ amount for the type.  if the salary range is out of bounds, don’t allow the object to be saved.
  3. When an employee leaves, I don’t want to delete his history, but I don’t want that employee to show up in the system under normal usage.
  4. I want to know when the employee information was last modified.

 

These changes will require some changes to the underlying data storage. Specifically we need :

  • A new database table for an employee type object.
  • New columns in our existing Employee table to hold information as to employee type, Contact method, a flag to say the record is inactive ( the employee has left)and when the record was updated.

 

Solution

We could make these changes by adding to the existing schemas, but it might be easier to simply throw away our existing schema and majic files and combine them into one new set.  Let’s start with the object schema files. (Leave the existing .spl files in your directory alone. We will continue to use them).

 

1. Requirement - Employee Types

 

wkshop03.maj

/////////////////////////////////////////////////////////////////

// Description:

//  .maj file for third tutorial.

//

//  Defines a more interesting employee object.

/////////////////////////////////////////////////////////////////

 

OBJECT emp {

    ATTRIBUTES Employee {

 

      last_mod             DATE { ON_CI SET NOW ;

                         } ;

      delete_flag  del SREL actbool REQUIRED {

                         ON_NEW DEFAULT 0 ;

                         } ;

 

      combo_name LOCAL    STRING;

      last_name        STRING {

             ON_PRE_VAL "::emp::upcase_first_char"( ) 5;

             ON_POST_VAL "::emp::reset_combo_name"( ) 50;

             };

      first_name       STRING {

             ON_PRE_VAL "::emp::upcase_first_char"( ) 5;

             ON_POST_VAL "::emp::reset_combo_name"( ) 50;

             };

      middle_name      STRING {

             ON_PRE_VAL "::emp::upcase_first_char"( ) 5;

             ON_POST_VAL "::emp::reset_combo_name"( ) 50;

             };

      salary           INTEGER;

      emp_id           STRING   REQUIRED;

      phone_num        STRING;

      hire_date DATE;

      type emp_type    SREL e_type REQUIRED {

             ON_POST_VAL set_min_salary() 50 ;

             };

    };

 

    TRIGGERS {

      DB_INIT init_combo_name(

          first_name, middle_name, last_name) 50;

      PRE_VALIDATE check_salary() 50;

    };

 

    FACTORY {

      STANDARD_LISTS {

SORT_BY "last_name, emp_id";

      };

      REL_ATTR id;

      COMMON_NAME combo_name;

      FUNCTION_GROUP inventory;

    };

};

 

OBJECT e_type {

    ATTRIBUTES Employee_Type {

      sym              STRING;

      description desc    STRING;

      base_pay         INTEGER;

      max_pay                 INTEGER;

      all_emps         BREL emp type;

 

    };

 

    FACTORY {

      STANDARD_LISTS {

SORT_BY "sym";

      };

      REL_ATTR id ;

      COMMON_NAME sym;

      FUNCTION_GROUP reference;

    };

 

};

 

wkshop03.sch

/////////////////////////////////////////////////////////////////

// Schema file for third tutorial.

/////////////////////////////////////////////////////////////////

#include "../schema.mac"

 

TABLE Employee LARGE LOW_VOLATILE {

id INTEGER uniq KEY;   // key id

del INTEGER NOT_NULL ;

last_mod LOCAL_TIME;

last_name ENT_NAME ;

first_name ENT_NAME ;

middle_name ENT_NAME    ;

salary INTEGER;

emp_id ENT_NAME;

phone_num PHONENUM;

hire_date LOCAL_TIME;

emp_type INTEGER;

}

p1 Employee -> CURR_PROV empl;

 

TABLE Employee_Type SMALL READ_ONLY {

id INTEGER   uniq KEY;      // key id

sym HIER_SYM  uniq  S_KEY; // type symbol

desc ENT_DESC;        

base_pay            INTEGER;

max_pay             INTEGER;

}

p1 Employee_Type -> CURR_PROV emp_ty{

desc -> nx_desc;

}

 

What do these files do.

e_type object

We have defined a new object class of e_type.  This class defines the valid employee types, and the salary ranges for each type.  We must decide what attribute in the e_type object that we want other objects to use as the relation attribute.  This is similar to deciding what a foreign key should be in SQL land.  For our example we have chosen to use the ‘id’ field. We can make any unique field which we don’t expect to change in the e_type object the relation attribute.  We define the relation attribute as part of the Factory definition.  Typical choices for the relation attribute are id, persistent_id, sym, or enum.   If you want to use the value of the rel attr directly (like an enum for priority) use the enum, if you always know the class of the thing pointed to, and don’t want to use the value directly, use id. In the future we will be supporting anonymous srels.  For these you would use persistent_id as this includes class information.

 

Since the number of e_type objects is not expected to change very often, and there are not to many of them, we will define an MLIST to hold the types.  We also define some information the GUI needs here.  Specifically what the security function group should be for this object and what attribute we should display when we show an SREL pointing to this object in the GUI. (Such as emp.type)

 

e_type SREL

In the emp object, we have defined a new attribute that represents the employee type.  This attribute is an SREL or single relation to another object.  The database storage for this attribute needs to match the REL_ATTR as defined in the e_type class.  We also have defined an attribute on the e_type which is a list of the employees of that type.  This is defined as a BREL.  What that means is that we have defined an SQL type where clause for the attribute which is all of the emp objects that have that type .  When the user requests information about the e_type.all_emps attribute, the domsrvr will do a query to create the list of employees.

 

2. Salary Range Checking Trigger

We need to define what the behavior should be before we can address how to meet the needs here.  As developers you must be sure you find out what the customer wants before you start coding.  The rules for us here should be that if the user modifies a salary or changes the employee type, we will check when the object is checked in to make sure the salary of the employee is under the ‘max’ amount.  We will not worry if we are underpaying the employee.  When we create a new employee, and assign a type, and the salary field is empty, we will fill in the salary.  Whenever we save an object, if the salary field is  not filled in, we will set it to the minimum type.

 

To do this, we have set up a pre-val trigger on type that will make sure we have not changed the type to one with insufficient salary.  We have set up a post_val trigger that will fill in the minimum if the salary is not filled in.  We have added a pre_val trigger on the salary to make sure it is in an ok range. We created an object trigger to validate the salary when the object is checked in.  Here is the procedural code for these triggers, etc.

 

wkshop03.spl

/////////////////////////////////////////////////////////////////

// emp.type::set_min_salary

// Called when the type is changed.

// If the salary field is 0 or NULL,

// we should load it with the value in the selected type.

/////////////////////////////////////////////////////////////////

  1. emp.type::set_min_salary(...)

{

int local_salary;

local_salary = salary;

 

if ( is_null( local_salary) || local_salary == 0) {

                 salary = type.base_pay;

}

}

 

/////////////////////////////////////////////////////////////////

// emp::check_salary

//

// Called when the object is saved.

// We want to set the salary tothe minimum if it is null

//

// Then check that we are < the max salary.

/////////////////////////////////////////////////////////////////

emp::check_salary(...)

{

//printf("in check salary");

 

int local_salary;

local_salary = salary;

if ( is_null(local_salary ) ) {

                 salary = type.base_pay;

                 local_salary = salary;

}

 

if ( local_salary > type.max_pay) {

                 set_error(-1);

                 set_return_data("Salary out of range");

}

}

 

A few points.

  • Note the checks for is_null().  Doing comparisons, upcases, etc on null values can cause the function to fail. At this point in the language development, you should always check for null prior to manipulating the object.
  • We are showing use of printf here.  This is a useful debugging tool.
  • Notice the comment blocks.  Please always comment your code, show revisions, etc.
  • In check_salary we create a temporary variable for salary.  Why?  Every time the salary attribute is accessed from the dob, it a message to be sent to the dob asking for the attribute value.  Creating a temporary variable saves multiple messages.
  • Notice the dotted names such as ‘type.base_pay’.  This is a way to refer to an attribute within the SREL.

 

For more details - Take a look at the Interpretive Language Document in the Reference Guide.

 

 

3. Other Requirements

 

Inactive Employees

We are intruding the idea of an inactive employee.  We simply define an SREL to a BOOL object.  There is a neat trick here.  The value of the attribute is 0 or 1 (or null).  Since we default it to 0, and make it required, we can be assured in our code that it will never be null.  We can then set up an RLIST in our emp factory to give us a list of ‘active’ where del =0.  If the delete_flag were NULL, things might not work as expected.

The last modified attribute

The final requirement is to add an attribute which tracks changes.  We do this by defining an attribute (last_mod) and setting it’s default rules to always set it to ‘NOW’ when the object is checked in.

Install the files & test

Install the files from the course/wkshop03 directory.  Copy wkshop03.maj and wkshop03.sql into NX_ROOT/site/mods/majic.  Copy wkshop03.sch into $NX_ROOT/site/mods.  Copy wkshop03.vr into $NX_ROOT/site/mods/gvr.  Do a pdm_configure and re-initialize your database (necessary because the database schema changed).  Check for any errors in syntax and fix them.  Start of vbop as before (vbop -P emp_list_manager). Explore the application.

 

Additional explorations

  • Add a count of the number of employees of that type to the e_type detail screen.
  • Change the e_type detail form to display a list of employees, sorted by name, of that type.
  • In the employee detail screen, notice the salary range fields.  When you change the type, these change.  What happens when you change the $ amount directly?  Sometimes you may want the user to be able to change the values of the underlying srel, but usually not.  The way to control this is to turn off the ability to modify the field in gfe.

 

 

 

Workshop 4

Spel - Command Line Interface

 

 

Table of Contents

Overview                                                                                                 

Documents Needed:                                                                                                                                   

The Workshop Example                                                                       

The Business Need                                                                                                                                         

What are the interesting things to note in this file.                                                                               

Explorations                                                                                         

 

 

Overview

Documents Needed:

 

  • Bop Components             (bop_components.doc)
  • Interpretive Reference     (INTERP-LANG)
  • Domsrvr Messages          (pub_msgs.doc)

 

This workshop is designed to take a closer look at the spel Interp language, and to explore the command line interface tool using bop_cmd.

 

In our earlier examples of spel, we were always in a function that was called from a trigger where context is defined.  In a manner similar to c++ we have an implied ‘this’ pointer.  When we use bop_cmd to execute some spel code, we don’t have a ‘this’ pointer.  This means that we need to either make explicit calls to an object to get the attribute, or access the attribute from a ‘base’ object.  We also will look at the send_wait call, and how to find the top object explicitly.

 

 

The Workshop Example

The Business Need

The customer wants a command line interface that will increase the salary of any employees of some employee type by 10%.  We start by creating a code fragment which contains a function which will do this task.  To execute the code fragment we will use the bop_cmd program.  This program takes as an argument the file containing the code fragment, and the function to call.  Let’s take a look at the fragment file.

 

wkshop04.frg

/////////////////////////////////////////////////////////////////

// Method:      raise_salary()

//

// Description: raise the salary for all members of the provided

// employee type by 10%.

//

// Input

// emp_type_string  String sym of the e_type you which to

//                    process raises

// Return/Reply

// Ignored.

//

/////////////////////////////////////////////////////////////////

raise_salary( string emp_type_string)

{

// Make sure we entered a good type.

send_wait( 0, (object) "@|domsrvr|TOP|0", "call_attr",

         "e_type", "sync_fetch", "STATIC",

         "sym = ?", 2, 1, emp_type_string);

 

// If an error-die, else we see if we got one back.

if ( msg_error() ) {

printf("Got error %d while searching for %s\n",

msg_error(),

emp_type_string);

return;

}

if ( msg[1] >1 ) {

printf("More than one match found using %s\n",

                  emp_type_string);

return;

}

if ( msg[1] == 0 ) {

printf("No matches found using %s\n", emp_type_string);

return;

}

 

// The e_type is was set.

// Get a domset of all of the employes of this type.

send_wait( 0, (object) "@|domsrvr|TOP|0", "call_attr", "emp",

"sync_fetch", "STATIC",

"type.sym = ?", -1, 1, emp_type_string);

 

// If an error, we simply exit (I just love writing demo code)

if ( msg_error() ) {

printf("got error %s\n", msg_error());

return;

}

int num_found;

object update_list;

 

update_list = msg[0];

num_found = msg[1];

 

printf("processing %d employees\n", num_found);

 

// Get the group leader we will use for revision control

object group_leader;

send_wait(  0,  (object) "@|domsrvr|TOP|0", "get_co_group");

group_leader = msg[0];

 

// For each entry in the static list, process the raise.

int index;

for ( index = 0 ; index < num_found ; index ++) {

set_ilimit( 1000000);

send_wait( 0, update_list, "dob_by_index", "DEFAULT",

              index, index);

object this_dob;

this_dob = msg[0];

 

// We call a function here to show it can be done,

process_raise (group_leader, msg[0], 0.10) ;

}

printf("Updates complete\n");

}

 

/////////////////////////////////////////////////////////////////

// Method:   process_raise()

//

// Description: PRIVATE function to adjust salaries for 1 employee

//

// Input:

// group_leader  group leader to use

// emp_dob             who to give the raise to

// pct_change    How much to give

/////////////////////////////////////////////////////////////////

void process_raise(

object group_leader,

object emp_dob,

double pct_change

)

{

// First checkout the dob - if we fail, simply note on stdout.

// In a real application we might also put an error in a log file,

// or timeout for a time and attempt to process later.

 

send_wait( 0, group_leader, "checkout", emp_dob);

 

if ( msg_error()) {

// The checkout failed

printf("Checkout failed for %s - No raise processed\n",

               emp_dob.combo_name);

} else {

int curr_salary;

curr_salary = emp_dob.salary;

curr_salary *= (1 + pct_change);

emp_dob.salary = curr_salary;

 

send_wait( 0, group_leader, "checkin");

if ( msg_error()) {

         string error_msg;

         if ( msg_length() >1) {

            error_msg = msg[1];

         } else {

            error_msg = msg[0];

         }

        printf("Salary update failed for %s - Reason was %s\n",

             emp_dob.combo_name, error_msg);

}

}

}

 

Copy the file to your working directory and execute

“bop_cmd -f wkshop04.frg “raise_salary ( ‘emp_type’ )”

 

What are the interesting things to note in this file.

  • Look at the comments before each function.  This is strongly suggested.
  • send_wait() sends a message to some BOP object. In this case the TOP object.  We send either to a well known address or to an object we have.
  • See Domsrvr Methods for the methods you can call on the Domsrvr
  • Check error returns.
  • See use of group leader.
  • set_ilimit() Important function to remember.
  • Calling another function.
  • Use of printf
  • See how to modify a record.
  • use of msg_error and msg[]
  • Accessing attribute names based on dotted naming convention.

 

 

Explorations

  • Bring up the GUI and run this app while looking at an employee of emp_type.  You see it change in real time on the GUI.
  • Make a change to the record from the GUI and do not check it in.  Now run the application.
  • Try modifying the function to take an additional percentage argument for the raise.
  • What happens when you modify a salary past the maximum?  Why?
  • Turn on bop_logging so you can see the message flow

 

 

 

Workshop 5

A look at the GUI application environment (vbop)

 

 

Table of Contents

Overview                                                                                                 

Documents Needed:                                                                                                                                   

Introduction                                                                                                                                                 

First Application                                                                                  

Statement Details                                                                                                                                        

Testing the Application                                                                                                                                

Vbop in action                                                                                        

Display Object Tree                                                                                                                                   

Addressing items in the tree                                                                                                                     

Exercises                                                                                                 

Key Points                                                                                                                                                       

Invoking methods from ‘outside’ the application                   

Additional vbop concepts                                                               

Managed                                                                                                                                                    

Modal vs Open Dispob                                                                                                                            

Triggers                                                                                                                                                      

Restyle                                                                                                                                                       

ADD                                                                                                                                                           

Summary                                                                                                

 

 

Overview

Documents Needed:

  • Wand Reference (wand.doc)
  • Builtins reference            (builtins.doc)
  • vre Files reference           (vrfiles.doc)

Introduction

This workshop looks at vbop, the application that drives the GUI interface that the customer sees. The vbop application is our GUI / application development tool.  It is designed and optimized to allow easy connection to the business objects in the domsrvr.  Understanding vbop, however,  is easier if we initially ignore the domsrvr.  Like the domsrvr it reads configuration files that define it’s behavior.  Once we see how the GUI tool works on it’s own, we can add the linkages to the domsrvr.  Workshop 5 looks at the vbop application without regard to the domsrvr.  Once we have explored how to define and modify simple applications using vbop which do not use the domsrvr, we will look at how to add objects from the domsrvr in Workshop 6.

 

vbop reads configuration files.  The two files it reads are .vr files and .wnd files.

  1. The .vr files are defined using the gfe tool.  The .vr files define the widgets that get displayed, the color of the form, how it re-sizes, what the default buttons are, accelerator keys, etc.  Naming conventions within the .vr files provide form group functionality which allows you to change the forms for various users With each widget you can associate a tag name which links the form to the Display objects defined in Wand.
  2. The .wnd files are defined using your favorite text editor.  The .wnd files define the Dispobs (display objects) that the window dialogs access.  These display objects provide the application behavior ‘behind the glass’.  A window dialog can access  any feature or functionality provided in the Dispob layer based on the tag names in the vr file.

 

Tag names are the glue between the window dialog and the dispob. They tell the dispob what the window dialog wants to display, what function to call when a button is pressed, or how to build search criteria to fill in a list.

 

 

First Application

The most important concept to understand is that of the dispob, and how multiple dispobs relate to each other.  A dispob (Display object) defines a ‘hunk’ of functionality and behavior you can associate with an entire form or a single row on a grid.  When you define a dispob you frequently will need to pass in some context.  For example if you want to display a form which shows a contact, you will need to pass in the contact you want to display in the form.  Let’s start with an example.  This file defines a dispob we will use in our workshop.  It will have some buttons to beep, pop up new forms, etc as well as display data.  You should create this file with a .wnd extension in the site/mods/wand directory.

 

DISPOB rec_test (int level = 0, string form ="rec_test")

{

int my_level;

string  info_text;

 

METHOD init() {

my_level = level;

}

METHOD new_dispob (){

open_dispob( dispob := “rec_test”, level := my_level +1);

}

METHOD new_modal_dispob () {

open_dispob_modal( dispob := “rec_test”, level := my_level +1);

}

METHOD show_tree() {

info_up( info := get_full_path());

}

METHOD set_info( string text = “hi mom”) {

info_text = text;

}

}

MANAGER rec_test_manager OF rec_test();

 

This should be in a file ending in a .wnd extension and should be put in the site/mods/wand directory.    When you start up vbop it will read in all files with a .wnd extension and create a dictionary for all of the dispobs defined in the files.

Statement Details

 

DISPOB rec_test (int level = 0, string form ="rec_test")

This line starts the definition of a new DISPOB.  The name of this dispob will be ‘rec_text’.  The rec_test dispob takes two initialization parameters, level (and integer) and form( a string).  If the DISPOB is invoked without defining a level or a form, we will take the provided defaults.  If no default is provided, the variable is required.  NOTE that unlike C++ the ability to default a value is not associated with the position in the declaration.

 

The parameter ‘form’ is a special built in parameter.  It defines the form name to pop up when a new instance of this dispob is opened.  By convention the form name is the same as the DISPOB name.  The form name is the name of a dialog as defined in a .vr file.

 

int my_level;

string  info_text;

Defines two local variables to the dispob.  my_level and info_text  These can be displayed on a form set by code, etc.

 

METHOD set_info( string text = "hi mom") {

   info_text = text;

}

The METHOD keyword defines a method (or procedural code) which can be invoked.  This one is intended to be brain dead simple, and to only serve as an example.  It simply sets the info_text variable to some value.  Note that the syntax of the language is similar to .spl code in the domsrvr.

 

METHOD init() {

   my_level = level;

}

The init method is special.  It gets called whenever the Domsrvr object is instantiated.

 

METHOD show_tree() {

      info_up( info := get_full_path());

}

This method shows how we can call a built in function from wand code, and demonstrates how parameter values get passed in functions.   Unlike C, all parameters are passed as name/value pairs. The example calls the info_up function.  It passes as a parameter to info_up info:=get_full_path(). The := operator is used in all function calls to assign a value to a parameter. This method of passing is one of the hardest things to get used to in the beginning.  Unlike C where we know parameters by their ordinal position in the function call, in Wand we must provide the parameter name.. In this call we are setting the ‘info’ parameter to the string returned by the built in function ‘get_full_path’. info_up(), and get_full_path() are defined in the builtins.doc  The info_up method pops up a dialog box with some text in it. In order to see what additional paramaters The info_up function is a built in function to vbop. In order to see what additional parameters info_up may have, refer to the documtation.

 

METHOD new_dispob (){

      open_dispob( dispob := "rec_test", level := my_level +1);

}

This call shows how we create a new instance of a dispob dispobs.  We make the open_dispob function call and provide the dispob name along with whatever parameters the dispob requires for it’s initialization. When we create a dispob, we create it as part of a display tree of objects.  When the new_dispob function is called, the dispob is created and made a child of the current dispob.

 

 

MANAGER rec_test_manager OF rec_test();

Creates a MANAGER object.  When you declare a DISPOB object, you do not instantiate anything, you are making an entry in a dictionary.  When you define a MANAGER object, vbop will instance the object when it starts up. For now think of managers as objects which are always there that you can request to perform an open_dispob function for you.

 

Testing the Application

Once the .wnd file is defined, you need to define a .vr form.  Bring up gfe and make a new .vr file.  Create a new form group and label it ‘default’.  Create a new form in the form group with the name ‘rec_test’.  Put (at a minimum) two Text Fields and 5 buttons on the form.  Set the tag on the Text Fields to be my_level and info_text.  Set the label/tags on the buttons to be New/new_dispob New-Modal/new_modal_dispob BEEP/beep Close/close Show Tree/show_tree.

 

The tag names for the Text Fields bind the field to the variables in the dispob.  The form will display the current value of the variable it is associated with, and will allow changes to the value.  The tag names in the buttons bind the button action either to a method defined in the DISPOB or to built in global methods.

 

Start the app with the following command line:

vbop -P -D -laf motif rec_test_manager

 

The flags on the vbop command set up for additional debugging (-D) and force a look and feel (motif).  The final command ‘rec_test_manager) says what object should be invoked when vbop starts up.

 

Vbop in action

In order to understand what is happening, lets look at what is vbop does.

  1. Check to see if there is already a vbop running on this box.  If so vbop will send a manage_dispob message to the ‘rec_test_manager’ object defined in that running vbop and will then exit.  The currently running vbop will then perform item 6 in this list.
  2. Talk to the domsrvr and gets schema information about the business objects.  Build default dispobs and manager objects dictionary entries for the business objects in the domsrvr.  We will ignore these dispobs for now.  This step is important when we are connecting to the domsrvr.
  3. Read in config files ending in .wnd and modify the dictionary based on these files.
  4. Instantiate a root object.  This is a system defined object that acts as parent to all other objects in the display tree.
  5. Instantiate all manager objects that have been defined.  For our example this would be the rec_test_manager object.
  6. Invoke the ‘manage_dispob’ method of the rec_test_manager.  Which manager we invoke the manage_dispob method on is determined by the final parameter on the command line.  This opens a dispob as the child of the rec_test_manager.  The kind of dispob that rec_test_manager opens is the rec_test dispob.
  7. When the rec_test_dispob is opened, it creates internal data structures, finds the form name and pops up the form.  The app examines the form for tag names and binds them to the display tree.
  8. When a variable is bound to the form, if the variable changes the form is updated, or if the user enters into the form, the underlying variable will change.
  9. When a button is associated with a method name, when the button is pressed, the method is called.

 

Display Object Tree

 

A key concept to understand is that of the display tree.  The display is a tree of all objects that are part of the GUI.  We create a node in the tree for each DISPOB. Within the DISPOB we create a node for each variable and method we define in the DISPOB.  When we open a new dispob, we create it as a new node on the tree attached to the object that opened it.  The root object is the first object in the tree that all other objects are connected to.  When vbop is started, the display tree consists of a root object and all of the manager objects that are defined in the dictionary.

 

The dialog we are displaying is not part of the display tree.  It is associated with some DISPOB in the tree.

 

For our initial test program, the display tree looks something like this:.

 

 

What we have here is a tree of objects that we can access from a form.  Our initial form is associated with the rec_test dispob.  We can access values (circles) or methods (squares) by simply using the name based on ‘where we are’ in the tree.  For example, from the rec_test dispob I can access my_level and info_text simply by using their names.  The beep method is not a method of the rec_test dispob, but is a method of the root dispob.  The application is able to find the beep method because it is an unambiguous name in the tree.

 

When we open a new dispob, we create a new entry in the tree that might look something like this.

 

As you add more rec_test objects with the open feature, you might have a tree that looks like this (Values and Attributes omitted for clarity)

 

Addressing items in the tree

 

When you press the ‘show_tree’ button you will print out the display tree for the dispob.  Note the dotted name convention for defining the tree. Here is the important point.  You can access ANYTHING in the source tree if you know the name., and the name is in the form of the dotted name of the dispob tree.  So if you wanted to write a method to pop up a modal dispob from the ‘top’ one, you could say:

METHOD new_root_child (){

     rec_test_manager.rec_test.new_dispob();

}

If you wanted to set the info_text field of the ‘top’ dispob you could define a method that would say:

METHOD modify_root_value ( string text = “hi igor”){

     rec_test_manager.rec_test.info_text = text;

}

Or more easily you could set up a text field in your vr file and give it the tag

rec_test_manager.rec_test.info_text

 

This ability to access variables and methods from any point in the source tree is a powerful and important feature of the architecture.

 

The mechanism that vbop uses to find the name is one of a partial match algorithm.  It starts in the current dispob and searches up and down till it finds a match.  In practical terms it means that you simply qualify the entry only as much as necessary to make it unique.

 

NOTE - Remember that a dot in domsrvr/spel means to follow a chain of attributes in a dob to get to some specific attribute.  In Wand, it means a node on the display object tree.  This nomenclature may change in the future, but at this point it is one of the items that cause confusion.

 

Exercises

  • Modify the rec_test dispob to take an new paramater (info).  Set the info_text to the passed in value.  When you open a new dispob pass the current info text to the child. Modify the info_text value in a form, and pop up children and observe how values are passed.
  • Look at other built in functions in builtins.doc.  Add some to your form. NOTE - some functions make no sense without the domsrvr.  Use your common sense.  You may wish to try some of the following

 

Key Points

  • How arguments are passed
  • How methods are defined
  • Binding buttons to methods
  • Binding fields to values
  • the init method
  • How we display and set the level value
  • Use of built in methods.

 

 

Invoking methods from ‘outside’ the application

vbop is built using the same distributed object messaging technology as the domsrvr.  This means that you can send messages to it from outside of the vbop process.  The root object of  vbop process can receive the following messages:

 

call( string method_name, 0 or more name value pairs)

manage( string manager_name)

open( string dispob_name)

stats(void)

 

We can use bop_msg to call a method in our running application.

 

bop_msg @ vbop.xxxxx root call rec_test_manager.rec_test.set_info text

“external info”

 

bop_msg  is the name of the process that will take the command line args, bundle them up and send it to the running vbop process.

 

@ vbop.xxxxx root is the address we want to send the message to where vbop.*** is your vbop process as noted by the slstat utility. root  is the ‘name’ of the top object in vbop.

 

call  is one of our 4 method names  that the root object will respond to.

 

rec_test_manager.rec_test.set_info  is the method name in the display tree we want to call.  We could have identified this as

  1. root.rec_test_manager.rec_test.set_info

but since the name rec_test_manager is unique, the ‘root’ is not needed to make the name unambiguous.

 

text is the paramater name we want to pass in a value for in the function call.

 

“external info” is the value we pass in.

 

This example is admittedly a bit contrived, but you can call ANY function from outside of the application.  This is a powerful feature.

 

Additional vbop concepts

Managed

Sometimes when you bring up a new dispob, you don’t really want a new one you just want one to pop to the front.  For example if you have a detail up on call request CR:87 and you iconize it, then later bring up another list and double click on CR:87, you really want the existing one to just pop back, not make a new instance of a dispob displaying CR:87.  If you request the manager to open the dispob for you, it will make sure that there is only one form display per instance.  See Wand documentation for details.

 

Modal vs Open Dispob

Note the open_dispob vs the open_dispob_modal.  Opening a dispob in modal means that the parents of that dispob cannot get focus while the child us up.

 

Triggers

Sometimes you want to specify a block of code to be executed when a value in a dispob changes.  This change can be from any source, the GUI, or a function call.  This example from the call request product shows how to set up a trigger on a variable named assigned_flag;

 

TRIGGER assigned_flag {

   if (assigned_flag != 1) list.@QBE.EQ.assignee = NULL;

}

We have said that whenever the variable ‘assigned_flag’ is changed, we will check the value, and if it is not 1, we will null out some other variable in the display tree.

 

Restyle

Each field displayed on a form has a style of interaction.  VBOP provides default interaction style (or style) for all fields based on the data type of the form.  For example an integer value will restrict valid entries to numbers.  There are times that you want to change the GUI behavior associated with a variable.  You may want to associate an action to be taken when the user double clicks in the field, or when field entry is finished. Some examples:

 

RESTYLE status( entry_finished :=“status_modified”);

Tells vbop that when the user finishes entry into the ‘status’ field, the method ‘status_modified’ should be called.

RESTYLE cr.extern_ref(Select_2 := "show_tt");

When cr.extern_ref field is double clicked, we want to call the show_tt method.

 

The examples above modify an existing style.  The actions (entry_finished, Select_2) are pre-defined and documented in builtins.doc

 

You can also define completely new styles of fields and interaction styles and associate that style with a field.  See the .wnd files for call manager for examples of how to define a new style, here is how you associate a value with a style:

 

RESTYLE cr_scoreboard_una.@ICOUNT bucket_style;

We are setting the style of the attribute to ‘bucket_style’.

 

We also have enum styles.  This code fragment defines a helper list to be used to set integer values into a variable ‘win_report_method’  It defines an interaction style called win_methods.  It then associated win_methods with a local variable win_report_method.

 

STYLE win_methods ENUM ()

{

   ENUM

   {

   "Printer" = 0,

   "Screen" = 1,

   "File" = 2,

   }

}

int win_report_method;

RESTYLE win_report_method win_methods;

ADD

It is often useful to be able to add to an existing dispob without having access to the file the dispob is defined in.  This is part of how we allow the customer to add features to our dispobs with assurances that the next release will not erase their changes. The keyword ‘ADD’ allows you to add methods and variables to already defined dispobs.  To add a new method to our rec_test object, we could say:

 

ADD rec_test {

   string new_added_string;

   METHOD hi_mom() {

   // do something

   }

}

 

 

Summary

At this point we have explored the non-domsrvr aspects of vbop.  You should be able to build simple applications which do not tie back to the domsrvr based on the information in this workshop.  The next workshop will show you how to tie the business objects into vbop for a full development environment.

 

 

 

Workshop 6

Connecting vbop to the Domsrvr

 

 

Table of Contents

Overview                                                                                                 

Documents Needed:                                                                                                                                   

Incorporating the Domsrvr                                                             

DOB sample                                                                                                                                                

Dob Details                                                                                                                                                  

Running the Sample                                                                                                                                   

Exercises                                                                                                 

Some Key Points                                                                                                                                         

Additional vbop features                                                                  

LIST variable type                                                                                                                                      

Statement Details                                                                                                                                        

Testing the Application                                                                     

Building the .vr file                                                                                                                                    

Starting the Application                                                                                                                            

Explorations                                                                                         

QBE Searching                                                                                                                                            

Different kinds of Domsets                                                                                                                       

Sorting on the Form                                                                                                                                    

@DISPLAY:                                                                                                                                                

Behavior Rules when popping up a Detail                                                                                             

LIST variable type as a QREL                                                                                                                  

DEFAULT DISPOBS                                                                                                                                

Directory Structures                                                                      

 

 

Overview

Documents Needed:

  • Wand Reference (wand.doc)
  • Builtins reference            (builtins.doc)
  • vre Files reference           (vrfiles.doc)

 

This workshop shows you how to connect the domsrvr to vbop making it easy to build complex applications that access business objects.

 

 

Incorporating the Domsrvr

What we have so far is not much more than a GUI toolkit that you could use to develop applications. It does have some useful features such as the ability to access values and methods anywhere in the display tree, the ability to send messages to the root object and have the gui respond, and the ability to allow a customer to modify code in a way that will persist over the next release.  The power of vbop lies in the ability to add variables which are directly connected to business objects in the domsrvr, and in making it easy to allow changes to those business objects.

DOB sample

We will start by building a new DISPOB which will display a dob on a form.  Here is the sample code:

 

DISPOB dob_test ( string persid, string form = "dob_test")

{

DOB <cnt> fred ( dob_ref :=

                    persid_to_dob( factory := "cnt", persid := persid);

string foo_string;

}

ADD rec_test {

METHOD pop_dob () {

open_dispob(dispob:="dob_test", persid :=info_text);

}

}

 

Dob Details

 

The first thing to notice is how similar this is to the previous examples.  We have defined a Dispob which takes as a required parameter a string which is a persistent id.  We have a new variable type in this dispob, a DOB.  We also have defined a variable ‘foo_string’.  You should understand foo_string from the previous class. Let’s look at the DOB variable in detail.

 

DOB <cnt> fred ( dob_ref :=

persid_to_dob( factory := “cnt”, persid := persid);

A DOB is a value type just like integer or string. From the standpoint of the domsrvr, it is a vbop object which is ‘tied’ to some specific dob in the domsrvr.   From the standpoint of vbop it is a object with a bunch of attributes that can be displayed on a form and that handles locking, changing, etc for you.  ‘DOB <cnt> fred’ Defines the DOB as one of class cnt (Contact) and gives it a name in the display tree of ‘fred’. When you define a DOB in a Dispob, you need to pass context, to define which dob it is.  When the DISPOB is initialized it will construct the DOB fred and will use the dob_ref parameter to determine which dob in the domsrvr to represent.  (dob_ref is an object reference pre-defined for a DOB constructor).  For our example we are setting dob_ref to the results of a function call that takes uses a persistent_id that is passed into the DISPOB when it is opened.

 

The ADD section adds a button to our existing rec_test form.  The user will enter a persistent id into the info_text field on the rec_test and push a button to pop up the detail.

 

 

Running the Sample

 

Build the .wnd file above and put it into the site/mods/wand directory.  Create a form in your vr file named dob_test to display information in the contact record.  You will want to add a ‘save’ and ‘close’ button to the form which call builtin functions.  In order to put a field from the DOB on the form, you use the same dotted naming convention as for other objects in the display tree.  IE to display the last name of the fred DOB, the tag name in the .vr file would be fred.last_name.  For now, don’t put any SREL attributes on the form, simply put concrete attributes like combo_name, phone_number, etc.  The Display tree that will be built for dob_test looks something like this.

 

We added a method to the rec_test DISPOB to bring up the dob_test DISPOB.  You need to add a button to the rec_test form to invoke the bop_dob method.  Note that the pop_dob method takes as context the variable info_text.  Before you pop up the new form you need to enter a persistent_id for a contact dob into this form.  The easiest way to find persistent id’s is to do a pdm_extract on the Contact table and build the persistent_id from the database id.  IE if the database id is 3456 the persistent id will be ct:3456

 

Start up vbop invoking the rec_test_manager.  Enter in a persistent_id and bring up a dob.  Make changes to the contact record.  Save the changes.  Note that the DISPOB handles creation of group_leaders, dirty flags, etc for you. All you have to do is set up the calls appropriately and add  a save button.

 

Look in the builins.doc for information as to what ‘for free’ methods you get for the DOB values.

 

 

Exercises

  1. Add a second DOB to the dob_test dispob.  Add a new value to the rec_test dispob to define context for the second dob.  Show information from both DOB’s on the form.
  2. Add a trigger on the foo_string variable so that if it is changed to a valid persistent id you will do a remap on the dob to the new contact.  (The remap function is defined in builtins.doc)
  3. Add a Popdown field to the form which references an SREL (such as type).   If there is an MLIST defined for the SREL you can use a popdown field to make selection easy.

 

Some Key Points

  • A DOB is similar to any other wand variable.
  • A DOB(wand) is not exactly ad dob (from the domsrvr)
  • DOB object requires context
  • DOB’s have names.  This is the name used in the tree name of the variable.
  • save() is a builtin function.
  • Where saves occur are controlled by additional parameters in the open_dispob command. See builtins.doc for details.

 

 

Additional vbop features

LIST variable type

The second type of variable associated with the domsrvr is the LIST variable.

Often in the GUI we want to show a grid of items.  These have lots of common attributes.  They may have a count, they show rows of information, each column in the grid shows the same kind of information, etc.  Sometimes a grid displays information from a QREL or BREL in a dob. Sometimes a grid is meant to be have search fields so the user can do a QBE style of searching by filling in the search fields.  The LIST construct is used to support this functionality.  Let’s look at a DISPOB that has a LIST in it.

 

DISPOB list_test (string form = “list_test”)

{

   LIST <cst> wilma ( source := “MLIST_STATIC”,

fetch_limit := 100)

        OF dob_test (dob_ref := current_dob) ;

   string my_string;

}

MANAGER list_test_manager OF list_test();

 

This list example is the kind of list that is intended to have a QBE style search.  This Dispob does not have any context that needs to be passed in.

 

Statement Details

LIST <cst> wilma (source := MLIST_STATIC,

fetch_limit := 100)

       OF dob_test (dob_ref := current_dob) ;

 

List is a value type just like DOB or string.  From the standpoint of the domsrvr, the list is some domset.  This domset could be an ad-hoc one used for searching, or it could be a domset which is a BREL in some business object.  From the standpoint of vbop, the LIST object is a construct that allows for grids to be placed on the form, and that has builtin in searching and selection behavior.

 

LIST <cst> wilma introduces the list variable.  It will be a list of objects as found in the cst factory, and will be named wilma in this DISPOB.

 

source := MLIST_STATIC tells us what kind of domset to use for this list.

 

fetch_limit := 100 tells the list to stop after fetching 100 and wait till the user asks for more.

 

OF dob_test (dob_ref := current_dob) When we display entries from the list in a grid, vbop needs to know what DISPOB to use to do the display.  This part of the list definition tells the list to use the DISPOB dob_test when it is displaying a row.  Since the dob_test dispob needs context, we tell it how to get the context.  It should use the ‘current_dob’ in the list. Remember that when you display a row in a grid, you are constructing a DISPOB for each row.  This means that you get the behavior of the DISPOB for the row.

 

 

Testing the Application

Building the .vr file

Add a form to your vr file with the name list_test.  Add a grid to the form.  Make the tag on the grid ‘wilma’.  Add two text fields to the form.  Associate the tag ‘wilma.@COUNT’ with the first, and ‘wilma.@ICOUNT’ with the second.  Open up the grid.  Add two fields to the grid and label them ‘fred.last_name, fred.first_name.  Add a search button to the form associate the method ‘wilma.search’ with the button.  Add a close button.

Starting the Application

Make sure you have MLISTs turned on for the cnt class in the domsrvr.  Start up vbop with the command

vbop -P -D -laf motif list_test_manager’

 

You will be presented with a blank list form.  When the form comes up, the dispob gets the named domset (MLIST_STATIC), displays any information about the entries in the domset, and waits for input.

 

Press search and the form should fill up with entries.  Double click on an entry and get a detail.  This detail is built into the list by default IE the style is set to do a detailed_cursed() when you do a Select_2 on a row.

 

Look at the difference between the @COUNT and @ICOUNT list.  ICOUNT is an integer count of the exact number of items in the list. @COUNT is a string value intended for display.

 

 

Explorations

QBE Searching

The user frequently wants to search for a subset of the items which can be contained in a domset. For example all of the contacts whose last name begins with ‘c’ and whose type is analyst.  Bop provides an easy way to  do this by using the form editor.  When you make a LIST variable in a DISPOB you make some new kinds of additional objects in the display tree.

 

First notice the @COUNT variable.  This is a node in the display tree like any other.  The LIST object will keep it in sync with the number of items it has to display The @QBE is an object whose function is to make it easy to do query by example searches.  Under the @QBE object we define a number of kinds of searches you can select from.  GT, LT, IN (like), etc.  See wand.doc for the details.  Under each ‘kind’ object (like ‘IN’) you have all of the attributes of the DISPOB that you are displaying in the list.  When you activate the search method, the @QBE object builds a where clause based on what variables have been set, and the LIST object uses that where clause in the fetch.  For example, to look for all contacts with last name containing some string, you would put a text field on your form, and associate the tag ‘wilma.@QBE.IN.last_name’ .

 

In order to avoid creating hundreds of dummy attributes, vbop does  not actually allocate the objects under @QBE unless there is a tag for them, or the code access the objects in some way.  This is  what is refereed to as lazy evaluation and is a consistent theme in the display tree. Sometimes you will see in the wand code a declaration with no code.  This is to force vbop to create the entry in the node tree.

 

Different kinds of Domsets

If you want to list to update dynamically to show the selection criteria, you need to define the source as a DYNAMIC list.  For example MLIST_DYNAMIC.

 

When a list starts up, should it be initially started with a list of ‘everything’ or should it be empty. If you set the source to MLIST, and there is an MLIST defined, you will get the list displayed on initial pop of the form.  When you do a search, this kind of source automatically changes to an MLIST_DYNAMIC.

 

If you want to restrict the list to a subset of possible values (Such as only those that are active) you can define the source to be an RLIST type.  You could also use any other domset which is defined which has some restriction added to it’s where clause.

 

Sorting on the Form

Users frequently want to sort their form on various criteria. The LIST object supports this by looking at the keys that are defined in the domset that it is displaying.  If the name of the key is the same as the name of a column in the grid, that column is defined as a sort column.  If the user double clicks on the label, we will sort by that column.

 

If the key in the domset is a composite key, the matching is done based on the name of the composite key. This is how we have a sort button on ‘name’.  If the key in the domset is a composite key, the In the event of multiple sorts

How various sorts work.

 

@DISPLAY:

When you display a row in a list, you need to retrieve the entire dob.  There is a fair amount of traffic involved in doing this.  A way to short circuit some of this traffic is to use the @DISPLAY construct for column names instead of actual DOB attribute names. @DISPLAY is a special name that means to take the display attribute of the domset and display that instead of using the attribute of the DOB.  Remember that @DISPLAY is a projection of the raw database table column, not a ‘real’ object attribute.  Also note that there is not an object with name of @DISPLAY, it is simply a naming convention.

 

If we wanted to put last_name and first_name on a selection form, we could do that if these columns from the database are used in sort keys.  We would use the tags of @DISPLAY.last_name and @DISPLAY.first_name respectively.

 

Behavior Rules when popping up a Detail

The application needs some way to know what the desired behavior is when a detail is popped up from a LIST.  Should it allow a next/previous button on the detail being popped up and only allow for one entry that is shared by all?  Should it force a separate form for each instance?

 

The correct type of behavior is defined by flags associated with the LIST object.  These pre-defined flags are:

  • DETAIL_MULTIPLE
  • DETAIL_MANAGED
  • SAVE_ALWAYS
  • SAVE_NEVER

Behavior and location of these flags are defined in wand.doc

 

LIST variable type as a QREL

The LIST construct is also used when we want to display a list of entries for a QREL (or BREL).  If for example, we had a DISPOB that was displaying a contact DOB, and we also wanted to show the QREL of all_open_creq on the form in a grid, we might modify our dob_test DISPOB as follows:

 

DISPOB dob_test ( string persid, string form = “dob_test”)

{

   DOB <cnt> fred ( dob_ref :=

persid_to_dob( factory := “cnt”, persid := persid);

 

   LIST <cr> all_open_creq ( source := all_open_creq);

}

 

This defines a list object named all_open_creq which will be updated whenever the DOB changes.  It can be accessed like our wilma list in the example above.

DEFAULT DISPOBS

Turns out that in a real world application most DISPOBS are very similar.  For each object you usually want a detail dispob that contains a DOB of the correct class.  This dispob should also contain LIST constructs for any QREL defined in the DOB. You also want a manager for the object, as well as pull down helpers and a smart style to make selector helpers easy. In order to save time and effort in development, vbop will generate a number of these standard dispobs for you. The set of objects that are automatically generated are:

  • DISOPOB factory_detail
  • DISPOB factory_list
  • MANAGER factory_manager
  • STYLE factory_pulldown_style
  • STYLE factory_smart_style
  • LIST OF factory_detail - list template

 

For example, for our cnt factory, we create  cnt_detail and cnt_list DISPOBS, and a cnt_manager MANAGER.

 

The default dispobs are documented in wand.doc.  You can alter the default dispobs by using the ADD statement, or you can completely re-define them by simply defining a new dispob with the same name.

 

Writing your own detail or list dispob to display some kind of business object in the domsrvr is usually a mistake.

 

 

Directory Structures

 

GFE:

Form Groups:

Other VR files

 

 

 

Workshop 7

Events and Lrels

 

 

Table of Contents

Overview                                                                                                 

Documents Needed                                                                                                                                    

Introduction                                                                                                                                                 

LREL’s                                                                                                        

ATTACHED EVENTS                                                                                   

Attached Events Manual                                                                                                                           

Workshop Example                                                                               

Macro hints                                                                                                                                                 

Options on event templates                                                                                                                      

 

 

Overview

 

Documents Needed

Introduction

 

This section covers two disparate topics.  We will look at event triggers, and LREL’s.  An event trigger solves the problem of ‘after three days, if this call is not handled, I want some action to be taken.  LREL’s are the last kind of attribute we will look at.  It is a many to many relationship attribute.

 

As we do not anticipate that you will be adding lrels very often, we will simply talk about them. As we do anticipate you needing to add and modify events quite often, we will talk about them as well as creating new events in our workshop.

 

LREL’s

A Value attribute is a simple concrete attribute in a business object. The storage for a Value attribute is ‘within’ the storage of the business object.  At this time this means that it is in the SQL table containing the data for the object.  An SREL is a ‘pointer’ or single reference to another object.  Like the Value attribute, we store the foreign key as part of the business object.  QREL’s (and BREL’s) can best be thought of as the results of an SQL style where clause. There is no storage associated with the QREL, and no direct way you can add or subtract objects into an QREL. (You can change values so it ‘shows up’ but you can’t say ‘Add this one’.  LREL’s provide the ability to both have a many to many mapping between business objects, and to have the ability to ‘add’ or ‘remove’ a dob from the mapping.

 

LREL’s are weird because you don’t just define one attribute, you define two.  You are defining a relation between two object classes, so you need to define an attribute in each of the two classes.  For example, if you want to set up an LREL of contacts to notify when a call request is closes, you define an LREL attribute in the call request (of persons to notify) and one in the customer object (of call requests).  You can add or remove relations from either end.

 

LREL’s are stored in a special ensemble which just contains entries for the left and right sides.  We provide a default one to use, or if necessary you can define your own.  For example, if you have a fairly small set of lrels you want to support in your application, and you need excellent performance, you might want to define your own storage table.

 

Let’s look at how LREL’s are defined. ( See majic.doc)

 

// Call request notification of list of contacts

LREL lrel1 cr notify_list <> "cnt:PDM" cntntf ;

 

The majority work in adding lrel’s is in creating a GUI form to help you set them up.  Let’s look at an example:

 

The customer form has a button (update).  the update button is bound to:

// for customer environment button or menu pulldown.

   METHOD cust_env() {

      if (!is_empty(cst.combo_name)) {

         open_dispob(dispob := "cst_env", dob_ref := cst.dob_ref,

                 anchor := "main", only_one := TRUE,

is_group_leader := FALSE);

      }

  }

Note: ‘anchor’ is a bug

 

 

The disbob we open:

 

DISPOB cst_env( string form = "environment",

                string function = "inventory",    object dob_ref  )

{

///////////////////////////////////////////////////////////////////////

// this dispob is similar to org_env. they both use the same form.

// if you change something here, do it to org_env too.

///////////////////////////////////////////////////////////////////////

 

DOB <cst> cst(dob_ref := dob_ref);

LIST<nr> env_list (source := cst.cenv);

LIST<nr> list (source := "RLIST", search_domset := "RLIST_DYNAMIC");

 

// add network resource to the customer environment

METHOD env_add() {

if (!is_null(list.current_dob)) {

         env_list.add(dob_ref := list.current_dob);

}

}

 

// remove network resource from the customer environment

METHOD env_remove() {

if (!is_null(env_list.current_dob)) {

         env_list.remove(location := env_list.current_index);

}

}

 

// popup the network resource detail form so can create one

METHOD new_network_resource() {

//printf("new_network_resource\n");

object new_nr;

new_nr = make_network_resource();

list.add(dob_ref := new_nr);

}

 

// close button

METHOD ok_env() {

close_dispob();

}

}

 

The form this is displaying is ‘environment’.  which is in base.vr.  The interesting Methods  are ‘env_add, env_remove, new_network_resource, and ok_env.

 

When you want to make your own form, you will need to create a dispob much like this one.

 

 

ATTACHED EVENTS

Consider a Call Request.  For some reason, if the call request is not closed in 3 days, you want to send some mail to the assignee.  How do you do this.  As opposed to a polling daemon that wakes up once an hour and looks for all call requests of that have been open for more than three days and that have a high priority, we provide the concept of an attached event.  The idea is that we associate an ‘event’ object with the call request that will ‘wake up’ or ‘animate’ three days from now.  When it is animate, it will check the call request to see if it needs to do any work, then execute a spel script.

 

The existing implementation is not terribly user friendly.  We anticipate that our customers will often want to use events to handle workflow requirements.  In this case you will need to create new events and automajically attach them to objects at the appropriate point in the object life cycle.

 

Attached Events Manual

We provide a set of objects which work together to handle attached events for you.  These are documented in ‘Attached Events’ which is part of the course handout.  Let’s review this document.

 

  • Purpose - To enable us to ‘animate’ an object at some time in the future, to check the state of the object with a condition, and to execute spel code against the object.
  • Major Classes:
  • macro - a code fragment
  • macro_type
  • evt - event template
  • atevt - the attached event
  • wrkshft - The workshift for time rules.
  • Interesting points
  • Context with macro.
  • Macros and Locking
  • Event Templates with repeating, time, and other rules
  • Workshift for when clock is active.
  • Smag fields in both event templates and attached events.
  • Functions to add and remove attached events programmatically.

 

Workshop Example

Let’s walk through an example to explore what you will need to do to add an event to some object programmatically.  Let’s assume we want to add an event trigger to a particular employee object that will increase the salary every 10 minutes. (Say for example if the last name is chenault)

 

  1. Creating macros against the new object.
    You need to add the new object class to the list of available types for the macros.  To do this you add a DISPLAY_NAME entry to the ensemble.  This defines a new producer/ class type to be available for macros.
  2. Create some macros
    You will want to create both condition and Action macros.  See below for hints on what to look out for.  The easiest way to create new ones is to use a GUI form.  In order to do this you will need to either turn on the ability to modify macros for everyone (a bad idea) or you will set up a new macro detail form that allows update of the spel in a form group that is yours and yours alone.
  3. Using your macros - Create some event templates
    You will create event templates that test a condition (Say has this record changed in the last 10 minutes), do some work (Increase the salary), etc.  There are lots of additional items you may choose to set on the template, we will detail these below.
  4. Create a trigger to add the event to the emp object
    You will want to add a trigger to add the event.  For example, you might want to add a trigger that, when an object is created, and the last name is your last name, you create the attached event.
  5. Create a trigger to remove the event from the emp object
    If someone changes the name field, you probably want to remove the event.  Why make someone else get rich?
  6. Write some spel code for the trigger to do the attachment.
    You will need to create the attached event. We will discuss how to do this below.
  7. Test - Test - Test
    You must carefully look for errors in spel, as well as logic errors.
  8. Document what you have done.
    Documentation standards are still being developed.  At the least, however you want to put in comment blocks.

Macro hints

When a macro is invoked, it has the context of the dob it is being run against as a ‘this’ pointer.  You may access the attributes of the object in standard ways.  There are two types of macros - Conditionals and Actions.  When the macro is created, you can set a flag that states the ‘this’ object should be checked out prior to the macro being called.

 

Conditionals are called with no arguments.  They should return an integer of ‘0’ for false, or ‘-1’ for true in the ‘first’ return argument. If the macro sets an error_return, we will ***** to the log and abort the macro.

 

Actions are called with three arguments.  The Group Leader used to check out the object, the attached_event that is being executed, and the event_template that is used.  You may pretty much do whatever you want to do in Actions EXCEPT to call the checkin method on the Group Leader.  Do NOT do this.

 

Options on event templates

The event template contains some interesting options you need to understand before you create new ones.

 

The delay time is the default time to delay from ‘now’ to the time the attached event should fire.  Depending on the flags in the Event, you may be able to override this manually.

 

Round to time provides you with a method to make events fire ‘on the hour’ etc. If you set a round_to time of one minute, all times will be rounded to a whole minute.

 

Workshift allows you to choose a workshift object which will modify the fire time.

 

Allow time resetting is pretty clear.  If set the user may change when the object should fire.

 

If Repeat event is set, the event will re-set itself after each firing.

 

If Save History is set, we maintain a history of what the event has done. No history avoids this.

 

The Condition is a macro fragment to give a true or false result.  If none is supplied we are ‘true’

 

Text field is a smag provided for your use.  The action or condition macros may want to reference information in this field.

 

Actions on true and Actions on false are the actions to be performed. They will be performed in order either until there are no more to be performed, or until one of them returns an error code.

 

 

 

Workshop 8

Introduction to Call Manager

 

 

Table of Contents

Overview                                                                                                 

Documents Needed                                                                                                                                    

Introduction                                                                                                                                                 

Reading Code                         

Where to make changes

Installing user options and Patches

Some thoughts on how to proceed with your design.

Key Call Manager Features

Case Studies

 

 

Overview tc "Overview"

Documents Needed

 

At this point the primary document is Call Manager Architecture/Design Release 3.5.  This document is intended as a stand alone document and reviews BOP as well as exhaustively talking about the classes and functions in Call Manager.

 

There is a plan afoot to generate reference documentation for the Business Objects and Application Dispobs automagically based on source code.  If this happens there will be additional rules you will need to follow to provide comments in your code which will be sucked into the documentation.

 

Introduction

Working with our toy employee system is well and good.  However, the majority of your effort will be in enhancing Call Manager (or other applications in the future).  The goal of this session is to introduce you to the more interesting objects and ensembles in call manager, to show you  how to document your changes, and to let you try your wings by making some real world enhancements to the system.

 

Reading Code

You can always read all code except for spel some spel code.  We pre-compile the spel code in bopcfg.  This makes it un-readable at a customer site.  If you are in Bellevue, you can read the current source code for all spel functions.

 

Where to make changes

Systems that allow substantial customization at a user site suffer from the ‘next release’ problem. When CA introduces a new version of Call Manager, how can the customer accept the new features and enhancements without blowing away the changes he has made.  We have addressed this problem by a convention of putting all customer changes in the site/mods directory, and having rules as to how to combine equivalent information in the site/mods tree and the bopcfg tree.  If you are modifying a customer site you should NEVER add files to the bopcfg tree.  If you think you need to, stop and think again.  If you still need to put some BIG comments in the site mods directory so there is a prayer of fixing the damage when the next release occurs.

 

What are the current rules for overriding changes?  NOTE - this is an area of the system that is inconsistent at this time.  We anticipate that it will be cleaned up in the next release.

  • .sch files
    We read the .sch files from the site directory, and the site/mods directory and combine them into a single .sch file which is used by the database as the schema.
    The granularity of override for is as fine as adding or modifying a column in a database table.
  • .maj and .mod files
    We first read the .maj files from bopcfg/majic. Then the .mod files from bopcfg/majic. Then the .maj files from site/mods/majic, and finally the .mod files from site/mods/majic.  We combine the information in all of these files.  The ordering is needed because of the use of the MODIFY statement.  (you can’t modify it if it is not there yet).
    The granularity of override is as fine as modifying an attribute.
  • .spl files
    We look in the bopcfg/majic directory. Then we read the site/mods/majic. We read any .spl files.  If we read a function that is compiled, and later find the same function name in a non-compiled format, we replace the compiled version with the non-compiled version. If we find two non-compiled functions with the same name, it is undefined which is used. The granularity of override is a spel function.
  • .wnd files
    We first generate the default Dispob definitions.  Then we read bopcfg/wand then site/mods/wand.  If we see an ADD statement, we add to an existing dispob. If we see a re-definition of a Dispob, we replace it with the last one read.
    The granularity of an ADD is on the Method, or Display level.
  • .vr files
    We look in the site/mods/gvr then the bopcfg/gvr directories.  We look for the form name first in a form group with the user’s name, then his form group, then the form group ‘default’.  As an additional conflict resolution tool, we have the idea of version numbers with a .vr file.  See VRE files for details.
    The granularity of an override is a form.
  • .frg files
    The .frg file is a naming convention for bop_cmd.  In truth the code fragment may have any name.  There is currently no replacement of these files built into the bop_cmd program.


Installing user options and Patches

At times we will put patches out on the system.  For example there is a patch to allow the user to change his helper lists to use DISPLAY fields instead of the slower attribute names.  Patches come with a script to install the patch, and keep a log of what was done.

 

We also supply user options. This is a GUI that can be used to install various options.  For example there is an option to turn off MLISTS for contacts, and another to allow a customer to be considered a ‘customer’ or an ‘agent’.

 



Some thoughts on how to proceed with your design.

 

Take a deep breath. Ask your customer questions about what he is trying to do.  See how the changes requested best fit into the BOP model.

 

Consider carefully where the changes belong.  Is it a Domsrvr change that is needed, or is it a wand/vbop change.  Your users will typically think of the changes as ‘I want the GUI to do this’.  A good way to resolve the question is to ask ‘How does this change affect the command line processing and the WEB interface’.  If the command line interface needs the same change, it probably belongs in the Domsrvr.  If not, it probably belongs in Wand.

 

Find out if anyone has done something like this before.  At this point, you need to ask questions.  Later we should have some documents as to what has been done.

 

Produce a Design Document. This should state the customer problem, and provide an overview of your solution.  For changes to the domsrvr, the overview should discuss any new objects, attributes or methods you plan to add, as well as the desire to override any methods.  For changes to Wand, you want to discuss any .vr changes, new Dispobs, or new ADD statements to existing dispobs.  This document will become part of the installation at the customer site when you are done.  To support unix sites, the document should be a simple ASCII file.  Not Word or Frame.  It should include the customer name, your name, date, etc.

 

If appropriate have your design reviewed.  Especially when you are starting out, you want to have someone who is knowledgeable in the application review what you are doing.  We plan to set up a review process for designs.  You need to submit the design for approval.

 

Code your changes.  Be sure to put comment blocks before each section explaining what it does.  Test them extensively.  Be sure to test all paths in your spel code.  The spel interpreter allows for many runtime errors due to spelling problems, etc.  You must execute every statement to make sure you have no errors.

 

Complete your design document by putting the file names you have chosen, and the date of implementation.

 

Put the design document in the site/mods directory.

 

Put a copy of the design document into ???  (Disk in Bellevue that holds all designs).

Key Call Manager Features

 

The call manager architecture is an exhaustive look at the product.  This section attempts to identify key features you will be using frequently in your enhancements.  Have a look at the code so you can see how these work.

Important Ensembles.

call request (cr)

Activity Log (alg)

Contact (cnt, agt, cst, grp)

Network Resources (nr)

 

Important Dispobs and Screens

Main Menu

Scratch pad

Caller Profile

Activity Helper Screens

Call Manager Detail

 

Other important pieces.

Interface to Problem Manager

Invoking scripts and Reports

Interface into a running vbop.

 

 

Call Request Ensemble / Activity Log Ensemble

These two classes ( cr & alg) are the core Business objects in the call request system.  They are defined in cm.maj.  Any alg object is designed to point to a cr.  There is a brel in the cr to point to the alg associated with the cr.  Since you need to associate the alg to the cr, there are a bunch of methods in cr to create alg entries, and change values in the call request.  These include:

  • close_call_request
  • reopen_call_request
  • log_comment

 

Of some interest are the local state variables in the call request The idea is that if an attribute is modified directly we just change it.  If it is modified via an activity we set a flag in the state variable saying that the change has been noted in the activity log. When the call request is saved, we look for any changed attributes and create an audit log if necessary.

 

The quick_ticket() and attach_tt() calls are of some interest. They both give the user the ability to manipulate a trouble ticket using the C-API style interface.  As such we recognize that the user may want to make changes in what gets set.  Accordingly there is a quick_ticket_site() and attach_tt_site() user exit function that is called from quick_ticket() and attach_tt().  These calls provide a way for you to add your additional functionality.

 

You also notice a large collection of Shared PRE_LOAD domsets. These are used for the global scoreboard counts.

 

 

Contact Ensemble

Two interesting wrinkles here.  The first is the ‘notify’ set of functions.  These let you send a notification to the contact.  The second is the ‘extra’ ‘restricted’ factories which are part of the ensemble.  We define a factory for customers, agents, etc.

 

Network Resources Ensemble

Interesting primarily as an example of wrapping an old style paradigm object.

 

Main Menu Dispob

Provides display of scoreboard stuff.  Shows mixing lots of weird stuff on a single dispob.

 

Scratch Pad Dispob

Shows use of local variable to hold information.

 

Caller Profile Dispob

The search information for how to bring up a caller is useful to look at.

 

Activity Helper Dispobs

Interesting to see how to make a dispob using simple screen to build an object.

 

Call Manager Detail Dispob

Note how the list and details are associated into the screen.

 

Interface to Problem Manager - From VBOP

The only real interface is to invoke tt_app with appropriate flags. The best way to do this is to use the launch_command to fire off a remote_reference for you.  This method handles the difference between windows and unix for you.

 

Interface to Problem Manager - From Domsrvr

We can call the c_api using the cr::send_api_msg() call.  Look at cr::quick_ticket() to see how it is called in this case (in cr.spl).  Note the user exit where you can add to what is being called.

 

Invoking Scripts and Reports

launch_command() - Fires off a remote_reference for you.

 

Interfacing into the Domsrvr.

bop_cmd is the best way.  bop_msg is also possible.

 

Interfacing into a running vbop.

Vbop will accept messages to the root object.  These are documented in Wand Builtins.  You can use bopmsg or bop_cmd to send a message to the vbop process. The most useful methods you can call are:

  • void call ( string method_name, 0 or more name/value pairs)
  • void manage( string manager_name)
  • void open ( string dispob_name)
  • void stats(void).

 

For example - to bring up the customer profile screen where TARGET is the box that vbop is running on based on a phone number (Assuming a method in ???? which pops up the user based on phone number):

 

bopmsg -s @ “vbop -k%:$TARGET” root call call_manager.manage_dispob ???

 

bopmsg @ “vbop -k%:TARGET” root call

caller.select_name.cst_smart_style.find_key

               key_name “phone_number” “\”$id\””

???? Check this it is not correct yet.

 

 

Case Studies

 

Lets do some real life modifications.  Prepare a design document for your change.  Present the document to your peers.  Implement your changes.

 

  1. Your customer is running a centralized call management system for multiple sites.  When he makes a quick ticket to handle a problem, he wants to select a ticket template based on the customer, and the priority of the call request.
  2. Your customer is interested in seeing log activities separately from all activities in call manager. The belief is that the comments in an activity of type log is very useful and needs to be seen separately.
  3. Your customer wants to be able to look at activities by agent / category.  The intent is to track the time an agent spends based on the activities he is performing.  In order to handle this, we then need to look at activities by agent and other parameters.
  4. Your customer wants a simplified form for call entry.  When the description field is completely entered, we want to pop-up the knowledge helper (if there is no solution entered).
  5. I want to be able to look for sub-sets of activity types in the call request log.  For example all logs, or all transfers, or all modifies for the call request..
  6. The customer wants to set the responsible organization for a quick ticket equal to the organization on the call request.
  7. Make a program to dump the ER database tables
  8. Build a ‘quick’ entry call system for a ‘dispatch’ based environment.  Calls get dispatched to areas, and a user has several areas he watches to get the ‘next’ problem.
  9. A simple ACD integration.  You are presented with a phone number who is the caller, and the phone it is being delivered to.  You need to pop up the caller profile form and, if the phone number is found, populate the caller profile with the caller information.
  10. When a call request is closed - if the ‘closer’ is not the assignee, contact the assignee that it has been closed.
  11. When a call request is created with a high priority, if it is not assigned in 1 hour, send email to marsha.  If it is open after 1 day, send email to marsha. Perhaps we should have a way to change who we send it to in case marsha leaves us.
  12. The customer wants to add two new fields to a call request.  The first is ‘Request type’  (A helper field that contains Help desk, Service Request, Change Request or Trouble Report.  The second is a target date the call should be satisfied by.  The user wants to automatically fill in the target date field based on the request_type and priority.  This target date should use workshift to properly handle work times. Additionally if the type is not Trouble Report, you should not be able to make a quick ticket for it.
  13. A customer wants to allow either analysts or customers to be able to create call requests.
  14. A customer is using the network resource field to represent products they carry.  Each product has a list of people who are available to work on problems in that area.  When the call request is created, and the network resource is associated with the call request, we want to display the analysts who know how to fix those problems and only allow one of those analysts to be selected as the assignee.

Outcomes