Vorige: , Nach oben: Dienste definieren   [Inhalt][Index]


12.18.5 Komplizierte Konfigurationen

Einige Programme haben vielleicht ziemlich komplizierte Konfigurationsdateien oder -formate. Sie können die Hilfsmittel, die in dem Modul (gnu services configuration) definiert sind, benutzen, um das Erstellen von Scheme-Anbindungen für diese Konfigurationsdateien leichter zu machen.

Das Werkzeug der Wahl ist das Makro define-configuration, mit dem Sie einen Scheme-Verbundstyp definieren (siehe Record Overview in Referenzhandbuch zu GNU Guile). Ein Scheme-Verbund dieses Typs wird zu einer Konfigurationsdatei serialisiert, indem Serialisierer aufgerufen werden. Das sind Prozeduren, die einen Scheme-Wert nehmen und einen G-Ausdruck zurückliefern (siehe G-Ausdrücke), der wiederum schließlich, nachdem er auf die Platte serialisiert wurde, eine Zeichenkette liefern sollte. Details folgen.

Makro: define-configuration name clause1 clause2 …

Create a record type named name that contains the fields found in the clauses.

Eine Klausel kann eine der folgenden Formen annehmen:

(Feldname
 (Typ Vorgabewert)
 Dokumentation)
 
(Feldname
 (Typ Vorgabewert)
 Dokumentation
 Serialisierer)

(Feldname
 (Typ)
 Dokumentation)

(Feldname
 (Typ)
 Dokumentation
 Serialisierer)

Feldname ist ein Bezeichner, der als Name des Feldes im erzeugten Verbund verwendet werden wird.

Mit Typ wird benannt, was als Typ des Werts für Feldname gelten soll. Weil Guile typenlos ist, muss es eine entsprechend benannte Prädikatprozedur Typ? geben, die auf dem Wert für das Feld aufgerufen werden wird und prüft, dass der Wert des Feldes den geltenden Typ hat. Wenn zum Beispiel package als Typ angegeben wird, wird eine Prozedur namens package? auf den für das Feld angegebenen Wert angewandt werden. Sie muss zurückliefern, ob es sich wirklich um ein <package>-Objekt handelt.

Vorgabewert ist der Standardwert für das Feld; wenn keiner festgelegt wird, muss der Benutzer einen Wert angeben, wenn er ein Objekt mit dem Verbundstyp erzeugt.

Dokumentation ist eine Zeichenkette, die mit Texinfo-Syntax formatiert ist. Sie sollte eine Beschreibung des Feldes enthalten.

Serialisierer ist der Name einer Prozedur, die zwei Argumente nimmt, erstens den Namen des Feldes und zweitens den Wert für das Feld, und eine Zeichenkette oder einen G-Ausdruck (siehe G-Ausdrücke) zurückliefern sollte, welcher Inhalt dafür in die Konfigurationsdatei serialisiert wird. Wenn kein Serialisierer angegeben wird, wird eine Prozedur namens serialize-Typ dafür aufgerufen.

Eine einfache Serialisiererprozedur könnte so aussehen:

(define (serialize-boolean field-name value)
  (let ((value (if value "true" "false")))
    #~(string-append #$field-name #$value)))

Es kommt vor, dass in derselben Datei mehrere Arten von Konfigurationsverbund definiert werden, deren Serialisierer sich für denselben Typ unterscheiden, weil ihre Konfigurationen verschieden formatiert werden müssen. Zum Beispiel braucht es eine andere serialize-boolean-Prozedur für einen Getmail-Dienst als für einen Transmission-Dienst. Um leichter mit so einer Situation fertig zu werden, können Sie ein Serialisierer-Präfix nach dem Literal prefix in der define-configuration-Form angeben. Dann müssen Sie den eigenen Serialisierer nicht für jedes Feld angeben.

(define (foo-serialize-string field-name value)
  )

(define (bar-serialize-string field-name value)
  )
  
(define-configuration foo-configuration
  (label
   (string)
   "The name of label.")
  (prefix foo-))

(define-configuration bar-configuration
  (ip-address
   (string)
   "The IPv4 address for this device.")
  (prefix bar-))

In manchen Fällen wollen Sie vielleicht überhaupt gar keinen Wert aus dem Verbund serialisieren. Dazu geben Sie das Literal no-serialization an. Auch können Sie das Makro define-configuration/no-serialization benutzen, was eine Kurzschreibweise dafür ist.

;; Nichts wird auf die Platte serialisiert.
(define-configuration foo-configuration
  (field
   (string "test")
   "Some documentation.")
  (no-serialization))

;; Das Gleiche wie oben.
(define-configuration/no-serialization bar-configuration
  (field
   (string "test")
   "Some documentation."))
Makro: define-maybe type

Manchmal soll ein Feld dann nicht serialisiert werden, wenn der Benutzer keinen Wert dafür angibt. Um das zu erreichen, können Sie das Makro define-maybe benutzen, um einen „maybe type“, zu Deutsch „Vielleicht-Typ“, zu definieren. Wenn der Wert eines Vielleicht-Typs nicht gesetzt oder auf den Wert %unset-value gesetzt ist, wird er nicht serialisiert.

Beim Definieren eines „Vielleicht-Typs“ ist die Voreinstellung, den dem Grundtyp entsprechenden Serialisierer zu benutzen. Zum Beispiel wird ein Feld vom Typ maybe-string nach Voreinstellung mit der Prozedur serialize-string serialisiert. Natürlich können Sie stattdessen eine eigene Serialisiererprozedur festlegen. Ebenso muss der Wert entweder den Typ „string“ (also Zeichenkette) aufweisen oder unspezifiziert sein.

(define-maybe string)

(define (serialize-string field-name value)
  )

(define-configuration baz-configuration
  (name
   ;; Wenn eine Zeichenkette angegeben wird, wird diese mit der
   ;; Prozedur „serialize-string“ serialisiert werden.  Sonst
   ;; ist vorgegeben, für dieses Feld nichts zu serialisieren.
   maybe-string
   "The name of this module."))

Wie bei define-configuration kann man ein Präfix für Serialisierernamen als Literal mit prefix angeben.

(define-maybe integer
  (prefix baz-))

(define (baz-serialize-integer field-name value)
  )

Auch gibt es das Literal no-serialization. Wenn es angegeben wird, bedeutet das, es wird kein Serialisierer für den Vielleicht-Typ eingesetzt, ganz gleich ob ein Wert gesetzt wurde oder nicht. define-maybe/no-serialization ist eine Kurzschreibweise, um das Literal no-serialization festzulegen.

(define-maybe/no-serialization symbol)

(define-configuration/no-serialization test-configuration
  (mode
   maybe-symbol
   "Docstring."))
Funktion: maybe-value-set? value

Mit diesem Prädikat können Sie ermitteln, ob ein Benutzer für das Vielleicht-Feld einen bestimmten Wert angegeben hat.

Funktion: serialize-configuration configuration fields

Return a G-expression that contains the values corresponding to the fields of configuration, a record that has been generated by define-configuration. The G-expression can then be serialized to disk by using something like mixed-text-file.

Funktion: empty-serializer field-name value

Ein Serialisierer, der nur die leere Zeichenkette zurückliefert. Die Prozedur serialize-package ist als anderer Name dafür vordefiniert.

Nachdem Sie einen Konfigurationsverbundstyp definiert haben, haben Sie bestimmt auch die Absicht, die Dokumentation dafür zu schreiben, um anderen Leuten zu erklären, wie man ihn benutzt. Dabei helfen Ihnen die folgenden zwei Prozeduren, die hier dokumentiert sind.

Funktion: generate-documentation documentation documentation-name

Generate a Texinfo fragment from the docstrings in documentation, a list of (label fields sub-documentation ...). label should be a symbol and should be the name of the configuration record. fields should be a list of all the fields available for the configuration record.

Unterdokumentation ist ein Tupel (Feldname Konfigurationsname). Feldname ist der Name des Feldes, das einen anderen Konfigurationsverbund als Wert hat. Konfigurationsname ist der Name dessen Konfigurationsverbundstyps.

Eine Unterdokumentation müssen Sie nur angeben, wenn es verschachtelte Verbundstypen gibt. Zum Beispiel braucht ein Verbundsobjekt getmail-configuration (siehe Mail-Dienste) ein Verbundsobjekt getmail-configuration-file in seinem rcfile-Feld, daher ist die Dokumentation für getmail-configuration-file verschachtelt in der von getmail-configuration.

(generate-documentation
  `((getmail-configuration ,getmail-configuration-fields
     (rcfile getmail-configuration-file))
    )
  'getmail-configuration)

Für Dokumentationsname geben Sie ein Symbol mit dem Namen des Konfigurationsverbundstyps an.

Funktion: configuration->documentation configuration-symbol

Take configuration-symbol, the symbol corresponding to the name used when defining a configuration record with define-configuration, and print the Texinfo documentation of its fields. This is useful if there aren’t any nested configuration records since it only prints the documentation for the top-level fields.

Gegenwärtig gibt es kein automatisiertes Verfahren, um die Dokumentation zu Konfigurationsverbundstypen zu erzeugen und gleich ins Handbuch einzutragen. Stattdessen würden Sie jedes Mal, wenn Sie etwas an den Docstrings eines Konfigurationsverbundstyps ändern, aufs Neue generate-documentation oder configuration->documentation von Hand aufrufen und die Ausgabe in die Datei doc/guix.texi einfügen.

Nun folgt ein Beispiel, wo ein Verbundstyp mit define-configuration usw. erzeugt wird.

(use-modules (gnu services)
             (guix gexp)
             (gnu services configuration)
             (srfi srfi-26)
             (srfi srfi-1))

;; Feldnamen, in Form von Scheme-Symbolen, zu Zeichenketten machen
(define (uglify-field-name field-name)
  (let ((str (symbol->string field-name)))
    ;; field? -> is-field
    (if (string-suffix? "?" str)
        (string-append "is-" (string-drop-right str 1))
        str)))

(define (serialize-string field-name value)
  #~(string-append #$(uglify-field-name field-name) " = " #$value "\n"))

(define (serialize-integer field-name value)
  (serialize-string field-name (number->string value)))

(define (serialize-boolean field-name value)
  (serialize-string field-name (if value "true" "false")))

(define (serialize-contact-name field-name value)
  #~(string-append "\n[" #$value "]\n"))

(define (list-of-contact-configurations? lst)
  (every contact-configuration? lst))

(define (serialize-list-of-contact-configurations field-name value)
  #~(string-append #$@(map (cut serialize-configuration <>
                                contact-configuration-fields)
                           value)))

(define (serialize-contacts-list-configuration configuration)
  (mixed-text-file
   "contactrc"
   #~(string-append "[Owner]\n"
                    #$(serialize-configuration
                       configuration contacts-list-configuration-fields))))

(define-maybe integer)
(define-maybe string)

(define-configuration contact-configuration
  (name
   (string)
   "The name of the contact."
   serialize-contact-name)
  (phone-number
   maybe-integer
   "The person's phone number.")
  (email
   maybe-string
   "The person's email address.")
  (married?
   (boolean)
   "Whether the person is married."))

(define-configuration contacts-list-configuration
  (name
   (string)
   "The name of the owner of this contact list.")
  (email
   (string)
   "The owner's email address.")
  (contacts
   (list-of-contact-configurations '())
   "A list of @code{contact-configuation} records which contain
information about all your contacts."))

Eine Kontaktelistekonfiguration könnte dann wie folgt erzeugt werden:

(define my-contacts
  (contacts-list-configuration
   (name "Alice")
   (email "alice@example.org")
   (contacts
    (list (contact-configuration
           (name "Bob")
           (phone-number 1234)
           (email "bob@gnu.org")
           (married? #f))
          (contact-configuration
           (name "Charlie")
           (phone-number 0000)
           (married? #t))))))

Wenn Sie diese Konfiguration auf die Platte serialisierten, ergäbe sich so eine Datei:

[owner]
name = Alice
email = alice@example.org

[Bob]
phone-number = 1234
email = bob@gnu.org
is-married = false

[Charlie]
phone-number = 0
is-married = true

Vorige: , Nach oben: Dienste definieren   [Inhalt][Index]