YAML and markdown based website rendering with AngularJS

Tags: , ,
No Comments »

A couple of weeks ago, Clark / @jeaneymerit told me he was digging into AngularJS, and as I’m working on a private project where a static website is involved, I thought this framework could help me make that website lightweight in terms of external dependencies.

The site I’m working on contains exclusively static content, and most of it is text, I wanted a simple and elegant method in order to manipulate that content easily, so I wrote a basic website generator in python based on jinja2, for the record it’s available here.

The principle is that a basic YAML file will contain the text of a page in a key / value fashion. For my own needs, there are two kinds of text, simple or markdown, the file look like this:

welcome: 'welcome!'
md_about: |
  **Yes**! this _new_ website is powered by [markdown][1]

  [1]: http://daringfireball.net/projects/markdown/

Now, with modern frameworks poping all around the web, I felt that “building” the website, even if it was a really simple task, was an oldschool approach, plus I needed a pretext to get my hands on AngularJS :)

It turns out there were plenty of modules to handle the YAML / markdown mechanism, I picked js-yaml and marked after trying a couple of other similar modules which were less effective and / or buggy.

Now the interesting part. I won’t explain AngularJS principles in this blog post as there are very informative and well written documentation on the subject, among them this quick tutorial from w3schools and of course the official AngularJS documentation; instead, I’ll drop here the main component of the AngularJS-based website, the controller:

var mySite = angular.module('mySite', ["ngSanitize"]);

mySite.controller('contentCtrl', function($scope, $parse, $http, $rootScope) {

    $http.get('content/index_' + $scope.lang + '.yaml')
    .success(function(response) {
        angular.forEach(jsyaml.load(response), function(v, k) {
            if (k.startsWith('md_') == true) {
                $parse('md.' + k.substring(3)).assign($scope, marked(v));
            } else {
                $parse(k).assign($scope, v);
            if (k == 'title') {
                $rootScope.pageTitle = v;

That’s right, in less than 20 lines of code, we read our YAML file, converted it to good old JSON and passed the content of both simple and markdown values to the HTML view.

A word of explanation on this controller. I chose that the YAML file name would correspond to the language the site is displayed with, for example, if lang == 'en', content/index_en.yaml will be read. The distinction between simple and markdown content is made by reading the first 3 characters of the key, if it begins with md_, then it’s markdown and it should be interpreted by marked.

There are two tricky traps here. First about the rendered markdown. For security reasons, AngularJS will not display the produced HTML when the expression is called with double braces {{ .. }} within the HTML view, instead it will show the HTML code escaped. It is mandatory to reference the key variable through a ng-bind-html tag, and as the documentation explains, you’ll have to add angularjs-sanitize.js to the list of loaded modules.

<span ng-bind-html="md.about"></span>

Now about the scope variables organization. I learned that an AngularJS expression will not be interpreted as a scope variable if it is the result of a string manipulation, for example, if foo == 'about':

<span ng-bind-html="'md_' + foo"></span>

will only print md_foo, it will not reference the md_about scope variable. In order to play with dynamic naming, I was forced to organize the markdown variables in an array, thus the following trick in the controller:

$parse('md.' + k.substring(3)).assign($scope, marked(v));

so I can have the following code in the HTML view:

<span ng-bind-html="md[foo]"></span>

Why so much pain? Because thanks to this little trick, I’ll be able to create loops for similar blocks in the HTML view, such as:

<div class="col-md-4" ng-repeat="cat in ['foo', 'bar', 'baz']">
  <img class="center-block" src="{{ 'media/images/' + cat + '.png' }}" />
  <h3 class="text-center">{{ cat }}</h3>
  <span ng-bind-html="md[cat]"></span>

I like to factorize.

I’m yet a total AngularJS newbie, if something in this blogpost seem awful to you, ng-guru, please feel free to contact me!

Annoying WiFi captive portal

No Comments »

The hotel I stay in Ibiza is awesome, it’s in front of the sea, the staff is nice and welcoming, there’s this amazing pool, AND… they have pretty good WiFi for the Island standards.

But this year, they’ve put those D-Link DWC-1000 repeaters all around the hotel which disconnects you every now and then, you have to re-register via a web page everytime, which is quite annoying. So I came up with this very simple python script that checks if we can reach the outside world, and if not, will use python mechanize in order to register automatically to the captive portal. Not the greatest script I’ve released, but hey, first beaches, parties and beers, then only comes IT :)

#!/usr/bin/env python

import os
import time
import mechanize

def recon():
    br = mechanize.Browser()
    response = br.response()
    headers = response.info()
    # that crappy repeater doesn't even reply the correct content-type
    headers["Content-type"] = "text/html; charset=utf-8"
    br.form['CaptivePortalSession.UserName'] = 'myuser'
    br.form['CaptivePortalSession.Password'] = 'mypasswd'
    print br.response().info()

while True:
    # yeah well, I'm not running for the best script of the year ok?
    r = os.system('ping -c 1')
    if r != 0:


I’m an Architect B****!

No Comments »

So this happened :)

AWS Certified Solutions Architect - Associate Level

Using pkgsrc on debian GNU/Linux

Tags: ,
No Comments »

While I tend to appreciate debian GNU/Linux, its tendency to be quite late on software versionning is sometimes annoying. Also, as a pkgsrc developer, I am used to have greater control over the packages I install, for example regarding the options I’d like to include.

For these reasons and a couple more, I sometimes choose to use pkgsrc along with apt to deal with particular packages. In this article, I’ll show how to achieve that task.

First install build pre-requisite packages:

# apt-get install cvs libncurses5-dev gcc g++ zlib1g-dev zlib1g libssl-dev

Then fetch pkgsrc:

# cd /usr && cvs -d anoncvs@anoncvs3.de.NetBSD.org:/cvsroot co pkgsrc

export the SH environment variable to /bin/bash:

# export SH=/bin/bash

And bootstrap pkgsrc:

# cd /usr/pkgsrc/bootstrap
# ./bootstrap

From now on, you’ll have a /usr/pkg directory filled with necessary bits for building packages from pkgsrc.

If you are to install services from pkgsrc packages, you’ll have to copy NetBSD‘s /etc/rc.subr to debian‘s /etc directory:

# wget -O/etc/rc.subr "http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/etc/rc.subr?rev=1.96&content-type=text/plain"

Create an ad-hoc rc.d directory:

# mkdir /usr/pkg/etc/rc.d

Let’s say you’d like to install nginx out of pkgsrc, possibly because debian‘s version is outdated or it does not contain your favorite module. Add the desired option to pkgsrc's options file, /usr/pkg/etc/mk.conf:

PKG_OPTIONS.nginx+= naxsi spdy

Build the software:

# cd /usr/pkgsrc/www/nginx
# /usr/pkg/bin/bmake install clean clean-depends

Copy the startup script:

# cp /usr/pkg/share/examples/rc.d/nginx /usr/pkg/etc/rc.d/

Enable the service:

# echo "nginx=YES" >> /etc/rc.conf

And start it:

# /usr/pkg/etc/rc.d/nginx start

Now, how you integrate services start to your favorite init system is up to you!

EC2 VPN connection informations (updated)

Tags: , , , ,
No Comments »

For a mysterious reason, EC2 VPN connection informations are stored in XML within the JSON data retrieved by either boto or the awscli command line tool.

Here’s a quick python snippet to convert those datas in a convenient, easily parsable dict:

#!/usr/bin/env python

import sys
import boto3
import xmltodict

profile = sys.argv[1]

s = boto3.Session(profile_name=profile)
ec2 = s.client('ec2')

vpn = ec2.describe_vpn_connections()
x = vpn['VpnConnections'][0]['CustomerGatewayConfiguration']

d = xmltodict.parse(x)

# ...

Combining this piece of code with jinja2 could help you generate racoon (or whatever IPSec software you use) on the fly.


here‘s a complete example of an automatic generation for racoon / ipsec configuration files using the previous snippet, along with jinja2.

Latency based Alias DNS record in Route53

Tags: , , , ,
No Comments »

Yes, I know I write a lot about AWS these days, but you know, obsession is my thing.

So as I wrote earlier, I generate my CloudFormation templates using troposphere, and the one thing I had to finish today was to register a latency based Alias record on Route53 for an ELB. While Route53 GUI is fairly easy to use, I’ve been stuck on its programmatic emanation for quite a while, so here’s a troposphere definition of such a CloudFormation object:

if scheme == 'internal':
    # for details about this condition, read:
    # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-elb.html
    canonzn = 'DNSName'
    canonzn = 'CanonicalHostedZoneName'

name = 'foo'
profile = 'eu-west-1'

fooDNSRecord = t.add_resource(RecordSetType(
    HostedZoneName = Join('', [Ref('SubZone'), '.']),
    Comment = '{0} DNS Name'.format(name),
    Name = Join('', ['{0}.'.format(name), Ref('SubZone'), '.']),
    Type = 'A',
    Region = region,
    SetIdentifier = '{0}-{1}'.format(name, profile),
    AliasTarget = AliasTarget(
        GetAtt('{0}LoadBalancer'.format(name), 'CanonicalHostedZoneNameID'),
        GetAtt('{0}LoadBalancer'.format(name), canonzn)

Note the catch, you can’t use Ref('AWS::Region') for the Region parameter or your CloudFormation will fail at the DNS entry creation with the Invalid request error. Do not forget to declare the SetIdentifier parameter which is mandatory for a latency-type record.

WP Theme & Icons based on GlossyBlue by N.Design Studio
Banner from www.trynthlas.com
Entries RSS Comments RSS Log in
Optimization WordPress Plugins & Solutions by W3 EDGE