Welcome to Glagol DSL’s documentation!

Introduction

What is Glagol DSL?

Glagol DSL is a domain specific language with the goal to help developers engineer microservices using the Domain-Driven Design [1] concepts out-of-the-box. To achieve this, Glagol DSL incorporates object-oriented structures that are much closer to the concepts of Model-Driven Design [2] than the usual object-oriented languages. Additionally, Glagol also supports basic web (api) framework capabilities like routing, request handling and controllers.

Glagol DSL includes grammatical declarations for Entities, Value Objects, Repositories and Controllers. In general, all of those are embedded into the language’s syntax and their usage implies following rules based on the concept behind each of them.

How does Glagol DSL work?

Glagol DSL is a transpiler and the output language is PHP. Simply put, the Glagol DSL sources (*.g) are parsed, type-checked and then compiled into PHP source code. To achieve most of its functional capabilities the Glagol DSL environment relies strongly on the Lumen framework from Laravel and the Doctrine 2 ORM.

Architecturally, Glagol DSL is a simple client-server application. The server app is the actually compiling your sources and the client is used to send compile requests and more. This approach provides performance benefits and it also allows the compiler to run over a network (Compiler-as-a-service).

Under the hood

Glagol DSL is mainly written on Rascal MPL for the server app and Java for the client app.

Footnotes

[1](1, 2) Domain-driven design: Tackling complexity in the heart of software, Eric Evans, 2004, Addison-Wesley Professional.
[2]Model-Driven Design is a set of design patterns and part of Domain-Driven Design as described by Eric Evans [1]

Installation

Before we create a simple Glagol DSL microservice lets setup Glagol DSL!

Prerequisites

Java

Make sure you have java version 1.8 installed before you can use Glagol DSL:

$ java -version
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

Get the server app

Download the glagol-dsl-server-0.6.3.jar from the latest release:

$ wget https://github.com/glagol-dsl/glagol-dsl/releases/download/0.6.3/glagol-dsl-server-0.6.3.jar ~/glagol-dsl-server.jar

Then you can alias the glagol-server as a command so that it can be used easily. To do that you simply add to the end of ~/.bashrc (or ~/.zshrc) the following command:

alias glagol-server='java -Xmx400m -jar /full/path/tp/glagol-dsl-server.jar'

And then reload your rc:

$ source ~/.bashrc

or for zsh:

$ source ~/.zshrc

Get the client app

Similarly to the server app the client app jars are available on Github releases. Download the latest release jar:

$ wget https://github.com/glagol-dsl/glagol-dsl-client/releases/download/0.3.7/glagol-dsl-client-0.3.7.jar ~/glagol-dsl-client.jar

Then you can alias the glagol as a command so that it can be used easily. To do that you simply add to the end of ~/.bashrc (or ~/.zshrc) the following command:

alias glagol='java -jar /full/path/tp/glagol-dsl-client.jar'

And then reload your rc:

$ source ~/.bashrc

or for zsh:

$ source ~/.zshrc

Start the Glagol DSL server

Glagol DSL server app can be initiated using the following command:

$ glagol-server daemon

The expected output is the following:

Initializing Glagol...
Daemon listens on port 51151...

The initialization phase might take up to 10 seconds depending on your machine’s performance. You need to keep the Glagol DSL server running if you wish to compile.

Next section will explain more about setting up an initial project structure, compiling and running an app.

Get the IDE plugin

Glagol DSL supports an IntelliJ IDEA (PhpStorm, WebStorm, etc.) plugin. You can find in in the JetBrains repositories .

_images/ide_plugin.png

Setup new project (with Docker)

After a successful Installation of Glagol DSL server and client apps you are ready to start developing. As discussed within the installation section the fastest way to start developing is to use Docker and Docker Compose.

Create a new project structure

Simply clone the default project skeleton:

$ mkdir glagol-hello-world
$ git clone git@github.com:glagol-dsl/docker-compose-project-skeleton.git glagol-hello-world

Attention

You need to have the Glagol DSL Server app running before you proceed. Remember, you can start the server app by simply running $ glagol-server daemon.

After you clone the default project skeleton you can compile it for first time! Simply run the compile command:

$ glagol compile

The output should be something like:

Successfully compiled glagol project

And then you are ready to spin-up the Docker Compose environment:

$ docker-compose up

Hint

The Docker Compose configuration will automatically install all PHP Composer dependencies upon containers startup. In fact, it will do so every time you spin-up the containers. See ./scripts/composer.sh for how it works under the hood.

Next you can proceed directly to Hello world controller.

Setup new project (without Docker)

This section describes how you can manually setup your environment for developing with Glagol DSL.

Warning

Skip this chapter if you already have setup your project using Docker.

Install PHP, MySQL and a Web Server

You need PHP 7.1 and MySQL 5.7 at the very least. The choice of a web server is not crucial, both nginx and apache were tested and proven to work perfectly fine.

PHP DS extension

Glagol DSL data structures rely on the ds extension. The simplest way to install is using pecl:

$ sudo pecl install ds
$ sudo phpenmod ds

PHP Pdo extensions

Currently, Glagol DSL supports only the MySQL database as a possible ORM backend. Therefore, the pdo_mysql extension is required. On Ubuntu/Debian you can install the MySQL PDO driver by typing:

$ sudo apt-get install php-mysql

Alternatively, you can also install it using pecl:

$ sudo pecl install pdo pdo_mysql
$ sudo phpenmod pdo pdo_mysql

PHP mbstring extension

Lumen framework requires the mbstring PHP extension. On Ubuntu you can install it simply by typing:

$ sudo apt-get install php-mbstring

Or alternatively using pecl:

$ sudo pecl install mbstring
$ sudo phpenmod mbstring

Composer

Finally, you need to make sure that you have composer installed. On Ubuntu you can install it using the package manager:

$ sudo apt-get install composer

Hint

If you want to significantly speed-up your composer you might consider installing the global performance boost package hirak/prestissimo: composer global require hirak/prestissimo

Create a new project structure

Lets create a new project. For the purpose we need a new directory:

$ mkdir glagol-hello-world

Navigate inside it and create two sub-directories src and out:

$ cd glagol-hello-world
$ mkdir src
$ mkdir out

The src folder will host the Glagol *.g source files, and the out directory will be used for the compiled sources.

Attention

You need to have the Glagol DSL Server app running before you proceed. Remember, you can start the server app by simply running $ glagol-server daemon.

Next, create a new package by making a new directory within the src folder:

$ mkdir src/HelloWorld

At the end your directory structure should look like this:

.
├── out
└── src
    └── HelloWorld

Important

Make sure that your Web Server’s virtual host maps to out/public folder. This is where the index.php is landing and where the routing starts.

Next, you need to compile for first time! Simply run the compile command:

$ glagol compile

The output should be something like:

Successfully compiled glagol project

Install Composer dependencies

The final step is to install the PHP Composer packages:

$ composer install --prefer-dist -d out/

Hint

PHP compiled sources are generated in the ./out/ directory of your project’s root. Make sure to map your web server’s root to ./out/public directory.

Set environment variables

The final step is to create an ./out/.env file that will hold environment variables (such as app’s name and MySQL connection credentials). Here is an example contents:

APP_NAME="Glagol DSL Sandbox"
APP_ENV=local
APP_DEBUG=true
DB_HOST=localhost
DB_PASSWORD=123
DB_USERNAME=root
DB_DATABASE=glagol

Hello world controller

After you have your directory structure set up it is time to create your first endpoint - the hello world page. For the purpose create a file src/HelloWorld/HelloWorldController.g with the following contents:

namespace HelloWorld

rest controller /hello-world {
    index {
        return "Hello world!";
    }
}

The snippet above represents an HTTP controller that is set to handle the index page of the /hello-world route by responding with a Hello world! message.

The index declaration is in fact the action method that is handling the GET request.

Furthermore, you might have noticed that the controller declaration does not have a name but only a route. Although controllers in Glagol DSL do look like classes they are a special type of structure. For example, controllers cannot be instantiated by the developer and they carry the routing information with them. More about controllers can be found in the language reference guide.

Compile the sources

Now compile the project! Simply run the compile command from your project’s root directory:

$ glagol compile

The output should be something like:

Successfully compiled glagol project

Hint

PHP compiled sources are generated in the ./out/ directory of your project’s root. If you are not using the preconfigured Docker Compose project skeleton you need to map your web server’s root to ./out/public directory.

Testing the index

The Docker Compose default project skeleton will run nginx on port 8081 (if you installed a fresh nginx manually the port is porbably 80):

$ curl localhost:8081/hello-world

The microservice should respond with:

$ curl localhost:8081/hello-world
Hello world!

First Entity

One the most fundamental patterns of enterprise software nowadays is the Entity. In general, an entity is simply an object that is used to persist data and state during runtime. Moreover, entities are usually distinguished by some form of identity. For example, in the case with a relational database table an entity can be identified by a primary key field and its value.

Glagol DSL supports a built-in syntax declaration for entities that can be automatically bundled with database tables. Generally speaking, the purpose of having a built-in language structure for entities is to hide the underlying application logic from the developer and bring the focus on the entity as a business item itself.

Usually, entities are maintained with the help of the so called Object-relational mapper libraries. Doctrine 2 ORM is one of the popular ORMs in the PHP world. Therefore, it is being used by the Glagol DSL compiled codes. However, a Glagol DSL developer does not need to worry about ORMs because this part is taken care by the language and the compiler itself.

Create your first entity

Lets create a Glagol DSL entity. Create a new package by making a directory named src/MusicLib. Next, put the following code inside:

namespace MusicLib

entity Song {
    int id;
    string title;
    string genre;
    string author;
}

Save this file as src/MusicLib/Song.g. Notice that the file name and path needs to match the defined namespace and entity name. Otherwise, the compiler will notify with an error that there is naming inconsistency.

To show a use cases of this entity lets create a new fresh controller for the purpose src/MusicLib/SongController.g:

namespace MusicLib

rest controller /song {
    index {
        return new Song();
    }
}

Compile the sources using $ glagol compile and then $ curl localhost:8081/song. The response should display the following json:

{
    "id": null,
    "title": null,
    "genre": null,
    "author": null
}

Constructors

Entities can have constructors. They are defined in a Java-like way:

namespace MusicLib

entity Song {
    int id;
    string title;
    string genre;
    string author;

    Song(string songTitle) {
        title = songTitle;
    }
}

Constructors must use the name of the entity. Additionally, you can override constructors as long as they do not have duplicating signatures (the combination of arguments being passed):

namespace MusicLib

entity Song {
    int id;
    string title;
    string genre;
    string author;

    Song(string songTitle) {
        title = songTitle;
    }

    Song(string song, string author) {
        title = song;
        this.author = author;
    }
}

Notice how the second constructor uses this to assign property values. This is because the argument names match the property names. Unlike PHP, properties can be accessed directly from the scope of any method (including constructors). Only when an argument has the same name as a defined property you need to use this.propertyName notation to differentiate the property from the parameter within the same scope.

Try to compile the app. What you will get is a type-check error like this:

Cannot compile, errors found:
[/src/MusicLib/SongController.g:5] Cannot match constructor Song()

This is because you do not have a constructor that accepts no argument as used in the controller. Lets add some parameters:

namespace MusicLib

rest controller /song {
    index {
        return new Song("Virus", "Marko Markovic Brass Band");
    }
}

This instantiation will use the second constructor because of the mathcing signature (string, string). Next, compile the app and after you $ curl localhost:8081/song the output will be:

{
    "id": null,
    "title": "Virus",
    "genre": null,
    "author": "Marko Markovic Brass Band"
}

Guards

Functional languages like Haskell support the concept of function guards. A guard is simply a boolean expression that is triggered before the function’s body. Furthermore, if the guard expression evaluates as true then the function logic will be executed. Otherwise, the next override declaration with the same signature will be checked until success. If no overriding qualifies for execution an error is thrown.

Glagol DSL implements guards that can be applied to constructors and methods. Lets modify our Song entity in a way it uses guards:

namespace MusicLib

entity Song {
    int id;
    string title;
    string genre;
    string author;

    Song(string title, string author) {
        this.title = title;
        this.author = author;
        this.genre = "Balkan";
    } when author == "Marko Markovic Brass Band";

    Song(string title, string author) {
        this.title = title;
        this.author = author;
        this.genre = "Jazz";
    } when author == "Miles Davis";
}

Obviously, if you instantiate the entity as new Song("Virus", "Marko Markovic Brass Band") the genre will be set to ‘Balkan’. The second constructor has a guard that will evaluate as true if the artist is ‘Miles Davis’, and as a result it will set the genre to ‘Jazz’.

In general, you can think of guards as a way to structurally avoid if-else statements. Their only purpose in Glagol DSL is to reduce code volume and enhance readability. Otherwise, guards work is any other typical conditional structure.

You might have noticed something in the example above, and that is that there is no fallback constructor yet - a one with the same signature but with no guard. In a case where we instantiate a Song entity and the author is neither Marko Markovic Brass Band or Miles Davis what would be the outcome? Logically - it should fail with an error, and this is exactly the way Glagol DSL will behave too. Lets update the controller with the following code:

namespace MusicLib

rest controller /song {
    index {
        return new Song("Soul Power 74", "Maceo Parker");
    }
}

Since there is not constructor to handle the entity instantiation the app will respond with an error message like this:

{
    "message": "Sorry, something went wrong",
    "error": "Cannot match constructor for Song",
    "trace": [
        {
            "file": "src/MusicLib/SongController.g",
            "line": 5,
            "name": null
        }
    ]
}

Glagol DSL will try to match methods/constructors with guards first. Furthermore, you can see that errors that originally happening in the generated PHP are mapped to the actual *.g source files.

Lets default the song genre by adding a constructor without a guard:

namespace MusicLib

entity Song {
    int id;
    string title;
    string genre;
    string author;

    Song(string title, string author) {
        this.title = title;
        this.author = author;
        this.genre = "Balkan";
    } when author == "Marko Markovic Brass Band";

    Song(string title, string author) {
        this.title = title;
        this.author = author;
        this.genre = "Jazz";
    } when author == "Miles Davis";

    Song(string title, string author) {
        this.title = title;
        this.author = author;
        this.genre = "Unknown";
    }
}

The next chapter explains how to read, persist and delete entities from the database.

Repositories and entities

In the previous chapter we investigated some basics of creating and using entities. However, the examples so far were leaving the entities static - without persistence with a database.

Prepare the database

Lets create a database table for our MusicLib::Song entity:

USE `glagol`;

CREATE TABLE `songs` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `title` VARCHAR(45) NULL,
  `genre` VARCHAR(45) NULL,
  `author` VARCHAR(45) NULL,
  PRIMARY KEY (`id`));

Attention

If you are using the Docker Compose default project structure you can just put the SQL snippet displayed above in the ./database/data.sql file. The database folder is imported by the MySQL docker container upon build - just rebuild your stack and the table will be created and ready to use. Additionally, you can tweak the database name the app is using by changing the DB_DATABASE environmental variable from docker-compose.yml. The database name for any Glagol DSL app defaults to glagol.

Annotating the entity

Next step is to tell Glagol DSL that the Song entity related to the glagol.songs database table.

First, add the @table annotation just before the declaration of the entity:

namespace MusicLib

@table="songs"
entity Song {
    int id;
    string title;
    string genre;
    string author;
}

After you compile the sources the environment will know that the Song entity is bundled with the glagol.songs table.

Secondly, we need to annotate the primary key field and flag it as auto-incremented. Simply add the @id and @sequence annotations just before the int id; property declaration:

namespace MusicLib

@table="songs"
entity Song {
    @id
    @sequence
    int id;
    string title;
    string genre;
    string author;
}

The @id annotation will tell Glagol DSL that the targeted field is the primary key field. As for the @sequence annotation - it will indicate that the field is set for auto-increment.

Compile the sources using the already familiar glagol compile command.

Now we told Glagol DSL about all the metadata required to bundle the entity with an existing database table.

Songs repository

Lets insert some data into the database using the Song entity!

First, we need a Repository object. In general, repositories are a special type of objects that hold the sole responsibility of supporting the lifecycle of entities. Moreover, a Repository is a frequently used enterprise design pattern which has been repeatedly publicised by a number of influential authors such as Martin Fowler and Eric Evans throughout the last two decades. To put it simply, a repository can persist(create, update), remove and retrieve entities from the data storage.

Glagol DSL provides a built-in syntax declaration for repositories:

namespace MusicLib

repository for Song {

}

Save this code as src/MusicLib/SongRepository.g.

Lets investigate the code snippet. From repository for Song we can conclude that repositories are, in a way, attached to entities. Additionally, Glagol DSL allows only one repository per entity (one-on-one relationship) to be created - doing otherwise will result in typecheck errors during compilation. Last but not least, the source file name has to follow the mandatory naming convention of <Entity>Repository.g where <Entity> is the name of the targeted entity.

So far so good. However, our new repository does not do anything yet. Lets change that by introducing a save method:

namespace MusicLib

repository for Song {
    public void save(Song song) {
        persist song;
        flush;
    }
}

Yep - it is that simple. Repositories expose the persist and flush statements. The first one tells Glagol DSL that we want to initiate the persistence of the object that is being passed. Secondly, flush statement will save all the data into the data storage. Think of it this way - persist will tell Glagol DSL “Hey, here is an object for you to keep an eye on!”, and flush says “Bring my changes to the database, please!”.

Note

Entity persisting and flushing is inspired and based on Doctrine 2 ORM’s way of operating with entities.

Finally, lets save our Song entity into the database! Modify the SongController.g to use the repository:

namespace MusicLib

rest controller /song {

    repository<Song> songs = get repository<Song>;

    index {
        Song balkanSong = new Song("Virus", "Marko Markovic Brass Band");

        songs.save(balkanSong);

        return balkanSong;
    }
}

Lets investigate this piece of code. The very first thing to notice is repository<Song> songs = get repository<Song>; - in this line we simply define a new property songs which is of type Song repository type. Moreover, you lets focus on two main keypoints here:

  • repository<Song> - this is the syntax used to address repository types;
  • get repository<Song> - this indicates that the property should be automatically set with the repository instance for a value.

Hint

The line repository<Song> songs = get repository<Song>; can be simplified to repository<Song> songs = get selfie;. In general, the selfie keyword will reflect the type declared in the property (in this case repository<Song>).

Now just compile once again and test using curl localhost:8081/song! You will see a response just similar to:

{
    "id": 1,
    "title": "Virus",
    "genre": "Balkan",
    "author": "Marko Markovic Brass Band"
}

Notice that this time there is a value assigned to the id field. Generally speaking, this is the first indicator that the data was successfully inserted into the data storage. In fact, lets query MySQL directly to see the stored data:

mysql> SELECT * FROM glagol.songs;
+----+-------+--------+---------------------------+
| id | title | genre  | author                    |
+----+-------+--------+---------------------------+
|  1 | Virus | Balkan | Marko Markovic Brass Band |
+----+-------+--------+---------------------------+
1 row in set (0.00 sec)

Removing entities

Very similarly to persisting entities we can also remove such. However, instead of the persist statement Glagol DSL provides the remove counterpart:

namespace MusicLib

repository for Song {
    public void save(Song song) {
        persist song;
        flush;
    }

    public void remove(Song song) {
        remove song;
        flush;
    }
}

Notice, that flushing the requested changes is still necessary if you want to delete the entity records.

In the next chapter we are going to look into how to query entities using Glagol DSL’s embedded query language!

Querying

So far we learnt how to create and persist entities but what about querying for such? Long story short, Glagol DSL uses a built-in SQL dialect for the purpose. In general, there are a few things to consider about the Glagol Query Language:

  • Queries can only be utilized within repository methods
  • Queries target entities not database tables
  • Queries are executed immediately
  • Queries are a type of expressions

Finding all entities

We start by creating a findAll method in the Song repository:

namespace MusicLib

repository for Song {
    public void save(Song song) {
        persist song;
        flush;
    }

    public void remove(Song song) {
        remove song;
        flush;
    }

    public Song[] findAll() {
        Song[] songs = SELECT s[] FROM Song s;

        return songs;
    }
}

Secondly, lets look closely at what we did here. Obviously, the SELECT s FROM Song s is a Glagol Query. One can easily recognize the great similarities between SQL and Glagol QL. However, think of it this way - Glagol Query Language is used to ask for entities where SQL usually queries database tables directly. Therefore, the result of a Glagol Query is either an entity or a collection of entities.

Lets break down the query from above. The first part is SELECT s[]. Simply put, we say that we want to select a list of s’s where s is just an alias defined in the FROM part. Furthermore, FROM Song s is exactly the part where we first select the target entity and then we alias it so it can be referenced in other parts of the query.

Notice how the selection part says s[]. In case you remove the brackets then the query will return the first entity directly. Hence, when you want to select the whole result set then you simply add the brackets.

Thirdly, since queries are just expressions we can refactor the bit from above and simply return the query expression directly:

namespace MusicLib

repository for Song {
    public void save(Song song) {
        persist song;
        flush;
    }

    public void remove(Song song) {
        remove song;
        flush;
    }

    public Song[] findAll() {
        return SELECT s[] FROM Song s;
    }
}

Finally, in order to see how the query works we simply use it in the controller:

namespace MusicLib

rest controller /song {

    repository<Song> songs = get selfie;

    index {
        return songs.findAll();
    }
}

Last thing to do is to compile using $ glagol compile. Now simply curl localhost:8081/song and you will see a list of song objects!

Querying for a single entity

Next task is to create a typical find(int id) method that will query for just one entity. You guessed it - we need a WHERE clause:

namespace MusicLib

repository for Song {
    public void save(Song song) {
        persist song;
        flush;
    }

    public void remove(Song song) {
        remove song;
        flush;
    }

    public Song[] findAll() {
        return SELECT s[] FROM Song s;
    }

    public Song find(int id) {
        return SELECT s FROM Song s WHERE s.id = <<id>>;
    }
}

As mentioned earlier, if we want to select only one entity we simply write SELECT s. Secondly, lets investigate the WHERE s.id = <<id>> clause. It is obvious that we access a field from the entity by targeting the s alias. The right side of the equation indicates that we embed a Glagol DSL expression. In the example above we simply pass the id parameter that is provided by the method. However, we can use any expression we like there (like WHERE s.id = <<id + 232>>). The << and >> are just delimiters that indicate the start and end of an embedded expression.

In order to show how the finder method works lets modify our sandbox controller in the following way:

namespace MusicLib

rest controller /song {

    repository<Song> songs = get selfie;

    index {
        return songs.find(1);
    }
}

Compile the source and simply curl localhost:8081/song to test as usual.

In the next chapter we are going to learn how to put everything together under a unified Rest API using the controller.

Rest controllers

The examples so far always relied on modifications of the index page of the Song controller. Although it is easy for displaying tutorial examples this approach is highly inappropriate for real-world scenarios. Luckily, Glagol DSL provides a built-in Rest API support in controllers.

Index action

Every rest controller supports the standard Rest actions which are based on the well-known HTTP 1.1 request methods. As you might have noticed already, so far we used the index action for testing. Generally speaking, when we want to run the index we simply make a GET request to the route that the controller defines. The response can be any value. Usually, the Rest index will return a list of resources. In our case, this fits with the result of querying for all entities. Therefore, lets modify our Songs index to select all song entities:

namespace MusicLib

rest controller /song {

    repository<Song> songs = get selfie;

    index {
        return songs.findAll();
    }
}

We already had this exact bit in the previous tutorial. Lets keep it this way!

Show action

What if we want to request a single resource? For this task Glagol DSL provides the developers with a show action. Lets introduce it:

namespace MusicLib

rest controller /song {

    repository<Song> songs = get selfie;

    index {
        return songs.findAll();
    }

    show (int id) {
        return songs.find(id);
    }
}

The main difference is that the show action accepts an argument which is used as a criteria for finding a resource. In the example above we rely on the existing find repository method that will do the job.

Now compile the sources and call the following curl command:

$ curl localhost:8081/song/1

Naturally, the show action is initiated by a GET request method in combination with an argument appended to the end of the route. Therefore, a show request URI has the /song/{parameter} format.

However, there is an alternative way of finding entities when we rely on the primary key field that is annotated with @id. For the purpose, we can even avoid our manually created find method or the repository altogether. To put it simply, developers can utilize the @autofind annotation to the first parameter of the action like this:

namespace MusicLib

rest controller /song {

    repository<Song> songs = get selfie;

    index {
        return songs.findAll();
    }

    show (@autofind Song song) {
        return song;
    }
}

Lets look at the differences. Instead of accepting int id as a parameter, we type Song song preceded by the @autofind annotation. This very annotation tells Glagol DSL to attempt to find the entity by primary key field and the value of the parameter being passed with the request.

Important

The annotation approach does not require you to have a find repository method whatsoever. It is a built-in behavior provided the Glagol DSL runtime environment.

Store action

The store method is there to handle POST requests aimed to create new resources. Additionally, the Glagol::Http::Request object is necessary for extracting input POST data from the request:

namespace MusicLib

import Glagol::Http::Request;

rest controller /song {

    repository<Song> songs = get selfie;

    index {
        return songs.findAll();
    }

    show (@autofind Song song) {
        return song;
    }

    store (Request request) {
        Song song = new Song(request.input("title"), request.input("author", "unknown"));

        songs.save(song);
        return song;
    }
}

Do not forget to compile the source!

Hint

The Glagol::Http::Request object is provided by the Glagol DSL standard library. It provides the following methods for extracting POST data:

string input(string key);
string input(string key, string default);

Use the string default parameter to provide a default value if the string key does not exist in the request payload.

Since Glagol DSL relies on Lumen Framework for its runtime both json and x-www-form-urlencoded payloads are supported. For this example we are going to use a json payload:

$ curl -X POST -d '{"title":"Born to raise hell", "author": "Motorhead"}' -H "Content-Type: application/json" localhost:8081/song

Update action

The update action is similar to both show and store actions in a way. First, just like show it requires a parameter by which to identify a resource. Secondly, just like store it can use the input() methods from Glagol::Http::Request.

In contrast to both actions, the HTTP request method for update is PUT. Lets look at this example:

namespace MusicLib

rest controller /song {

    repository<Song> songs = get selfie;

    index {
        return songs.findAll();
    }

    show (@autofind Song song) {
        return song;
    }

    store (Request request) {
        Song song = new Song(request.input("title"), request.input("author", "unknown"));

        songs.save(song);
        return song;
    }

    update (Request request, @autofind Song song) {
        song.setTitle(request.input("title"));
        song.setAuthor(request.input("author", "unknown"));

        songs.save(song);

        return song;
    }
}

Additionally, the example above requires two setter methods in the entity:

namespace MusicLib

@table="songs"
entity Song {
    // ... properties and constructors from before...

    public void setTitle(string title) {
        this.title = title;
    }

    public void setAuthor(string author) {
        this.author = author;
    }
}

Compile the sources and test with curl:

$ curl -X PUT -d '{"title":"Killed by death", "author": "Motorhead"}' -H "Content-Type: application/json" localhost:8081/song/2

Delete action

Glagol DSL provides the delete action to handle DELETE HTTP requests:

namespace MusicLib

rest controller /song {

    repository<Song> songs = get selfie;

    index {
        return songs.findAll();
    }

    show (@autofind Song song) {
        return song;
    }

    store (Request request) {
        Song song = new Song(request.input("title"), request.input("author", "unknown"));

        songs.save(song);
        return song;
    }

    update (Request request, @autofind Song song) {
        song.setTitle(request.input("title"));
        song.setAuthor(request.input("author", "unknown"));

        songs.save(song);

        return song;
    }

    delete (@autofind Song song) {
        songs.remove(song);
    }
}

Compile the sources and test with curl:

$ curl -X DELETE localhost:8081/song/1

Create and edit actions

Additionally, you can also implement the create and edit actions that are basically responding to GET requests but with additional route extensions.

First, the create action is usually used to return an initial state entity. Typically this functionality is used to retrieve a blank resource from the service:

namespace MusicLib

rest controller /song {

    repository<Song> songs = get selfie;

    index {
        return songs.findAll();
    }

    show (@autofind Song song) {
        return song;
    }

    store (Request request) {
        Song song = new Song(request.input("title"), request.input("author", "unknown"));

        songs.save(song);
        return song;
    }

    update (Request request, @autofind Song song) {
        song.setTitle(request.input("title"));
        song.setAuthor(request.input("author", "unknown"));

        songs.save(song);

        return song;
    }

    delete (@autofind Song song) {
        songs.remove(song);
    }

    create {
        return new Song("Please, enter song title", "Please, enter song author (band)");
    }

    edit (@autofind Song song) {
        return song;
    }
}

To get the response from this endpoint you need to append /create to the route like this:

$ curl localhost:8081/song/create

Similarly, edit is just like show but it requires the /edit extension to be accessed:

$ curl localhost:8081/song/1/edit

Indices and tables