A few days ago, I announced the project publicly and decided to make a todo app using the current version of Clio in order to showcase the features of the language. This todo app uses Clio microservices to transfer data between its client and server side. It is fully reactive and all data changes on the server will be pushed to the client. Here’s how the todo app looks like and behaves:
To run this example you’ll need
rethinkdb installed. In this blog post, I assume you’re familiar with the basic concepts of Clio and its syntax, if not you can refer to the introduction blog post. Let’s see together how to make this todo app in Clio!
Let’s first start by the server-side. In Clio, functions are isolated and each function can be considered as a microservice. Clio provides a
host utility you can use to host your functions, event emitters and variables on the network. This utility will make them directly available to clients when they import them. You can even write your functions in another programing language and import them in your Clio code. It’s also possible to import Clio functions in code written in other languages. You can find an example of how to create Python microservices and import them in Clio in the Clio GitHub repository.
When you host a Clio file, Clio makes a cluster with the specified amount of workers and starts listening for connections. Connections can be made using http[s] and ws[s] protocols. The Clio remote call protocol is a work in progress that defines how these functions can be exported, imported and called.
The created workers don’t have a shared state. To share data between workers I decided to use Rethinkdb because it makes it super easy to get a change feed of our data. Alternatively, if persistence isn’t important Kafka or RabbitMQ can be used to share state between workers.
Clio can import
.clio files from a relative path, or from the project virtual environment. In Clio, virtual environments are built-in. Even though it’s still a work in progress, they are already usable. Simply installing the Rethinkdb NPM package and importing its main file would work, but instead, I decided to make a wrapper library and install it in the project virtual environment in order to better manage dependencies.
As you can see in the above screenshot, we use NPM to install Rethinkdb in
node_modules. Next we import (and export) the module in
index.js and finally we import it from
index.js in the
rethinkdb.clio file. I didn’t import it directly in case I later need to modify a part of this library (to add better compatibility with Clio or fix conflicts). This way I can simply do it in the index.js file we created.
Now we can import the Rethinkdb library in our
host.clio file, and connect to our
In Clio, we use the pipe operator
-> to call a function, and we use
=> to assign the result to a variable. As you can see in the above screenshot these function calls and assignments can be chained. In Clio, these chains are called flows. They always start with data or some data sources, and they can contain several function calls or assignments.
On the server-side, Clio first tries to find the function in the local scope, if not found it checks whether there exists a builtin with that name and if that fails it will try the flow data object methods. On the browser, it first tries the local scope, then the builtins, then the window and finally it will try the object methods.
In the above code, we connect to our local Rethinkdb instance. We then select the
clio database and assign it to variable named
database. Next we choose the
todos table and assign it to the
todos variable. There are two types of string in Clio. The first one is defined between single quote signs (for example
'string') . The other one is defined after a
# sign and contains only a single word. It starts and ends with a white space character (for example
Still in the
host.clio file, let’s define a function to get all the todo items in our database:
By default, all defined functions are lazy, but we can use the
@eager decorator to make them eager. All functions should have at least one argument. In this example we used a
timestamp variable, but to keep things simple we didn’t actually use it in the function body. Clio being a functional programming language, Clio functions are functions in the mathematical sense. This means that functions always have an input and an output.
get_todos function runs our
todo query, gets the result cursor and calls its
toArray method which returns an array of todo items. In Clio, returns are implicit and functions return the last evaluated expression.
Let’s add a few more functions for updating a todo item, adding a todo item and removing a todo item:
Now it’s time to set up our event emitter. Let’s create an
emitter and a function that gets the data and emits it:
In the above example, we create an emitter by piping a string (the name or id of the emitter) to the built-in emitter function. The
emit_data function takes the data as argument, emits a
change event and sends the data using our
todo_emitter. Now we need to listen to the changes in our database and emit these changes using our emitter, so our clients can receive the latest version of the todo items stored in the database. This is done using the
changes method of the Rethinkdb query object
Now let’s host this file, and use these functions and our emitter as microservices! To do that, we need to create a host variable in our
In the configuration, we can specify the port (defaults to 3000), exports (functions, variables, and emitters to make available as microservices) and workers (defaults to the number of CPUs). Now, if we use the host utility, we get:
That’s it! We’re done for the server-side.
clio.js library in the
To fix conflicts caused by the name resolution order (local scope, then builtins, then window and then methods) we need to rename some of our jQuery methods, and then call
clio.process_scripts() to process all Clio scripts on the todo webpage:
Let’s also add a few DOM elements and some stylesheets to our page:
We then create a simple Handlebars template in our html file:
In order to import our Clio client file, we make a Clio script tag:
Next, we edit
client.clio and import the microservices that we created on the server-side:
We can import functions using
ws[s], but emitters can only be imported using the
ws[s] protocol because emitters provide a stream of data, and streaming is only supported by WebSockets. Let’s make a function to add a new todo item and bind it to our
preventDefault on our event to prevent the default click behavior of the browser which is to follow links. Instead our event sends a request to
add_todo. Notice that in the above function, we are using the microservice
add_todo we created in the server-side just like a regular function! We then use
$ (jQuery) to select our
#add-todo button and listen to its click events. Now we compile our handlebars template and assign it to a variable named
We still need a few functions and event handlers. One is for updating a todo item on the server if the user modifies it on the browser:
do_update_todo function, we first get the target of the event which we pipe to jQuery in order to select the parent element (which is our todo item element), and store it in a variable named
$todo. We then add the
editing class to it. We do this because when we receive data updates from the server, we need to rerender todo items with their updated data. However we should make sure we don’t replace an element with its updated version while the user is editing it. In the next lines, we get the
data-todo-id of this element and pass them to our
update_todo function. We also define a function to remove the
editing class which we will call when the user is done modifying a todo item.
We now define a function for removing a todo item that exists on the server:
Let’s set up some DOM event listeners and react to them. For example when a todo item is removed or modified, we want to call the corresponding functions:
In the above code, we define some global event listeners. If any
.remove button is clicked, we call
do_remove_todo. If we have an
input event in any
.todo element, we call
do_update_todo. Finally, if we have a
focusout event for any
.todo, we remove the editing class.
We also need a function that updates our rendered todos if they’re modified on the server, for example when another user modifies the todo items:
The above function receives a todo item as an argument. If our todo item has
new_val values, it means it was modified. If it only has
new_val it means it is new, and if it only has
old_val it means it was removed.
If we got new todo item, we simply pass its new value to our template, then to jQuery (to make a new element) and then append it to our todos container. If it was removed, we use
data-todo-id to select it and remove it. The
cat function is a Clio built-in function that joins strings together. Finally, if we got a modified todo item, we select it, check if it has the
editing class, and if not, we replace it with the new value.
One last step is to react to changes on the server, and actually render the current existing todo items:
In Clio, we use the map operator
-> *) to map the result of a function to another function. In the above code, we listen to the
change events of our
todo_emitter and update the page using the
update_page if there are any changes. In the next flow, we call the
get_todos function, map the
template function to the result, then map jQuery and then append them to our todos container one by one.
Now if we host the
index.html using a simple
python -m http.server for example), we can see our fully reactive todo app in the browser:
I hope you liked this tutorial and that I got some of you interested in the Clio programming language. Feel free to take a look at our roadmap, website, documentation and GitHub repo. If you like Clio, it would be extra nice if you could give star us on GitHub! You can check the full source code of the todo app here. If you would like to contribute to Clio, you can check our contribution guide.