An attribute is a reference to an object of a defined type and belongs to an object or a service.
For example, a customer object could have attribute identifier of type positive32, and attribute name of type string. Or, service XML_utilities could have attribute tag_open of type string.
Each attribute is accessed through its identifier which must be unique within the object or service.
In case of an attribute belonging to an object, the interface to the attribute (i.e. the way how the attribute can be accessed and used) is defined in the object's type, and the implementation of the attribute is defined in the factory that implements the object's type. Hence, in our above example of attribute name in a customer object, the attribute's interface is defined in type customer, and the implementation is defined in factory customer.
In case of an attribute belonging to a service, the attribute's interface and implementation are both defined in the service.
Table 18.1. Attribute
| Production | Syntax | Links |
|---|---|---|
attribute |
| the section called “Attribute” |
As we can see from the above syntax, an attribute has a number of properties. Their usage depends on whether the attribute is defined in a type, a factory or a service. The following table shows a summary of all properties.
Table 18.2. Attribute properties
| Property | Value | Required | Default value | Defined in types | Defined in factories | Defined in services |
|---|---|---|---|---|---|---|
id | a prefixed identifier | yes | yes | yes | yes | |
type | any existing type | yes | yes | (1) | yes | |
voidable | yes or no | no | no | yes | (1) | yes |
check | an expression or a script | no | void | yes | (1) | yes |
default | an expression or a script | no | void | yes | (1) | yes |
private | yes or no | no | no | no | (1) | yes |
kind | one of the following:variable constant readonly_variable readonly_constant | no | constant | yes | (1) | yes |
setable | one of the following:all factory service creator none | no | (2) | yes | (1) | yes |
get | script used to get the attribute's value | yes if property kind is readonly_variable or readonly_constant | script that returns the attribute's value | no | yes | yes |
set | script used to set the attribute's value | yes if property kind is variable | script that sets the attribute's value | no | yes | yes |
obsolete | yes or no | no | no | yes | no | yes if not private attribute |
(1): yes if attribute is private, no if attribute is not private
(2): default value for property setable is defined as follows:
Table 18.3. default value for property setable
| if ... | then default value is |
|---|---|
property kind is set to constant | creator |
property kind is set to variable and attribute is used in service | service |
property kind is set to variable and attribute is used in type or factory | factory |
property kind is set to readonly_variable or readonly_constant | none |
All properties will now be explained.
The value of property id is an attribute identifier used to reference the attribute. Each id must be unique within an object or service. An attribute identifier is a prefixed identifier starting with a_ as prefix. For more information about prefixed identifiers see the section called “Prefixed identifier”.
The value of property type defines the attribute's type, i.e. the type of the object referenced by the attribute.
example:
type product_2 attribute identifier type:positive32 end attribute name type:string end attribute price_in_cents type:positive32 end end
The value of property voidable defines whether the value void is allowed for the attribute. By default, void is not allowed, which means that a runtime error will immediately occur whenever an attempt is made to set the attribute's value to void.
example:
type product_3 default_factory:yes attribute identifier type:positive32 end attribute name type:string end attribute price_in_cents type:positive32 voidable:yes end end
In the above example, the value of attribute price_in_cents can be void at runtime (voidable:yes), but all other attributes cannot be void
See also: Chapter 6, Void values
Property check is used to define which values are allowed for the attribute. Each attempt to set the attribute to an invalid value at runtime (i.e. a value that doesn't pass the check) will immediately result in a runtime error.
There are two ways to define this property:
by an expression that must evaluate to yes for any valid value (used in simple cases)
example:
type customer_2 default_factory:yes attribute identifier type:positive32 check: i_identifier >= 10000 and i_identifier < 100000 end attribute name type:string check: i_name.item_count >= 3 end end type
In the above example identifier must be a value between 10000 and 99999, and name must contain at least 3 characters. Hence, writing:
var customer_2 garfield = fa_customer_2.co_create ( & identifier = 123 & name = "Garfield" )
will immediately generate a runtime error because 123 is an invalid value for identifier.
by a script that evaluates if the value is valid (used in more complex cases)
example:
type customer_3
attribute identifier type:positive32
check
script
var positive32 min; max
// retrieve minimum and maximum values from database:
// connect to database
// min = ...
// max = ...
// disconnect from database
check i_identifier >= min and i_identifier <= max
end script
end check
end attribute
attribute name type:string check: i_name.item_count >= 5 end
end typeIn 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.
example:
type customer_4
attribute identifier type:positive32
check
script
var positive32 min; max
// retrieve minimum and maximum values from database:
// ...
check i_identifier >= min and i_identifier <= max &
error_message: "Identifier must contain a value between " & v_min.to_string & " and " & v_max.to_string & "!" &
error_id: invalid_identifier &
error_data: fa_resource_positive32_min_max_error.co_create ( &
resource = void &
data = i_identifier &
min = v_min &
max = v_max )
end script
end check
end attribute
attribute name type:string &
check: i_name.item_count >= 5 &
error_message: "The name must contain at least 3 characters!" &
error_id: invalid_name
end attribute
end type![]() | Note |
|---|---|
| Checks are one of the most important features for writing more reliable code in Obix. See Chapter 4, Contract Programming, also called Design by Contract (TM) for more information. |
See also:
Sometimes the validity of one attribute value depends on the values of one or more other attributes, that is to say there is a mutual dependency of the validity of several attributes. In such a case we have to use property attribute_check.
Because attribute_check concerns several attributes, this property doesn't belong to a single attribute, such as the check property in the previous section, but it belongs to the type or service in which the attributes are defined.
The syntax of attribute_check is defined as follows:
Table 18.4. attribute_check
| Production | Syntax | Links |
|---|---|---|
attribute_list_check |
| the section called “Property attribute_check” |
There are two ways to define this property:
by an expression that must evaluate to yes (used in simple cases)
by a script that contains any number of check instructions and other instructions (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.
Example 18.1. attribute_check example
Suppose that a graphical user interface (GUI) library (or framework) supports automatic resizing of visual objects. For example, the width of a text input field increases automatically whenever the user increases the width of the window that contains the text input field. Many GUI libraries implement this feature with so-called layout managers who are responsible for defining the sizes of the visual objects. For that purpose, each visual object has a minimum size, maximum size, and preferred size, and the actual size on screen is computed by the layout manager who has to respect all visual objects' constraints. It is clear that for each visual object the maximum size must always be greater or equal to the minimum size, and the preferred and actual sizes must always be between the minimum and maximum sizes. As we can see, the validities of attributes depend on each other, so we have to use an attribute_check.
As the size of a two-dimensional visual object is composed of a width and a height, we decide to define types widthable and heightable. widthable could be coded as follows:
type GUI_widthable
attribute_list type:zero_positive32
attribute minimum_width end
attribute maximum_width end
attribute preferred_width end
attribute width kind:variable end // actual width on screen
end
attribute_check
script
check i_preferred_width >= i_minimum_width and i_preferred_width <= i_maximum_width
check i_width >= i_minimum_width and i_width <= i_maximum_width
end script
end attribute_check
end type
![]() | Note |
|---|---|
It is not necessary to check the condition i_minimum_width <= i_maximum_width, because both checks above imply this condition. |
Type heightable would be defined analogously to type widthable. To show both possibilities, the following type uses an attribute_check expression, instead of an attribute_check script:
type GUI_heightable
attribute_list type:zero_positive32
attribute minimum_height end
attribute maximum_height end
attribute preferred_height end
attribute height kind:variable end // actual height on screen
end
attribute_check check: &
i_preferred_height >= i_minimum_height and i_preferred_height <= i_maximum_height and &
i_height >= i_minimum_height and i_height <= i_maximum_height end
end type
See also:
Property default defines the attribute's value in case no value is explicitly defined in the source code.
As for property check, there are two ways to define this property:
by an expression (used in simple cases)
example:
type customer_5 default_factory:yes attribute identifier type:positive32 end attribute name type:string default: "unknown" end end type
In the above example name will be "unknown" if no other value is explicitly defined when the object is created. Thus, name will be "unknown" when we code:
var customer_5 unknown_customer = fa_customer_5.co_create ( & identifier = 123 )
and name will be "Ron" in the following case:
var customer_5 ron = fa_customer_5.co_create ( & identifier = 123 & name = "Ron" )
by a script whose output argument holds the default value (used in more complex cases)
example:
type customer_6
attribute identifier type:positive32
default
script
var positive32 last_used_identifier
// retrieve last used identifier from database:
// last_used_identifier = ...
o_identifier = last_used_identifier + 1
// save o_identifier to database
end script
end default
end attribute
attribute name type:string end
end typeSee also:
Factories and services can contain private attributes 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 17.6, “Two creators and a private attribute in a factory”
Property kind defines how the attribute's value can be read and written, as shown in the following table:
Table 18.5. Attribute property kind
value of property kind | read/write permissions | |
|---|---|---|
| attribute belongs to an object | attribute belongs to a service | |
constant | value is defined when object is created, and cannot be changed afterwards | value is defined the first time the service is used, and cannot be changed afterwards |
variable | initial value is defined when object is created, but value can be changed afterwards | initial value is defined the first time the service is used, but value can be changed afterwards |
readonly_constant | value is defined through a get script that must always return the same value | |
readonly_variable | value is defined through a get script that can return a different value each time it is invoked | |
example:
type person
attribute first_name type:string kind:constant end // first name never changes
// ('kind:constant' could be omitted,
// because 'constant' is default value for 'kind')
attribute last_name type:string kind:variable end // last name can change, e.g. when woman gets maried
attribute birthdate type:date end // birthdate never changes ('kind' defaults to 'constant')
attribute age type:zero_positive32 kind:readonly_variable end // age changes each year, and depends on birthdate
end type![]() | Note |
|---|---|
The default value for property kind is constant because objects in Obix are immutable by default. For more information see Chapter 7, Object immutability. |
If property kind is set to variable, then property setable defines which scripts are allowed to change the attribute's value, according to the following table:
Table 18.6. Attribute property setable
Value of property setable | Description |
|---|---|
all | value can be changed by any script |
factory | value can only be changed by a script in the factory that creates the object holding the attribute |
service | value can only be changed by a script in the service holding the attribute |
creator | value can only be changed by the creator script in the factory that creates the object holding the attribute |
none | value cannot be changed after initial value has been set |
example:
type person_2 default_factory:yes
attribute_list type:string
attribute first_name end // attribute is immutable. it can only be set in the creator.
// (property 'kind' defaults to 'constant', in which case
// property 'setable' defaults to 'creator')
attribute last_name &
kind:variable setable:factory end // only scripts in the factory that creates the object can change 'last_name'
attribute city &
kind:variable setable:all end // any script can change the value
end attribute_list
end typeSee also:
The value of property get is a script that defines and returns the attribute's value. This script is executed each time the attribute's value is read. A get script is used whenever the attribute's value is not simply a reference to an object stored in memory, but has to be determined by a more or less complex script.
example:
service stock_market
attribute current_dow_jones_index type:positive32 kind:readonly_variable
get
script
// connect to webservice and retrieve index
// o_dow_jones_index = ...
end
end
end
endThe above script to retrieve the Dow Jones index from a webservice will be executed each time the attribute's value is read, such as when executing:
var positive32 dow_jones_index = se_stock_market.a_current_dow_jones_index
![]() | Note |
|---|---|
In Obix attributes are always accessed with the syntax object.attribute_id (e.g. customer.name), even if a get script is defined. There are no so-called "getters" like customer.getName() in Java, C# and other languages. |
See also:
The value of property set is a script that is executed each time the attribute's value is changed. A set script is used whenever the attribute's value is not simply a reference to an object stored in memory.
example:
service GUI
attribute default_font_size type:positive32 kind:variable setable:all
get
script
// retieve value from XML file
// o_default_font_size = ...
end
end
set
script
// write i_default_font_size to XML file
end
end
end attribute
end service
![]() | Note |
|---|---|
In Obix attribute values are always set with the syntax object.attribute_id = <expression> (e.g. customer.city = "Luxembourg"), even if a set script is defined. There are no so-called "setters" like customer.setCity("Luxembourg") in Java, C# and other languages. |
See also:
Setting property obsolete to yes is a hint for the programmer to not use the attribute anymore. The attribute will possibly disappear in a future version of the software. The compiler might display a warning in case the attribute is used, but setting obsolete to yes doesn't change the behavior of the program.
example:
type person_3
attribute_list type:string
attribute name obsolete:yes end // replaced by first_name and last_name
attribute first_name end
attribute last_name end
end
end typeFor additional examples please refer to: