Part VIII - Enumerated Types in OpenText Content Server

OpenText Content Server OScript does not provide native support for enumerated types. In this blog post I'll discuss why enumerated types are useful, and introduce a simple framework for mimicking the data type with OScript.

So what is an enumerated type? The Wikipedia page on Enumerated types (opens new window) sums it up in the introduction:

In computer programming, an enumerated type is a data type consisting of a set of named values called elements, members or enumerators of the type. The enumerator names are usually identifiers that behave as constants in the language. A variable that has been declared as having an enumerated type can be assigned any of the enumerators as a value.

I like to think of an enumerated type as a set of related constants, where the value of each constant is somewhat irrelevant as long as they are unique within the set. A variable that is declared to be of the enumerated type can be assigned any of the values within the set.

Enumerated types reduce bugs and increase code readability. Take the following OScript as an example (and yes, you do see this sometimes):

if eventType == 1
    ...
elseif eventType == 2
    ...
elseif eventType == 3
    ...
end
1
2
3
4
5
6
7

This works, but is difficult to read since we have no idea what the values 1, 2, and 3 represent. It's error prone and difficult to debug.

However, if we had the following (more on the construct later):

$MyModule.EventType.kDelete = 1
$MyModule.EventType.kCreate = 2
$MyModule.EventType.kModify = 3
1
2
3

With this we can rewrite the example as:

if eventType == $MyModule.EventType.kDelete
    ...
elseif eventType == $MyModule.EventType.kCreate
    ...
elseif eventType == $MyModule.EventType.kModify
    ...
end
1
2
3
4
5
6
7

Much better! This is easier to read since there is no guessing as to what the values 1, 2, and 3 represent. It also provides context to our variable since we know it belongs to the $MyModule.EventType enumerated type.

In broader terms, it doesn't matter what values kDelete, kCreate, and kModify have. They just need to be defined and unique within the enumerated type.

# Enumerated Types in OpenText Content Server

RHCore provides a framework for loosely defining an enumerated type. By no means is this strict (e.g., a developer could set eventType = 101, which isn't valid). It's a small inconvenience, but doesn't deflect from the concept. A developer just needs to ensure comparison and assignment operations are done with the enumerated type and not with a hardcoded integer.

The framework doesn't deviate far from how Content Server manages the states of tasks (e.g., via $Inbox.InboxUtil). However, the framework I introduce is far more generic and reusable in other contexts.

An enumerated type with RHCore is created by:

  • orphaning RHCore :: RHCore Root :: Enum into our OSpace;
  • adding it to our globals;
  • implementing the elements() function; and
  • executing 0Setup() to generate and assign some features based on elements().

For example, say we orphan Enum into our module, name it EventType, and implement the elements() function to return the following list of elements:

function List elements()
    return {'Delete','Create','Modify'}
end
1
2
3

Running 0Setup() generates the following features, while removing any old features that started with the letter "k" (the "k"-prefix is a convention I picked up from iOS development and denotes a constant):

  • kDelete is set to 0;
  • kCreate is set to 1; and
  • kModify is set to 2.

The values $MyModule.EventType.kDelete, $MyModule.EventType.kCreate, and $MyModule.EventType.kModify become globally visible for use in our module.

This may seem overly complicated for defining some constants. Why not just manually add the constants to $MyModule.Utils or some other arbitrary location? It's the grouping of these elements that provides context and increases code readability. The framework also provides some additional features, which I'll outline in the next section.

# Example and Additional Features

The Enum class has some additional features to assist when using enumerated types. Let's look at another example to demonstrate.

The OScript Date package has a function called Date.DayofWeek(), which accepts a date and returns the day of the week as an integer. The convention is 1=Sunday, 2=Monday, etc. The documentation provides the following example:

if ( dayOfWeek == 0 || dayOfWeek == 7 )
    Echo( todayString, " is the weekend!" )
else
    Echo( todayString, " is a weekday." )
end
1
2
3
4
5

See the bug? The dayOfWeek == 0 comparison is wrong since 0 is not a valid value. This is the exact type of programming error we avoid with enumerated types.

Let's introduce an enumerated type for the days of the week and fix the example. I wrote earlier that the values of our constants are somewhat irrelevant, but in this case we want to force our constants to match the convention used by the Date.DayofWeek() function (i.e., 1=Sunday, 2=Monday, etc.).

We orphan Enum into our module, name it DayOfWeek, and implement the elements() function as follows:

function List elements()
    return { \
        {'Sunday', [LLIAPI_LABEL.Sunday], 1}, \
        {'Monday', [LLIAPI_LABEL.Monday], 2}, \
        {'Tuesday', [LLIAPI_LABEL.Tuesday], 3}, \
        {'Wednesday', [LLIAPI_LABEL.Wednesday], 4}, \
        {'Thursday', [LLIAPI_LABEL.Thursday], 5}, \
        {'Friday', [LLIAPI_LABEL.Friday], 6}, \
        {'Saturday', [LLIAPI_LABEL.Saturday], 7} \
        }
end
1
2
3
4
5
6
7
8
9
10
11

This looks different than the elements() function I introduced earlier. This alternate syntax for each element is {elementName, verboseName, constantValue}. The elementName is the element name (same as before), the verboseName is the user-friendly display name (which we XLATE for language compatibility), and the constantValue (optional; defaults to the list index) is the constant value for that element.

With this definition we can rewrite the example as:

if ( dayOfWeek == $MyModule.DayOfWeek.kSunday || dayOfWeek == $MyModule.DayOfWeek.kSaturday )
    Echo( todayString, " is the weekend!" )
else
    Echo( todayString, " is a weekday." )
end
1
2
3
4
5

This is clearly easier to read and less error prone. The name() function is available to return the verbose name of a value. For example:

Integer dayOfWeek = Date.DayofWeek( Date.Now() )
echo("Today is ", $MyModule.DayOfWeek.name(dayOfWeek) )
1
2

This would return something like:

Today is Wednesday
1

Or, if we're using German (due to the use of XLATES in our definition):

Today is Mittwoch
1

The Enum class also has a choices() function, which can be used with the RHModel and RHForm frameworks. This allows a model field to be restricted to the values of an enumerated type, and the elements to be rendered as a <select> form field.

For example, a form field that uses the $MyModule.DayOfWeek enumerated type would render as follows:

<select name="myField">
  <option value="1">Sunday</option>
  <option value="2">Monday</option>
  <!-- ... -->
</select>
1
2
3
4
5

I haven't done much with Enum in my projects yet, but have identified multiple places where I wish I had. This will certainly make things easier going forward.

Questions or comments about this blog post or anything else you read? Please leave a comment or send me an e-mail at chris@rhouse.ch.