Symphonious

Living in a state of accord.

Modernising Our JavaScript – Supporting OpenSource

At LMAX, we’ve been using Vue.js for all our new JavaScript work very successfully for a while now. Our existing products used Vue.js for the logic but continued to use bootstrap as the component library to ensure the UI remained consistent.  For anything new though, we started using Vuetify to give us better UIs much more easily.

Vuetify is close to reaching it’s 1.0 release now and we’ve been using it for a while so when we first started with it, the vast majority of development and support was being done by the project founder, John Leider in his spare time. Migrating UI libraries is painful so having an early stage project without a lot of community (at the time) was a pretty big risk for us, but the code was excellent and John was obviously very committed to the project, being very responsive to bug reports and questions.  So LMAX jumped on board as a platinum supporter of Vuetify, making regular monthly payments to support Vuetify development.

It’s been a resounding success.

As a project, Vuetify has continued to grow – mostly because of the excellent work John was doing to build the community – giving more stability and now has multiple regular contributors. With the number of supporters also growing John quit his day job and is now focussed on Vuetify full-time. So our primary aim of supporting the viability and longevity of the project has worked out well.

We’ve also had secondary benefits. As part of being a platinum supporter the LMAX logo appears on the Vuetify site and we’ve set it up to link to our jobs site. It’s driven quite a lot of traffic that way and I think is actually the top source of traffic to our careers site.

I believe we also get some guaranteed support time as part of the package, but frankly John is so responsive I’m not sure if we’d actually notice the difference. We’re also keen to contribute code but the project is now moving along so well that stuff gets fixed for us before we get a chance to do it. It’s possible some of my colleague who are doing more with Vuetify have contributed some stuff, but every bug I’ve found has been fixed almost as soon as I can finish the bug report…

Overall, it’s been a huge success and well worth the money. I’d definitely encourage others to become financial backers of projects they depend on.

IPv6 on EdgeRouter X (ERX) and SkyMesh (EdgeOS 1.9.7)

The internet is full of forum posts with various questions, tips and suggestions for IPv6 on EdgeRouters as people struggle to get it working. The challenge is that the fix you actually need depends on which version of EdgeOS you’re running (ubnt are continuing to flesh out the IPv6 configuration support) and the setup of your ISP.

So here’s the magic steps I needed for an ERX running EdgeOS 1.9.7+hotfix.3 with SkyMesh NBN over HFC (cable).

  1. Use the Basic Setup wizard to get the basic config in place
    • Port eth0
    • Internet connection type PPPoE (and enter your PPPoE login)
    • Enable the default firewall
    • Enable DHCPv6 Prefix Delegation
      • Prefix length /56
      • Enable the default IPv6 firewall
      • IPv6 LANs switch0
    • Only use one LAN
    • Setup the IPv4 LAN address and users as required.
  2. Additional config required either via the command line ‘configure’ or Config Tree tab in the UI.
    • set interfaces ethernet eth0 pppoe 0 ipv6 enable 
    • set interfaces ethernet eth0 pppoe 0 ipv6 address autoconf
    • set interfaces ethernet eth0 pppoe 0 dhcpv6-pd rapid-commit disable
    • set interfaces switch switch0 ipv6 address autoconf

The extra config essentially boils down to three main things:

  1. PPPoE interface needs to have IPv6 enabled if IPv6 is going to work.
  2. The PPPoE and switch0 interfaces need to auto configure their IPv6 address.
  3. SkyMesh do not support rapid-commit for prefix delegation. Almost every ERX IPv6 config I’ve seen has this enabled but with SkyMesh the devices on the LAN and the switch0 interface will fail to get global IPv6 IPs unless it’s disabled.

As an optional extra if you want to avoid ICMP filtering you should duplicate rule ’30’ from firewall ipv6-name WANv6_LOCAL to   (action accept, protocol ipv6-icmp being the key values). This is allowing extra traffic through your firewall to all devices on your network so you should only do that if you’ve done the research and are comfortable with it. You should have working IPv6 connectivity without doing this – don’t just do it as a desperate option to get things working.

Internationalising Vue Components

As Vue gradually spreads through our JavaScript, it’s reached the point where we need to support internationalisation. We already have a system for localising numbers and dates that we can reuse easily enough with Vue filters. We also have an existing system for translations, but it’s tied into the mustache templates we’ve used to date so we’ll need to adapt or change it somehow to work with Vue.

In mustache we simply wrap any strings to be translated with {{#i18n}}…{{/i18n}} markers and a webpack loader takes care of providing a translation function as the i18n property of the state for the compiled template.

There are a number of vue translations plugins around all of which tend to take one of two approaches:

  1. a directive that automatically pulls out the element text (e.g. vue-translate)
    <div v-trans title=hello>hello</div>
  2. a method you call (e.g. vue-i18n-mixin)
    <h1 v-text=”t(‘header.title’, ‘fr’)”></h1>
    <h1>{{ t(‘header.title’) }}</h1>

Using a directive is nice and simple but doesn’t work if the text is dynamic (any {{data}} values are already resolved in the element text). Explicitly calling a function to perform the translation leads to pretty unwieldy templates.

What we’ve settled on is a pretty direct translation of our mustache system to Vue. We use <i18n></i18n> instead of {{#i18n}}{{/i18n}} since the content of {{…}} in Vue is expected to be valid JavaScript and IDEs flag it as an error if it’s not. So a simple example might be:

<p><i18n>Hello world!</i18n></p>

To make it actually translate, we use a HTML pre-loader for the webpack vue-loader that translates that into:

<p>{{translate("Hello world!")}}</p>

Finally, on the client side, a global Vue mixin provides the implementation of translate that looks up the translated version of that string and replace it.

When the string includes parameters we write them in the same way as a normal Vue template, for example:

<p><i18n>Hello {{name}}!</i18n></p>

And our pre-loader translates it in exactly the same way:

<p>{{translate("Hello {{name\}\}!")}}</p>

The only slightly tricky bit is that the pre-loader escapes the } characters, otherwise Vue treats them as the end of the template string and it results in JavaScript errors.

At this point, Vue won’t replace {{name}} with the actual value of name because it’s part of a JavaScript string, not part of the template. The good part of that is that our translation function will now be looking up the string to translate prior to variable substitution.  So it will look for a translation for “Hello {{name}}!” rather than needing translations for every possible name. Once we find the translation, the translate function then uses a regex to find and replace each placeholder with the actual value from the vue instance. 

Notably, this process means that you can’t use anything but a simple variable name in placeholders within translated strings (though it can refer to a computed property). This is actually a good thing because we don’t want to send a string off to the translators that contains complex JavaScript they won’t understand. In fact, we have tests that place additional restrictions on strings to translate to ensure they are easily manageable through the translation process.

Moolah Diaries – Automating Deployment from Travis CI

Thanks to Oncle Tom’s “SSH deploys with Travis CI“, I’ve now fully automated Moolah deployment for both the client and server.

Previously the client was automated using a cronjob that pulled any changes from GitHub, built and ran the tests and if that all worked resync’d the built resources to apache’s docroot. That meant my not-particularly-powerful server was duplicating the build and test steps already happening in Travis CI and there was up to a 10 minute delay before changes went live.

The server didn’t have automated deployments because it has database tests that need an actual database to test against.  I’m crazy, but not crazy enough to run database tests anywhere near real production data.

Now with Travis CI trigging the deployment after the tests have already passed everything is triggered automatically from a git push and it’s a nicely isolated server running all the tests.

I’ve deliberately split the deployment in two – committed to the Moolah codebase is just enough to push the artefacts over to the server and trigger it’s deployment script. The deployment script on the server is managed as part of it’s puppet configuration and controls where things are finally deployed to, manages database migrations etc. That gives a nice clear delineation between the development of the service and the details of how it’s deployed in this particular environment and a simple, clear interface between the two. Now I can change how things work in either the code or the server setup without thinking too much about the other half.

Moolah Diaries – The Moment Dilemma

moment is one of those javascript libraries that just breaks my heart. It does so much right – nearly every date function you’d want right at your finger tips, no major gotchas causing bugs, clean API and good docs. I’ve used moment in some pretty hard code, very date/time orientated apps and been very happy.

But it breaks my heart because when you pull it in via webpack, it winds up being a giant monolithic blob. I only need a couple of date functions but with moment I wind up getting every possible date function you can imagine plus translations into a huge range of languages I don’t need. If I use moment, about 80% of Moolah’s client javascript is moment.

So I was quite pleased to find date-fns which also provides every date function you can imagine but is specifically design to be modular so you only pull in what you actually need. Works a treat, so Moolah uses that for the date operations it needs and it barely adds any size at all.

Except then when I went to use a graph library, it turns out that Chart.js which I was keen to try depends on moment. Sigh.