Webpack 2 with Pug and SASS

Until now I used some Gulp tasks with Jade and SASS to create the HTML and CSS files as a base for the webcodr blog theme. A crude little setup, but it worked.

I decided to write an replacement with Webpack 2 and to share it with you: WebCodr/design GitHub repo

First steps

The original setup used the identend SASS syntax and Jade templates. Since the indented syntax is more trouble than it’s worth, I had to convert to the SCSS syntax, a superset of the CSS syntax.

Unfortunately there was a copyright claim for “Jade” and they had to rename the project to Pug. The Jade Node module is still available but unmaintained. It’s time to use Pug.


Don’t worry, that’s quite simple. The SASS people provide a tool called sass-convert and it’s really easy to use:

sass-convert --from=sass --to=scss -R assets/styles/

Just pass the tool the format you want to convert, the new format, -R for recursive conversion and add the directory name. After sass-convert finished, you find the new SCSS files along with their SASS predecessors. You can now delete the old SASS files, adjust your build tool and you’re done.

Jade to Pug

It depends on your templates how easy or complicated this step is. My template files are rather simple, so just renaming the file extensions from .jade to .pug and using Pug instead of Jade did the job. If you are having trouble, there is a migration guide from Jade to Pug.

Using Webpack 2

Webpack 2 isn’t finished yet. The current version is 2.2.0 RC3, but even the Beta version were stable enough to use it for production purposes.

Here’s my package.json file with all necessary modules:

  "name": "WebCodr_Design",
  "version": "2.0.0",
  "scripts": {
    "server": "webpack-dev-server",
    "dist": "webpack --config webpack.config.prod.babel.js"
  "dependencies": {},
  "devDependencies": {
    "autoprefixer": "^6.6.1",
    "babel-core": "^6.21.0",
    "babel-preset-es2015": "^6.18.0",
    "css-loader": "^0.26.1",
    "csso-loader": "^0.2.1",
    "extract-text-webpack-plugin": "^2.0.0-beta.4",
    "html-loader": "^0.4.4",
    "node-sass": "^4.1.1",
    "postcss-loader": "^1.2.1",
    "pug": "^2.0.0-beta6",
    "pug-html-loader": "^1.0.10",
    "sanitize.css": "^4.1.0",
    "sass-loader": "^4.1.1",
    "webpack": "2.2.0-rc.3",
    "webpack-dev-server": "2.2.0-rc.0"

I wrote two little helper scripts to run a dev server on port 8080 and to build a production version of the assets, mainly the CSS file. Currently there is no JavaScript file for webcodr. But it would be really easy to add JavaScript to the Webpack config.

The Webpack config or where the magic happens

I can’t stand the old JavaScript syntax anymore, so I write all my stuff in ES2015 and that’s why Babel is present in the dev dependencies.

import webpack from 'webpack'
import path from 'path'
import autoprefixer from 'autoprefixer'
import ExtractTextPlugin from 'extract-text-webpack-plugin'

let extractStyles = new ExtractTextPlugin('[name].css')
let extractHtml = new ExtractTextPlugin('[name].html')

let config = {
  stats: {
    assets: false,
    colors: true,
    version: false,
    hash: true,
    timings: true,
    chunks: false,
    chunkModules: false
  entry: {
    index: [
      path.resolve(__dirname, 'templates/index.pug')
    post: [
      path.resolve(__dirname, 'templates/post.pug')
    'css/application': [
      path.resolve(__dirname, 'assets/styles/application.scss')
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: '[name].js'
  module: {
    rules: [
        test: /\.pug$/,
        loader: extractHtml.extract({
          loader: ['html-loader', 'pug-html-loader?pretty&exports=false']
        test: /\.scss$/,
        loader: extractStyles.extract({
          loader: [
              loader: 'css-loader'
              loader: 'postcss-loader'
              loader: 'sass-loader'
  plugins: [
    new webpack.LoaderOptionsPlugin({
      minimize: false,
      debug: true,
      options: {
        postcss: [
            browsers: ['last 2 version', 'Explorer >= 10', 'Android >= 4']
        sassLoader: {
          includePaths: [
            path.resolve(__dirname, 'node_modules/sanitize.css/')

export default config


  • stats: Webpack 2 is really verbose, this options will help to make it shut up. ;)

  • entry: I defined three entry points for Webpack. Two for Pug files and one for my SCSS file. The property name is the filename for saving the finished file and the array item points to the source file.

  • output: Defines where to put the files and how to name them.

  • module: The module rules tell Webpack what to do with different file extensions. Since Webpack 2 is JavaScript module bundler it creates JavaScript files and we have to use a plug-in to get HTML and CSS files. The extract text plug-in looks for the content of the defined loaders and extracts it into the file you want.

    To convert Pug into HTML I had to use the Pug HTML loader and the HTML loader. The extract text plug-in now knows what to extract of the generated JavaScript files.

    The same goes for SASS. The SASS loader converts SASS into CSS, after that the PostCSS loader applies the Autoprefixer and finally the CSS loader tells the extract text plug-in what to do.

  • plugins: webpack.LoaderOptionsPlugin contains the loader config for Webpack 1 loaders. Webpack 2 compatible loaders can be configured directly with the loader definition. As you can see, PostCSS loader and SASS loader are not compatible with Webpack 2 yet.

    After the configuration of the legacy loaders, I just had to add the extraction plug-in instances and that’s it.

When Webpack is started, it will iterate through the entry points and apply the appropriate rules based on the regular expressions in the property test. Each rule applies the loaders in reverse order, so the last loader will be applied first. The loaders itself obtain their configs from the query as the Pug HTML loader does or the from the loader options plug-in, only the extract text plug-in is an exception as it needs two configurations: the loaders and where to put the extracted content.

After Webpack is finished, the files are located in the output directory. There are two files for each entry point: a HTML or CSS file and JavaScript file. As mentioned above, Webpack 2 is a JavaScript module bundler and can only create JavaScript files without help from plug-ins. Without extraction, this JavaScript files would contain the HTML or CSS code as a Webpack runtime module and could be used within JavaScript.

You could now open one of the HTML files in your browser, but there’s a better way. You would have to manually start Webpack for each change to make … meh.

yarn run server

This will start the Webpack Dev Server on Port 8080. It watches for changes and re-builds all affected files. You just have to reload the browser tab. Not comfy enough? There is a possibility of auto-reloading and even hot JavaScript module replacement which will be explored in a future blog post.

Adding JavaScript support

What do you think? Is it complicated to create JavaScript files with a JavaScript module bundler? Of course not!

Add the following rule to your config:

  test: /\.js$/,
  exclude: /node_modules\/*/,
  use: ['babel-loader'],
  options: {
    presets: [

Any entry point with JavaScript files will be processed with Babel for ES2015 support. Pretty neat, huh?

Need ES2016 or ES2017? No problem, just add a suitable Babel preset with npm or yarn to your project and adjust the config.

You’re using TypeScript or CoffeeScript? No problem, just look for a compatible loader.


Some say Webpack is too complicated. Well, it can be quite confusing if you’re just using it without knowledge what it really does, but it’s no rocket science.

I used Webpack to replace a rather large and complex collection of Gulp tasks to build the assets for the website of a customer. For certain things like SVG sprite maps I still use Gulp, but all other tasks are done by Webpack faster and way more easy, especially things like hashes in filenames for production purposes or automatically splitting the main JavaScript file into an application file and a vendor file with certain libraries/frameworks like AngularJS.

The same goes for Karma to run the Jasmine tests. Just add the Webpack plug-in for Karma, make some small adjustments to the Webpack config, let Webpack handle the rest and you’re done.

Compared to the old Gulp tasks, the Webpack setup is easier to understand, faster and much more fun to use. New team members have not to dig into a bunch of Gulp tasks and related helper methods. A quick introduction to the Webpack config is mostly enough to understand how it works.

If you have about 1,000 lines of code with Gulp tasks and helper functions or some really small tasks and a about 150 lines of Webpack config, what do you prefer?


Use Webpack 2, it’s awesome. :P

VDSL via Vigor 130 und EdgeRouter X

Meine Fritzbox 6490 ging mir ja schon l├Ąnger auf den Zeiger, aber seit ca. acht Wochen geht auch die Verbindung von Vodafone bzw. Kabel Deutschland zunehmend zum Teufel. Passenderweise hat die Telekom hier vor ein paar Wochen den VDSL-Ausbau inkl. Vectoring abgeschlossen. Seit dem 4.1. steht die Leitung und ich bin bisher ├Ąu├čerst angetan. Es halbiert sich zwar die Bandbreite auf 100 Mbit/s down (daf├╝r gibt’s 40 statt 25 Mbit/s up), daf├╝r ist die Leitung wesentlich stabiler. Genauers dazu schreibe ich in einem separaten Blog-Post in K├╝rze. Genaueres dazu hier.

Bisheriges Netzwerk

Die Internet-Verbindung wird ├╝ber die Fritzbox 6490 aufgebaut, die sonst au├čer VoIP nichts tut. An der Fritte h├Ąngt ein Ubiquiti EdgeRouter X, der dann das eigentliche Netzwerk aufbaut. Das Netz verteilt sich vom Wohnzimmer aus ├╝ber drei SoHo-Switches von Cisco und zwei Ubiquiti UniFi AC Lite Access Points an die jeweiligen Endger├Ąte.

Diese Konstellation f├╝hrt zu doppeltem NAT. H├Ąsslich, aber auch nicht weiter tragisch. Der EdgeRouter X kann Hardware-NAT und kostet h├Âchstens 400 - 500 ┬Ás. Ein geringer Aufpreis f├╝r wesentlich mehr Kontrolle ├╝ber mein Netzwerk. Nur die IPv6-Konfiguration zwischen Fritbox und EdgeRouter wollte einfach nicht klappen – diese unendliche Geschichte will ich hier aber nicht weiter ausbreiten, hat sich eh erledigt.

Neues Netzwerk

Mit VDSL sieht die Sache nat├╝rlich etwas anders aus. Der Telefonanschluss ist im Flur, also mussten erstmal Kabel gelegt werden … bei dem Thema sind Mietwohnungen dezent schei├če. Von Kabelkan├Ąlen hatte zum Bauzeitpunkt kein Mensch was geh├Ârt.

Zum Testen der Verbindung kam erstmal ein Speedport 724 V von der Telekom zum Einsatz. Klingt doof? So ├╝bel ist die Kiste gar nicht, allerdings ging’s auch nur mit doppeltem NAT, weil die Speedports leider keinen Modem-Betrieb mehr k├Ânnen und generell ist mein Nutzungsprofil doch etwas anspruchsvoller. Daher habe ich einen Vigor 130 von DrayTek gekauft und als Vectoring-taugliches VDSL-Modem im Einsatz. Der EdgeRouter wird nun einfach per PPPoE ├╝ber den Vigor versorgt.


Kommen wir zum Herzst├╝ck des Posts, denn die Konfiguration ist zwar grunds├Ątzlich einfach, hat aber auch ein paar T├╝cken – insbesondere wenn IPv6 im Spiel ist.

DrayTek Vigor 130

Der gute Vigor wird bereits als Modem inkl. VLAN-Tagging f├╝r die Telekom vorkonfiguriert geliefert. Man muss eigentlich nur die neueste Firmware einspielen und das war’s.

Falls der Vigor wider erwarten doch nicht als Modem vorkonfiguriert geliefert wird, findet bei iDomiX Hilfe (Text und Video).

Ubiquiti EdgeRouter

Ausgangsbasis ist der EdgeRouter X mit EdgeOS 1.9.1. Es sollte grunds├Ątzlich genauso mit einem gr├Â├čeren EdgeRouter und neueren EdgeOS-Versionen funktionieren.


Ich gehe hierbei davon aus, dass jemand, der diesen Post liest zumindest Grundkenntnisse im Umgang eines EdgeRouters hat, also Default-IP, Default-Login-Daten, Web-Interface-Zugang usw.

F├╝r die grunds├Ątzliche Konfiguration bietet Ubiquiti zum Gl├╝ck entsprechende Assistenten (Wizards) an, die den ganzen Vorgang vereinfachen und mir auch ersparen hier monstr├Âse Listings mit Firewall-Regeln zu posten.

Der Wizard “Basic Setup” macht grunds├Ątzlich alles, was man braucht:

EdgeRouter Screenshot 1

Als WAN-Port habe ich mich f├╝r eth0 entschieden. Die Einwahl erfolgt ├╝ber PPPoE mit den entsprechenden Login-Daten der Telekom. VLAN-Tagging ist hier nicht n├Âtig, das ├╝bernimmt der Vigor bereits (VLAN 7). Die Default-Firewall sollte auf jeden Fall aktiviert werden, genauso wie DHCPv6 PD. Die Prefix Length ist mit /56 bereits korrekt voreingestellt und auch hier gilt, dass die Firewall an sein sollte.

Die Option “Only use one LAN” wird deaktiviert. Anschlie├čed wird das Netz f├╝r eth1 konfiguriert. Ich nutze hier ein privates Klasse A-Netz. F├╝r die Switching-Ports eth2, eth3 und eth4 gibt es ein separates Klasse A-Netz.

Unter “User Setup” verwende ich meine bestehenden User weiter. Sollte der EdgeRouter bisher nie konfiguriert worden sein, empfehle ich aber dringend einen neuen Nutzer mit eigenem Passwort anzulegen. Ein Router sollte nie ├╝ber seine Standard-Zugangsdaten zug├Ąnglich sein.

Nach dem Speichern startet sich der EdgeRouter neu und ist anschlie├čend ├╝ber die IP an eth1 wieder erreichbar. Im Dashboard sollte nun relativ bald unter pppoe0 die ├Âffentliche IPv4-Adresse sichtbar sein und damit auch die Internet-Verbindung bestehen.

Grunds├Ątzlich ist’s damit getan, wenn einem IPv4 ausreicht.

F├╝r mein privates Netzwerk nehme ich anschlie├čend noch ein paar Einstellungen an der Firewall vor (Port-Weiterleitungen) und am DHCP-Server vor. Wenn man zufrieden ist, sollte man ein Backup der Konfiguration machen. Das geht unter “System” -> “Back Up Config”.


Leider unterst├╝tzt das Web-Interface bisher kaum IPv6, daher muss man ein paar Einstellungen via CLI vornehmen.

Unter macOS und Linux ist das einfach im Terminal im SSH m├Âglich. Unter Windows bleibt einem aktuell nur das Linux Subsystem von Windows 10 oder ein SSH-Client wie PuTTY.


ssh user@

Anschlie├čend wird nach dem Passwort des Users gefragt.

Bei mir sieht die Sache so aus (Login ├╝ber SSH-Alias mit Key):

~ ÔŁ»ÔŁ»ÔŁ» ssh edge                                                                                                                              
Welcome to EdgeOS

By logging in, accessing, or using the Ubiquiti product, you
acknowledge that you have read and understood the Ubiquiti
License Agreement (available in the Web UI at, by default, and agree to be bound by its terms.

Linux 3.10.14-UBNT #1 SMP Mon Nov 14 03:56:39 PST 2016 mips
Welcome to EdgeOS

Damit ist nun das CLI des EdgeRouters offen und man kann loslegen.

Folgendes muss eingegeben werden:


set interfaces ethernet eth1 ipv6 dup-addr-detect-transmits 1
set interfaces ethernet eth0 pppoe 0 dhcpv6-pd pd 0 interface eth1 host-address '::dead:beef'
set interfaces ethernet eth0 pppoe 0 dhcpv6-pd pd 0 interface eth1 no-dns
set interfaces ethernet eth0 pppoe 0 dhcpv6-pd pd 0 interface eth1 prefix-id 42
set interfaces ethernet eth0 pppoe 0 dhcpv6-pd pd 0 interface eth1 service slaac
set interfaces ethernet eth0 pppoe 0 dhcpv6-pd pd 0 prefix-length 56
set interfaces ethernet eth0 pppoe 0 dhcpv6-pd prefix-only
set interfaces ethernet eth0 pppoe 0 dhcpv6-pd rapid-commit enable
set interfaces ethernet eth0 pppoe 0 ipv6 address autoconf
set interfaces ethernet eth0 pppoe 0 ipv6 dup-addr-detect-transmits 1
set interfaces ethernet eth0 pppoe 0 ipv6 enable
set system offload hwnat enable


  • configure startet das Konfiguration-System von EdgeOS/Vyatta
  • set interfaces ethernet eth1 ipv6 dup-addr-detect-transmits 1 legt die Anzahl fest, wie oft versucht wird doppelte IPv6-Addressen herauszufinden
  • set interfaces ethernet eth0 pppoe 0 dhcpv6-pd pd 0 interface eth1 host-address '::dead:beef' legt die Host-Adresse nach dem Adress-Prefix der Telekom fest. Ich finde ::dead:beef ziemlich witzig, aber hier kann sich jeder selbst austoben, solange es im Hexadezimal-Bereich liegt.
  • set interfaces ethernet eth0 pppoe 0 dhcpv6-pd pd 0 interface eth1 prefix-id 42 legt die Prefix-ID fest, die zus├Ątzlich in die Adresse aufgenommen wird. Was au├čer 42 sollte es sonst sein? :D
  • set interfaces ethernet eth0 pppoe 0 dhcpv6-pd pd 0 interface eth1 service slaac SLAAC steht f├╝r Stateless Address Autoconfiguration – damit erzeugt der Port seine IP-Adresse anhand des Prefixes selbst
  • set interfaces ethernet eth0 pppoe 0 ipv6 address autoconf aktiviert die automatische IPv6-Adress-Konfiguration f├╝r das PPPoE-Interface
  • set interfaces ethernet eth0 pppoe 0 ipv6 enable aktiviert dann letztendlich IPv6
  • set system offload hwnat enable aktiviert Hardware-NAT f├╝r IPv4
  • commit wendet die neue Konfiguration an
  • save speichert die neue Konfiguration ab

Nach commit starten sich die betroffenen Interfaces neu und es erfolgt eine neue Einwahl ├╝ber PPPoE. Anschlie├čend befinden sich im Dashboard unter pppoe0 die IPv4-Adresse sowie die entsprechenden globalen und lokalen IPv6-Adressen.

Alternativ l├Ąsst sich das via CLI anschauen (au├čerhalb von configure):

webcodr@ubnt:~$ show interfaces
Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down
Interface    IP Address                        S/L  Description
---------    ----------                        ---  -----------
eth1                       u/u  Local 2

Alle Ger├Ąte im Netzwerk sollten nun eine oder mehrere IPv6-Adressen besitzen und via IPv6 ins Internet kommen. Das l├Ąsst sich sehr einfach ├╝ber die Seite ipv6-test.com herausfinden.

Zugang zum Vigor-Web-Interface

Da der Vigor auf der IP-Adresse rumh├Ąngt, kommen wir nun leider erstmal nicht an sein Web-Interface ran. Das l├Ąsst sich aber recht einfach in EdgeOS ├Ąndern:

Dazu muss eth0 (├╝ber diesen Port der EdgeRouter ja am Vigor 130) eine IP-Adresse aus Netz des Vigors zugewiesen werden. Ich verwende hier

EdgeRouter Screenshot 2

Das alleine reicht noch nicht, da die NAT bisher die Verbindung in das andere Netz nicht kennt. Unter “Firewall/NAT” -> “NAT” -> “Add Source NAT Rule”, tr├Ągt man daher folgendes ein und speichert.

EdgeRouter Screenshot 3

Anschlie├čend sollte der Zugriff ├╝ber die IP-Adresse auf den Vigor sofort funktionieren.

So, das war’s dann eigentlich schon. Ich hoffe, diese kleine Anleitung konnte dem ein oder anderen etwas weiterhelfen.

Da nicht alles davon auf meinem Mist gewachsen ist, m├Âchte mich an dieser Stelle noch beim Autor des Blogs TauSys bedanken. Falls jemand in og. Konfiguration noch Entertain miteinbeziehen m├Âchte, sei ihm dieser Post w├Ąrmstens empfohlen.


F├╝r uns Entwickler ist die Tastatur ohne Zweifel das wichtigste Werkzeug. Ohne sie k├Ânnten wir keine Zeile Code schreiben und dennoch wird ihr selten Beachtung geschenkt. Sie ist da und funktioniert – bis man eine Tasse Kaffee dar├╝ber sch├╝ttet oder sie nach Jahren in Kr├╝meln, anderen Essensresten bzw. Staub ersoffen ist.

Wenn sie kaputt ist, wird f├╝r ein paar Euro eine neue gekauft und gut ist. Manchmal gibt man auch etwas mehr aus, z.B. f├╝r ein Apple Keyboard oder ein besseres Modell von Logitech.

Dabei macht sich aber niemand Gedanken dar├╝ber, wie sie eigentlich funktioniert und ob da nicht gerade f├╝r uns Vieltipper Verbesserungspotenzial ist.

Die meisten Tastaturen haben eines gemeinsam: sie nutzen die sog. Rubberdome-Technik. Dabei wird beim Tastendruck eine Gumminoppe mit Kontaktpad nach unten auf die Platine gedr├╝ckt und so der Anschlag durch den Kontakt ausgel├Âst. Alle Noppen befinden sich auf einer Gummifolie, die einfach ├╝ber die Platine gelegt wird. Kabel ans PCB, Tasten drauf, Geh├Ąuse drumrum und fertig ist die Tastatur.

Das ist g├╝nstig, aber auch gut? Leider nein. Es gibt zwar durchaus ganz gute Vertreter dieser Gattung, aber die Schwachstellen haben sie alle gemein:

  1. Um den Anschlag auszul├Âsen, muss man die Taste vollst├Ąndig durckdr├╝cken.
  2. Der Anschlag ist nicht ├╝ber alle Tasten konsistent und ver├Ąndert sich mit der Zeit.
  3. Gerade bei den billigen Vertretern, mitunter aber auch bei den teuren ist das Geh├Ąuse nicht verwindungssteif.
  4. Geringe Haltbarheit von max. 5 Millionen Anschl├Ągen.

Die ersten drei Punkte fallen insbesondere f├╝r Vieltipper deutlich st├Ąrker ins Gewicht, als man meinen m├Âchte. Vollst├Ąndiges Durchdr├╝cken der Tasten l├Ąsst die H├Ąnde bzw. Finger schnell erm├╝den – gleiches gilt, wenn sich die Tastatur beim Tippen verwindet bzw. nach unten verbiegt. Da sich der Anschlag ungleichm├Ą├čig ├╝ber die Zeit und je nach Nutzung der Tasten ver├Ąndert, kann sich kein Muskelged├Ąchtnis bilden, um schneller tippen zu k├Ânnen.

Und jetzt?

Wie w├Ąr’s mit einer Alternative? Mechanische Tastaturen!

Huch? Sind die nicht sauteuer, laut und sperrig? So wie fr├╝her diese Totschl├Ąger von IBM …

Hach ja, das IBM Model M. Wenn man wollte, k├Ânnte man damit wohl wirklich Einbrecher niederschlagen. Zur Selberverteidigung empfehle aber doch eher andere Ger├Ątschaften.

Ein billiges Vergn├╝gen sind mechanische Tastaturen wirklich nicht, aber sie sind es defintiv wert. Hier geht es schlie├člich um ein Werkzeug. Handwerker geben sich schlie├člich auch nicht mit einem Consumer-Akkuschrauber f├╝r 20 Euro zufrieden. Warum also sollten wir das bei unseren allt├Ąglichen Helfern tun? Ob sie laut und gro├č sind, h├Ąngt dagegen ganz vom jeweiligen Geschmack ab.

Aha, und warum k├Ânnen die Dinger das nun besser?

Um die Kritikpunkte von oben direkt aufzugreifen:

  1. Mechanische Schalter m├╝ssen nicht vollst├Ąndig durchgedr├╝ckt werden, um einen Anschlag auszul├Âsen – die H├Ąlfte gen├╝gt. Die verbreitetesten Schalter (Cherry MX) haben einen Hub von insgesamt 4 mm und l├Âsen darum nach 2 mm aus.
  2. Der Anschlag ver├Ąndert sich weder mit der Zeit noch nutzen sich verschiedene Tasten je nach Gebrauch unterschiedlich ab. So bildet sich mit der Zeit ein Muskelged├Ąchtnis, das schnelleres und komfortableres Schreiben erm├Âglicht.
  3. Bei guten mechanischen Tastaturen sind die Schalter auf einer Stahlplatte angebracht, darunter befindet sich die Platine mit der sie verl├Âtet sind. Da verwindet und verbiegt sich nichts.
  4. Mechanische Tastaturen halten Jahrzehnte durch. Die durchschnittliche Lebensdauer von Cherry-Schaltern liegt bei ca. 50 Mio. Anschl├Ągen. Unzerst├Ârbar sind sie aber nat├╝rlich nicht.

Je nach Schalter-Typ gibt es auch noch zus├Ątzliche Vor- und Nachteile, so dass man sie ganz nach seinen Pr├Ąferenzen aussuchen kann.


Ich beschr├Ąnke mich hierbei auf die g├Ąngen Varianten von Cherry, da sie am h├Ąufigsten verbaut werden.

  • MX blue: l├Âst mit einem Klick-Ger├Ąusch aus und hat dazu beim Anschlag einen sp├╝rbaren Widerstand. Bet├Ątigungskraft: 60 cN
  • MX brown: l├Âst ohne Klick aus, besitzt aber den gleichen sp├╝rbaren Widerstand beim Anschlag wie der MX blue. Bet├Ątigungskraft: 55 cN
  • MX black: linearer Schalter, l├Âst beim Anschlag wieder einen Klick noch einen Widerstand aus. Bet├Ątigungskraft: 60 cN
  • MX red: wie MX black, nur mit geringerer Bet├Ątigungskraft: 45 cN

Dar├╝ber hinaus gibt es noch verschiedene Varianten der MX blue und MX brown mit h├Âherer Bet├Ątigungskraft. Man trifft sie allerdings eher selten an, manche davon sind sogar regelrecht rar.

Grunds├Ątzlich gilt es beim Kauf einer Tastatur zuerst den Schalter-Typ zu w├Ąhlen. Zum Entwickeln oder Schreiben sind die MX blue definitiv die beste Wahl, da sie ein h├Âr- und sp├╝rbares Feedback bieten und daher nach der Eingew├Âhnung, sehr schnelles und fl├╝ssiges Tippen ohne Erm├╝dungserscheinungen erm├Âglichen.

Nun mag nat├╝rlich so manchen das Klicken auf die Palme bringen, andere widerrum k├Ânnen nicht mehr ohne. Wer sich daran st├Ârt oder die Kollegen im B├╝ro nicht zum Amoklauf treiben will, aber dennoch sp├╝ren m├Âchte, wenn der Anschlag ausgel├Âst wird, sollte zum MX brown greifen. Er tippt sich ein klein wenig leichter und ist ohne den Klick erheblich leiser.

Findet man dagegen Widerstand zwecklos, sollte man einen Blick auf die MX black oder MX red werfen. Letzterer ist besonders bei Spielern sehr beliebt, da man sehr schnell mehrere Anschl├Ąge hintereinander ausl├Âsen kann, ohne wie ein Bekloppter auf die Tastatur h├Ąmmern zu m├╝ssen. Daf├╝r neigt man u.U. eher zu Vertippern.

Grunds├Ątzlich kann man die Schalter in drei Kategorien aufteilen: Profi-Tipper ohne Klick-Allergie, die eher wenig bis gar nicht spielen, werden mit MX blue am gl├╝cklichsten. M├Âchte man dagegen einen guten Allrounder zum Schreiben und Spielen, ist der MX brown das Mittel der Wahl. Wer mehr spielt als Texte tippt, d├╝rfte mit einem MX black oder MX red am besten klarkommen.

Ich empfehle aber jedem, verschiedene Schaltertypen auszuprobieren. Es gibt genug Spieler, die sich nicht am Klicken des MX blue st├Âren und den MX red hassen wie die Pest – und umgekehrt genauso.

Noch etwas zur Lautst├Ąrke: man kann auch mit einem nicht klickenden Schalter sch├Ân Krach machen, wenn man auf der Tastatur rumhackt wie ein Irrer. Dazu h├Ąngt der Ger├Ąuschpegel auch von der Konstruktion bzw. dem Geh├Ąuse ab. Dazu sp├Ąter etwas mehr (dasKeyboard Model S vs. dasKeyboard 4 vs. Corsair K70).

Was kostet der Spa├č und wo bekomme ich so ein Teil her?

Der Preis der g├Ąngigen Modelle liegt zwischen 60 und 200 Euro.

Die definitiv g├╝nstigste Wahl gibt’s von Cherry selbst mit dem MX Keyboard 3.0. Zu bekommen bei Amazon oder den g├Ąngigen Hardware-H├Ąndlern.

Ansonsten ist die Auswahl an Shops eher eingeschr├Ąnkt. Die EU und insbesondere Deutschland geh├Âren bei mechanischen Tastaturen eher zur dritten Welt.

Die bisher besten Quellen, die ich neben Amazon ausmachen konnte: GetDigital (dasKeyboard, Ducky, Filco, Leopold), CaseKing (Cherry und Ducky), The Keyboard Company UK (prim├Ąr Filco und deren Zubeh├Âr).

Ansonsten stellen diverse bekannte Marken aus dem Spiele-Sektor wie Corsair, Razer, CMstorm, Func und auch Logitech mechanische Tastaturen her, die man auch in g├Ąngigen L├Ąden und Shops bekommt.

F├╝r professionelle Anwender rate ich zu Filco, Ducky oder dasKeyboard, da sie qualitativ etwas mehr bieten. Besonders Ducky und Filco bauen regelrechte Panzer. Grunds├Ątzlich kann mit den anderen aber nicht viel falsch machen. Man sollte aber inzwischen aufpassen, von welchem Hersteller die Switches sind. Da gibt es leider nicht nur Unterschiede in der Schaltcharakteristik, weil inzwischen wegen abgelaufener Patente auch billigere Kopien der MX-Schalter auf dem Markt sind, zu deren Haltbarkeit es noch keine Erfahrungswerte gibt. Die einschl├Ągigen Foren sehen die Sache allerdings weniger gelassen …

Meine Erfahrung

Ich hatte mich im November vergangenen Jahres dazu entschlossen eine mechanische Tastatur auszuprobieren und entschied mich f├╝r eine Corsair K70 mit MX brown und blauer Hintergrundbeleuchtung. Die Tastatur hing nur an meinem Windows-Rechner, an dem ich eher wenig schreibe und mehr spiele.

Abgesehen von der Lautst├Ąrke durch die offene Konstruktion ohne einen Rahmen, der die Tasten umgibt, war ich mit der K70 sehr zufrieden. Nach einem Tip im Computerbase-Forum, habe ich Gummi-O-Ringe zwischen die Tasten und Schalter montiert, was die Lautst├Ąrke deutlich d├Ąmpfte. Allerdings ver├Ąndern solche Ma├čnahmen den Anschlag – einfach ausprobieren, die Ringe kann man sehr g├╝nstig kaufen.

F├╝r meine Arbeit als Entwickler, die ich ausschlie├člich am Mac verrichte, habe ich l├Ąngere Zeit eine geeignete mechanische Tastatur gesucht. Zum Testen entschied ich mich f├╝r ein dasKeyboard Model S Professional for Mac mit MX blue. Leider gibt es dieses Modell nur mit ANSI-Layout, was mich anfangs vom Kauf abgehalten hat und schlie├člich auch nicht damit zurecht kam. Der st├Ąndige Wechsel bringt einen nur durcheinander und das deutsche Tasten-Mapping (ISO-Layout) mit einem ANSI-Layout zu nutzen klappt auch nicht – da fehlt schlie├člich eine Taste.

Die MX blue dagegen haben mich auf Anhieb begeistert. Kurzum: ich habe die Tastatur gegen ein dasKeyboard 4 mit MX blue umgetauscht. Die hat zwar kein Mac-Layout, aber das l├Ąsst sich durch den Tausch der Tastenkappen und ein paar Einstellungen in OS X problemlos anpassen. Au├čerdem funktionieren die Media-Tasten und selbst der Lautst├Ąrkeregler ohne jegliche Anpassung mit OS X. Wozu da also noch ein spezielles Mac-Modell?

Parallel lie├č mich leider die K70 im Stich. Innerhalb eines halben Jahres gingen drei LEDs der Hintergrundbeleuchtung kaputt. Nach einer Recherche in den Corsair-Forn scheint das ein verbreitetes Problem mit den blauen LEDs (gibt auch rote) bei der K70 zu sein. Corsair hat das Modell zwischenzeitlich vom Markt genommen und wird wohl demn├Ąchst eine ├╝berarbeitete Version liefern.

Amazon nahm die K70 anstandslos zur├╝ck und als Ersatz habe ich mich f├╝r ein dasKeyboard 4 mit MX brown entschieden und es bisher nicht bereut.

Zum Abrunden des Portfolios der MX-Schalter, habe ich mir noch als Abwechslung zu den beiden dasKeyboard eine Ducky Shine 3 mit MX black geg├Ânnt. Sie wirkt noch einen Tick solider, hat ein abnehmbares Kabel und zig Beleuchtungsoptionen. Der lineare Gang und h├Âhere Widerstand der Switches ist etwas gew├Âhnungsbed├╝rftig, aber angenehm, besonders beim Spielen.

Eine weitere Anmerkung zur Lautst├Ąrke: die dasKeyboard 4 haben zwischen ihrer Deckplatte aus Aluminum und der Stahlplatte einen sehr festen Schaumstoff, der die Ger├Ąuschkulisse d├Ąmpft. Der Unterschied zum Vorg├Ąngermodell oder auch der Corsair K70 ist ziemlich gewaltig. Sogar die MX blue-Version ist leiser als die K70 samt Gummi-O-Ringen. Die Ducky liegt durch ihre sehr stabile Konstruktion auch recht gut im Vergleich. Dazu tragen aber auch die MX black sicherlich ihren Teil bei.

Generell habe ich mit keiner der Tastaturen eine schlechte Tipp-Erfahrung gemacht. Nur das ANSI-Layout und die defekten LEDs haben mich zu einem Umtausch veranlasst.


Nach dem ich nun in einem halben Jahr drei Hersteller und drei unteschiedliche Schalter-Typen durch habe, kann ich nur sagen, dass mich mechanische Tastaturen begeistern, wenn sie nicht sogar eine Sammlerwut entfacht haben (ich hoffe nicht, das w├Ąre ein teures Hobby).

Der Tipp-Komfort und die Zuverl├Ąssigkeit sind es mir aber wert. Schlie├člich sind die Tastaturen auch ma├čgeblich daran beteiligt, dass ich ├╝berhaupt Geld verdiene. Wenn man die Unterschiede erstmal kennt, m├Âchte man nur ungern zu Rubberdome-Tastaturen zur├╝ckkehren. Selbst mein fr├╝her geliebtes Apple Keyboard lasse ich f├╝r eine mechanische Tastatur links liegen.

Dazu gibt es eine unglaubliche Vielfalt an Individualisierunsm├Âglichkeiten. Seien es nun Tastenkappen, modifizierte Federn in den Schaltern oder gar leicht verr├╝ckte Dinge wie Tastatur-Baus├Ątze, mit denen man wirklich alles bis ins letzte Detail f├╝r sich anpassen und aust├╝fteln kann. Interessenten zu diesen Themen sowie Kaufberatung etc. kann ich das Forum geekhack ans Herz legen.

Amplify 2.0

Ich habe Amplify grundlegend ├╝berarbeitet und mit ein paar neuen Features ausgestattet:

  • Redcarpet wurde durch kramdown ersetzt und unterst├╝tzt damit auch die Markdown-Erweiterungen, die kramdown anbietet.
  • Automatisches Verlinken von URLs
  • HTML-Sanitation
  • Das Syntax-Highlighting ├╝bernimmt nun das Gem ‘pygments.rb’. Der Umweg ├╝ber Pygmentizr f├Ąllt damit intern weg und verbessert die Reaktionszeiten deutlich.
  • Das Frontend basiert nun auf AngularJS.
  • Komplett ├╝berarbeiteter Quelltext.
  • JSON-basierte API

kramdown, das automatische Verlinken und die HTML-Sanitation werden ├╝ber das Gem ‘slodown’ von Hendrik Mans erledigt.

Die neue JSON-basierte API ist ├╝ber eine andere URL erreichbar:


Ein Beispiel-Request via POST:

 	"source": "# Hello World!"

Und die entsprechende Antwort von Amplify:

	"html": "<h1 id=\"hello-world\">Hello World!</h1>",
 	"source": "# Hello World!"

Wer, wie fr├╝her, ohne JSON auskommen m├Âchte, nutzt bitte folgende URL:


Die bisherige M├Âglichkeit einfach einen POST-Request auf die Amplify-URL abzusetzen ist nur noch aus Gr├╝nden der Kompatibilit├Ąt zu bestehenden Anwendungen aktiv, wird aber langfristig deaktiviert.

Zuk├╝nftige Features werde ich au├čerdem nur f├╝r die JSON-basierte API 2.0 implementieren.

Des weiteren plane ich Amplify langfristig nicht mehr auf Heroku laufen zu lassen, da die App einfach zu h├Ąufig in den Schlafmodus versetzt wird und anschlie├čend sehr lange braucht, bis sie auf Anfragen reagiert.

Ich k├Ânnte zwar einfach einen Dyno hinzubuchen, aber das ist nicht gerade billig und es gibt wirklich kosteng├╝nstigere M├Âglichkeiten, Ruby-Web-Applikationen zu hosten – beispielsweise bei Uberspace.de.

Aus technischer Sicht d├╝rfte noch interessant sein, dass Amplify nun mit Capybara automatisiert getestet wird und auf Travis CI sowie testweise Circle CI (mit automatischem Heroku-Deployment) l├Ąuft.

Say Hello to Mango

Was’n das?

Finger weg! Diese Mango schmeckt nicht.

Mango ist ein Object Document Mapper f├╝r MongoDB und PHP.

Und MongoAppKit?

MongoAppKit hat ein Problem, denn es ist nicht nur ein ODM und kann noch so einiges mehr. Theoretisch kann man zwar die ODM-Komponente auch ohne den ganzen anderen Krempel nutzen, aber es bleibt eine gro├če Abh├Ąngigkeit zu Silex, die man auch nicht so einfach wieder los wird.

Dies und das im Vergleich schlechte Handling bzw. der geringe Komfort von MongoAppKit, haben mich dazu bewogen mit Mango einen universell einsatzbaren und leicht handzuhabenden ODM zu entwickeln.

Mango wurde stark von Mongoid f├╝r Ruby inspiriert und soll dessen Funktionalit├Ąt zumindest teilweise in PHP abbilden. Das ist einfacher gesagt als getan, denn Ruby bietet wesentlich elegantere M├Âglichkeiten diverse Probleme zu l├Âsen, als es mit PHP derzeit machbar ist.

Los geht’s ÔÇŽ

Installation via Composer

$ php composer.phar require webcodr/mango:*

Ein Dokument anzulegen ist ein Kinderspiel


namespace MyProject\Model;

use Mango\Document;
use Mango\DocumentInterface;

class User implements DocumentInterface
    use Document;

    private function addFields()
        $this->addField('name', ['type' => 'String']);
        $this->addField('email', ['type' => 'String']);
        $this->addField('created_at', ['type' => 'DateTime', 'index' => true, 'default' => 'now'];
        $this->addField('updated_at', ['type' => 'DateTime', 'index' => true, 'default' => 'now'];

Es ist lediglich n├Âtig, dass die Model-Klasse das Interface DocumentInterface implementiert und den Trait Document einbindet. In der Hook-Methode addFields() werden anschlie├čend noch die Felder des Dokuments deklariert.

Mango nutzt etwas Magic: Der Klassenname des Models ist gleichzeitig auch der Name der Collection (klein geschrieben). Soll die Collection anders hei├čen bzw. das Model eine vorhandene nutzen, muss lediglich die Methode getCollectionName() ├╝berschrieben werden.

Go, Mango, go!


use Mango\Mango;
use Mango\DocumentManager;

use Document\User;

$mango = new Mango('mongodb://localhost/galactica');
$dm = new DocumentManager($mango);
$user = new User();
$user->name = 'William Adama';
$user->email 'william.adama@bsg-75.mil';

Das Mango-Object erwartet eine g├╝ltige MongoDB URI, falls notwendig inkl. Benutzer, Passwort, Port usw.

Dem Document Manager kommt eine vergleichbare Aufgabe zu, wie dem Entity Manager in Doctrine2.

F├╝r mehr Komfort holt sich eine Model-Klasse den Document Manager ├╝ber eine statische Methode ab. Daher k├Ânnen Methoden wie store() direkt ├╝ber die Model-Klasse abgewickelt werden.

Dokumente abfragen


$user = User::where(['name' => 'William Adama']);
echo $user->count(); // = 1
echo $user->first()->email; // = william.adama@bsg-75.mil

Eine Abfrage kann einfach ├╝ber die statische Methode where() ausgef├╝hrt werden. Die Syntax der Abfragen entspricht derzeit noch der normalen MongoDB Query API. F├╝r die Zukunft plane ich aber eine Abstraktionsebene f├╝r die Abfragen, vergleichbar mit Mongoid.

Eine Abfrage kit where() oder find() gibt immer ein Cursor-Objekt zur├╝ck, das anhand der aufgerufenen Methode entscheiden kann, ob der Zugriff auf den MongoCursor oder das Abfrageergebnis in Form einer Instanz von MutableMap erfolgt.

In obigem Beispiel ist count() eine Methode des Cursors, w├Ąhrend first() schon auf der Ergebnis zugreift. Wie MongoCursor kann auch die Cursor-Klasse von Mango einfach ├╝ber das Ergebnis iterieren.

Durch die dynamische Unterscheidung zwischen MongoCursor- und Datenzugriff, k├Ânnen auf eine Instanz der Cursor-Klasse auch alle Methoden von MutableMap angewandt werden.



User::where()->reverse()->slice(0, 2)->each(function($document) {
    echo $document->name;

Nat├╝rlich macht dieser Code wenig Sinn, da man das wesentlich effizienter ├╝ber die Cursor-Methoden erledigen kann. Das Beispiel soll lediglich zeigen, was m├Âglich w├Ąre.


Mango sorgt automatisch daf├╝r, dass die Dokumente im Ergebnis immer Instanzen ihrer jeweiligen Model-Klasse sind.

Die Hydration-Automatik sorgt au├čerdem daf├╝r, dass die Daten intern als jeweilige Typ-Klasse von Mango gehalten werden.

Typ-Klassen halten die Daten und k├Ânnen sie in zwei Formaten zur├╝ckgeben. Konfiguriert man ein Feld als DateTime bekommt Mango intern beim Speichern automatisch ein MongoDate-Objekt. Greift man hingegen au├čerhalb von Mango auf den Wert zu, bek├Ąme man in diesem Fall eine Instanz der Klasse DateTime zur├╝ck.

Soweit zum aktuellen Funktionsumfang von Mango. Es ist bei weitem noch nicht fertig, kann aber f├╝r kleine Projekte schon einsetzt werden. Ich verwende es selbst in der aktuellsten Version von CodrPress und es macht wesentlich mehr Spa├č als MongoAppKit, ohne ein monstr├Âses Schlachtschiff wie Doctrine zu sein.

Nat├╝rlich gibt’s Mango auch bei GitHub.

Services ftw!

Wer kennt das nicht? Man findet eine nette Software-Bibliothek in einer bestimmten Sprache, die vom Server der eigenen Web-Applikation nicht unterst├╝tzt wird oder nur sehr umst├Ąndlich auf andere Weise genutzt werden kann.

So erging es mir mit Tools f├╝r Markdown-Rendering und server-basiertem Syntax-Highlighting. Zwar habe ich daf├╝r ja im Oktober die Composer-Pakete SilexMarkdown und Radiant geschrieben, die beide auf bereits vorhandenen Bibliotheken fu├čen.

Ich war mit beiden nie recht gl├╝cklich. F├╝r Ruby und Python gibt es viel sch├Ânere, wesentlich umfangreichere L├Âsungen:

  • Pygments ist ein Python geschriebener Syntax-Highlighter, der nahezu jede relevante Sprache unterst├╝tzt – selbst esoterische Merkw├╝rdigkeiten wie Brainfuck.

  • Redcarpet wurde in Ruby verfasst und bietet einen sehr leicht erweiter- und modifizierbaren Markdown-Renderer.

Gerade f├╝r ein Blog-System wie CodrPress liegt es nahe, beide zu kombinieren und damit zumindest teilweise GitHub flavoured Markdown zu unterst├╝tzen.

Wie bekomme ich also drei Programmiersprachen unter einen Hut, ohne dass CodrPress nur auf angepassten Server-Konfigurationen l├Ąuft? Ganz einfach: Services!


Pygmentizr ist logischerweise in Python geschrieben, um Pygments nutzen zu k├Ânnen.

Per POST-Anfrage auf die verlinkte URL erreicht man den eigentlichen Service, der als Parameter die Sprache und den Quelltext erwartet. Zur├╝ck kommt HTML, das nur noch per CSS h├╝bsch gemacht werden muss.

Ein entsprechendes Stylesheet f├╝r den bekannten Monokai-Stil ist auf der Seite verlinkt oder im GitHub-Repository zu finden.

Pygmentizr bei GitHub


Amplifyr nutzt Redcarpet und bindet Pygmentizr als Syntax-Highlighter ein.

Wie Pygmentizr l├Ąsst sich Amplifyr per POST-Anfrage ansprechen und gibt den in HTML konvertierten Markdown-Quelltext zur├╝ck.

Amplifyr bei GitHub

Beide Dienste laufen auf der Cloud-Plattform Heroku.


Um beide Services in CodrPress nutzen k├Ânnen, habe ich in SilexMarkdown ein paar Umbauten vorgenommen. Beim Registrieren des Service-Providers in einer Silex-Applikation, l├Ąsst sich nun ganz einfach ├╝bergeben, ob der eingebaute Renderer samt Radiant oder Amplifyr genutzt werden soll. Eine entsprechende Anleitung findet sich in der ReadMe-Datei des SilexMarkdown-Repositories bei GitHub.

Beide Dienste sind bei Heroku untergebracht und kosten mich keinen Cent. daher gebe ich die Nutzung f├╝r jeden frei. Viel Spa├č!

Array almighty

Wenn man sich zulange nur mit PHP besch├Ąftigt vergisst man schnell, dass man oft Dinge tut, die kaum in andere Sprachen ├╝bertragbar sind.

Letztens habe ich mir Scala etwas n├Ąher angesehen. Kurz am Rande: eine sch├Âne Sprache, wenn auch die verschiedenen Syntax-Modi etwas verwirrend oder zumindest recht gew├Âhnungsbed├╝rftig sind.

Vorteil PHP

Scala bietet wie fast jede andere typisierte Sprache verschiedene Listen-Klassen f├╝r diverse Nutzungsf├Ąlle. In PHP gibt es das nicht. Man hat sein Array, das jederzeit ver├Ąnderlich ist, jede noch so wilde Mischung von Datentypen akzeptiert und assoziative Schl├╝ssel erlaubt. Es ist einfach umgemein praktisch.

Vorteil Scala

Da PHP leider weit davon entfernt ist vollst├Ąndig objekt-orientiert zu sein und darum ein Array leider keine Objekt ist, kann man Arrays nur mit diversen Funktionen bearbeiten.

Zwar funktioniert das einwandfrei, ist aber umst├Ąndlich. Ein Array-Objekt, das entsprechende Methoden bietet, die sich am besten auch noch verkettet aufrufen lassen, w├Ąre doch toll.

PHP goes Scala/Java/Objective-C

Daher habe ich mich ans Werk gemacht und die bereits existierende Klasse IterateableList in MongoAppKit in drei neue Klassen des Namespaces \MongoAppKit\Collection aufgeteilt: MutableMap und ArrayMap.

Die Namen orientieren sich an ihren Pendants in Scala, Java oder auch Objective-C. W├Ąhrend alle die SPL-Interfaces Countable und IteratorAggregate implementieren, verwendet ArrayMap zus├Ątzlich das Interface ArrayAccess und kann damit wie ein PHP-Array verwendet werden.

Au├čerdem implementieren alle drei die Magic Methods __get(), __set(), __isset() und __unset. Das erleichtert z.B. die Verwendung einer Liste in Twig, in dem keine Methode mehr angesprochen werden muss, um innerhalb eines Templates auf die Inhalte zuzugreifen.

Um diverse Array-Funktionen von PHP abzubilden implementieren alle drei die Methoden:

  • first(): gibt das erste Element der Liste zur├╝ck
  • last(): gibt das letzte Element der Liste zur├╝ck
  • reverse(): dreht die Reihenfolge der Elemente innerhalb der Liste um
  • each(): wendet eine Callback-Funktion mittels array_walk auf alle Elemente an, in der auch auf die Schl├╝ssel zugegriffen werden kann
  • map(): wendet eine Callback-Funktion mittels array_map auf alle Elemente an
  • slice(): schneidet einen Teil der Elemente heraus und gibt sie in einem neuen Listen-Objekt zur├╝ck
  • filter(): filtert die Elemente einer Liste anhand einer Callback-Funktion und gibt das Ergebnis in einem neuen Listen-Objekt zur├╝ck

Fluent Interface

Um eine Verkettung von Methodenaufrufen zu erm├Âglichen gibt jede Methode, die sonst keinen R├╝ckgabewert h├Ątte, eine Referenz auf ihre Klasse zur├╝ck. Nur slice() und filter() geben eine neue Liste mit den herausgeschnittenen bzw. gefilterten Werten zur├╝ck.

Hier ein kleines Beispiel aus CodrPress, was man damit alles anstellen kann:


Post::where()->each(function($document) use ($app) {
    $md = $document->getProperty('body');
    $html = $app['markdown']->transform($md);
    $document->set('body_html', $html)->save();

Die statische Methode Post::where() liefert ohne Quert alle Posts als MutableMap-Objekt zur├╝ck. Auf die R├╝ckgabe l├Ąsst sich sofort each() anwenden, das alle Elemente der Liste iteriert und die definierte Closure auf jedes Element einzeln anwendet.

In diesem Fall wird das rohe Markdown aus dem Feld body in HTML transformiert und im Feld body_html abgespeichert.


MutableMap und seine Sub-Klassen sparen viel Schreibarbeit durch ein simples und komfortables Fluent Interface – einer Vorgehensweise, der in PHP leider viel zu wenig Beachtung geschenkt wird.


Die drei Klassen sind nicht l├Ąnger Teil von MongoAppKit. Ich habe sie in ein separates GitHub-Repository und Composer-Paket ausgelegt, um eine unkomplizierte Nutzung ohne MongoAppKit zu erm├Âglichen. Viel Spa├č!


Da eine neue Sprache nicht genug ist, besch├Ąftige ich mich neben Ruby neuerdings auch noch mit Python.

Als Lernprojekt setze ich aktuell CodrPress als Python-Version um. Nat├╝rlich will ich das nicht das Rad neu erfinden, daher setze ich auf zwei Frameworks, dank denen ich sehr schnell ein vorzeigbares Ergebnis zusammenbauen konnte:

  • Flask ist ein Micro-Framework vergleichbar mit Sinatra (Ruby) oder Silex (PHP). Es k├╝mmert sich also um alles, was man braucht, um eine Website zu bauen. Vom Routing bis hin zur Template Engine (Jinja2).

  • MongoEngine bietet einen ODM (Object Document Mapper) vergleichbar mit Mongoid (Ruby) oder meinem eigenen Projekt MongoAppKit in PHP.

Routen-Definitionen mit Flask

from flask import Flask

app = Flask(__name__)

def hello(name):
    return 'Hello %s!' % name


Zu Beginn wird die Klasse Flask aus dem Package flask importiert und anschlie├čend eine Instanz erstellt.

Im Gegensatz zu PHP oder anderen C-Syntax-Sprachen kennt Python das Schl├╝sselwort new nicht. Um ein neues Objekt zu instanziieren reicht es den Klassennamen samt den Klammern und ggf. den Constructor-Argumenten zu schreiben.

Es folgt die Routen-Definition. Variable Werte werden in spitze Klammern gesetzt und der anschlie├čenden Methode mit gleichem Namen als Parameter ├╝bergeben.

Wie auch in Silex oder Sinatra wird der R├╝ckgabewert einer Routen-Methode zur├╝ck an den Browser geschickt. In diesem Fall ist das nur ein simpler String-Wert.

Templates in Flask

Flask nutzt die Template Engine Jinja2. Wer aus der PHP-Welt Twig kennt f├╝hlt sich sofort heimisch. Die Sprachelemente sind nahezu identisch.

Datei: ./templates/hello.html

<h1>Hello {{ name }}!</h1>  

In obiger Route m├╝sste die Methode nun so aussehen:

from flask import Flask, render_template
def hello(name):
    return render_template('hello.html', name = name)

Nicht vergessen: render_template muss zus├Ątzlich importiert werden!


from mongoengine import *


class Post(Document):
    _id = ObjectIdField()
    created_at = DateTimeField()
    published = BooleanField()
    title = StringField()
    body = StringField()

Der Aufruf von connect stellt eine Verbindung zur Datenbank test her. Da keine Verbindungsdaten angegeben werden, geht MongoEngine automatisch von einem lokalen MongoDB-Server auf dem Standard-Port aus.

Die Klasse Post ist eine Sub-Klasse von Document aus MongoEngine. Anschlie├čend werden die Felder der Klasse definiert. MongoEngine stellt f├╝r jeden von MongoDB unterst├╝tzten Datentyp entsprechende Klassen zur Verf├╝gung.

Sofern nicht ├╝ber das Attribut meta eine andere Collection definiert wird, greift MongoEngine auf den Klassennamen in Kleinbuchstaben als Collection zu.

Um ein neues Dokument von Post zu erstellen und zu speichern, reicht schon folgender Code:

post = Post()
post.published = True
post.title = 'Hello World!'
post.body = 'Hallo, ich ein Test.'

Abfragen mit MongoEngine

Als vollst├Ąndiger ODM bietet MongoEngine nat├╝rlich auch die M├Âglichkeit vorhandene Daten abzufragen. In folgendem Beispiel werden die letzten zehn ver├Âffentlichten Eintr├Ąge absteigend nach der Erstelldatum sortiert, in ein Array geschrieben.

posts = Post.objects(published = True).order_by('-created_at').limit(10)

Abfragen erfolgen statisch, daher ist keine Instanz n├Âtig. Die Methode objects() enth├Ąlt die Bedingungen, also in diesem Fall, dass ein Eintrag ver├Âffentlicht wurde. order_by() erwartet den Feldnamen mit der Sortierrichtung als Pr├Ąfix. Hierbei steht + f├╝r aufsteigend und - f├╝r absteigend. Zu guter letzt wird das Ergebnis mit limit() auf 10 Dokumente eingeschr├Ąnkt.

Mit diesem Wissen l├Ąsst sich nun ganz schnell eine Basis-Applikation bauen, die aus der vorhandenen CodrPress-Collection Eintr├Ąge ausliest und anzeigt.

Der bisherige Stand ist nat├╝rlich bei GitHub.


Ich bin mal wieder so wahnsinnig und arbeite an einem Blog-System. Diesmal will ich das Rad aber nicht neu erfinden und ein zweites Wordpress bauen. Stattdessen orientiert sich CodrPress an Schnitzelpress.

Da Schnitzelpress auf Ruby basiert und prim├Ąr f├╝r den Einsatz auf Heroku ausgelegt ist, habe ich mich dazu entschieden mit CodrPress quasi eine PHP-Version von Schnitzelpress zu entwickeln.

Nat├╝rlich ist das wieder mal einfacher gesagt als getan, vor allem da es f├╝r diverse Ruby Gems, die Schnitzelpress nutzt, in der PHP-Welt kaum brauchbaren Ersatz gibt.

Mit Redcarpet und CodeRay hat Ruby zwei wundervolle Gems, die sich um Markdown-Rendering bzw. Syntax-Highlighting k├╝mmern.

CodrPress basiert auf meinem Projekt MongoAppKit, das widerrum auf Silex sowie Twig setzt und seine Abh├Ąngigkeiten mit Composer regelt. Keine der PHP-basierten L├Âsungen, um diese zwei Ruby Gems zu ersetzen, bietet Composer-Unterst├╝tzung an und die Strukturen sind z.T. auch nicht PSR-0-kompatibel, so dass ein Autoloading der Klassen nicht m├Âglich ist.

Daher habe ich zwei neue Projekte aus der Taufe gehoben, die genau diesen Mangel beseitigen:


Da ich keine Lust und Zeit habe, selbst einen Markdown-Renderer zu schreiben, basiert SilexMarkdown auf php-markdown von Michel Fortin.

Ich musste es erstmal in brauchbare Struktur bringen, da das Original leider weder Namespaces nutzt und sogar zwei Klassen in einer Datei besitzt.

SilexMarkdown stellt nun eine Service-Prodiver-Klasse f├╝r Silex und eine entsprechende Twig-Extension zur Verf├╝gung. Dazu wurde es noch mit einer Unterst├╝tzung f├╝r Code-Bl├Âcke angereichert, um Syntax Highlighting wie in GitHub nutzen zu k├Ânnen.


Die Kern-Komponente von Radiant ist ebenfalls nicht auf meinem Mist gewachsen und stammt aus dem Projekt Nijikodo von Clint Campbell.

Immerhin war die Grundlage schon mal PSR-0-kompatibel und damit auch relativ leicht Composer-tauglich zu machen.

Meine Arbeit bestand zum Gro├čteil darin, entsprechende Unit-Tests zu schreiben und einige Fehler zu beseitigen und es in SilexMarkdown einzubinden.


Alle genannten Projekte, also MongoAppKit, SilexMarkdown, Radiant und CodrPress werden mittels PHPUnit laufend von mir und automatisiert via Travis CI getestet. Abgesehen von SilexMarkdown betr├Ągt die Code-Coverage zwischen 70 - 90%.


Aktuell ist CodrPress mit dem vollst├Ąndigen Twitter Bootstrap versehen, um auch in der Entwicklunsphase ein halbwegs ahnsehnliches Design zu haben. Sp├Ąter werde ich nur ein paar Komponenten aus Bootstrap nutzen, z.B. das Grid und die responiven F├Ąhigkeiten.

F├╝r das Syntax-Highlighting habe ich ein Farb-Theme basierend auf meinem Farbschema von PhpStorm geschrieben, das auch Radiant beiliegt. Dank einer recht einfachen Struktur kann man sich auch sehr schnell ein eigenes Theme zusammenstellen.


Die Frontend-Funktionen von CodrPress sind mit einer gef├╝llten Datenbank (Schnitzelpress-kompatibel) schon nutzbar. Homepage, Einzeldarstellung von Eintr├Ągen, eigene Seiten und das Markdown-Rendering mit Syntax-Highlighting funktionieren soweit einwandfrei.

Als n├Ąchstes werde ich einem Admin-Bereich und anschlie├čend einem ansprechenden Design widmen.

Menlo Park, start your photocopiers ...

… oder warum Software-Patente und Patentkriege schei├če sind.

Gestern habe f├╝r die Share-Funktionen von Twitter, Google+ und Facebook jeweils ein Modul nach dem CommonJS-Standard gebaut, um sie in meinem privaten Weblog zu nutzen.

Daran ist nun nichts besonders, wenn ich nicht eine kleine Entdeckung gemacht h├Ątte. Offenbar hat Facebook den n├Âtigen JavaScript-Code von Twitter kopiert oder Twitter von Facebook.

Quelltext vom Twitter

!function(d, s, id) {
	var js, fjs = d.getElementsByTagName(s)[0];

	if(!d.getElementById(id)) {
		js = d.createElement(s);
		js.id = id;
		js.src = "//platform.twitter.com/widgets.js";
		fjs.parentNode.insertBefore(js, fjs);
}(document, "script", "twitter-wjs");

Quelltext von Facebook:

(function(d, s, id) {
	var js, fjs = d.getElementsByTagName(s)[0];
	if(d.getElementById(id)) return;
	js = d.createElement(s);
	js.id = id;
	js.src = "//connect.facebook.net/de_DE/all.js#xfbml=1";
	fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));

Zwecks der Lesbarkeit habe ich die Funktionen entsprechend formatiert.

Selbst Nicht-Programmierern d├╝rften die ├ähnlichkeiten kaum entgehen. Die Variablennamen sind identisch und sogar die Art der URL-Angabe ohne Protokoll. Selbst das if-Conditional und damit die Methode das externe JavaScript nicht zweimal einzubinden, stimmen ├╝berein – nur die Schreibweise ist etwas anders.

Aus meiner Sicht geht dieses Vorgehen das vollkommen in Ordnung. Man muss nicht st├Ąndig das Rad neu erfinden. Gerade Programmierer tun das sehr gerne, obwohl es nur selten notwendig ist.

Twitter und Facebook sind Technologie-Vorreiter, neben Google die zwei wichtigsten im gesamten Netz – warum sollten sie also nicht gegenseitig voneinander profitieren? Auch wenn es nur um einen Code-Schnippsel geht, der externe JavaScripts l├Ądt.

Andere Firmen (Hallo, Oracle!) holen selbst bei wesentlich geringeren Quelltext-├ähnlichkeiten gleich die Klage-Keule raus. Durch die M├Âglichkeit in den USA Patente auf Software zu bekommen, ist sowas sogar oft von Erfolg gekr├Ânt …

Ich bin kein Verfechter von Open Source, auch wenn ich es grunds├Ątzlich f├╝r eine gute Sache halte. Noch bin ich der Meinung, dass Software ein Allgemeingut w├Ąre und jedem kostenlos zur Verf├╝gung stehen m├╝sse.

Jedem Entwickler muss das Recht zustehen, sein Produkt zu verkaufen und es sch├╝tzen zu d├╝rfen. Im Fall von Trivial-Patenten, geht es nicht mehr darum.

Man will nur noch der Konkurrenz schaden und Geld rausholen, obwohl man selbst oft mehr als genug hat. Firmen werden gekauft, um an die Patente zu kommen und anschlie├čend andere Firmen mit Klagen zu ├╝berziehen.

Egal, ob Apple, Samsung, Motorola (Google), Nokia oder sonst wer. H├Ârt endlich auf damit! Keiner eurer Kunden will Import-Verbote, absurd hohe Patentabgaben f├╝r verkaufte Ger├Ąte oder sonstige Ausw├╝chse euer Advokaten-Armeen.

Aus Apples Sicht ist Android ein geklautes Produkt. Objektiv gesehen kann man dem sogar in Teilen zustimmen. Nur: na und?

Apple hat gute Ideen, Google hat gute Ideen. Nutzt sie, um euch gegenseitig zu verbessern und stellt diese l├Ącherlichen Grabenk├Ąmpfe ein, die euch letztlich mehr schaden als n├╝tzen.