This translation is older than the original page and might be outdated. See what has changed.

System objects


The model of objects that is used in the XOne script machinery allows access to all the functionality of the application with only some objects of global entity and without having to memorize a hierarchy of objects and functions typical of the runtime, since priority has been given to the knowledge of the structure of the application (collections and relationships between them) that is easiest for the developer.

Following are listed the objects that make up the model used by XOne script machinery.

Element Description
Objects Model in VBScript. Objects Model in VBScript.
Objects Model in JavaScript. Objects Model in JavaScript .

Scopes and Visibility


One of the most important things to understand before using the object model is understanding within which scope each script runs, in order to access the data correctly. At the XOne machinery you can find different areas of execution::


An script is running in object scope when the action that has invoked has been called from an object.


For instance, the actions of type <action> defined within the behaviour nodes of a collection, such as the case of <create> are in the scope of the object that contains them. .


So when an object is created new, the actions are executed within the <create> node of that object and if any of them calls a script, it will be executed in the scope of the newly created object.
In the object scope the global variable. This contains a reference to the object that the script is executing, in the case at hand, This would be the object that is being created.
In this scope the global variable ThisDataColl is Nothing.
The variables defined in this scope will last while the script execution lasts and they will be destroyed when the action in question ends.



This scope is much less common than the object. The scripts that are executed in this scope are the ones that are defined in <coll-action> nodes.

When a collection action is called, the scope will be that of the collection that contains the action that is running.

The most representative case in this scope are the <onlogon> actions that are executed when the user connects to the application. In this specific case, the scope in which each script is executed is that of the collection that contains the <onlogon> node.

When scripts are executed in collection scope, the global variable ThisDataColl contains the collection that called the script and the This variable is Nothing.



The variables defined in this scope will have the same lifetime as those in the object scope.


When a function is called (defined within an inclusion file, or within the same script block), a local scope is created in which you can define variables that follow the same rules as any language that allows local variables.

Local variables will be destroyed when leaving the function.Local variables will be destroyed when leaving the function.


For each object of the model, the following visibility rules are met:

  • AppData is visible always from any scope and in any circumstance.
  • This is visible while the scripts execution lasts. The calls to funcions do not exit from the execution scope. This same rule is valid for ThisDataColl. Both objects, in case being defined are visible within all the functions that are called during the execution of the script.
  • User has the same visibility than AppData and its existance is defined only by the presence of a connected user..
  • The variable defined in the script main block (object scope or collection ) will be visible for all the fucntions called within the script.
  • Cannot be declared (DIM) a local variable with the same name than a global variable already existing. If reference is made to a variable defined in the global scope within a function, it will be accessed to the global variable..
  • The local variables are not visible within the functions called within another function (e.g. Ifi A declares the V1 variable and then calls B, within B you don´t have access to V1).




When an script runs an action that causes the execution of events of the machinery:

  
 (e.g.. When an assignment to a property of an object is executed, said action can call another script).\\


It is important to bear in mind that when calling a nested script inside another one that is already running, the value of the global variables may change, since the action may be executing from a different place.
Each nested action must be considered as a completely independent execution. Global variables defined within a script are not visible within a nested action.
If an action wants to exchange data with another nested within it, it has to use methods that do not consist of the use of script variables, but have to resort to storing that data somewhere that is visible to any execution environment:
(e. g. Data objects within the collections, AppData object, parameters stack … etc.)

The nesting of scripts follows “the same rules” than the XML actions nesting of the XOne Machinery, so we will not expand too much on this concept.


Although this concept is related to the field of data, we have separated it by the importance of understanding it. Once we have assimilated the operation (data life time) of the local and global variables within a script, we will refer to the storage of data in other media that, although, they are within the reach of the script code, this is not part of the runtime script in concrete, but of the XOne machinery in general.

So we have the following cases:

  • AppData has a collections list named “globals”. These collections are are those that are defined in the mappings and for each one of the definitions <coll> it will have one and just one global collection. The objects that have these collections are visible for all the scripts executed at any time, so an action can use a global collection to storage data in an object and giving values to another different script.


It is important to take into mind that if inside an script are loaded data in a global collection and from another one this collection is cleaned, the effects of both actions have scope within both scripts (e.g. If an object executes an script in its action <create> and inside this script is assigned value to a property that calls to another script in the <onchange> action, the second script will see the same data that was assigned by the first one, since it is operating over an object of the machinery. If the script of the <create> has created a global variable to save any value on it, this variable will not be visible within the script of the <onchange>.

  • If inside an script an object or a collection is created (e.g. It is called to CreateClone of a global collection) and in this object or collection values will be loaded and data will be stored, these data will not be visible for any other script nested or not within the one which has been created by the object or collection, since it is about an object restricted to the scope of the script that created it.


  • AppData defines an stack of parameters that can be used to pass values among scripts that are executed in different scopes or that are nested. This stack has global scope for the whole application and and it is accessed through AppData.PushValue and AppData.PopValue. If inside this stack is entered an object or a collection created inside an script, this one will be available for any other script to get it out of there.
  • The global object User can be used as region of data exchange. If an script stores data in the User properties, or in the User variables, these data will be available for any other script, both nested and subsequently executed, as long as they are not modified.


  • Any data stored in the database may be recovered from any other scope (this can be obvious, but a lot of questions I had to answer about that in these years).



Before starting to develop processes using a script, it is interesting to review this section, since the questions posed here are common questions that are raised repeatedly. In this way, the objective is to clarify some key concepts that are elementary for the correct understanding and execution of the developments.


If a global collection is “full” using the loadall method, independently of the script language used, will be full after exiting from the script, and therefore the framework will see it like that, full.

If a global collection is emptied or filtered within an script, these changes will affect the framework execution and of course other scripts execution.

This secondary effect may be exploited to search certain functionalities, but it may cause many headaches if it is not taken into account.


For instance, if the application uses the “Clientes” collection to show the clients list on screen and from an script a filter is applied as follows:

AppData.GetCollection(“Clientes”).LinkFilter=”CODIGO=1”


We are going to work only with this client, but when leaving the script, the clients global collection will be filtered by that code, and therefore, in the clients list of the application only will come up the client 1.


This that can be obvious it is a very difficutl problem to find when it is about complex applications, so it is good have into account these rules to minimize this problem:

  • Whenever possible, DO NOT work with the same collections in the framework (UI of the application) and the scripts. IT DOESN´T MATTER to have two collections exactly the same in the mappings.


  • If you do not want to have duplicated the collections for UI and script, is good to get use to work with a backup of the collection, so, instead:
Set c = AppData.GetCollection(“Clientes”)
c.LinkFilter = “CODIGO=1”
c.StartBrowse


The most suitable is doing:

Set c = AppData.GetCollection(“Clientes”).CreateClone
... resto del proceso ... 
' No olvidar destruir la referencia
Set c = Nothing


Once exiting from the scope in question, the “c” variable is destroyed, and as before the reference was canceled, the local collection is destroyed and the memory recovers.

The global collection “Clientes” has not been touched at all, so it doesn´t affect the UI behaviour of the application.



These two ways of going through a collection are totally differents and do not interfere at all between them, so it is important to understand what they are and how they work.


Although the detailed explanation is in the reference, here we just indicate some wrong behaviors or not at all appropriated that we have found in real applications:


  • Doing LoadAll before going through a collection with StartBrowse.


This is not only unnecessary, it can be counterproductive. LoadAll loads all the objects of the collection in memory, so it can be big penalty to the application execution.

In fact, the StartBrowse mechanism has been developed to avoid loading all the objects of a collection in memory to work with them.

If we are going through a collection that have many objects to make any type of work with them, DO NOT call to LoadAll. \\In fact try NOT to call toLoadAll unless it is about cases in which you have the situation totally under control..

  • Doing LoadAll for counting the amount of elements of a collection.


This is not necessary.
If we want to count the amount of elements of a collection in most of the platforms, just StartBrowse(True) which counts the elements.
If the platform doesn´t support the counting for this way, the most recommended is preparing a little collection in which you have an SQL of grouping to which put a filter and it is done by StartBrowse to read the counting value.

Loading an undetermined amount of objects in memory only to know how many there are is not necessary and besides consumes much more time than previous solutions.


  • Mofifying the CurrentItem. This practice is supported in some platforms, but is limited by the database features.


Those databases that allow modify a table with open cursors they will will not give problems, those that do not allow it will cause execution errors.

As it is about an error not associated to any code but to the database, it can be difficult to determine the problem that originates it.

It have been made this affect affects as little as possible but always there are limitations. If the amount of objects to modify is not big, all of them can be loaded with LoadAll and modify them there.

If it is about a big amount of objects, we can think in modify them with an SQL sentence, or by default, locally store the Ids of the objects affected and then upload and modify them.


If in an script a backup of a collection is created, or it is been working with a backup of an existing collection already (as it could be a contents, for instance) any reference done to an object of that collection must be canceled before exiting from the script.


If the reference to the collectin is canceled before exiting from the script, the references to the objects this collection contains must be canceled BEFORE canceling the collection.


This is because once the reference to the collection is canceled, the scripting machinery will try to destroy it, and this in turn will destroy the objects it contains. If the object is referenced later, this will try to work with a collection that has been destroyed with the consequent problem of access to released memory.

In some places, this may not cause problems, in anothers may cause the immediate exit from the program.

So, the idea is:

  1. Canceling the references inverse to its creation (Collection and then object, so the object is canceled and then the collection).
  2. Not storing collections created locally in global instances (a collection created inside a function must not be stored in global variable, a collection created within an script must not be stored in a variable of AppData neither in the parameters stack.)



When an script can be called from several different situations it has to be ready to be reentrant.

An example is an script executed within an <insert> node and that in turn it calls to Save, or an script executed within an <onchange> that can causes the call to itself when modifying the same property that has executed it. These cases may cause infinite loops which will derive in an overflow stack.

At any of these cases marks stored in visible backgrounds for any script must be used, such as a property of the object itself, or the variables of the collection or the object in question.

Like this, a reentrant script could be more or less like this one:

<insert....
<action...
	If This(“MAP_SAVING”) = 0 Then
		This(“MAP_SAVING”) = 1
		. . . hacer cositas . . .
		This.Save	' Esto llama a esta acción otra vez
		This(“MAP_SAVING”) = 0
	End If
</action>
</insert>


When the call to Save causes the execution of the action nested inside the script we are executing, the execution in This will be interrupted .Save and it will be created a new scope of the same object called again to the script code.

The first thing it will be done is checking if MAP_SAVING has zero value. As the condition is not fulfilled, the script ends and comes out, so the previous scope is recovered and the script continues after This.Save, the flag is cleaned again and the normal execution of the script is finished.