Command

Description

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:

Figure 6.1. Command

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.

Syntax

Table 6.7. Command

ProductionSyntaxLinks
command

"command" ( "id" ":" ) ? command_id command_properties
   input_argument *
   in_check ?
   output_argument *
   out_check ?
   script ?
"end" "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.

Table 6.8. Command properties

PropertyValueRequiredDefault valueDefined in typesDefined in factoriesDefined in services
ida prefixed identifieryes yesyesyes
privateyes or nononono(1)yes
obsoleteyes or nononoyesnoyes if not private command

(1): yes if command is private, no if command is not private

All properties will now be explained.

Command property id

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”.

Command property private

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”

Command property obsolete

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.

Input and output arguments

Each input argument as well as each output argument has the following properties:

Table 6.9. Command input and output argument properties

PropertyValueRequiredDefault valueDefined in typesDefined in factoriesDefined in services
ida prefixed identifieryes yesyesyes
typeany existing typeyes yes(1)yes
voidableyes or nononoyes(1)yes
checkan expression or a scriptnovoidyes(1)yes

(1): yes if command is private, no if command is not private

[Note]Note
The above properties for command arguments are similar to the previously discussed properties of attributes (see Table 6.2, “Attribute properties”).

Argument property id

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”.

Argument property type

The value of property type defines the argument's type, i.e. the type of the object referenced by the argument.

Argument property voidable

voidable defines whether the value void is allowed for the argument. By default, void is not allowed, which means that:

  • in case of an input argument, a runtime error will immediately occur whenever an attempt is made to call the command with the input argument's value set to void.
  • in case of an output argument, a runtime error will immediately occur whenever a command returns with the output argument's value set to void.

See also: Chapter 12, Void values

Argument property check

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:

  1. by an expression that must evaluate to yes for any valid value (used in simple cases)

  2. 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]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:

Input argument property default

Each input argument has the following additional property that doesn't exist for output arguments:

Table 6.10. Additional command input argument property

PropertyValueRequiredDefault valueDefined in typesDefined in factoriesDefined in services
defaultan expression or a scriptnovoidyes(1)yes

(1): yes if command is private, no if command is not private

[Note]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:

  1. by an expression (used in simple cases)

  2. by a script whose output argument holds the default value (used in more complex cases)

See examples below:

See also:

Examples

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]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 file
  • o_log_file: a reference to the file that has been used to store the message
  • o_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

  • in case of successful writing to the file, o_log_file will contain a value, and o_file_error will be void
  • in case of failure to write to the file, o_log_file will be void, and o_file_error will contain a value

This 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

See also