Friendly Ghost

@vogti is writing.

Why Chess Will Destroy Your Mind

...chess is a mere amusement of a very inferior character, which robs the mind of valuable time that might be devoted to nobler acquirements, while at the same time it affords no benefit whatever to the body.


Working with Jira from CLI

The company I work for heavily relies on Jira. Its not only used for planning Sprints or filing new issues, but also a resource for solutions of known problems. As a DevOps engineer I'm working a lot on the CLI and try to automate as much as possible. For this reason I was looking for a solution to automatically create new Jira issues or commenting the current status.

What I found was jira-cli by Ali-Akber Saifee. His solution is written in python and in active development. It's easily installable via pip install jira-cli.

After installation you run jira-cli configure or populate ~/.jira-cli/config.cfg with following data:

[jira]
v2 = True  
base_url = http://url.to-your-jira.com  
username = bruce.wayne  
password = batmobile  
protocol = rest  

This now allows me to view Issues from terminal:

$ jira-cli view DEVOPS-19
status               : Closed  
reporter             : bruce.wayne  
summary              : Investigate performance drop  
assignee             : harvey.dent  
link                 : http://url.to-your-jira.com/DEVOPS-19  
issue                : DEVOPS-19  

Create Issues

jira-cli new --type Configuration "Create VHost for new subdomain" --priority=normal --project DEVOPS --description='Please create a new VHost for {{new.subdomain.com}}.' --label=server --label=configuration  

Or create comments

$ jira-cli update DEVOPS-20 --comment # opens up the editor
this is a new comment added to DEVOPS-20  

You do not need to store your credentials in an external configuration file. It´s also possible to declare them within the command:

$ jira-cli view DEVOPS-20 -u brucewayne -p gordon

For the full list of features take a look at the docs. To me its the perfect solution for small custom scripts. Not too bloated, just enough.


3 Wochen Urlaub, bitches!


Closures: JavaScript vs. Elixir

Ein kleiner Vergleich zwischen JavaScript und Elixir Closures.

Nicht so coole Closures: JavaScript

Ich verwette meinen Hut darauf, dass die ersten Probleme derer die mit JavaScript neu beginnen, mit Closures zutun haben - ohne dass sie überhaupt von deren Existenz wissen. Spätestens bei dem Versuch über ein Array zu iterieren und eine Aktion basierend auf dem aktuellen Index des Elements auszuführen kommt man in die Bredouille.

var array = []

for (var i = 0; i < 3; i += 1) {  
  array.push(function() {
    console.log(i);
  });
}

array[0]()  
array[1]()  
array[2]()  

Sieht logisch aus - ähnlich würde man es wohl auch in anderen Sprachen machen. Okay, lassen wir das mal laufen:

$ node closure.js
3  
3  
3  

Oha. Was ist hier los?

JavaScripts Funktionen speichern die Umgebung in der sie definiert wurden. Somit haben wir innerhalb jeder Funktion zugriff auf das gleiche i. Setzt man in die condition ein console.log(i); wird relativ deutlich was passiert:

var array = []

for (var i = 0; i < 3; i += 1) {  
  console.log(i);
  array.push(function() {
    console.log(i);
  });
}

array[0]()  
array[1]()  
array[2]()  
node closures.js  
0  
1  
2  
3  
3  
3  

Der Wert von i wird vom ersten Funktionsaufruf (array[0]()) von 0 zu 1, von 1 zu 2 und anschließend von 2 zu 3 verändert. Von allen drei Funktionsaufrufen wird der letzte wert bei dem die condition anhält (i < 3, also 3) ausgegeben. Die Funktionsaufrufe array[1]() und array[2]() greifen also auf das gleiche, von array[0]() bereits hochgezählte i zu. In meinen augen ziemlich unintuitiv.

JS Closure not working

Aber wie löst der geneigte JavaScript Entwickler nun das Problem?

var array = []

for (var i=0; i<3; i+=1) {  
  (function(i) {
    array.push(function() {
      console.log(i);
    });
  })(i);
}

array[0]()  
array[1]()  
array[2]()  
$ node closure_fix.js
0  
1  
2  

Löppt! Wir haben einen neuen Kontext erzeugt indem wir die definition unserer anonymen Funktion in eine weitere anonyme Funktion verpackt haben. Anschließend wird die Funktion aufgerufen... Et voila!

JS Closure working Mozilla erklärt Closures wie folgt:

A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created.

Source: Mozilla Developer Network: Closures

Meines Erachtens nach sind Anfänger dazu verdammt hierüber zu stolpern, da die Lösung dieses Problem zu umgehen fernab von intuitiv ist.

For the cool kids: Elixir

Das gleiche in Elixir:

closures = (0..2)  
  |> Enum.map &( fn () -> IO.puts(&1) end)

closures  
  |> Enum.each &(&1.())
$ elixir closure_pretty.ex
0  
1  
2  

Hier wird der Pipe-Operator (|>) genutzt. Dieser erlaubt es uns die Reihenfolge der Methodenaufrufe umzukehren, den vorherigen Ausdruck zu nehmen und sie als ersten Parameter der nächsten Methode zu verwenden.

Somit wird aus:

closures = Enum.map (0..2), &(IO.puts(&1))  

einfach:

closures = (0..2)  
  |> Enum.map &(IO.puts(&1))

Das funktioniert. Die spitzfindigen unter euch könnten jedoch argumentieren, dass hier auch nur Enum.map als Iterator verwendet wird.

Hier noch einmal deutlicher: wir verändern den Aufruf von IO.puts explizit:

closures = []

i = 0  
closures = closures ++ [fn -> IO.puts i end]

i = 1  
closures = closures ++ [fn -> IO.puts i end]

i = 2  
closures = closures ++ [fn -> IO.puts i end]

Enum.at(closures, 0).()  
Enum.at(closures, 1).()  
Enum.at(closures, 2).()  
$ elixir closure_explict.ex
0  
1  
2  

Warum funktioniert das? Schließlich müsste closures doch nun eine Liste von fn -> IO.puts i end haben. Elixir nutzt immutable (unveränderliche) Datenstrukturen. Wenn wir der Variable i einen neuen Wert zuweisen ändern wir nicht den Wert der Variable (Streng genommen werden Variablen in Elixir nicht zugewiesen. Man nutzt "Pattern Matching"). Tatsächlich wird für jede Variable i=1, i=2 und i=3 eine neue Variable i im aktuellen Scope erzeugt. Unsere Funktionen referenzieren die Werte mit dem gleichen Bezeichner i, aber die Werte bleiben unverändert, wenn sie später wieder genutzt werden.

Dieses default-Verhalten kann einem eine Menge Ärger ersparen. Sowohl in single- als auch multi-threaded applications.

Fazit

Beide Sprachen unterstützen Closures. In Elixir sind diese jedoch meines Erachtens nach zugänglicher als in JavaScript. Daher würde ich in JavaScript letztendlich lieber zu diversen Iteratoren greifen, als mich mit unleserlichen Closures herumzuschlagen. So oder so kommt man jedoch nicht umhin sie wenigstens zu verstehen ;)


Export your DayOne Diary to Ghost

As I said before I thought about using Ghost as my main journaling tool. But what about my old DayOne entries? Im journaling since over a year - there are quite a few memories I'd really like to keep.

So what I need is a DayOne to Ghost export tool.

After some research I found out that no one seems to have done the work until now. After a few evenings I may now present the DayoneToGhost export tool.

All you need to do is

$ python dayoneToGhost.py <path to your Journal.dayone>

This will create an dayone_export_<date>.json file and a content directory which contains all your DayOne Photos. All you need to do is to import the dayone_export_<date>.json file and copy the content directory into your Ghost installation directory.

Things that will be exported

  • entry text
  • creation Date
  • images (as post image)
  • tags
  • stars (as featured posts)

Coming soon

  • tag imported entries with a custom tag
  • use external hosted images (to support also hosted ghost blogs)

Currently Missing

  • Wheather information
  • Location information

This are both things the Ghost Data Model doesn't support. Maybe I can fill this gap with the upcoming Ghost Apps...! Make sure to follow me on twitter to get the latest news about the tool.

Big thanks to José Padilla as my tool is based on his tumblr-to-ghost exporting tool.