Chapter 7. Model-View-Controller internals

Table of Contents

7.1. Model
7.1.1. Model relationship
7.1.2. Developing eBox types
7.2. View
7.3. Controller
7.4. Publishing models and CGI mapping

The main aim from this chapter is getting into the guts of the Model-View-Controller framework. From an eBox developer is not required to get into if it is not strictly necessary. However, it could be useful to use it properly, to enhance it in some way or, what is more, to add features not already addressed before.

7.1. Model

The main logic related to the data model is encapsulated on EBox::Model::DataTable. Its constructor requires at least a GConf module, i.e. an eBox module, a directory to store the data model and a Gettext domain which normally match with the GConf module one. The model description is instanced just one using the Singleton method pattern at table method. As the Figure 7.1 depicts, there are two main data model DataTable and DataForm which represents the template system to be used by real data models such as EnableForm or ConfigureLogTable.

Figure 7.1. Data model class diagram

Data model class diagram

Data manipulation is done by addRow, removeRow and setRow, they are convenient methods to add, remove and update rows. The fields to fill the row are tailored to simplify and speed up the data input management from the HTTP request through a CGI. Moreover, a typed version is intended to be developed to ease the internal management and the exposed API as Section 1.5.3 requires. For instance, to add a member in the member table you may use this piece of code:

$model->addRow( ( name => 'admin',
                  ipaddr_ip => '192.168.1.2',
                  ipaddr_mask => '32',
                  macaddr => '00:50:57:D0:11:08'
                )
    

The data from a model is stored and retrieved from GConf, some caching is done in order to speed up the data access times. In order to describe the GConf structure, it had better to use the result from gconftool command:

Example 7.1. GConf structure from a model

 data_version = 1
 /ebox/modules/objects/objectTable:
  /ebox/modules/objects/objectTable/keys:
   version = 44
   /ebox/modules/objects/objectTable/keys/x6666:
    readOnly = false
    name = RRHH
    /ebox/modules/objects/objectTable/keys/x6666/members:
     /ebox/modules/objects/objectTable/keys/x6666/members/keys:
      version = 1
      /ebox/modules/objects/objectTable/keys/x6666/members/keys/memb44:
       readOnly = false
       name = Claudia
       ipaddr_ip = 192.168.1.92
       ipaddr_mask = 32
   /ebox/modules/objects/objectTable/keys/x1850:
    readOnly = false
    name = MKT
    /ebox/modules/objects/objectTable/keys/x1850/members:
     /ebox/modules/objects/objectTable/keys/x1850/members/keys:
      version = 3
      /ebox/modules/objects/objectTable/keys/x1850/members/keys/memb9612:
       readOnly = false
       name = Florencia
       ipaddr_ip = 192.168.1.42
       ipaddr_mask = 32
      /ebox/modules/objects/objectTable/keys/x1850/members/keys/memb9390:
       readOnly = false
       name = Hugo
       ipaddr_ip = 192.168.1.46
       ipaddr_mask = 32
      /ebox/modules/objects/objectTable/keys/x1850/members/keys/memb2675:
       readOnly = false
       name = Silvina
       ipaddr_ip = 192.168.1.43
       ipaddr_mask = 32

As you may see, the object data model stores two objects RRHH and MKT, which the former saves one and the latter three members. The data_version indicates the data structure version in the same sense as Section 3.9 explains. Each data model stores their values under keys/id directory, the id is obtained using get_unique_id as Section 3.3 describes. Version property is used by caching scheme, as you might guess, which is currently done per data model basis. As each object data model row has several members using the member data model, they are stored within the object directory as if another model. Each type has its value stored in GConf, for instance the field name for object x6666 has as value RHHH. readOnly attribute indicates whether the row is read only or not.

With regard to form data model, the GConf structure is quite simplified flattening the keys/id to the main GConf directory to let the form developer see its content easily.

7.1.1. Model relationship

Model relationship is describe through EBox::Types::HasMany and through EBox::Types::Select and whose management is done by EBox::Model::ModelManager and EBox::Model::DataTable. The former singleton class coordinates all the available models along eBox, see Section 7.4 for details. Currently, there are two kind of relationships:

HasOne

Determined by a select type which fetch its possible values or options from a foreign model, that is, from a value which is stored in a field in another model. To describe so, the foreignModel and foreignField must be set at table description. The model manager will store the one-to-one relationship to maintain the referential integrity.

HasMany

The HasMany relationship represents that a single field from a data model stores another data model. The inner model looks like a submodel within the outer model and thus the rows and printableValueRows methods must return the information within that model per row. Likewise, when a row with a HasMany type is removed, the inner data model content is deleted as well.

Furthermore, the Observer pattern is applied to the data models as well. The models can subscribe to actions performed in foreign models such as addition, removal or updates. In order to achieve so, the table description must have notifyActions attribute as an array reference indicating which are models if you are interested to. So the model manager notifies an action is performed by a model calling notifyForeignModelAction method on the observer.

Whenever an changing state action is performed, the data model asks the model manager if any of other modules is using that row. To determine whether an observer is using a row or not, it is asked by calling warnOnChangeOnId when an update is the performed action or isUsingId on removals as well as a search on the observer model data. If there are observers subtle to be changed and automaticRemove is set, a warning is shown before forcing the operation. If automaticRemove is not set, the data model must specify how it should act subscribing to the changing actions as it is described above instead.

7.1.2. Developing eBox types

The current eBox types selection is quite small and limited. We can see at first glance how its class structure at Figure 7.2.

Figure 7.2. eBox types class diagram

eBox types class diagram

As we can see Abstract manages the essential information for every eBox type. That is, their common attributes such as optional, editable, printableValue, etc. It also provides the main methods not to be overridden such as setMemValue, restoreFromHash and so on. Basically, an eBox type allows the developer abstracting from GConf storage and user input correctness since their handling is automatic. Furthermore, an eBox type is responsible for determining which setter and viewer must be used in eBox user interface by asking to HTMLSetter and HTMLViewer respectively.

Firstly, in order to add a new eBox type is required to look over Figure 7.2 to check it if something similar has been developed before and just a class extension is required. Once we are sure a new type is needed for our models, these following methods, at least, must be overridden to make the type work:

_setMemValue

Set the memory value for the type. It is assured that this method will be called if only there is something to fill the type and its content is valid.

_storeInGConf

Store the given type in a GConf directory from a GConfModule. The expected behaviour is if it has no value to store, remove any previous stored data.

_restoreFromHash

Restore the type value from a hash reference which usually comes from hash_from_dir returned value.

_paramIsValid

Check the correctness from the parameters passed. It assures that it is something to be checked

_paramIsSet

Check if the given parameters contain the data needed to fill the type, i.e. it exists and it is not empty

_setDefaultValue

Set the default value for the type

Apart from overriding these methods, it should set HTMLSetter, HTMLViewer and type on the constructor. The former two attributes determines which mason template, giving its path, must be used to set and show the type value respectively. The latter must be the class name in lower case. Methods compareToHash, isEqualTo and printableValue are recommended to be overridden.

The common eBox types are in ebox base debian package, however it is also possible to develop a custom type which will be used only by a single eBox module or by any of its dependencies. This happens to SinglePortService which depends on the services eBox module.