JIX
(C) Henri Lesourd 2018, 2019
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1. Rationale JIX (aka. Javascript, Interactive, and eXtensible) is a Javascript toolkit meant to enable the development of simple and evolveable software. It is made of a set of combinable mini-languages that enable setting up and developing simple apps mostly without programming, and of a set of orthogonal libraries that enable extending these simple apps by means of Javascript code to develop more sophisticated designs. This provides an excellent basis to develop modern, leightweight webapps in an incremental fashion. JIX is free software. It is released under the LGPLv3. 2. A tour of JIX In this document, we describe the functionalities available in JIX by means of showing how to develop the most salient parts of a webapp that enables browsing a little music database: Figure 1: a simple webapp to browse a music database In Section 3 below, we explain how data can be represented in JIX by means of objects. In Section 4, we show how to use an extension of HTML called JXML to develop user interfaces, while the development of JIX applications directly by means of the Javascript programming language is discussed in Section 5. In Section 6, we talk about how to build, package, install & run these applications by means of JIX. Finally, in Appendix A and Appendix B, we give a summary of the APIs and of the events system. 3. Data description language Objects in JIX are represented as attribute-value lists, (mostly) like any other Javascript objects. For example, the song "Imagine", by John Lennon, could be represented (using JSON notation) as the following object: The simplest way available in JIX to store such objects is inside CSV files, with the first line describing the objects' attributes, and with one object per line. For example: CSV files can be edited with any spreadsheet software like Excel or Open Office, which makes the creation and manipulation of data files very convenient in JIX. On the first line of such a CSV data file, a given column's description contains its name, plus a number of optional informations, related to the type of its values, separated from the name by a colon (":"). For example, in the CSV snippet shown above, the column Year has values that are of type int, while the column Authors has values that are of type "list of strings" (written as "*str"). The data types currently available in JIX are:
To make all this more concrete, we will now develop our previous example a little bit more, and on the one hand, show how to create data files containing objects of several classes (i.e., songs, as above, but also authors), and on the other hand, show how precisely these data files can be laid out in the filesystem, along with the way to query and modify the objects that they contain. Let's now turn our little media database to something a bit more elaborate. In the CSV file shown above, the information related to the authors of a song is stored as a list of strings, using only the name and the first name of each author to indentify him/her. Let's now create another CSV file (named "Persons.csv") to store the description of our authors, now using first-class objects to this purpose (rather than only strings): In the CSV file above, the authors' descriptions have an id that can be used to refer to them, plus well-defined attributes to store their names and first names. Also, we use a list of int (type written as "*int") as the type of the objects' Years attribute, to store the birth and death years of our authors. To use this newly minted way of representing our authors' information in the CSV file (named "Songs.csv") that contains the songs' descriptions, we will modify it in the following way: Observe how we changed the type of the column Authors, turning it to a set of objects' references (written as "*!obj"), and how we replaced the content of the related cells by lists of references pointing to the objects stored in "Persons.csv" that describe the relevant authors. Finally, we will now show how to query and modify the data which is stored in our little database. Let's suppose we have a folder (named "mwiki") for our project, located in "/home/henri" in the filesystem. Under the UNIX shell, we have: To query our database, we will use the jix command. To list all the songs stored in the CSV files located in the directory /home/henri/mwiki having a Genre equal to "Rock", we can do: To list all the songs from John Lennon stored in the database (when the name of the folder containing the CSV files is omitted, the jix command looks for the data files in the shell's current working directory), we can do: And to add another author in the database, we do: Now to to set the first name of the author we just added in the database, we can proceed as such, first fetching the corresponding object by means of its id attribute, then modifying it: 4. User interfaces User interfaces in JIX can be implemented (mainly) in two ways : either by means of an HTML page containing Javascript code that creates widgets by means of the JIX APIs, and then attaches the obtained widget tree to the browser's DOM to activate it, or either by means of an extension of HTML called JXML, enabling the designer to develop the user interface directly by means of markup. 4.1. Core HTML+CSS As we just noted, JXML is an extension of HTML, which means that all the HTML tags are available in JXML, along with all the CSS styling functionalities that come with them (since for each JXML tag there is one corresponding HTML tag once the JXML has been expanded to HTML, the whole set of available CSS styling functionalities also applies to JXML tags, by means of the class and style attributes, as usual). In the current Section 4.1, as a remainder, we describe some of the core functionalities of HTML+CSS, as they are often used directly in the development of JIX/JXML user interfaces. 4.1.1. Text attributes Basic HTML tags:
Basic CSS properties:
4.1.2. Characters
4.1.3. Composite markup Grouping elements:
Alignment, size & borders (CSS):
4.1.4. Other common HTML tags
4.2. Layout (JXML)
4.3. Displaying objects JXML markup elements can have a content which is given as markup, but they can also display data (i.e. Javascript objects). This is done by means of setting the value of the value attribute of an element, which can be given as a string containing a JSON expression (the <lines> and <columns> tags are the only JXML elements which work this way at the moment). For example, the following markup:
is displayed as:
More complex Javascript objects can also be displayed, for example the following <lines> element:
is displayed as:
Arrived here, we would like to be able to not only display our Javascript elements as JSON values, but also to have a way to customize the display of these Javascript elements. This is the purpose of the skins that exist in JXML, and can be attached to the kind of data-displaying JXML tags that we are currently describing. Such skins enable to, given a set of Javascript classes, for each one of these classes, define a shape, which contains informations that are used to redefine various aspects of the behaviour of the elements of this class (especially, how the elements of a given Javascript class should be displayed). For example, the following skin: defines a way of displaying the Javascript objects of the classes "person" and "song". Let's now extend the markup given in our previous example, by means of adding a skin attribute that refers to the skin above:
Our modified <lines> element is now displayed as:
4.4. Queries In Section 3 above, we saw how to query a small database made of a set of CSV files by means of a local jix command (which, used in this way, is in fact directly accessing the files, just like, for example the UNIX grep does). But to be able to access the data in a remote way, we need to do something else, namely to start a server that enables people to access the data from outside. The JIX system includes a little web/data server that provides a simple way to do this. For example, if the machine where the data files are located has the domain name jix-software.com, and if our data files are located in /home/henri/mwiki in the local filesystem: then we can start jix as a server on the port 8081 by means of the following command: From there, it is now possible to query the database thru the internet from another machine by means of a jix db command, as we did before. For example: Once our data are exported by a server in this way, it is possible to access them from inside a JXML page in the browser. To do this, we must first connect to the server, by means of declaring a JXML variable that refers to it. For example: Overall, in this case we use this variable to refer to the location where the data are stored, i.e. we use it as something that acts as a container for these data. With such a container, we can perform queries that fetch data from this container, in a way that is in essence the same as what we did with the command jix db fetch in the example above. Performing queries inside JXML is done by means of setting the src attribute of one of the data-displaying JXML tags described in Section 4.3 above to the name of the container we want to query, and of setting its value attribute with a string that contains the query. For example: is displayed as:
Let's add a skin to the <lines> element above: We now obtain:
4.5. Events In JXML, markup elements can be made sensitive to events, and turn these events to messages that are finally sent to other elements that act as targets of these messages. Events and messages propagate into the markup in a process called bubbling, namely they propagate from the inner elements to the outer elements, until an element able to process them is found. This mechanism proceeds in two steps: first, when an event occurs, a markup element that is sensitive to this event must be found. The event is then turned to a message. This relationship is specified by means of setting the events attribute of the appropriate markup elements. The syntax of an events' specification, as given in the events attribute of a JXML element is as follows (in BNF-like notation): Next, once a given event-sensitive markup element turned an event to a message, this message is emitted at the point in the markup where the initial event appeared. From there, the second step of the bubbling mechanism proceeds, and the emitted message propagates upwards until a markup element which "knows" how to choose the target of the message can be found. The information that is needed to choose the target of the bubbling messages of a certain kind is given by means of setting the targets attribute of the appropriate markup elements. The syntax of a targets' specification, as given in the targets attribute of a JXML element is: The parameters (denoted by the PARM non-terminal symbols in the first BNF above) that appear in a parameters list can be atomic JIX objects (i.e. of the type int, num or str). Such parameters, along with the targets (denoted by the TARGET non-terminal symbol in the second BNF above) that appear in a target expression can also be JIX objects' references. Additionaly, there are a number of predefined symbols which refer to some of the most salient elements which take part in an event's bubbling process:
To make all this more concrete, we now show how to build a simple form, which sends a query to a <lines> element (which will then be loaded with the query's result), by means of a load message (for a complete list of JXML messages, see Appendix B below). Here is the <lines> element which will be the target of this load message: The <lines> element above is initially displayed as: And here is our form, implemented by means of another <lines> element, with its events and targets attributes defined in an appropriate way: This form, made of two text input fields and of an "OK" button, is initially displayed as: When, for example, the user edits the input field corresponding to the songs' release year to select all the songs of the year 75 (i.e.: "*75"), and then clicks on the "OK" button to submit:
this button emits a click event, which bubbles until it reaches the containing <lines> element. Since this <lines> element's events attribute says that click events should be turned to load messages having the value of the current markup element (which is the very <lines> element we are talking about) as a parameter, the following load message: is then emitted, at the level of the button which received the initial click event. The second step of the bubbling process takes place, and this message bubbles until it reaches the containing <lines> element, which has a targets attribute that defines the target of load events (i.e.: we have targets="load:#view1"). The chosen target in this case is the markup element having "view1" as its id, which is the target <lines> element that has been shown in the beginning of the current example. The message is then sent to this target, which becomes:
Finally, its value attribute having been redefined, our target <lines> element is redisplayed as:
5. Javascript programming The JIX system is made of a set of orthogonal Javascript libraries which provide an excellent basis to develop modern webapps, especially:
5.1. Objects The JIX object system enables the programmer to manipulate Javascript objects, which can either be instances of a Javascript primitive datatype, or instances of a JIX datatype. In Section 5.1.1 below, we discuss how the JIX object system deals with objects that are instances of Javascript primitive datatypes, while in Section 5.1.2, we discuss the creation and manipulation of JIX objects. 5.1.1. Primitive datatypes The primitive datatypes known to JIX are the datatypes bool (booleans), num (numbers), str (strings), array (arrays), obj (objects: the root datatype from which all JIX datatypes inherit), func (functions), and type (type descriptors). The bool, num, str, array, obj and func datatypes are equivalent to the corresponding primitive Javascript datatypes, i.e. Boolean, Number, String, Array, Object and Function, while the type datatype is the datatype of all the JIX type descriptors. The basic functions available in JIX to manipulate objects are the functions typeOf(), that returns the type descriptor of an object, the functions parse() and serialize(), which convert JSON strings to Javascript objects (and vice-versa), and the setMethod() method, that enables attaching a new method to a given datatype (this method also works on primitive datatypes). Example 1:
Example 2:
Example 3:
Finally, the JIX type descriptors (i.e. objects of the type type, like num, str, etc.) are also factory functions, namely they can be directly used as constructors (without needing to use the Javascript operator new), for example:
5.1.2. JIX objects The JIX object system also enables the programmer to create new datatypes, by means of the type() datatype constructor, which has the following prototype:
The two methods name() and parent() respectively return the name of a datatype, and its first parent in the inheritance relation. Thus we can create a new type person (which corresponds to the person datatype we used in the examples given in Section 3 and below), and then display its name and the name of its parent datatype:
Once created, such a datatype can be used to create new instances of itself. When calling the constructor type() to create a datatype τ, the JIX object system automatically creates a polymorphic constructor τ() for this newly created datatype which has the following prototype:
For example, to create a new objet of the type person we can do: Here is a more complex example, which illustrates how to create composite objects made of objects that belong to different datatypes (i.e. an object of the type song corresponding to the song datatype we used in the examples given in Section 3 and below, which contains objects of the class person in its Authors attribute), along with some of the options of the serialize() method to handle these kinds of objects:
Finally, composite objects can be parsed by means of the function parse() ; the example below also illustrates how to fetch objects from memory by means of their id using the method getById():
5.2. Widgets The JIX system provides widgets, that all inherit from the datatype widget. Such widgets can be attached to any browser's DOM element by means of the method attach() of the dom datatype, which is the JIX datatype that enables manipulating DOM elements. Moreover, to make constructing composite XML-like objects more convenient, the type() datatype constructor also supports datatype descriptions having an optional array-like part (created by means of defining the "$" attribute) ; in that case, the constructor τ() of such a given JIX datatype τ has the following prototype:
For example, to represent HTML <div> tags, a (rather incomplete) JIX widget datatype could be defined as follows: Then, to create an HTML <div> tag having its name attribute equal to "div1", containing the three text elements "a", "b" and "c", we could do: And to fetch the page's <body> tag, and to attach the DIV1 widget to it, we can do: Here is a more complex code snippet, that shows how to implement a simplified version of the last example given in section Section 4.3 directly in Javascript: Finally, to enable representing markup as a pure Javascript datastructure, rather than as a Javascript expression containing constructor calls, the markup() function enables turning a Javascript array representing a chunk of markup to the corresponding JIX widget. Such a Javascript array representing a chunk of markup should have the following syntax: Above, the TAG parameter should be a JIX datatype (i.e., an instance of the type datatype), the ATTRS and ELTS parameters are both optional, ATTRS should be a composite object (i.e. a pure attribute-value list), while ELTS should be an array containing elements having the datatype widget or str. The previous example can then be rewritten as follows by means of two single calls to the markup() function: The instances of datatypes that inherit from the widget datatype have attributes that directly map to those of the corresponding HTML/JXML tags, and thus, have the same names. A subset of the HTML tags' attributes is available (e.g. id, name, class, style, etc.), plus the additional attributes that JXML provides (especially events, targets and value, and a bunch of others, like title or current, for example). The following example shows how to implement a <superpose> JXML tag containing three <div>s and a button that triggers a focus event inside it: Just after the call to attach() above, our markup is initially displayed as:
When the "OK" button is clicked, it becomes:
5.3. Containers The JIX containers are the abstraction by means of which groups of data can be manipulated, loaded from disk and saved to disk. These containers can be queried, and the objects that they contain can be directly modified, copied, moved or deleted. Here is a simple code snippet, which illustrates how to implement the first example given in Section 3 directly in Javascript. We first open the database by means of the container() constructor, we perform the query by means of the query() method of the container datatype, and then we display the objects that have been fetched by the query: We obtain: In the following example, we open the container and first create an object, then we fetch this object by means of its id from the container, and finally display it: We obtain: To create the very same object inside the container without performing a query, we could have used the constructor of the type person directly, with an extra parameter to specify the target container: Or either, we could have used the parse() function, which also can take such an extra parameter that specifies the target container: To delete objects that belong to a container, we simply use the method delete() that is available for all objects that are instances of a datatype inheriting from obj. In the example below, we delete all the songs having a genre equal to "Rock": Now, to copy objects, we use the method copy() (without specifying a target container, which means that the copy will be created in memory(), which is the container representing the Javascript memory). The method containerOf() is used below to test to which container the source and target objects respectively belong: We obtain: The two objects representing, respectively the author of the first song and the author of its copy, are in fact the same object, i.e., if we do: We obtain: In the example above, the copy() method performs only a flat copy, thus the instance of the person datatype representing the author of the song is not copied. It is possible to obtain a full recursive copy by means of specifying a traversal mode when calling copy() (see Appendix A.1 for the definition of the available traversal modes). In the example below, we create a new author and a new song, we perform a full recursive copy of this song, and then we display a number of things to show the difference: We can now see that the copy() method duplicated the object representing the song, but also the object representing the author: To move objects, we use the method move(). The move() operation is roughly equivalent to a copy() followed by a delete(). When the method delete() is called on an object, this object does not immediately disappears, it is only scheduled for deletion (so the object remains, but its behaviour changes, for example, such objects can still be read, but can not be fetched by queries) ; the method isDeleted() may be used to know if an object is scheduled for deletion. The example below performs a copy of an object, then it moves this object, and it performs a number of outputs to show what happens: We obtain: Finally, to save the modifications to disk, we use the method sync(): 5.4. Servers The JIX system provides a server datatype to implement servers (and the corresponding clients) that can handle the communications between the different parts of an application across a network. These servers enable implementing remote procedure calls on the one hand, and access to remote data on the other hand. In this section, we cover these two topics in turn. 5.4.1. Remote procedure calls A JIX server is able to receive messages on a given port, and these messages are remote procedure calls. In most of the cases, JIX servers are able to deal automatically with the process of serialization/deserialization of the data they exchange with the caller. Here is a simple example of a server and of its corresponding client. The server (implemented by means of a Javascript file named "srv1.js") is as follows: And here is the client (implemented by means of a Javascript file named "cli1.js"): Let's try, now. First we start the server: And now we run the client: The terminal from which we started the server becomes: 5.4.2. Containers & access to remote data The JIX containers are the abstraction by means of which groups of data can be mapped from one memory area to another. To this end, containers can be derived from one another, including across a network. On the one hand, a derived container behaves as a cache with respect to the container it is derived from, and on the other hand, when the data located in a derived container have been modified, the modifications can be propagated to the containers it is derived from by means of the sync() method. Here is a simple example, which illustrates how to create two containers, the second one being derived from the first one, how to query the second container and modify the data that has been fetched from the first container, and finally how to synchronize the two containers: We obtain: We now show a more elaborated example featuring a simple server that enables the little database we used in Section 3 above to be accessed in a remote manner, and a web page containing Javascript code that creates a <lines> JXML tag that performs a query to this database and displays the data by means of the appropriate skin. The code of the server is given below: The code of the corresponding web page is: 6. Applications In this section, we describe the command-line tools provided by the JIX system to manage a project on the one hand, and the installation procedure of the JIX system itself on the other hand. We will now cover these two topics in turn. 6.1. Command-line tools The JIX command line tools provide means to run applications in a test environment, to compile them, to package tarballs, and to install applications. The basic directory layout of a JIX application is as follows:
The JIX command line tools are all available via the jix command. The table below describes its most common options:
6.2. Installation The installation procedure is minimally simple:
7. Conclusion With JIX, we have a toolkit which is first of all based on a set of well-established, mature standards (HTML5, CSS and Javascript), and next, which provides a set of combinable components to build applications. This makes lightweight, modular software easier to develop, and enables an incremental style of development in which design and implementation can be meshed. Thus, ideally, at any time in the development process, the developers should remain able to continue refining the design, rather than being forced to freeze it early due to rigidities stemming from implementation concerns. As far as we know, such problematic implementation concerns are primarily of two kinds: (1) an excessive complexity of the underlying software and tooling ; (2) a lack of elegance in the overall software's architecture. There is of course no magic receipt that would enable developers to address these issues without investing the appropriate amount of creative effort: that's why they are all the more important and indeed, are relatively well-known. Especially:
Happy hacking ! Appendix A. The JIX APIs A.1. Objects Types (class type):
Objects (class obj):
Objects (wrapper functions that can be called with the main parameter being an unboxed javascript value):
Predefined constants:
Objects (miscellaneous):
Numbers (class num):
Arrays & strings:
Strings (class str):
Arrays (class array):
JIX:
A.2. Servers Servers: (class server)
APIs (class api):
A.3. Containers Containers: (class container)
Queries (class query):
Servers: (container-related methods)
A.4. Widgets, wobjets and skins Widgets (class widget):
DOM elements (class dom):
Widgets (constructors):
Appendix B. JXML Events and messages Events:
Messages:
|
About JIX Services Download Manual Contact |