A command belongs to an object or a service and is used to execute a script (i.e. a set of instructions) relative to that object or service.
Each command is accessed through its identifier which must be unique within the object or service.
A command is executed through a command caller, which is a source code instruction or part of a source code expression that explicitly calls the command (see examples below).
A command can have 0, 1 or more input arguments. Input arguments are objects that are transferred from the command caller to the command. They are created by the command caller and then used within the command's script. Each input argument is a reference to an object of a defined type and is accessed through its unique identifier.
A command can also have 0, 1 or more output arguments. Output arguments are objects that are transferred from the command back to the command caller. They are created by the command's script and then used by the command caller. Each output argument is a reference to an object of a defined type and is accessed through its unique identifier.
The following figure shows a schematic representation of a command:
In case of a command belonging to an object, the interface to the command (i.e. the way how the command can be accessed and used) is defined in the object's type, and the implementation of the command is defined in the factory that implements the object's type.
In case of a command belonging to a service, the command's interface and implementation are both defined in the service.
Table 6.7. Command
| Production | Syntax | Links |
|---|---|---|
command |
| the section called “Command” |
A command has a number of properties whose usage depends on whether the command is defined in a type, a factory or a service. The following table shows a summary of all properties.
(1): yes if command is private, no if command is not private
All properties will now be explained.
The value of property id is a command identifier used to reference the command. Each id must be unique within an object or service. A command identifier is a prefixed identifier starting with co_ as prefix. For more information about prefixed identifiers see the section called “Prefixed identifier”.
As for attributes, factories and services can also contain private commands which cannot be accessed from the outside, but which are needed internally by the factory or service. This property is not used in types.
For an example, please refer to Example 5.8, “Factory with private command”
Setting property obsolete to yes is a hint for the programmer to not use the command anymore. The command will possibly disappear in a future version of the software. The compiler might display a warning in case the command is used, but setting obsolete to yes doesn't change the behavior of the program.
Each input argument as well as each output argument has the following properties:
(1): yes if command is private, no if command is not private
![]() | Note |
|---|---|
| The above properties for command arguments are similar to the previously discussed properties of attributes (see Table 6.2, “Attribute properties”). |
The value of property id is the argument's identifier which is used to reference the argument. Each id must be unique among all arguments of the command. An argument identifier is a prefixed identifier starting with the prefix i_ for input arguments, and o_ for output arguments. For more information about prefixed identifiers see the section called “Prefixed identifier”.
The value of property type defines the argument's type, i.e. the type of the object referenced by the argument.
voidable defines whether the value void is allowed for the argument. By default, void is not allowed, which means that:
void.void.See also: Chapter 12, Void values
Property check is used to define which values are allowed for the argument. If this property is defined then:
in case of an input argument, a runtime error will immediately occur whenever an attempt is made to call the command with an invalid input argument value (i.e. a value that doesn't pass the check).
in case of an output argument, a runtime error will immediately occur whenever a command returns with an invalid output argument value (i.e. a value that doesn't pass the check).
There are two ways to define this property:
by an expression that must evaluate to yes for any valid value (used in simple cases)
by a script that evaluates if the value is valid (used in more complex cases)
In both cases it is also possible to optionally specify:
a specific error_message to display to the user (overrides the default error message; can be used by UI frameworks).
a specific error_id used to programmatically identify the error, e.g. for logging purposes and statistics.
a specific error_data object that holds any useful information which can programmatically be explored. By default, error_data simply contains the value to be checked.
See examples below:
![]() | Note |
|---|---|
| Checks are one of the most important features for writing more reliable code in Obix. See Chapter 17, Contract programming for more information. |
See also:
Each input argument has the following additional property that doesn't exist for output arguments:
Table 6.10. Additional command input argument property
| Property | Value | Required | Default value | Defined in types | Defined in factories | Defined in services |
|---|---|---|---|---|---|---|
default | an expression or a script | no | void | yes | (1) | yes |
(1): yes if command is private, no if command is not private
![]() | Note |
|---|---|
The above property for command input arguments is similar to the previously discussed property default for attributes (see Table 6.2, “Attribute properties”). |
Property default defines the input argument's value in case no value is explicitly defined in the source code that calls the command.
As for property check, there are two ways to define this property:
by an expression (used in simple cases)
by a script whose output argument holds the default value (used in more complex cases)
See examples below:
See also:
Let's start with a very simple example of a logger service and then improve it step by step in order to see how to apply the above features:
Example 6.2. A simple logger service
service logger_1
command log_message
in message type:string end
script
se_console.co_message ( i_message )
end
end
end As we can see, command log_message has one input argument (message of type string) and simply displays its value on the system console
A first improvement would be to check that the message is not empty. This can be done with the check property:
Example 6.3. Check input argument (using an expression)
service logger_2
command log_message
in message type:string &
check: i_message.item_count >= 1 &
error_message: "Message must contain at least one character" &
error_id: invalid_message &
error_data: i_message end
script
se_console.co_message ( i_message )
end
end
end Sometimes, a check expression isn't powerful enough to check an input argument. In that case, we can use a check script, in which case we can use the whole power of the language to define the check. For example, if the minimum length of the message is stored in an XML configuration file, we can use a check script as follows:
Example 6.4. Check input argument (using a script)
service logger_3
command log_message
in message type:string
check
script
var positive32 minimum_length
// read minimum length from XML file
// v_minimum_length = ...
check i_message.item_count >= v_minimum_length &
error_message: "Message must contain at least " & v_minimum_length.co_to_string & " character(s)." &
error_id: invalid_message
end
end
end
script
se_console.co_message ( i_message )
end
end
end ![]() | Note |
|---|---|
A check script can contain any number of check instructions. The script is interrupted as soon as one check fails. |
It would be useful to identify each logging message with a message identifier that is the same for the same kind of message, even if the text of the message changes (for example in a multi-language application). We can therefore add a second input argument, and provide a default value that will be used if no value is explicitly specified in the command caller:
Example 6.5. Default value for input argument (using an expression)
service logger_4
command log_message
in message type:string check: i_message.item_count >= 1 end
in message_id type:string default: "message" end
script
se_console.co_message ( i_message_id & ": " & i_message )
end
end
end As for the check property, we can also use a script instead of a simple expression to define the default value. For example, if the default value is stored in a database:
Example 6.6. Default value for input argument (using a script)
service logger_5
command log_message
in message type:string check: i_message.item_count >= 1 end
in message_id type:string
default
script
var string default_message_id
// instructions to read default message id from database
// ...
// v_default_message_id = ...
o_message_id = v_default_message_id
end
end
end
script
se_console.co_message ( i_message_id & ": " & i_message )
end
end
end Sometimes it is not enough to be able to check a single value of one input argument, but we need checks that involve several or all input arguments, because of mutual dependencies. For example, suppose that the length of i_message must be greater than the length of message_id. (This wouldn't be a reasonable check in a real application, but this is just an example). In such a case we can use the in_check property, as follows:
Example 6.7. in_check (using an expression)
service logger_6
command log_message
in message type:string check: i_message.item_count >= 1 end
in message_id type:string default: "message" end
in_check check: i_message.item_count > i_message_id.item_count end
script
se_console.co_message ( i_message_id & ": " & i_message )
end
end
end An in_check can also be defined with a script, instead of a simple expression (see previous examples of check scripts).
Besides simply displaying the message on the system console, our logger could also append each message to a text file and return us the following feedback through output arguments:
o_result: the complete message that has been displayed and saved to the fileo_log_file: a reference to the file that has been used to store the messageo_file_error: an error message in case of failing to write the message to the file (file operations can always fail at runtime!)Moreover, let's require that
o_log_file will contain a value, and o_file_error will be voido_log_file will be void, and o_file_error will contain a valueThis can be done as shown below:
Example 6.8. Multiple output arguments
service logger_7
command log_message
in message type:string check: not i_message.is_empty end
in message_id type:string default: "message" end
out result type:string end
out log_file type:file_handle voidable:yes end
out file_error type:string voidable:yes end
out_check check: i_log_file =r void xor i_file_error =r void end
script
o_result = i_message_id & ": " & i_message
se_console.co_message ( o_result )
// pseudo code for file operation:
// o_log_file = ...
// if problem with file then
// o_log_file = void
// o_file_error = "Error writing to file"
// end if
end
end
end Now the command can be used like this:
var string result; file_error var file_handle log_file se_logger_7.co_log_message ( & i_message = "The sun is shining!!!" & i_message_id = "sunshine" ) & ( v_result = o_result & v_log_file = o_log_file & v_file_error = o_file_error ) if v_file_error =r void then // everything ok else // we have a problem end if
in_check script”out_check script”check script”check script”check instruction”default script”