Javascript – Sebastians Blog https://sgaul.de Neues aus den Softwareminen Mon, 17 Jun 2019 08:32:38 +0000 de-DE hourly 1 https://wordpress.org/?v=6.1.1 https://sgaul.de/wp-content/uploads/2019/02/cropped-sgaul-2-1-32x32.jpg Javascript – Sebastians Blog https://sgaul.de 32 32 Vue-Code-Formatierung in VSCode https://sgaul.de/2019/06/01/vue-code-formatierung-in-vscode/ Sat, 01 Jun 2019 18:13:19 +0000 https://sgaul.de/?p=2940 Vue-Code-Formatierung in VSCode weiterlesen]]> Mit .vue-Dateien und Visiual Studio Code hatte ich oft Probleme, nun aber endlich eine Konfiguration gefunden, die sowohl Javascript- wie auch HTML-Segmente automatisch überprüft und formatiert. Als Plugins kommen dabei ESLint und Vetur zum Einsatz, meine settings.json sieht folgendermaßen aus:

{
  "git.autofetch": true,
  "eslint.autoFixOnSave": true,
  "eslint.validate": [
    {
      "language": "vue",
      "autoFix": true
    },
    {
      "language": "html",
      "autoFix": true
    },
    {
      "language": "javascript",
      "autoFix": true
    },
  ]
}

Problem ist nun, dass das HTML nicht eingerückt wird, wenn ich die Vue-Standardeinstellungen verwende. Um dies zu beheben, wechsele ich in der .eslintrc im Extends-Teil von plugin:vue/essential auf plugin:vue/recommended:

module.exports = {
  root: true,
  env: {
    node: true
  },
  'extends': [
    'plugin:vue/recommended',
    'standard'
  ],
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
  },
  parserOptions: {
    parser: 'babel-eslint'
  }
}

Ggf. müssen hier noch Pakete installiert werden. Die Extension standard sorgt dafür, dass simplere Javascript-Regeln wie Einrückungen automatisch vorgenommen werden.

Dies ist ein sehr viel strengerer Linter, der viele Fehlerquellen ausschließt, aber auch einige subjektive Entscheidungen trifft. Dass HTML-Tags immer mehrzeilig werden, sobald mehr als ein Attribut definiert wird, finde ich gewöhnungsbedürftig. Dass hingegen die Standardeigenschaften einer Vue-Komponente immer konsistent sortiert werden, weiß zu gefallen.

]]>
Datenbeschaffung in Vue-Router-Views vereinfachen https://sgaul.de/2019/02/09/datenbeschaffung-in-vue-router-views-vereinfachen/ Sat, 09 Feb 2019 20:45:55 +0000 https://sgaul.de/?p=2911 Datenbeschaffung in Vue-Router-Views vereinfachen weiterlesen]]> created und watch

Ein typisches Vue-Router-Szenario: Daten müssen initialisiert und bei Routen-Updates zurückgesetzt und geladen werden, da die Komponente nicht neu erzeugt sondern nur intern aktualisiert wird.

data () {
  return {
    user: null,
    loading: true
  }
},
created () {
  this.fetchData()
},
watch: {
  '$route': 'fetchData'
},
methods: {
  async fetchData () {
    this.loading = true
    this.user = await this.$network.getUser(this.$route.params.userId)
    this.loading = false
  }
}

Watchers und immediate: true

Die Dopplung in created und watch kann durch einen Watcher mit der Eigenschaft immediate eliminiert werden:

watch: {
  '$route': {
    handler: 'fetchData',
    immediate: true
  }
}

Neuladen statt Updaten

Zudem kann man ein Neu-Rendern der Komponente bei Routen-Änderung erzwingen, um die Unübersichtlichkeit und das Fehlerpotential eines Updates zu vermeiden.

data () {
  return {
    user: null,
    loading: true
  }
},
async created () {
  this.loading = true
  this.user = await this.$network.getUser(this.$route.params.userId)
  this.loading = false
}

Hierfür muss im Template, welches den Router-View einfügt, einfach ein Key übergeben werden, der idealerweise die gesamte URL darstellt:

<router-view :key="$route.fullPath" />
]]>
Pakyows Ansatz für Views in Web-Applikationen https://sgaul.de/2014/08/24/pakyows-ansatz-fuer-views-in-web-applikationen/ Sun, 24 Aug 2014 11:33:38 +0000 https://sgaul.de/?p=2685 Pakyows Ansatz für Views in Web-Applikationen weiterlesen]]> Auch wenn die Vertreter der Client-Seite den Kampf um die Frage, wo Views künftiger Web-Applikationen gerendert werden, schon lange gewonnen haben, so bleiben doch einige Diskussionspunkte offen. Was wird aus Suchmaschinen, Javascript-Verweigerern und den Nutzern von schwacher Hardware oder alter Browser? Der Schritt zur Client-Side-Web-Application ist an Konsequenzen geknüpft, die nicht jeder in Kauf nehmen möchte. Ich halte immer die Augen nach Projekten offen, die das Rendern sowohl auf Server als auch Client mit möglichst wenig Overhead ermöglichen.

Das Pakyow-Projekt erfüllt diese Anforderung nicht, zeigt aber einen interessanten Ansatz für Views. Sicher nicht der erste oder gar einzige seiner Art, aber einer mit konkreter Umsetzung. Zudem scheint eine weitere Entwicklung in Richtung Client- und Server-Seite möglich, da Entwickler Bryan Powell bei dem Thema recht euphorisch wirkt.

<article data-scope="post">
  <h1 data-prop="title">My First Post</h1>
  <div data-prop="body">Lorem ipsum dolor sit amet</div>
</article>

Das einfache Beispiel führt dabei zum selben Ergebnis wie das folgende HAML-Beispiel, nur dass die obigen Data-Attribute für spätere Updates erhalten bleiben.

- @posts.each do |post|
  %article
    %h1= post.title
    %div= post.body

Man beachte die Each-Schleife: Das obige Template erzeugt das Article-Element für jeden Post der Datenquelle.

Das obige Beispiel hat gegenüber der HAML-Notation entscheidende Vorteile.

Der Template-Entwickler kann die Templates mit beliebigen Inhalten befüllen (siehe oben „My First Post“) und ohne Backend-Logik ein sinnvolles Ergebnis sehen. Template und Backend sind besser getrennt.

Der wesentliche Vorteil ist aber, dass das Template mit einfachen Datenstrukturen, üblicherweise einem JSON-Dokument, bestückt werden kann. Eine solch relativ einfache Template-Engine ließe sich für Server (z. B. in Ruby) und in Javascript für den Client realisieren. So kann der Server den mit korrekten Werten befüllten View ausliefern. Weitere Updates würde er nur als JSON schicken, welches der Browser anhand der gegebenen Data-Notationen ins DOM einbaut.

Der Backend-Entwickler muss sich somit nur noch um die JSON-Struktur sorgen. Ob die Response als HTML oder JSON ausgeliefert wird, ließe sich weitgehend im Framework abstrahieren.

Für mich als Rails-Gefangenen ist das Hauptproblem mit Pakyow, dass es Rails ersetzen statt ergänzen würde. Einen Ansatz zur Integration konnte ich bisher nicht finden, so dass hier eigene Überlegungen notwendig wären.

]]>
Javascript: Klick außerhalb eines Bereiches https://sgaul.de/2013/10/15/javascript-klick-ausserhalb-eines-bereiches/ Tue, 15 Oct 2013 15:46:43 +0000 https://sgaul.de/?p=2368 if $('.container').has(ev.target).length == 0 # ... Dies ist beispielsweise hilfreich um ein Popup oder Dropdown-Menü schließen möchte, wenn der Nutzer „irgendwo“ außer auf das Widget selbst klickt.]]> Folgendes Beispiel überwacht ein Dokument auf einen beliebigen Klick, außer wenn dieser in einem bestimmten Element (.container) liegt:

$(document).on 'click', (ev) ->
    if $('.container').has(ev.target).length == 0
      # ...

Dies ist beispielsweise hilfreich um ein Popup oder Dropdown-Menü schließen möchte, wenn der Nutzer „irgendwo“ außer auf das Widget selbst klickt.

]]>
Coffeescript nur nach Änderung kompilieren https://sgaul.de/2013/04/13/coffeescript-nur-bei-anderung-kompilieren/ Sat, 13 Apr 2013 12:11:23 +0000 https://sgaul.de/?p=2110 Mein Build- und Entwicklungshilfe-Script für Node-Projekte kann bereits das Wesentliche: Coffeescript kompilieren, Tests ausführen, einen Server starten und beenden sowie das Dateisystem überwachen, um die anderen Aufgaben nach jedem Speichern automatisch auszuführen. Die schnell geschriebene Build-Task kompilierte bisher jede Coffeescript-Datei, die sie finden konnte. Um nach dem Speichern möglichst schnell Testergebnisse zu erhalten, sollte nur neu gebaut werden, was sich auch geändert hat.

Um dies möglichst einfach zu erreichen, schlage ich ein Source- (z.B. project/src) und ein Zielverzeichnis (z.B. project/build) vor. In src kann man weitere Grobstrukturen wie main und tests festlegen. So fließen meine zuvor definierten Tasks build_project und build_tests zu einem einzigen build zusammen.

Unterschiede erkennen? Rsync!

Um möglichst effizient etablierte Technologien verwenden zu können, werden vor dem Kompilieren alle Quelldateien ins Buildverzeichnis kopiert. Dank dieser Vereinfachung lässt sich Rsync verwenden, welches nur wirklich geänderte Dateien synchronisiert und ihren Pfad ausgibt. Nach dem Sync muss die Liste der Änderungen nur noch nach Coffee-Dateien durchsucht und diese kompiliert werden. Der gesamte Vorgang sieht dann folgendermaßen aus:

build() {
    changes=( "$(rsync -av "$source_directory/" "$build_directory")" )
    for filepath in $changes; do
      if [[ $filepath == *.coffee ]]; then
        $compiler --compile "$build_directory/$filepath"
      fi
    done
}

Einziger Schönheitsfehler: Im Buildverzeichnis liegen auch die Coffeescript-Dateien neben ihren Javascript-Äquivalenten, obwohl sie nicht gebraucht werden. Diese dürfen im Buildprozess nicht entfernt werden, da ihre Löschung den Rsync-Ansatz ad absurdum führt. Während der Entwicklung stören sie auch nicht. Für das produktive Deployment empfiehlt sich jedoch eine zusätzliche Task, die das Buildverzeichnis zuvor bereinigt.

Implementierte Build-Tasks

Das Script ist mittlerweile vollständig genug, um die Node-Entwicklung zu begleiten. Folgende Tasks stehen zur Verfügung:

Task Beschreibung
build Build project and tests.
test Build project and run all tests.
Equivalent to „build.sh build run-tests“.
run-tests Run all tests.
server Stop running server (if existing), build project and start server.
Equivalent to „build.sh stop-server build start-server“.
restart-server Stop running server (if existing) and start server.
Equivalent to „build.sh stop-server start-server“.
start-server Start server.
stop-server Stop running server (if existing).
watch Start infinite loop triggering succeeding tasks whenever a file changes
in project directory. Use only once.
dev Build everything, restart server, run tests and repeat whenever a file
changes. Equivalent to „build.sh test watch test“.

Node-Build-Script auf Github

]]>
Server im Build-Prozess starten und stoppen https://sgaul.de/2013/04/09/server-im-build-prozess-starten-und-stoppen/ Tue, 09 Apr 2013 13:41:32 +0000 https://sgaul.de/?p=2094 Server im Build-Prozess starten und stoppen weiterlesen]]> Entwickelt man eine Server-Anwendung, so sollte das verwendete Echtzeit-Build-Script auch in dieser Lage sein, diesen zu starten und zu beenden. So muss der Entwickler nach dem Speichern im Browser nur noch F5 drücken, um die Änderungen zu sehen (das völlig unzumutbare F5-Drücken wird später auch entfallen).
Einen vorhandenen Node-Server von der Bash zu starten ist keine Raketenwissenschaft. Man sollte sich nur gleich beim Start merken, welche PID der neue Prozess bekommen hat, so dass man diesen gezielt beenden kann. Da das Script zwischenzeitlich beendet werden kann, wird die PID in einer Datei hinterlegt.

start_server() {
    echo "Start server..."
    $application_launcher "$build_directory/run-server.js" &
    echo $! > .serverpid
}

stop_server() {
    if [ -f ".serverpid" ]; then
        local server_pid=$(head -n 1 .serverpid)
        rm .serverpid
        if [[ $server_pid > 0 ]]; then
            echo "Stop server..."
            kill $server_pid
            server_pid=0
        fi
    fi
}

Es erscheint hier etwas unschön, stop_server() im Falle einer nicht gegebenen PID einfach nichts machen zu lassen. Die Bedingung sollte an der Aufrufstelle stehen oder der Funktionsname auf das nur bedingte Stoppen hinweisen. Da ich aber kurze und einfache Funktionen für ein Kommandozeilentool suche, halte ich diese Variante für pragmatisch und akzeptabel.

Im Argument-auf-Funktionsmapping des Scripts werden noch zwei oft genutzte Zusammenfassungen ergänzt:

process_tasks() {
    while [[ ${#tasks[@]} > 0 ]]; do
        local task=${tasks[0]}
        unset tasks[0]
        tasks=("${tasks[@]}")
        case "$task" in
            # ...
            'server')
                stop_server
                build_project
                start_server
            'restart-server')
                stop_server
                start_server
                ;;
            'start-server')
                start_server
                ;;
            'stop-server')
                stop_server
                ;;
            # ...
        esac
    done
}

Node-Build-Script auf Github

]]>
Bash-Build-Script mit Desktop-Nachrichten und Dateiüberwachung https://sgaul.de/2013/04/07/bash-build-script-mit-desktop-nachrichten-und-dateiuberwachung/ Sun, 07 Apr 2013 12:43:30 +0000 https://sgaul.de/?p=2079 Bash-Build-Script mit Desktop-Nachrichten und Dateiüberwachung weiterlesen]]> Ich habe mich die letzte Zeit viel mit Grunt herumgeschlagen. Grunt ist ein umfangreiches und mächtiges Build-Werkzeug für Node, dennoch hatte ich stets das Gefühl, dass die Konfiguration umständlicher als der wirkliche Nutzen ist. Ich habe daher mit einem Build-Script auf Bash-Basis begonnen, welches vom Nutzer spezifizierte Aufgaben nacheinander ausführt. Diese frühe Version unterstützt Coffeescript-Kompilierung, Testausführung und Dateisystemüberwachung, welche die anderen Tasks bei jeder Änderung automatisch ausführt. Nach dem Speichern bekommt man so stets die direkte Rückmeldung, ob die Unittests erfolgreich waren.

Bash-Build-Script für Node
Bash-Build-Script für Node

Boilerplate: Task-Abarbeitung

Auszuführende Aufgaben werden aus den Argumenten in ein Array tasks übernommen. Jede atomare Task Aufgabe hat eine Funktion (im Beispiel mytask()), welche die entsprechenden Operationen ausführt.

Die Abarbeitung selbst ist in process_tasks() definiert, was auch den Startpunkt des Scripts darstellt. Hier wird über das Aufgabenarray iteriert und anhand des Names die passende Funktion ausgeführt.

tasks=("${@:1}")

mytask() {
    # ...
}

# ...

process_tasks() {
    while [[ ${#tasks[@]} > 0 ]]; do
        local task=${tasks[0]}
        unset tasks[0]
        tasks=("${tasks[@]}")
        case "$task" in
            'mytask')
                mytask
                ;;
            # ...
        esac
    done
}

process_tasks

Testausführung mit Desktop-Benachrichtigungen

Die Funktion notify nimmt eine Nachricht und einen Typ (Erfolg oder Fehler) und stellt sie auf dem Desktop dar. Entsprechend des Typs wird ein Icon gewählt. Dieser Teil ist leider plattformabhängig. Für Linux habe ich notify-send gewählt.

Vom Unittest-Script wird hier in der Funktion run_tests erwartet, dass bei fehlgeschlagenen Tests auch ein Fehlercode, also ungleich 0, zurückgegeben wird. Ist dies nicht der Fall, muss hier die Ausgabe überwacht werden.

notify() {

    local message=$1      # the message to be sent
    local message_type=$2 # "error" || "success" || undefined

    if hash "notify-send" 2> /dev/null; then
        local icon="face-plain"
        if [[ "$message_type" == "error" ]]; then
            icon="error"
        elif [[ "$message_type" == "success" ]]; then
            icon="face-smile-big"
        fi
        notify-send -u low -t 500 -i "$icon" "$message"
    fi
}

run_tests() {
    $application_launcher $build_directory/test/precompiler.spec.js
    if [[ $? > 0 ]]; then
        notify "One or more tests failed!" "error"
    else
        notify "All tests passed." "success"
    fi
}

Watch: Dateisystem überwachen

Anwendung

Die Watchtask führt alle ihr nachfolgenden Tasks immer wieder aus, wenn sich im überwachten Verzeichnis etwas ändert.

./build.sh t1 t2 watch t3 t4

Die Aufgaben t1 und t2 werden direkt ausgeführt, ab watch wird gewartet. Mit jeder Änderung werden t3 und t4 ausgeführt. Ein sinnvolles Beispiel wäre somit:

./build.sh test watch test

Die Task test beinhaltet auch den Build. Der Aufruf führt die Tests zu Beginn und nach jeder Änderung aus.

Umsetzung

Das Dateisystem wird von inotifywait auf Änderungen überwacht. Es blockiert die Abarbeitung, bis eine Änderung vorgenommen wird. Hiernach werden die restlichen Tasks aus der Liste ausgeführt. Damit das Script dann nicht terminiert, wird die Taskliste um eine weitere Watch-Task und sich selbst ergänzt. So entsteht eine Endlosschleife.

watch() {
    tasks=("${tasks[@]}" "watch" "${tasks[@]}")
    inotifywait -rqe close_write,moved_to,create ./lib ./test
}

Code auf Github

Das Script kann als Vorlage für eigene Build-Scripts verwendet werden. Es steht unter der üblichen Für-alle-außer-Microsoft-und-Apple-Lizenz und ist auf Github zu finden:

Node-Build-Script auf Github

]]>
Node-Coding-Style: Vergleich zwischen Node FS und NPM https://sgaul.de/2013/04/01/node-coding-style-vergleich-zwischen-node-fs-und-npm/ https://sgaul.de/2013/04/01/node-coding-style-vergleich-zwischen-node-fs-und-npm/#comments Mon, 01 Apr 2013 14:41:27 +0000 https://sgaul.de/?p=2054 Node-Coding-Style: Vergleich zwischen Node FS und NPM weiterlesen]]> Noch immer führt die Suche nach einem Coding-Style für Node.js ins Leere. Da man sich für ein gleichmäßiges Code-Bild auch ohne offizielle Vorgabe Richtlinien schaffen muss, habe ich hier einen Vergleich zweier wesentlicher Module vorgenommen: Nodes integriertes FS und das unangefochtene Paketwerkzeug NPM.

Ich betrachte für mich derzeit relevante Punkte, wie etwa:

  • Einrückung
  • if-Statements
  • Stringausdrücke
  • Variablendeklaration
  • Semikolons

Mein Fazit: Den einen Node-Coding-Style gibt es nicht. Selbst die beiden Kernmodule weisen elementare Unterschiede auf. Den eigenen Stil muss man sich nach wie vor selbst zusammensuchen.

Eine Alternative: In Coffeescript ist der Code-Style Teil der Syntax, was konsistenten Code begünstigt.

Gemeinsamkeiten

Code-Einrückung

Zwei Leerzeichen:

Node FS

function read() {
  if (size === 0) {
    buffer = new Buffer(8192);
    fs.read(fd, buffer, 0, 8192, -1, afterRead);
  } else {
    fs.read(fd, buffer, pos, size - pos, -1, afterRead);
  }
}

Einzeilige If-Bedingungen

Meist einzeilig, aber nicht konsequent. In Verbindung mit else immer geklammert und mehrzeilig.

Node FS

if (er) return callback(er);
if (bytesRead === 0) {
  return close();
}

Argumentlisten

Keine Leerzeichen zwischen Klammer und Argument, Argumente durch Komma und Leerzeichen getrennt:

NPM

cb(null, made)

Strict-Mode

Keines der beiden Module verwendet den Strict-Mode von ECMA-Script 5.

Unterschiede

Variablen deklarieren

Node FS

var binding = process.binding('fs');
var constants = process.binding('constants');
var fs = exports;

NPM

var EventEmitter = require("events").EventEmitter
  , npm = module.exports = new EventEmitter
  , config = require("./config.js")

Verwendung von Semikolons

Node FS

Übliche, konsequente Verwendung des Semikolons am Ende eines Ausdrucks:

var kMinPoolSpace = 128;

return function(err) {
  if (err) {
    throw err;
  }
};

fs.Stats.prototype._checkModeProperty = function(property) {
  return ((this.mode & constants.S_IFMT) === property);
};

NPM

Soweit möglich konsequenter Verzicht auf Semikolons:

var cmd = require(__dirname+"/"+a+".js")

function defaultCb (er, data) {
  if (er) console.error(er.stack || er.message)
  else console.log(data)
}

npm.deref = function (c) {
  // ...
  var a = abbrevs[c]
  if (aliases[a]) a = aliases[a]
  return a
}

Stringausdrücke

Einfache Anführungszeichen, Leerzeichen zwischen String und Operator:

Node FS

'Unknown file open flag: ' + flag

NPM

Doppelte Anführungszeichen, keine Leerzeichen zwischen String und Operator:

"npm requires node version: "+j.engines.node

Vergleichsoperatoren: == gegen ===

Node FS

Kein konsequent beachteter Stil:

typeof data == 'string'
typeof arg1 === 'string'

NPM

typeof cli === "function"

Objektliterale

Node FS

Meist einzeilig:

options = { encoding: 'utf8', mode: 438 /*=0666*/, flag: 'a' };

NPM

Wie Variablendeklaration mehrzeilig mit vorgestelltem Komma:

npm.modes = { exec: 0777 & (~umask)
 , file: 0666 & (~umask)
 , umask: umask }
]]>
https://sgaul.de/2013/04/01/node-coding-style-vergleich-zwischen-node-fs-und-npm/feed/ 11
Current Working Directory in Node https://sgaul.de/2013/04/01/current-working-directory-in-node/ Mon, 01 Apr 2013 13:26:53 +0000 https://sgaul.de/?p=2044 Current Working Directory in Node weiterlesen]]> Nutzt man verschiedene Node-Applikationen, die sich gegenseitig aufrufen, kann dies zu Problemen mit relativen Pfaden führen. Ein require('./...'); schlägt schnell fehl, wenn Programm A Programm B im eigenen Arbeitsverzeichnis ausführt. B kann das CWD ändern, etwa von einer Datei im Rootverzeichnis:

// in B/app.js
process.chdir(__dirname);
// oder in B/sub/file.js
process.chdir(__dirname + '/../');

Ist kein Aprilscherz. Dafür wählt Youtube endlich seinen Gewinner:

[youtube clip_id=“H542nLTTbu0″]

]]>
Live-Unittest-Entwicklungsumgebung für Node https://sgaul.de/2013/03/24/live-unittest-entwicklungsumgebung-fur-node/ Sun, 24 Mar 2013 15:01:48 +0000 https://sgaul.de/?p=2031 Live-Unittest-Entwicklungsumgebung für Node weiterlesen]]> Und wieder mal mehr Zeit in die Arbeitsvorbereitung als in die eigentliche Arbeit gesteckt. Eigentlich wollte ich in einem bestehenden Node-Projekt auch mit Coffee-Script arbeiten können. Herausgekommen ist eine Entwicklungsumgebung, in der man jede auf Node basierende Sprache wie eben Coffee- oder auch Type-Script verwenden kann, eine die Änderungen in Echtzeit deployt, die Unittests ausführt und eventuelle Fehler via Desktop-Benachrichtigung anzeigt. Das Ganze funktioniert unabhängig vom verwendeten Editor bzw. verwendeter IDE. Wer in Sublime Text noch Strg+B drückt, macht es sich zu kompliziert…

Unittest-Ergebnisse bei jedem Speichern
Unittest-Ergebnisse bei jedem Speichern

Die Node-Applikation

Die einzurichtende Node-App ist nichts besonderes: Ein Express-Webserver aus Javascript und Coffescript. Getestet wird das Ganze mit Jasmine-Node, der Node-Variante des beliebten Testframeworks.

Der Javascript-Task-Runner Grunt

Grunt
Grunt

Die Grundlage für die Übersetzung und Echtzeitfunktionen bietet das Javascript-Build-Tool Grunt. Wie man ein Projekt damit anlegt ist auf der Seite Getting Started recht gut beschrieben (genau genommen finde ich die Beschreibung gar nicht so doll, habe aber keine Lust es besser zu machen).

Von Haus aus kann Grunt in etwa gar nichts, alles ist streng modular und muss manuell hinzugefügt werden. Ich brauche die folgenden Plugins:

grunt-contrib-copy

Mit Copy verschiebe ich reine Javascript-Dateien und andere Resourcen vom Source- ins Buildverzeichnis.

grunt-contrib-coffee

Mit der Coffee-Task schiebe ich Coffee-Dateien vom Source- ins Buildverzeichnis. Statt sie nur zu kopieren, werden sie dabei auch direkt in Javascript umgewandelt.

grunt-contrib-jasmine-node

Dieser Task dient dazu, nach dem Build auch Jasmine-Node-Unittests ausführen zu können (nicht mit grunt-contrib-jasmine für Frontend-Tests zu verwechseln). Tatsächlich nutze ich dieses Plugin nicht direkt, da ich eine Modifikation benötige (mehr dazu gleich).

grunt-contrib-watch

Dieses Modul überwacht ein angegebenes Verzeichnis auf Änderungen und startet jeweils eine spezifizierte Task. In meinem Fall ist das der Build (Copy und Coffee) sowie die Unittests.

Im Wesentlichen war es das auch schon: Die App wird bei jedem Speichern gebaut und automatisch getestet. Nur doof, dass man für die Ergebnisse ins Terminal gucken muss.

Testergebnisse als Desktop-Benachrichtigung

Hier konnte ich keinen wirklich eleganten Ansatz finden, der folgende Weg macht aber was wir wollen. Statt grant-contib-jasmine-node zu verwenden, kopieren wir uns die Task-Registrierung direkt in unser Gruntfile. Hier modifizieren wir die folgende Zeile:

var write = function(data) { process.stdout.write(data.toString()); }

Bevor die Write-Funktion die Nachricht in den Stdout schreibt, müssen wir analysieren, ob es sich um unsere Unittest-Ergebnisse handelt. Sieht die Nachricht etwa wie „x tests, y assertions, z failures“ aus, können wir Test- und Fehlerzahl direkt herausholen.

Nun, da wir unsere Ergebnisse extrahiert haben, müssen wir sie auf dem Desktop ausgeben. Ich nutze hierfür Notify-Send, welches Ubuntus Notify-Send-Benachrichtigungen bedient. Ggf. muss das Paket erst installiert werden. Für proprietäre Systeme gibt es andere Lösungen, die vergleichbar funktionieren.

]]>