Why implementing equals() in javascript is hard and how use Symbols to do it correctly!

A while back, we released ferrum.js, a relatively small javascript library that “brings features from rust to JavaScript” in a way that is supposed to feel native to js developers. When we first started to write the library we began by trying to answer two key questions; the first of which is a bit provocative I admit: Why is there no good library for working with es6 iterators that integrates well with javascript syntax?

We decided to tackle the problem head on: by writing just such a library!

The second question we had to address, turned out to be more complex; “How can we implement a function like equals() or hash() correctly in javascript?" Here’s how we tackled that one, too.

How to (badly) implement equals

It seems like there are a lot of implementations of these kinds of functions; lodash has one for instance. For hash() there is object-hash, which I contributed to myself a while ago! In principle, implementing a function like equals is relatively easy:

const assert = require('assert');

const equals = (a, b) => {
  if (a.constructor !== b.constructor) {
    return false;  // Implement for a variety of simple types

  } else if (a.constructor === Date) {
    return a.toString() === b.toString();  // Implement for data structures using recursion

  } else if (a.constructor === Array) {
    if (a.length !== b.length)
      return false;    for (let idx = 0; idx < a.length; idx++)
      if (!equals(a[idx], b[idx]))
        return false

    return true;
  } else if (a.constructor === Object) {
    if (Object.keys(a).length !== Object.keys(b).length)
      return false;    for (const [key, val] of Object.entries(a))

    if (!equals(val, b[key]))
      return false;

    return true;
  } else {
    // Provide a fallback for any other types
    return a === b;

// Test our equals implementation!
const d = new Date();
const e = new Date(d.toString())

    foo: 42,
    baz: d,
    bar: [1, "foo", 3],
    foo: 42,
    bar: [1, "foo", 3],
    baz: e,

All these implementations have a drawback though: They can’t support any types they don’t know about; like this one for instance:

class Rational {
  constructor(p, q) {
    this.p = p;
    this.q = q;

assert(equals(new Rational(2, 2), Rational(1, 1)));

Even if the equals() implementation you are using has some support for custom types (automatically comparing each field), this example would still fail, even though 2/2 clearly equals 1/1.

Using ferrum.js to get it right

So, in order to implement equals() correctly, we need to support all the types your users might want to create; the function needs to be extensible!

Here is where ferrum.js comes in; it provides a helper class called Trait (one of the features borrowed from rust — see Rust Traits) to define extension points for functions like equals(). Ferrum already has an Equals trait and an eq() function, so we can just reuse it:

const { Equals, eq } = require('ferrum');

class Rational {
  constructor(p, q) {
    this.p = p;
    this.q = q;

  // ...

  normalize() {
    // ... Normalize the rational so that 2/2 becomes 1/1

  [Equals.sym](other) {
    // Tell ferrum in here how to compare your custom type
    const a = this.normalize();
    const b = other.normalize();
    return a.p == b.p && a.q == b.q;
}assert(equals(new Rational(2, 2), Rational(1, 1)));

Ferrum already provides implementations of eq() for all standard types (Array, Map, Number, Date, etc.), so you just have to implement an equality function for your new type.

Internally, the library uses ES6 Symbols to find the implementation; this is the recommended way to implement generic interfaces in JavaScript. All ferrum does is wrap this in order to provide a more convenient interface to handle a lot of edge cases.

The Iterator Protocol uses Symbols in this way to implement ES6 iterators. You can even wrap existing protocols; for instance, the Sequence Trait is just a wrapper around the iterator protocol, created to make use of the advanced edge case handling of ferrum. One example: The Sequence Trait can support plain Objects, while the Iterator Protocol cannot.

Ferrum is also designed to be null/undefined safe; many functions explicitly handle null/undefined as a special edge case. Traits can even be implemented for null and/or undefined; our equals() implementation above on the other hand would simply crash. Ferrum even provides the typesafe module to safely deal with null/undefined values.

This was one of our main motivations when creating ferrum; while Rust has been designed to be safe and avoid a lot of those edge cases, JavaScript has historically had a lot of them. Ferrum is designed to take as much of the edge case load of the developer…anything that fits that description should probably be part of the Ferrum ecosystem — make JavaScript a bit safer.

What’s next?

Ferrum is currently under active development. One upcoming big feature (again, borrowed from rust) is documentation testing. Ever found that the examples in your documentation were full of bugs? This allows you test your documentation!

Other features expected to be released this year as a part of ferrum:

  • A Hash trait, and Hash tables supporting arbitrary keys.
  • A Ord trait and ordered maps supporting arbitrary keys.
  • Support for rxjs Observables and Asynchronous Iterators; all using the familiar Ferrum Sequence api!

This was originally posted on the adobe tech blog.

Sun Jan 2020 19:02 UTC
No Comment
Write a comment.

All the fibonacci sequence algorithms you will ever need

This blog post shall given an overview over different methods to compute the fibonacci sequence or parts of it. Since this is a common interview question, you may print out this blog article and show it to your potential employers, not only to demonstrate how specialized your abilities are in the broad area of fibonacci computation but also to demonstrate your humor and free spirit.

TL;DR: Type "fast fibonacci algorithm" into your mobile browser before the eyes of your soon-to-be boss and download the resulting algorithm.

The silly algorithm

O(wtf this is calling itself twice recursively must be something truly horrific)

def fib(n):
    if n < 2:
        return n
        return fib(n-1) + fib(n-2)

The naive algorithm


Simply generate the entire fibonacci sequence, discarding unneeded values.

# Utilities

def seek(n, seq):
    it = iter(seq)
    for _ in range(n):
    return it

def at(n, seq):
    seek(n, seq)
    return next(seq)

# Implementation

def fib_sequence():
    a = 0
    b = 1
    while True:
        yield a
        yield b
        a += b
        b += a

def fib(n):
    return at(n, fib_sequence())

The sensible

O(log n)

Search the internet for "fast fibonacci algorithm", find this page https://www.nayuki.io/page/fast-fibonacci-algorithms, and download the source code for fast doubling from that page. This is the only fibonacci implementation you will ever need. Use this and see how amazed (or potentially angry or scared) recruiters will be at how fast you can compute any fibonacci number.

Bonus: Recursive naive


This is like the naive algorithm generating the entire sequence except that it's recursive, much more pretty and it will fail after a couple hundred iterations because the python joksters have out of pure trolling prowess not yet implemented proper tail call optimization

def fib_sequence(a=0, b=1):
    yield a
    yield from fib_sequence(b, a+b)

Mon Jun 2017 09:45 UTC
No Comment
Write a comment.

Why neither the command line nor the GUI is usable

Note: I wrote this as a comment http://gandre.ws/blog/blog/2015/04/07/why-the-command-line-is-not-usable/

TL;DR: As you said, the shell is lacking is a way to hint users towards common use cases, while in the GUI, it is almost impossible to combine applications.

I think we need to use a new, combined approach that lets users start with user interfaces as concise as today’s excellent GUIs, but providing tools to empower users to learn the power of programming in a rewarding learning curve. Without the leap of faith that is jumping from GUI to CLI or any programming language today.

Thanks for your post; the shell is indeed entirely unsuitable for use by non-experts: if a user opens a shell to solve a particular task (let’s say they want to display the current time), they will probably understand that they have to text something. Maybe they think of typing ‘time’ or ‘help’, but both of these commands will be entirely useless for what they want to achieve. Maybe they will take some time to research, and after a considerable time they might successfully be able to type date. Next, if they want to list the items on their desktop, they won’t have any idea where to begin with: after even more research they will learn about cd and ls, but at this point we’re just in day two of learning a programming language.

In the GUI, by contrast, you are likely to find out in seconds how to do these things; you will see the current time anyways on any decent system and even from only knowing how a mouse works you can probably learn how to navigate the file system from randomly clicking on the screen within 30 minutes or so.

Now let’s take a much more complex task; let’s delete all the images from a folder. This can be accomplished easily if you are an experienced shell users, yet if you are an experienced GUI user, it will be extremely difficult. I just tried to research how to “mac delete all mp3 from folder”, and the first result I got was a forum showing me an appropriate command line.

We create GUIs often by analyzing what usecases an application will have and based on that we will create hints for maybe 90% (arbitrary guess) of them. We can optimize that by using sane defaults (values for preferences, or just showing all the tracks in the playlist without any need for user intervention). We can group actions/views so users can make an educated guess about where to find a certain action. Ultimately though, there is a limit to the space of actions that can be performed using a GUI; we can only display so many options and hints until we simply can’t fit more information onto a screen: a list of 10000 preferences is about as useful as displaying none – just like we do in a shell.

GUIs also put a considerable load on the programming team: I am a programmer and I’d usually estimate two times the effort for creating a terrible GUI I design myself and more than ten times the effort for creating an excellent GUI with professional designers and usability studies. This is because good user interface design is very hard, but also because with a GUI you have to explicitly handle every single use case: if a want a feature that lets the user select a file to write to, or read from a website, I will have to built that in manually. Moreover, for the URL feature I should probably even display the website I am reading from within my application. In the shell, I usually just assume that the user will use curl and redirection to accomplish both those features, so I can support those features without actually programming anything.

GUIs show what can be achieved, by limiting what can be achieved. Having the computer hint users at what the system needs them to do quickly reaches it’s limits: ultimately the user needs to tell the computer what they want it to do.

I think it is possible to combine those approaches, because in a way GUIs already do this: App stores let you expand your system and customize it to your needs. At the beginning you see a very limited set of possibilities with those applications you are most likely to use. When you then expand your set of applications and add an application, you will have actively selected it and learn what it does.

I also think, we can take that even further and create a learning curve for normal users that empowers them to expand the way they use their system and even get to a point where they can perform moderately complex programming tasks (such as removing all mp3s from a folder).

It is hard to predict what such a system could look like and you’d need a big research team to get any meaningful idea, but my best guess would be having a normal GUI on top, backed by a graphical gui editor and a flow based, graphical programming language as a replacement for the shell and it’s pipes. Simple things like moving a button, or connecting the value of one field to some data receiver could be done using drag and drop (drag a text field to the notification icon to display notifications when it changes; drag it to the browsers URL bar to automatically follow the url). You could show users what happens in the flow interface when you drag a value somewhere, to teach them about the flow interface. If the GUI can easily be edited, this could also be useful to admins, optimizing an interface for their specific organization. The Flow interface could be stored as a full programming language (Rust or GO or something), so you can use that for algorithms and tasks that exceed the flow interface and you could learn about the language by using the flow interface and looking at the resulting code. You could even package sets of hints how to program as GUIs: how about a window that just displays the date in various configurations, so when you want your date in some weird format, use the GUI to configure how the date format should look like and then drag the result to the menu bar (on os x).

Note: I didn't mention in the comment, because I don't like plugging things, but this was the idea behind https://shocto.de/ , which at the moment purely exists in the form of an idea written on a website.

Sun Mar 2016 17:33 UTC
No Comment
Write a comment.

Blackmailed with Patreon account data

Note: if you receive a similar mail, don't pay. Your data is already public and their claims to have sensitive data is very likely a bluff.

So, some time ago I created an account with Patreon, mostly to support the Hoaxilla Podcast (check it out. They're really great). Unfortunately they have been cracked end of September and lots of data (torrent/magnet) has been leaked.

So, today I received the following mail:

Subject: Noah recommends - I will leak your identity
From: sharingservices@aol.com
Reply-To: abc8537458@163.com
To: patreonjo@jayceland.com, patreonjohari@gmail.com, patreonkaro@cupdev.net

Unfortunately your data was leaked in the recent hacking of the Patreon web site and I now have your information. I have your tax id, tax forms, SSN, DOB, Name, Address, Credit card details and more sensitive data. Now, I can go ahead and leak your details online which would damage your credit score like hell and would create a lot of problems for you.

If you would like to prevent me from doing this then you need to send 1 bitcoin to the following BTC address.

Bitcoin Address: 1QAQTyhCzAfvp8uLpneBNamWTNRR1hx9Cp

You can buy bitcoins using online exchanges easily. The bitcoin address is unique to you. Sending bitcoin takes take, so you better get started right now, you have 48 hours in total. abc8537458@163.com has shared an article with you

(Other people also received this)

First blackmail of my life. Yay. Fortunately it's a also bluff. The data was leaked publicly; It seems to contain postal addresses, email addresses and well encrypted passwords.

This means, that their claims to having tax ids, tax forms, SSN, DOB and credit card details are wrong. Their claims to be able to damage your credit score are also wrong. Since the data is already public, they can not do any more harm by leaking it again.

Besides, I am pretty sure I didn't even give them anything but a password, my legal name and a paypal account. So there is no way they could ever have data like a Tax ID. And what the hell is a SSN and a DOB. The worst thing they could possibly do with the data I gave patreon in the first place is to send me money, and I wouldn't object to that.

In the end this just adds to the miserable experience micropayment services provide. Bot my patreon and my flatter payments started failing after a few weeks; I tried to restore flattr payments and failed. I didn't even try with patreon.

Patreon was probably hacked because the run the same half assed approach most projects have towards security. I mean, I am running around finding random occurrences of possible buffer overvloas and private keys in random code I find. The most common cause of breaches is that someone calls and says "Hi, this is tech support. Could I please have your password".

How the hell are micropayments supposed to work with such sloppy tech. You are dealing with money, hire someone to do code reviews. Asshole.

(The full source of the mail for anyone interested):

Return-Path: <sharingservices@aol.com>
X-Original-To: patreonkaro@cupdev.net
Delivered-To: mapc@cupdev.net
Received: from taclomr-a001e.mx.aol.com (taclomr-a001e.mx.aol.com [])
    (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits))
    (No client certificate requested)
    by cupdev.net (Postfix) with ESMTPS id BCB4E203C1E
    for <patreonkaro@cupdev.net>; Sat, 21 Nov 2015 12:30:12 +0100 (CET)
Received: from vm-149-174-150-116.asset.aol.com (vm-149-174-150-116.asset.aol.com [])
    by taclomr-a001e.mx.aol.com (Outbound Mail Relay) with ESMTP id 1104F3800214;
    Sat, 21 Nov 2015 06:30:10 -0500 (EST)
From: sharingservices@aol.com
Reply-To: abc8537458@163.com
To: patreonjo@jayceland.com, patreonjohari@gmail.com, patreonkaro@cupdev.net
Message-ID: <534381049.34352343.1448105409977.JavaMail.dpadmin@vm-149-174-150-116.asset.aol.com>
Subject: Noah recommends  - I will leak your identity
MIME-Version: 1.0
Content-Type: text/html; charset=us-ascii
Content-Transfer-Encoding: 7bit

Unfortunately your data was leaked in the recent hacking of the Patreon web site and I now have your information. I have your tax id, tax forms, SSN, DOB, Name, Address, Credit card details and more sensitive data. Now, I can go ahead and leak your details online which would damage your credit score like hell and would create a lot of problems for you.
If you would like to prevent me from doing this then you need to send 1 bitcoin to the following BTC address.
Bitcoin Address:
You can buy bitcoins using online exchanges easily. The bitcoin address is unique to you. Sending bitcoin takes take, so you better get started right now, you have 48 hours in total.
<title>abc8537458@163.com has shared an article with you</title>
Sat Nov 2015 13:20 UTC
1 Comment
Write a comment.

Refactoring Sauerbraten – When two STLs break

Yesterday night I found this commit in one of the branches of Inexor:

commit ba8dd46289e39a51a67b40fe32d3e19607b63f2e
Author: a_teammate <madoe3@web.de>
Date:   Wed Nov 4 12:29:11 2015 +0100

    replace std::min max with selfwritten implementations to fix lightmapping

    vec::min/max were broken with the std versions

diff --git a/inexor/shared/tools.h b/inexor/shared/tools.h
index 6a32520..51f9d20 100644
--- a/inexor/shared/tools.h
+++ b/inexor/shared/tools.h
@@ -40,8 +40,13 @@ typedef unsigned long long int ullong;

 using std::swap;
-using std::min;
-using std::max;
+/// return the minimal value of the two given.
+/// we do not use the std:: version here because it crashes inside the vec-implementation.
+template<class A, class B> inline A(min)(A val1, B val2) { return val1 > val2 ? val2 : val1; }
+/// return the bigger value of the two given.
+/// we do not use the std:: version here because it crashes inside the vec-implementation.
+template<class A, class B> inline A(max)(A val1, B val2) { return val2 > val1 ? val2 : val1; }
 using boost::algorithm::clamp;

Sauerbraten has a lot of self written implementations for a lot of standard algorithms and containers. That includes things like vector, hashmaps (unordered_map), sorting algorithms and also min, max and clamp implementations and even it's own math and crypto libraries. It has been argued that the predecessor of Sauerbraten – Cube – is so old, that all the libraries did not exist back then, but I looked it up and both OpenSSL and the first standardization of C++98 date back do 1998 while Cube started development in 2001.

In the course of Inexor development I was trying to replace at least a tiny little part of the sauerbraten special STL with the standard stuff, because the sauerbraten implementations do have some nasty stuff.

Here is an except from the sauerbraten vector implementation:

  /// get the last index and decrement the vector's size
  T &pop() { return buf[--ulen]; }

This is a pop method. It should remove the last element and pass it to the caller. Maybe by using return value optimization r possibly by taking a writable lvalue reference from the outside and moving/swapping into that. The STL vector's pop() just deletes the last element because both of the above implementations of pop are not quite optimal. The sauer implementation does neither and just returns a reference to the last element in the vector's buffer and marks that element as non-existent.
This is extremely dangerous; first of all any successive push/appends will overwrite the old element; secondly the destruction of the element is now in the hands of the caller who would need to manually call the destructor so the code behaves correctly (of course this would have to be done before another element is appended). Unfortunately large parts of the Sauerbraten code rely on such misbehaviour and thus I was unable to remove them easily.

I was however able to replace the implementations of min, max, clamp and swap and quite recently I was also able to replace sauerbratens custom random number generator. Though even that created some problems.

Now one of the other Inexor developers reintroduced the old implementations of min/max because STL implementation of std::min was crashing. This kind of problem already smelled like a deeper underlying problem so I decided to investigate:

First of all, I tried to reproduce the problem and I failed on recent clang and gcc versions, so it must be some compiler related problem.

Then I looked at the signature of both versions of min/max; maybe some code relied on the quirks of sauerbratens min/max, just like with vector::pop(). And indeed found a difference – the STL version of min max takes a single template argument and operates entirely on lvalue references while the sauerbraten version takes two template parameters (one for each parameter), uses the first parameter's type as return value and operates entirely by value rather than by reference.
I looked at the code that crashed but I found nothing to indicate such a problem.

Finally I got access to some from the crash itself:


>    inexor.exe!_VCrtDbgReportW(int nRptType, void * returnAddress, const wchar_t * szFile, int nLine, const wchar_t * szModule, const wchar_t * szFormat, char * arglist) Line 481    C++
     inexor.exe!_CrtDbgReportW(int report_type, const wchar_t * file_name, int line_number, const wchar_t * module_name, const wchar_t * format, ...) Line 273    C++
     [External Code]    
     inexor.exe!vec::min(const vec & o) Line 156    C++
     inexor.exe!genclipplanes(const cube & c, const ivec & co, int size, clipplanes & p, bool collide) Line 1220    C++
     inexor.exe!getclipplanes(const cube & c, const ivec & o, int size, bool collide, int offset) Line 22    C++

from _CrtDbgReportW on were somewhere inside error reporting code probably. however checking the values of the variables inside that code reveals an error inside algorithm:4178 which is:

definition where we get stuck inside std::min:

        // TEMPLATE FUNCTION min
template<class _Ty> inline

    _Post_equal_to_(_Right < _Left ? _Right : _Left)

    _CONST_FUN const _Ty& (min)(const _Ty& _Left, const _Ty& _Right)
    {    // return smaller of _Left and _Right
    return (_DEBUG_LT(_Right, _Left) ? _Right : _Left); /// <- THIS IS the line which crashes

Here you can see that it really crashes inside the min implementation. A look at the stack trace reveals that _CrtDbgReportW and _VCrtDbgReportW are being called; at first I though those where outputting debug info about the crash, but apparently there wasn't any, so we tried compiling in Release mode and the crash was gone.

Seems we found a bug in the Visual C++ STL Debug version; serves to remind that you should always trace bugs to the root cause before patching them.

We had recently updated to Visual Studio 2015 and I guess that introduced the problem.

If you want to reproduce this, I suggest you try with Visual Studio 2015 (minor update 1) and the current master at the time of this writing.

Mon Nov 2015 10:33 UTC
No Comment
Write a comment.

Hetzner Supportanfrage zu Zensur

Ich habe gerade einen Heise Artikel und einen Artikel auf SPON gelesen in denen beschrieben wird, dass Hetzner offenbar den Zensurwüschen der Russischen regierung nachkommt. Das passt mir nicht, weil mein eigener Server ja auch bei Hetzner läuft, also habe ich beschlossen mal ne Supportanfrage zu senden (und so vielleicht etwas Druck aufzubauen):

Sehr geehrte Damen und Herren, ich habe soeben folgenden Spiegel Artikel gelesen:

Webhoster Hetzner: Deutsche Firma hilft russischer Medienaufsicht bei Zensur


Darin wird beschrieben, dass sie eine Mitteilung der russischen Medienaufsicht erhalten haben, die die Aufforderung enthielt teile der Ukrainischen Seite Glavcom zu Zensieren. Offenbar haben sie darauf reagiert indem sie die Website aufgefordert haben, die Inhalte vom Netz zu nehmen.

Das irritiert mich sehr, denn sie sind einen Deutsche Firma und ich hätte von ihnen erwartet, dass sie die Interessen ihrer Kunden wahren.

Bitte erläutern sie, wie rechtfertigen sie diese Vorgehensweise? Inwiefern widersprechen die Inhalte auf der Seite Glavcom Punkt 6.2 ihrer AGB? Im lichte auf meine eigenen Webseiten, möchte ich außerdem wissen unter welchen Voraussetzungen sie weitere Zensurmaßnahmen durchführen werden? Ist generell damit zu rechnen, dass den Zensurwünschen anderer Regierungen nachkommen?

Mit freundlichen Grüßen, Karolin Varner

Die Anfrage lizensiere ich natürlich unter CC-0 und ich lade alle anderen Herzlich ein dieses Schreiben als Vorlage für ihre eigenen Anfragen zu benutzen!


(Heute ist der 17.3.2015)

Hetzner hat mir in der Tat geantwortet und das auch recht schnell – also zumindest viel, viel schneller als ich das hier veröffentliche.

Guten Tag,

Wir möchten uns zu dem von Spiegel Online veröffentlichten Artikel wie folgt äußern.

Wir als Webhosting-Unternehmen konzentrieren uns auf unsere Kernkompetenz, unseren Kunden auf aller Welt Leistungen rund um das Hosten von Webseiten bereit zu stellen, und halten uns politisch neutral. Wir pflegen keine politischen Beziehungen im In- und Ausland.

Grundsätzlich werden bei uns alle Beschwerden gleich behandelt. Sobald eine Beschwerde über eine auf unseren Servern gehostete Webseite eingeht, wird die Beschwerde formell geprüft und der betroffene Kunde per E-Mail an die bei uns hinterlegte E-Mail-Adresse informiert und gebeten, binnen 24 Stunden eine Stellungnahme dazu abzugeben.

Bei der formellen Prüfung prüfen wir, ob die beanstandete Webseite tatsächlich bei Hetzner Online gehostet wird. Darüber hinaus erfolgt eine erste Blickprüfung der Webseite, eine inhaltliche Prüfung findet in dieser Stufe noch nicht statt.

Erfolgt nach Fristablauf keine Reaktion/Stellungnahme des Kunden, so wird eine Erinnerung an den Kunden gesendet, mit Angabe einer evtl. Sperrung bei weiterer Nichtreaktion.

Bei weiterer Nichtreaktion wird diese Erinnerung nach weiteren 24 Stunden in der Regel nochmals versendet. Reagiert der Kunde weiterhin nicht, wird der Inhalt der beanstandeten Webseite geprüft und ggf. die Sperrung durchgeführt, um Schaden für den Kunden und Hetzner Online abzuwenden.

In dem von Spiegel Online veröffentlichten Fall wurde von unserem Kunden auf unsere Anfragen nicht fristgerecht eingegangen. Eine Serversperrung ist trotz der mangelnden Kommunikation des Kunden nicht erfolgt.

Bezüglich des Inhalts der angemahnten Website (sogut das über die Sprachbarriere hinweg möglich ist) sehen wir unsererseits keinen Grund, das Abuse-Ticket weiter zu verfolgen. Der Kunde wurde darüber informiert, dass wir das Abuse-Ticket schliessen, und unsererseits keine weiteren Schritte notwendig sind. Was uns in künftigen ähnlichen Fällen sehr helfen würde, wäre eine Stellungnahme des Kunden. Wenn man die Argumente beider Seiten kennt, fällt eine vernünftige Entscheidung im Abuse-Vorgang wohlmöglich leichter, als wenn nur die Beschwerde eines Beschwerdeführers vorliegt, welche vom Kunden ignoriert wird.

Wir bedauern den Vorfall sehr und hoffen, dass die kritischen Stimmen unsere Vorgehensweise nachvollziehen können.

Mit freundlichen Grüßen / Best Regards [NAME ENTFERNT]

Sat Aug 2014 09:03 UTC
No Comment
Write a comment.

Non breaking multi line strings in Ruby

Personally I like my code to be no longer than 60 characters (that's because so I can comfortably split my Vim screen and view two files side by side).
When I am coding rails it often happens that I need to create some error message or user feedback and these strings get far longer than 60 characters normally. Until now I've split my lines using + in these cases.

print "This is a nice, long string giving you some " +
    "feedback. Unfortunately it does not fit on the " +
    "screen so I have to split it."

I dislike that syntax because it is a lot of hassle to get the spaces and the quotes right. There seem to be a few better syntaxes (according to this StackOverflow Question), but they do not substantially improve the situation I think:

print "This is a nice, long string giving you some "\
      "feedback. Unfortunately it does not fit on the "\
      "screen so I have to split it."

The solution I came up with was a little helper function:

# Helper for multi line strings:
# Normalizes the string, so that every sequence of spaces is
# replaced by a single space.
# This also strips the string.
def NOCR(s)
  s.gsub!(/\s\s*/, ' ')

print NOCR "This is a nice, long string giving you some
    feedback. Unfortunately it does not fit on the screen
    screen so I have to split it."

This is still not a perfect solution, but it at least saves me the hassle of manually formatting the spaces and adding lots of quotes.

Of course, when using this, support for inserting newlines or tabs is completely gone. It would be possible to use some kind of escape syntax (%%, %n, %t, %s) to introduce it again, but I think in these cases it is better to fall back to manual formatting.

Fri Aug 2014 10:44 UTC
No Comment
Write a comment.

Git stats – LOC per author in the current head

So suddenly I am fluent in awk. Well, this is unexpected!

I've been working on one of my projects for quite a while and today I got interested in how much code I actually produced, so I wrote a little script to generate that statistic:

#! /bin/bash
# List the amount of code per author in the current HEAD

git ls-files "$@" | xargs -l1 git blame -s -- | awk '
  function get_author(commit) {
    author = commit_map[commit]
    if (author == "") {
      cmd = "git show -s --pretty=format:%ae " commit 
      cmd | getline author
      commit_map[commit] = author
    return author

    author = get_author($1)
    counter[author] = counter[author] + 1
    if (counter[author] % 500 == 0)
      print($1 " -> " author " -> " counter[author]) | "cat >&2"

  END {
    for (author in counter) {
      print counter[author] " " author
' | sort -n | column -t

Github Gist

This lists the files in the current head, git-blames each of those and runs the result through AWK. Awk does the author lookup (caches the result in an associative array) and then amount of lines for each author in another associative array. Finally the result is being printed, sorted and formatted.

Fri Aug 2014 02:08 UTC
No Comment
Write a comment.

Shuffling movies with mplayer and bash

Suppose you have a bunch of movies in a directory and want to play them randomly; switch into that directory and run:

$ find -print0 | sort -zR | xargs -0 mplayer
  1. Generate a list of files in this directory and it's subdirectories; the list is zero terminated because I have some special characters in my filenames
  2. Shuffle the list with sort -R
  3. Pass each line as an argument to mplayer

The zero-termination of the list is accomplished with find -print0, sort -z and xargs -0.

You can of course alter the find command: For instance use find -maxdepth 1 to skip subdirectories.

Thu Aug 2013 20:20 UTC
No Comment
Write a comment.

Batch rename in Bash

There are a few tools that provide batch renaming for the shell, but most of them are quite huge and need installing. It is not necessary to use these utilities, because unix already all tools necessary:

$ ls -d *.txt | sed 'p;s/foo/bar/' | xargs -l2 mv

The first part should be quit clear: Print all files ending with txt. The \*.txt makes use of the shell's globbing features and filters all the .txt files. The -d switch tells ls not to print the contents of directories.

The sed expression consists of two parts: p prints the current line and s/foo/bar/ is the actual transformation (in this case: a replacement). If I run this on my home directory I get this:

$ ls
down  duh  files  fuu  fuubar pr0j  tmp  usr

$ ls | sed 'p;s/fuu/bar/'

Notice that most files have just been printed twice, but fuu and fuubar where changed to bar and barbar.

Now comes the tricky bit: xargs takes each two lines and applies them to mv as arguments, so when I run xargs in debug mode I get this:

$ ls | sed 'p;s/fuu/bar/' | xargs -l2 echo mv
mv down down
mv duh duh
mv files files
mv fuu bar
mv fuubar barbar
mv pr0j pr0j
mv tmp tmp
mv usr usr

Notice that I did not use the '-d' flag this time, because I print the content of the directory '.' this time, not a list of given files. This would happen if I did use '-d'.

$ ls -d

$ ls | sed 'p;s/fuu/bar/' | xargs -l2 echo mv
mv . .

Getting complicated

In the above example I did not do any filtering, because the files foo and foobar (which existed before I began to write this article) do not have an extension, but I could use a filter to select only those files I actually want to rename:

$ ls -d *fuu* | sed 'p;s/fuu/bar/' | xargs -l2 echo mv
mv fuu bar
mv fuubar barbar

I can get as elaborate as I want with my filter if I use grep; here is the same as above using grep:

$ ls | grep 'fuu' | sed 'p;s/fuu/bar/' | xargs -l2 echo mv
mv fuu bar
mv fuubar barbar

Instad of using xargs you can also use a while loop; I use that variant to save me the trouble of dealing with escaping in xargs:

$ ls | grep 'fuu' | sed 'p;s/fuu/bar/' | while read a && read b; do echo mv "$a" "$b"; done
mv fuu bar
mv fuubar barbar

One last example, where we replace files recursively in the home directory: I use find instead of ls which lists a directory recursively (I am not actually running this and neither should you):

$ find | sed 'p;s/fuu/bar/' | xargs -l2 mv
Sun May 2013 21:25 UTC
No Comment
Write a comment.

Hello World

Soo. If you can see this it means I managed to setup my blog. Yaaay

A wonderful world awaits you on this blog…just not yet. Move along. Nothing to see here.

Mon May 2013 21:30 UTC
      No Comment
      Write a comment.