Data Model Types

In the data model, the root is a value of type hash. When you write something like user, it means that you want the "user" variable stored in the root hash. This is similar to root.user, except that there is no variable called "root", so that would not work. We call this root the root namespace.

Note that in that our example, the data model, that is the root hash, contains further hashes and sequences (lotteryNumbers and cargo). This is because a hash contains other variables, and those variables have a value, which can be of any type, including a hash or sequence.

Supported types

RPL supports the following types:

  • Scalars:

    • String

    • Number

    • Boolean

    • Date

  • Containers:

    • Hash

    • Sequence

    • Collection

  • Subroutines:

    • Methods and functions

    • User-defined directives

  • Miscellaneous/seldom used:

    • Node

Scalars

A variable that stores a single value (size, price, and test).

To use a scalar in a template, you specify its path from the directory, separated by a period. For example, to access the price of a mouse, write mouse.price. When you put the ${...} code around an expression like this (${mouse.price}, you are instructing RPL to output the corresponding text based on the value of the scalar.

These are the basic values of one of the following type:

String

Simple text.

To specify a string value, enclose the string in double quotation marks ("") or single quotation marks (''). Enclosing at string in quotation marks is known as quoting. If the string contains the character used for quoting (either " or ') or a backslash (\), you must precede the symbol with a backslash (\); this is called escaping.

For example, if you use a " for quoting and want to print It’s "quoted" and this is a backslash: \, use:

${"It's \"quoted\" and

this is a backslash: \\"}

If you use ' for quoting and want to print It’s "quoted" and this is a backslash: \, use:

${'It\'s "quoted"

and this is a backslash: \\'}

You can concatenate two strings with the + operator, for example:

${profile.firstname + ‘ ‘ + profile.lastname}

Remember that there is a distinction between text in the template and strings. Strings appear where an expression would appear, inside an interpolation or a directive.

Number

RPL does not distinguish between whole numbers and non-whole numbers. For example, for example 3/2 will be always 1.5, not 1.

Note that "50" and the number 50 are different in RPL. The former is a string of two characters, while the latter is a numerical value.

Boolean

A boolean value represents a logical true or false (yes or no). For example, whether or not the visitor has logged in. Typically, you use booleans as the condition of the if directive, for example: <#if loggedIn >...</#if> or <#if price == 0>...</#if>; in the last case, the result of the price == 0 part is a boolean value.

Date

A date variable stores date/time related data. It has three variations:

  • A date with day precision (often referred to simply as "date") as April 4, 2003

  • Time of day (without the date), as 10:19:18 PM. Time is stored with millisecond precision.

  • Date-time (sometimes called "time stamp") as April 4, 2003 10:19:18 PM. The time part is stored with millisecond precision.

    Due to system limitation, RPL cannot always determine which parts of the date are in use (i.e., if it is date-time, or a time of day, etc.).

    It is possible to define date values directly in templates using the date, time and datetime built-ins. These built-ins are described in Built-Ins Reference.

IMPORTANT: Bear in mind that RPL distinguishes strings from numbers and booleans, so the string "150" and the number 150 are different.

Containers

Container variables hold other variables, referred to as sub-variables. RPL supports the following container types:

Hash

A hash is a mapping technique used to associate a key with a value. In other programming languages, hashes are called maps or dictionaries.

You specify a hash as:

{‘key1’:’value1’, ‘key2’:’value2’}

Keys are usually strings. Values can be of any type.

A variable that acts as a directory (root, mouse, elephant, and python). Hashes store other variables called sub-variables, by name (e.g., mouse and size).    
A hash associates a unique lookup name with each of its sub-variables. The name is an unrestricted string. A hash does not define an order for the sub-variables in it. The variables are accessed by name.

Note that since a value can have multiple types, it is possible for a value to be both a hash and a sequence. In this case, the value supports index-based access as well as access by lookup name. However, typically a container will be either a hash or a sequence, not both.

As the value of the variables stored in hashes and sequences (and collections) can be anything, it can be a hash or sequence (or collection) as well. This way you can build arbitrarily deep structures.

The data-model (or its root) is a hash. It is also called the root namespace.

Accessing items in a hash

To retrieve a value in a hash, specify the key of the item. For example, to obtain the color of the apple:

<#assign fruitColors={‘apple’:’red’, ‘orange’:’orange’}>

${fruitColors[“apple”]}

Alternately, when the key is a valid identifier (letters, followed by letters and/or digits), you can access an item using the dot notation. Using the previous example, you can obtain the color of the apple this way:

${fruitColors.apple}

Sequence  

Associates an integer number with each of its sub-variables. A sequence is similar to a hash, but it stores sub-variables sequentially by index number. You use a sequence to build a list.

The first sub-variable is associated with 0, the second with 1, the third to 2, and so on. These numbers are often called the indexes of the sub-variables. The sub-variables are ordered. Sequences are usually dense, i.e., all indexes up to the index of the last sub-variable have an associated sub-variable, but it's not strictly necessary. The type of the sub-variable values need not be the same.

For example, in the following data model, animals and whatnot.fruits are sequences:

 (root)
  |
  +- animals
  |   |
  |   +- (1st)
  |   |   |
  |   |   +- name = "mouse"
  |   |   |
  |   |   +- size = "small"
  |   |   |
  |   |   +- price = 50
  |   |
  |   +- (2nd)
  |   |   |
  |   |   +- name = "elephant"
  |   |   |
  |   |   +- size = "large"
  |   |   |
  |   |   +- price = 5000
  |   |
  |   +- (3rd)
  |       |
  |       +- name = "python"
  |       |
  |       +- size = "medium"
  |       |
  |       +- price = 4999
  |
  +- whatnot
      |
      +- fruits
          |
          +- (1st) = "orange"
          |
          +- (2nd) = "banana"  

To access a sub-variable of a sequence, you use a numerical index in square brackets. Indexes start from 0, thus the index of the first item is 0. To get the name of the first animal you write animals[0].name. To get the second item in whatnot.fruits (which is the string "banana") you write whatnot.fruits[1].

Note that since a value can have multiple types, it is possible for a value to be both a hash and a sequence. In this case, the value supports index-based access as well as access by lookup name. However, typically a container will be either a hash or a sequence, not both.

Collection    

A restricted sequence. You cannot access its size or retrieve its sub-variables by index, but you can list them with the list directive.

Subroutines

Subroutines include methods, functions, and user-defined directives.

Methods and functions

Methods and functions are used to calculate a value, influenced by the parameters you give to it.

The difference between methods and functions is that methods are typically defined by the language and the data-model, and functions are defined in templates using the function directive. Both methods and functions can be used in the same way.

Methods/functions are first-class values, just like in functional programming languages. This means that functions/methods can be the parameters or return values of other functions/methods, you can assign them to variables, and so on.

RPL provides predefined methods as described  in Method Reference. It is also possible to create methods and functions using the function directive. 

Consider the provided method avg, which is placed in the root of the data model as part of the language definition. This method can be used to calculate the average of numbers.

The usage of methods will be explained in detail in a later section, but the following example illustrates methods:

The average of 3 and 5 is: ${avg(3, 5)}

The average of 6 and 10 and 20 is: ${avg(6, 10, 20)}

The average of the price of a python and an elephant is:

${avg(animals.python.price, animals.elephant.price)} 

 

produces this output:

The average of 3 and 5 is: 4

The average of 6 and 10 and 20 is: 12

The average of the price of a python and an elephant is:

4999.5 

User-defined directives

A user-defined directive is a reusable template fragment which can be used as an RPL tag. Creating user-defined directives is described in Defining your own directives.

User-defined directives (such as macros), are first-class values similar to functions/methods.

For example, assume the value of a variable box is a user-defined directive that prints an  HTML message with a title bar and a message. You can use the box variable in the template as shown in the following example:

<@box title="Attention!">

  Too much copy-pasting may lead to

  maintenance headaches.

</@box> 

 

Using methods/functions versus user-defined directives

As a general rule, you should use a user-defined directive instead of a function/method in the following cases:

  • The output (the return value) is markup (HTML, XML, etc.). The main reason is that the results of functions are subject to automatic XML escaping (due to the nature of ${...}), while the output of user-defined directives are not (due to the nature of <@...>; its output is assumed to be markup, and hence already escaped).

  • The side-effect is important and not the return value, for example, a directive whose purpose is to add an entry to the server log. In fact, a user-defined directive cannot have a return value, but some type of feedback is still possible by setting non-local variables.

  • You need to perform flow control.

Miscellaneous types

Node variables represent a node in a tree structure, and are used mostly with XML processing.

A node is similar to a sequence that stores other nodes, referred to as child nodes. A node stores a reference to its container node, referred to as the parent node. The node value can be of multiple types, for example both a node and a number. In such cases, it can store a number as the "pay-load". In addition to the topological information, a node can store meta data such as a node name, a node type (string), and a node namespace (string). For example, if the node symbolizes an h1 element in an XHTML document, then its name is h1, its node type is element, and its namespace can be http://www.w3.org/1999/xhtml. The designer of the data model determines what this meta data means and whether it is used.

Next steps

Learn more

Data Model Values