Skip to main content

The Prototype Framework

The prototype framework provides the runtime environment for Ampersand applications. This documentation is intended for developers of the framework and more advanced users of Ampersand. It explains the key concepts, classes and project setup.

Please take note that this part of the documentation is under construction.

Configuration of the prototpye

Logging configuration

  • The logging configuration is configured in framework.php

Project configuration

The prototype framework automatically loads the following configuration files, in the following order: This order allows to have different environments with the same base project configuration Configuration values overwrite each other, when specified in multiple files

  1. src/Ampersand/Misc/defaultSettings.yaml -> framework default settings
  2. generics/settings.json -> project specific settings from Ampersand compiler
  3. config/project.yaml -> project specific framework settings

The configuration file has the following components:

  • settings: a key/value pair list of config settings
  • config: list of other config files that must be imported
  • extensions: named list of extensions to be included, its bootstrapping and config files

Environment variables

Finally certain settings can be set using environment variables. These are loaded last and overwrite previous set settings.

  • AMPERSAND_DEBUG_MODE -> global.debugMode
  • AMPERSAND_PRODUCTION_MODE -> global.productionEnv
  • AMPERSAND_SERVER_URL -> global.serverURL
  • AMPERSAND_DATA_DIR -> global.dataPath
  • AMPERSAND_DBHOST -> mysql.dbHost
  • AMPERSAND_DBNAME -> mysql.dbName
  • AMPERSAND_DBUSER -> mysql.dbUser
  • AMPERSAND_DBPASS -> mysql.dbPass

Explanation of settings

  • global.debugMode (env AMPERSAND_DEBUG_MODE)

    This setting determines how much debug information is provided to the user. When set to true, the detailed error message, including full stack trace is provided by the api and shown in the frontend. You can view the stack trace by opening (clicking on) the red error message.

    When you set the debug mode to false, error details are not displayed and the message states: An error occured (debug information in server log files). The end user doesn't see what's wrong.

  • global.productionEnv (env AMPERSAND_PRODUCTION_MODE)

    This setting determines which management functions are (not) allowed. Most important one is that in production mode true reinstalling the database is not allowed, never! This ensures that by accident all data is lost.

    This means that when you start the application in production mode true, and the database doesn't exist or is outdated (new tables/columns are needed), an exception is thrown. And you are stuck.

File System

Introduction

Currently we have limited support for working with files

Usage in Ampersand application and prototype

To work with files and uploads you need to add a FileObject model and view to your ADL script:

CONCEPT FileObject ""
IDENT FileObjectName : FileObject (filePath)

RELATION filePath[FileObject*FilePath]
RELATION originalFileName[FileObject*FileName]
REPRESENT FilePath TYPE ALPHANUMERIC
REPRESENT FileName TYPE ALPHANUMERIC

VIEW FileObject : FileObject DEFAULT {
apiPath : TXT "api/v1/file",
filePath : filePath,
fileName : originalFileName
} HTML TEMPLATE "View-FILEOBJECT.html" ENDVIEW

The template View-FILEOBJECT.html is a built-in template, but you can substitute this with your own template.

The Flysystem component

In the prototype framework backend we've implemented the Flysystem library, which is a file system abstraction for PHP.

"Flysystem is a filesystem abstraction library for PHP. By providing a unified interface for many different filesystems you’re able to swap out filesystems without application wide rewrites."

File system property of AmpersandApp object

A concrete implementation of a file system is set as property of the AmpersandApp object. You can access the file system like this to perform e.g. a read:

<?php
/** @var AmpersandApp $app */
$fs = $app->fileSystem();
$fs->read('path/to/file.txt');

Other methods of the file system interface include put(), has(), listContents() and others. see:

Using another file system implementation

By default a 'Local' file system adapter is added to the AmpersandApp. This read/writes files to the data directory.

If you want to use another file system adapter you can use the setter method during bootstrap phase of the application. E.g. to use a SFTP file system:

<?php

use League\Flysystem\Filesystem;
use League\Flysystem\Sftp\SftpAdapter;

$filesystem = new Filesystem(new SftpAdapter([
'host' => 'example.com',
'port' => 22,
'username' => 'username',
'password' => 'password',
'privateKey' => 'path/to/or/contents/of/privatekey',
'root' => '/path/to/root',
'timeout' => 10,
]));

/** @var AmpersandApp $app */
$app->setFileSystem($filesystem);

Event Dispatcher

Introduction

The prototype framework dispatches important events in order for other software projects to extend the framework. An example of such an event is the AtomEvent that is dispatched upon added and deleted atoms.

The event dispatcher is the central object in the event dispatching system and is set as a property of the AmpersandApp class which is everywhere available in the codebase. You can access the event dispatcher like so:

<?php
/** @var AmpersandApp $app */
$dispatcher = $app->eventDispatcher();
$dispatcher->dispatch();

The Symfony event dispatcher component

Currently we use the Symfony event dispatcher component as implementatation.

"The Symfony EventDispatcher component implements the Mediator and Observer design patterns to make all these things possible and to make your projects truly extensible."

Dispatched events

Below a list of dispatched events. More events will be added upon request. Please create an issue for that in the repository.

Event classEvent nameComment
AtomEventADDEDWhen a new (non-existing) atom is created
AtomEventDELETEDWhen an atom is deleted
LinkEventADDEDWhen a new (non-existing) link is created
LinkEventDELETEDWhen a link is deleted
TransactionEventSTARTEDWhen a new Ampersand transaction is created/started
TransactionEventCOMMITTEDWhen an Ampersand transaction is committed (i.e. invariant rules hold)
TransactionEventROLLEDBACKWhen an Ampersand transaction is rolled back (i.e. invariant rules do not hold)

Adding a listener

You can easily connect a listener to the dispatcher so that it can be notified/called when certain events are dispatched. A listener can be any valid PHP callable.

Below two examples of connecting a listener to the atom added event

<?php
// Listener class method example
class MyListener
{
public function methodToCall(AtomEvent $event) {}
}

$listener = new MyListener();
$dispatcher->addListener(AtomEvent::ADDED, [$listener, 'methodToCall']);
<?php
// Closure example (i.e. anonymous function)
$dispatcher->addListener(AtomEvent::ADDED, function (AtomEvent $event) {
/* code here */
});

Generics folder

A compiled Ampersand script results in different model files that configure the prototype framework and must be placed in a specific. This folder is historically called 'generics'.

tip

More about the Ampersand compiler can be found here.

Generated files by Ampersand compiler

The generated config and ampersand model files include:

  • concepts.json
  • conjuncts.json
  • interfaces.json
  • mysql-installer.json
  • relations.json
  • roles.json
  • rules.json
  • settings.json
  • views.json

Ampersand compiler

The prototype framework depends on a compatible Ampersand compiler to takes your Ampersand script (ADL files) and transform it into model files for the backend and generate a UI for the frontend.

Backend model files are generated in the generics folder. These are picked up by the backend implementation. See README for more information about which files are generated.

Frontend UI is generated using the HTML and JS templates specified in templates folder. These output components consisting of HTML views and JS controllers are put into public/app/project folder. These are picked up by the frontend application.

Together with the prototype framework, the backend model files and the generated frontend files provide for a complete prototype application.

Compiler version constraints

As of Ampersand compiler version 5.x, the compiler checks if its version is compatible with the deployed prototype framework. The prototype framework specifies the compatible compiler version(s) by means of semantic versioning constraints specified in compiler-version.txt.

The compiler uses Haskell package Salve to check the constraints. See documentation of Salve to understand the contraint language.

On-board Ampersand compiler

The Docker file of the prototype framework includes a compatible Ampersand compiler in the container. Somewhere in the build script the following line is specified

COPY --from=ampersandtarski/ampersand:v4.6 /bin/ampersand /usr/local/bin

This copies a pre-compiled and released Ampersand compiler from related image from Docker Hub.

You can make use of this compiler when building your own prototype application. Simply by extending the prototype-framework image and calling the compiler in a RUN statement like so:

FROM ampersandtarski/prototype-framework:v1.14

# The script content
COPY model /usr/local/project/

# Generate prototype application using Ampersand compiler
RUN ampersand proto /usr/local/project/script.adl \
--proto-dir /var/www \
--verbose

RUN chown -R www-data:www-data /var/www/log /var/www/data

WORKDIR /var/www

For a complete example and template folder for your project take a look at the project-template repository

Adding custom HTML templates

If you are using your own VIEW definitions and custom BOX specifications with custom HTML templates in your Ampersand script files, you need to copy them to the templates folder BEFORE running the compiler.

You can add the following line to your Docker file:

# If you have custom templates, they need to be copied to where the Ampersand compiler expects them (/var/www)
RUN cp -r -v /usr/local/project/shared/templates /var/www/

Build with custom Ampersand compiler

For developers that work on the Ampersand compiler itself it may be convenient to copy a locally build Ampersand compiler into the prototype-framework. You can do this by a) injecting the custom Ampersand compiler in a specific prototype project directly or b) locally building a new prototype-framework image.

Option A: inject custom compiler in prototype image

The quickest and most easiest way is to inject a custom Ampersand compiler directly in your prototype image. Update your Docker file and add the following line BEFORE running the compiler:

Custom compiler that is released on Github:

# Lines to add specific compiler version (from Github releases)
ADD https://github.com/AmpersandTarski/Ampersand/releases/download/Ampersand-v4.1.0/ampersand /usr/local/bin/ampersand
RUN chmod +x /usr/local/bin/ampersand

Custom compiler from specific (local) Docker image

# Line to add specific compiler version from some (local) Docker image
COPY --from=ampersandtarski/ampersand:local /bin/ampersand /usr/local/bin

Custom compiler from local binary

COPY /path/to/bin/ampersand /usr/local/bin

Option B: locally build prototype-framework image

For option A: replace the following line in the Docker file of this repo:

COPY --from=ampersandtarski/ampersand:v4.6 /bin/ampersand /usr/local/bin

Copy the compiler from a locally build Ampersand image ór from local bin directly instead of the ampersandtarski/ampersand image.