Puppet 6 Type System - More about Object Attributes
Introduction
This is the second posting in the series about the Object data type in the Puppet Type System - Pcore. The first post introduced the Object data type and the history behind Pcore. You probably want to read that first.
In this post I am going to show how attributes of Objects work in more detail.
Recap Defining an Object data type in Puppet
As you may recall from the earlier post - an Object data type can be created like this in Puppet:
type Car = Object[attributes => {
reg_nbr => String,
# ...
}]
(And, if done in Ruby, the part between the brackets goes into the interface
section of the create_type
body (see the first post).)
When defining an Object, the above can be shortened:
# The brackets can be omitted
type Car = Object {attributes => { reg_nbr => String }}
# The type Object can be omitted
type Car = {attributes => { reg_nbr => String }}
Attributes
Attributes are the instance/member variables of instances of an Object data type and they come in different flavours: required, optional, derived (two kinds), constant and they can be marked as being an override or being final - all explained below.
Recap of type creation, creating a new instance, and getting attributes:
# This defines the type
type Car = Object {attributes => { reg_nbr => String }}
# This creates an instance
$a_car = Car('ABC 123')
# This gets that instance's variable/attribute reg_nbr so
# 'ABC 123' will be noticed
notice($a_car.reg_nbr)
Attribute Definition - Short and Long Form
Attribute Name
The name is given as a hash key in both the short and long form.
The attribute’s name is a String and it must be unique within the object among both attributes and operations. This rule extends to its parents attributes and operations as a redeclaration means it is an override of the parent’s definition and it must be marked as such to be accepted. The name must start with a lowercase letter and cannot be qualified (i.e. defined as being in a namespace).
Short Form
You have already seen the short form of attribute definition:
reg_nbr => String
Which is an attribute name to data type mapping. An attribute specified like this is always a regular required attribute. All other kinds of definitions require use of the Long Form.
Long Form
In the long form of attribute declaration the map is from an attribute name to a Hash of attribute options. The equivalence of the short form is this:
reg_nbr => { type => String }
When using the long form there must at least be a definition of type
.
Attribute Options
name | type | meaning |
---|---|---|
annotations |
- | Advanced: Allows association of annotations - to be explained in a separate blog post |
final |
Boolean |
A final attribute cannot be overridden in sub types. |
kind |
Enum |
See “Attribute Kind” below |
override |
Boolean |
Must be set to true if overriding a parent type attribute or operation, an error is raised if attribute/operation does not exist in a parent type. |
type |
Type |
An upper cased type reference |
value |
Any |
A literal default value constrained by kind and type |
Note: Inheritance will be covered in a coming blog post and I will explain the importance of
final
andoverride
then.
Attribute Kind
name | meaning |
---|---|
constant |
The attribute is a constant; cannot be set when creating an instance, must have value specified. |
derived |
The attribute’s value is computed. There must exist a method at runtime to compute the value. The attribute’s value cannot be given when creating an instance. |
given_or_derived |
Same as derived, but the value may be given when an instance is created. Think of this as a computed default value. |
reference |
Advanced: Default is false , and true means that the value is not contained and is thus serialized as a reference to a value that must exist elsewhere (typically in the same serialization). To be explained in another blog post. |
Note:
derived
was covered in the first blog post in this series.
Multi Valued Attributes
Multi valued attributes are simply defined as being of Array
/Tuple
or Hash
/Struct
data type where the type parameters are used to constrain the number of allowed values and their data type which can be any type in Pcore.
This is a big win compared to some other modeling technologies where multi valued attributes must be scalars.
type Garage = { attributes => {
parked_cars => Array[Car]
}}
Union Values are Variants
Since Pcore has a Variant data type; describing that a value must be one out of several possible data types, it is easy to model more complicated data models.
means_of_transportation => Variant[Car, Boat, Bike]
Extra attributes/values
The Object type does not allow “extra attributes” like in some modeling
technologies where it is possible to specify a required set and any named additional extra attributes. With Pcore Object you have to model that
as an Object with a hash attribute where the extra “optional” values go.
Typical Usage
Typically, attributes are either required and can be specified using the short form, or they are optional and can either be specified:
- in short form using
Optional[T]
if it is acceptable to haveundef
as the default value, or… - in long form with
type
andvalue
(if default value should be something other thanundef
). In this case the type should not beOptional[T]
unless you want to be able to explicitly assignundef
even if default value is something else.
Here is an example showing different kinds of attributes:
type Person = { attributes => {
# name is required
name => String,
# optional with undef default value
nick_name => Optional[String],
# fav color is optional and defaults to 'blue'
fav_color => {
type => Enum['blue', 'red', 'green'],
value => 'blue',
}
# avg_life_expectancy is a constant
avg_life_expectancy => {
type => Float,
kind => constant,
value => 70.5, # globally
}
}}
Summary
In this post I covered the details of specifying Object attributes and the various kinds of attributes “required”, “optional”, “constant”, and “derived”. In the next post I will cover inheritance of Object types.
No comments:
Post a Comment