Artanis applications suitable for Guix packaging

This page discusses the guidelines for writing an Artanis application such as LIMS*Nucleus that is suitable for Guix packaging. If you are looking for modifications needed in the Artanis source code, look here.

Write a Guix packageable Artanis application

First read the discussion on modifications required to the Artanis source code so you are aware that all temporary files created by your application must be moved to a directory outside the application directory. In addition, Artanis’ cache must be move outside the application directory, as well as artanis.conf so it remains editable. These activities can be handled with a post installation initialization file.

One time initialization

Because your artanis app needs some directories outside of the application directory to work with temporary files, configuration files etc., a one time initialization script can be run to set up your server. The overall process would look like:

  1. guix package -i limsn
  2. init-limsn.sh
  3. install-pg-aws.sh
  4. start-limsn.sh

The initialization file would create required directories and copy over the artanis.conf file. Note that the placeholder PATH_INTO_STORE is replaced on installation by the path into the store.

init-limsn-channel.sh
1
2
3
4
5
6
7
8
9
10
#! /bin/bash
export LC_ALL="C"
echo export LC_ALL=\"C\" >> $HOME/.bashrc
echo export GUIX_PROFILE=$HOME/.guix-profile >> $HOME/.bashrc
echo . $HOME/.guix-profile/etc/profile >> $HOME/.bashrc
echo export GUIX_LOCPATH=$HOME/.guix-profile/lib/locale >> $HOME/.bashrc

sudo mkdir -p $HOME/.config/limsn
sudo cp PATH_INTO_STORE/share/guile/site/3.0/limsn/conf/artanis.conf $HOME/.config/limsn

artanis.conf

Because a configuration file outside the project directory is being used, you can use a startup script to launch your application:

start-limsn.sh
1
2
3
4
5
6
#!/bin/bash
!#
export LC_ALL="C"
mkdir -p /var/tmp/limsn/tmp/cache
cd PATH_INTO_STORE/share/guile/site/3.0/limsn
art work -h0.0.0.0 --config=$HOME/.config/limsn/artanis.conf

In this example -h0.0.0.0 is needed for running on Amazon Web Services

GNUPLOT

With regular Artanis Gnuplot can be run on the server and generate *.png files in the ../pub directory that are accessible to the .html.tpl file via:

page1.html.tpl
1
<image src= <%= myplot %>>

Where in the above snippet the variable myplot == “../pub/myplot.png”. With guix modified Artanis, the .png file would have to go into /tmp/limsn and would become inaccessible due to security restrictions. To get around this, load the myplot variable with svg commands. This is accomplished by first creating the gnuplot script with the following commands:

my-gnuplot-script.txt
1
2
3
4
5
6
reset session
set terminal svg size 800,500
save '-'
set key box ins vert right top
set grid
...etc.

The ‘save’-’’ statement will save to standard output so that you can pipe into your variable. Here is a helper function that will collect the svg commands:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
(use-modules (ice-9 regex) ;;list-matches
(ice-9 textual-ports)
(ice-9 rdelim) ;;read-line
(ice-9 popen))

(define (get-svg-content gnuplot-script-file)
;;requires: set terminal svg size 600,400
;; save '-'
;;in the script
;;gnuplot-script-file is the script as a text variable
(let* ((port (open-input-pipe (string-append "gnuplot " gnuplot-script-file)))
(a "")
(dummy (let loop ((line (read-line port)))
(if (not (eof-object? line))
(begin
(set! a (string-append a line))
(loop (read-line port))))))
(dummy (close-pipe port))
(dummy (pretty-print a))
(coord-start (string-match "<svg width=" a ))
(coord-end (string-match "</g></svg>" a)))
(xsubstring a (match:start coord-start) (match:end coord-end))
))

Then in your controller:

mycontroller.scm
1
2
(let* ((myplot-svg (get-svg-content gnuplot-script-file))
etc....

And in the web page template:

page1.html.tpl
1
<%= myplot-svg %>

Now in the above snippet myplot-svg == ’<svg width=“600” height=“350” viewBox=“0 0 600 350” xmlns=“http://www.w3.org/2000/svg” xmlns:xlink=“http://www…etc….”

Application specific variables

Application specific variables are defined in ENTRY such as:

ENTRY
1
2
3
...
(conf-set! 'maxnumplates 100)
...

Since ENTRY is in the store, this will only work for variables that are not available to the end user for (re)setting. To provide editable application specific variables, put them in artanis.conf. Variables are grouped in artanis.conf. LIMS*Nucleus specific variable ‘maxnumplates’ is placed in the ‘cookie’ group for convenience, even though it is not a cookie.

artanis.conf
1
cookie.maxnumplates = 100

artanis/config.scm must be patched appropriately:

artanis/config.scm
1
2
3
4
5
6
7
8
(substitute* "artanis/config.scm"		
((" \\(else \\(error parse-namespace-cache \"Config: Invalid item\" item\\)\\)\\)\\)")
"(else (error parse-namespace-cache \"Config: Invalid item\" item))))\n\n(define (parse-namespace-cookie item)\n (match item\n (('expire expire) (conf-set! '(cookie expire) (->integer expire)))\n (('maxplates maxplates) (conf-set! '(cookie maxplates) (->integer maxplates)))\n (else (error parse-namespace-cookie \"Config: Invalid item\" item))))"))

(substitute* "artanis/config.scm"
(("debug.monitor = <PATHs>\")")
"debug.monitor = <PATHs>\")\n ((cookie expire)\n 3600\n \"Cookie expiration time in seconds.\n 1 hour is 3600\n 6 hours 21600\n 1 month 2592000\n cookie.expire = <integer>\")\n\n ((cookie maxplates)\n 10\n \"Maximum number of plates per plate-set.\n cookie.maxplates = <integer>\")"))

Modify controller syntax

Controllers as they are written will generate a warning:

guix package: warning: failed to load ‘(limsn app controllers plates)’: no code for module (lims app controllers plates) /home/mbc/.guix-profile/share/guile/site/3.0/myapp/app/controllers/pages.scm:4:0: warning: module name (app controllers plates) does not match file name ‘limsn/app/controllers/plates.scm’ hint: File `/home/mbc/.guix-profile/share/guile/site/3.0/limsn/app/controllers/plates.scm’ should probably start with: (define-module (limsn app controllers plates))

Guix expects the first statement in a module to begin with (define-module ()….)
Artanis wants modules to (use-modules (artanis mvc controller))
Satisfy both requirements with:

plates.scm
1
2
3
4
5
6
7
8
9
10
11
12
13
(define-module (limsn app controllers plates)
#:use-module (artanis artanis)
#:use-module (artanis mvc controller)) ;;to eliminate error

;;error: define-artanis-controller: unbound variable
;;hint: Did you forget `(use-modules (artanis mvc controller))'?

(define-artanis-controller pages) ; DO NOT REMOVE THIS LINE!!!
;;libraries must be imported after define-artanis-controller
(use-modules (myapp lib mylib) )

(pages-define plates
(lambda (rc)......etc.

Be sure to import libraries after (define-artanis-controller )

Make Libraries accessible

Artanis provides the …./lib directory to hold libraries. This directory is placed on the GUILE_LOAD_PATH for you. To avoid warnings, you may wish to write your library as a module e.g. (define-module (limsn lib mylib)…) in which case you need to place your project directory on GUILE_LOAD_PATH with a statment in ENTRY such as:

(add-to-load-path (string-append (find-ENTRY-path identity #t) “/..”))

Another option is to create a separate package for your library, package separately, and make it available as an input.