How to use Internal Objects in DataBasic

Introduction

Internal DataBasic objects (DBOs) are variables that reference objects stored within the Reality executable, in contrast to external DBOs which reference language objects by way of a language server.

Internal objects are a subset of generic DataBasic objects. These objects are used in DataBasic as you would if writing JavaScript code, the main exception being that the "." dot operator is replaced with the DataBasic "->" (hyphen greater-than) operator. Internal objects can be easily converted to and from JSON (JavaScript Object Notation) format to enable industry-standard data interchange.

To support an object orientated language syntax (for example, new) a set of special methods is available. Special methods have a % character prefix (for example, %New) to identify them from standard methods. Special methods and their keywords are case-insensitive.

Initialising an Object Manager

Before any internal objects can be used or created an Object Manger must be initialised by using the %ObjMgr special method, for example:

CxnObj = %ObjMgr()

This form of the method creates a connection object that references an Object Manager. This connection object — also known as a manager object — automatically becomes the default connection object, and does not have to be explicitly named thereafter. It is possible to initialise multiple Object Managers but there is no benefit in this.

If you want your DataBasic program to use language and internal objects you must initialise both a Language Server and an Object Manager. To avoid potential confusion, both connection objects should be explicitly named.

Creating objects

Once there is a default connection object the %New special method can be used to create DBOs. An object is instantiated and this object is referenced by a DBO variable.

An empty object can be created by specifying %New() with no parameters and then populating it with ad hoc fields and values:

UserAccount = %New()
UserAccount->surname  = "Smith"
UserAccount->forename = "Paul"
UserAccount->email    = "paul.smith@Mail4U.eu"

Alternatively an object can be created directly from an in-line JSON document by using %New with the JSON keyword:

UserAccount = %New(JSON, '{ "surname"  : "Smith",
                            "forename" : "Paul",
                            "email"    : "paul.smith@Mail4U.eu" }' )

Or a reference to a previously-defined JSON document:

UserProfile = '{ "surname"  : "Smith",
                 "forename" : "Paul",
                 "email"    : "paul.smith@Mail4U.eu" }'

UserAccount = %New(JSON, UserProfile)

By default, new fields cannot be added ad hoc to any object created from a JSON document — that is, the structure of the document is invariant — but they can be added to a copy of such an object. This default behaviour can be changed with the %Set special method.

Arrays

Square brackets [ ] are used to access array items, as in JavaScript, although for backwards compatibility curved brackets ( ) are also supported. Array indices start at 0. Arrays can be multi-dimensional.

PayRoll = '{ "employee" : [
  { "fullName" : "Smith, Paul",     "grade" : 2, "salary" : 50000 }
  { "fullName" : "Andrews, Chris",  "grade" : 7, "salary" : 75000 }
  { "fullName" : "Newbury, Alison", "grade" : 1, "salary" : 45000 } ] }'

Personnel = %New(JSON, PayRoll)

Promotee = Personnel->employee[2]->fullName
Personnel->employee[2]->grade   = 9
Personnel->employee[2]->salary  = 83000

Associative arrays

Although array indices must be numeric, object elements can also be accessed by using the name of the required object element. With reference to the previous example:

Personnel = %New(JSON, PayRoll)

Promotee  = Personnel->employee[2, "fullName"]

Or even:

Personnel = %New(JSON, PayRoll)

Recipient = "fullName"
Promotee  = Personnel->employee[2, Recipient]

Object references

It is possible to create a reference (pointer) to any object, or object element. With reference to the previous example:

Personnel   = %New(JSON, PayRoll)

Staff       = Personnel
StaffMember = Staff->employee[0]
StaffName   = StaffMember->fullName

Note that Staff and StaffMember are both references into the Personnel object. Any changes made through these references are reflected in Personnel, and any other references to the same object.

Object references can be used to improve performance, and code readability when objects are structurally complex. For example, instead of:

Project->Team->Member->fullName    = "Gray, Michael"
Project->Team->Member->jobTitle    = "Engineer"
Project->Team->Member->skillRating = 7

Substitute:

CoWorker = Project->Team->Member

CoWorker->fullName    = "Gray, Michael"
CoWorker->jobTitle    = "Engineer"
CoWorker->skillRating = 7

However, fields of an internal object cannot reference another object.

Copying an object

Objects, and object elements, can be duplicated by using the %New special method with the Object keyword:

Personnel = %New(JSON, PayRoll)

Staff       = %New(Object, Personnel)
StaffMember = %New(Object, Personnel->employee[0])

Alternatively an object, or object element, can be copied to a field in another object by simple assignment:

Personnel = %New(JSON, PayRoll)

HumanResources = %New()
HumanResources->Staff = Personnel
HumanResources->StaffMember = Personnel->employee[0]

Because fields of an internal object cannot reference another internal object, assigning an object to a field duplicates the assigned object.

Comparing two objects

Because DBO variables are only references to objects, the only meaningful comparison is equality or inequality: in other words, do these two DBO variables reference the same object, or not. Only the individual non-object fields of two objects can be compared. Even comparing duplicated objects always returns FALSE.

Deleting an object

Objects are automatically deleted when a DBO variable is reassigned or deleted (as in local subroutine or user function variables after the RETURN statement). In addition all objects saved in local variables are deleted when the program ends. Any DBOs saved in named common sections can be shared between program levels and are not lost when the program ends; this is not the case with data in unnamed common sections.

However, an object remains in existence as long as there is at least one pointer to the object. To delete an object you must make sure that the object variable and any pointers to it are assigned to something else. For example, given:

CoWorker = Project->Team->Member

then Project = "" is not enough to release the space allocated to Project because CoWorker still exists. CoWorker = "" is also required.

Object error handling

Object errors are treated as DataBasic exceptions and, once code is developed, it is recommended that an exception handler is always defined to catch any exceptions that may be thrown by the object. If no exception handler is defined, any errors generated by the DataBasic Object in run time will be treated as fatal and trapped by the DataBasic debugger.

Internal object errors are most often likely to be caused by attempts to access a non-existent field. Fields in an empty object can be created as required by simple assignment, but they must be created before they can be referenced. By default, accessing a non-existent field throws an exception, but optionally DataBasic can be configured to return a null object.

See DBO Errors and the DataBasic Debugger.