Tuesday, November 14, 2017

Making xdg-open use Chrome instead of Firefox

One of the more annoying things in Linux Mint that I came across is the fanatic persistence of using Firefox to open links after making Chrome the default browser. I know, I know... Google spying, I'm giving up privacy to some corpo that will do evil things with my sexual preferences, yadda, yadda, yadda - I don't care. I want my links to open in Chrome. Period.

Recently I came across a post on askubuntu.com that explains what needs to happen. Basically all URL opening mechanisms are using xdg-open underneath the covers so all it takes is to teach it the lesson it needs:

$ xdg-mime default google-chrome.desktop text/html
$ xdg-mime default google-chrome.desktop x-scheme-handler/http
$ xdg-mime default google-chrome.desktop x-scheme-handler/https
$ xdg-mime default google-chrome.desktop x-scheme-handler/about

Done. Now ctrl-click any links in the console and you'll have that opened in Chrome.

Happy clicking!

Saturday, October 28, 2017

Poi that!

You might already be familiar with Webpack. It's a great tool and a bastard when it comes to configuration. It's a beast! It's a daemon! It's one of those sons of bitches that you can't loose and that will hunt you down everywhere you go. Like a woman! Just look at the amount of configuration code that the vue-cli produces when used in combination with the webpack template. That is right down INSANE!!!

Although there's not much that can be done with woman, Webpack has received a shell that is finally making some sense! That shell is called POI and is created by a gentleman who goes by the name of Egoist. Let's take a closer look at what it does.

At first glance it is a simplified build tool. One that you would probably never look upon knowing what kind of power Webpack brings to the table. But if you look a bit closer it turns out it is just (easier said than done) a box around webpack configuring it to some standards using a set of predefined presets for the most common tasks. However, knowing what kind of a daemon sits in the guts of it, you'll be pleased to know that poking around that dragon is very much possible and easy at the same time.

So what does it look like to use it? First you need to know that Poi stems from the ashes of a slimming treatment of vue-cli. This means it will out-of-the-fricken-box work just perfect with Vue.js! All you need is a file that will bootstrap your application. Let's see the content of an index.js that does it.

import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app',
  render: h => h(App)
})

And then the single-file-component App.vue

<template>
  <h1>Hello, world!</h1>
</template>

You can obviously put everything else in there (the <script> and <style> sections for example).

Now let's see how do we actually use Poi with that nice application. First of all you need to install Poi. For the sake of this oversimplified and butt-ugly example we'll perform the worst voodoo magic ever and install Poi globally. But please, beyond demonstration purposes NEVER EVER EVER EVER DO THAT. EVER. Poi is your build system so it must be part of your project. You have been warned!

npm install -g poi

Now having index.js and App.vue in that same directory run the following command:

$ poi index.js

# some useless output here

                               Asset       Size  Chunks                    Chunk Names
abf8058814c81f7a160b.hot-update.json   44 bytes          [emitted]         
                           vendor.js     1.5 MB       0             [big]  vendor
                           client.js    13.6 kB       1                    client
                         manifest.js    31.2 kB       2  [emitted]         manifest
ff42074b3ef0f4f41971.hot-update.json   35 bytes          [emitted]         
                          index.html  578 bytes          [emitted]         

> Open http://localhost:4000
> On Your Network: http://127.0.0.1:4000

 DONE  Build 664748 finished in 141 ms!

That is it! Nothing more, nothing less! You have access to everything that you would expected: Hot module reloading, bundling app in separate files so that the part that actually changes is not the whole package, an index.html that is automatically generated for you to host your application - you name it!

There is much more to poi than just running the project. For example to be able to run unit tests you need to start using poi presets. That's not very hard - requires a few more packages though.

$ npm install poi-preset-karma karma-webpack karma-mocha karma-chai webpack mocha chai

With those packages installed we need to make Poi aware how to run tests. We do that by creating a poi.config.js like so:

module.exports = {
  presets: [
    require('poi-preset-karma')({
      frameworks: [ 'mocha', 'chai' ]
    })
  ]
}

That's really it! Nothing more! Just basically 4 lines telling that we would like to use karma with mocha and chai. Jeez! That is truly magnificent! Totally Vue.js-style! Now let's have the simplest test possible. All unit tests are to be stored in test/unit folder. This location can obviously be further configured but for now it will do just fine. So let's create test/unit/example.test.js like so:

import { expect } from 'chai'

it('will work', () => {
  expect(1).to.equal(1)
})

Having that in place let's run the tests!

$ poi test

# some useless output here

                    Asset    Size  Chunks                    Chunk Names
test/unit/example.test.js  925 kB       0  [emitted]  [big]  test/unit/example.test.js

 DONE  Build 78afa9 finished in 526 ms!

28 10 2017 13:22:30.310:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:5001/
28 10 2017 13:22:30.310:INFO [launcher]: Launching browser Chrome with unlimited concurrency
28 10 2017 13:22:30.314:INFO [launcher]: Starting browser Chrome
28 10 2017 13:22:30.892:INFO [Chrome 62.0.3202 (Linux 0.0.0)]: Connected on socket BUV....
  ✔ will work

Finished in 0.002 secs / 0 secs @ 13:22:31 GMT+0200 (CEST)

SUMMARY:
✔ 1 test completed

Now ain't that a pretty! I myself was looking to simplify the use of webpack for quite some time and it is only my bad luck that I didn't run into poi sooner. I would have saved a whole lot of time! Tumbling down the rabbit hole if you would like to run those tests in headless mode you can specify the browser to use. I like headless Chrome:

$ poi test --browsers ChromeHeadless

Boooom! Your tests can now be executed in headless environments like CI! That is so easy and soooo exciting!

Want to do some code coverage?

$ poi test --coverage

# some useless output here

                    Asset    Size  Chunks                    Chunk Names
test/unit/example.test.js  925 kB       0  [emitted]  [big]  test/unit/example.test.js

 DONE  Build 78afa9 finished in 508 ms!

28 10 2017 13:46:33.489:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:5001/
28 10 2017 13:46:33.489:INFO [launcher]: Launching browser ChromeHeadless with unlimited concurrency
28 10 2017 13:46:33.493:INFO [launcher]: Starting browser ChromeHeadless
28 10 2017 13:46:33.747:INFO [HeadlessChrome 0.0.0 (Linux 0.0.0)]: Connected on socket 9Xjcc...
  ✔ will work

Finished in 0.006 secs / 0.001 secs @ 13:46:33 GMT+0200 (CEST)

SUMMARY:
✔ 1 test completed
----------|----------|----------|----------|----------|----------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines |Uncovered Lines |
----------|----------|----------|----------|----------|----------------|
----------|----------|----------|----------|----------|----------------|
All files |      100 |      100 |      100 |      100 |                |
----------|----------|----------|----------|----------|----------------|

So one last thing that we need is to deliver our super duper app to some hosting. scp-ing files is not the task for Poi but building those files is and Poi does it very well:

$ poi build index.js

You will end up with the dist folder containing everything. One property of the created files is that they use absolute paths for resources like scripts and styles. It's great when it comes to hosting but trying it out using the file:// protocol (simply opening the index.html file in the browser) won't work. But it is very easy to fix. In poi.config.js add the webpack method like so:

module.exports = {
  // ...
  webpack(config) {
    config.output.publicPath = ''
    return config
  }
}

Now run the build again and open the resulting index.html file in the browser. Boom! It works!

There is way more to Poi than that. Check out the official documentation at http://poi.js.org and the GitHub page at http://github.com/egoist/poi. You will be amazed what's in store!

Happy... poi'ing!

Sunday, September 10, 2017

DotNet core and vuejs - a quick recipe

The problem

So you're stuck in .NET world (maybe with Sitecore or some other God forsaken creation) and you need to do some front-end code. How the hell do you get started?

The solution

Use dotnet core 2.0.0. Scaffold your application, install all the required dependencies and start the application in development mode

    $ sudo apt-get install dotnet-sdk-2.0.0
    $ dotnet new --install Microsoft.AspNetCore.SpaTemplates::*
    $ dotnet new vue -n hello-world
    $ cd hello-world
    $ dotnet restore
    $ npm install
    $ ASPNETCORE_ENVIRONMENT="Development" dotnet run

Happy coding

Tuesday, September 5, 2017

Back to the roots: defining a jQuery plugin in TypeScript

The problem

You're so proficient in TypeScript that you decided all your code will be type-safe. All that is left is this damn jQuery plugin that won't bow to your will and you have no idea how to make all the typing information work.

The solution

This is actually quite easy! Let's start with the obvious import:

    import * as $ from 'jquery'

Next you need to decide if your plugin will take configuration options. If that is the case then you need an interface describing those options

    interface FooOptions {
      color?: string
    }

Now we need to tell TypeScript that we're going to extend the global JQuery interface with our method:

    declare global {
      interface JQuery {
        foo(options?: FooOptions): JQuery
      }
    }

The question mark after options means this is an optional parameter and if not defined then an undefined value will be passed on

All that is left is to write the plugin itself

    $.fn.foo = function(this: JQuery, options?: FooOptions) {
      const settings: FooOptions = {
        color: 'blue',
        ...options,
      }
      return this.each(function() {
        $(this).css({ 'background-color': settings.color })
      })
    }

The method signature tells TypeScript that this in this context is a jQuery object and that there can be a FooOptions passed on.

The rest is obvious: using object spread operator we get the actual set of settings and next we just go on with the plugin's body.

Happy coding!

Back to the roots: jQuery plugins and TypeScript

The problem

So you have learned TypeScript but there is no escape from jQuery. It's crawling back over and over again from the dungeons. But you'd like to write your code in TypeScript and this damn thing doesn't know your new cool method? Want to teach it a lesson? Read on!

The stage

TypeScript is great! No question about it. I use it primarily with webpack and it is a wonderful combination. One of the best things it has is the ability to merge interface definitions. That's right! You can literally extend the interface as you wish! This will come very handy when adding new methods to be called in a jQuery chain. First let's define a plugin foo in plain JavaScript.

    import * as $ from 'jquery'

    $.fn.foo = function () {
      return this.each(function() {
        $(this).css({ 'background-color': 'blue' })
      })
    }

Nothing fancy, right? Each element passed on will receive a blue background. I love blue that is why it is blue and not red.

The solution

Now for the tricky part: teaching TypeScript the definitions. I say definitions because we'll teach it first the JQuery interface by installing the @typings/jquery package

    $ npm install --save-dev '@types/jquery'

Now if you're like me and you use jQuery 3 or later with the default TypeScript configuration you will see a message saying something about Iterable. That's easy to fix. In the tsconfig.json make sure you have the lib line containing the es2015.iterable element, like so:

    "compilerOptions": {
        "lib" : [ "es2015", "es2015.iterable", "dom" ]
    }

With that out of the way let's teach TypeScript the new foo

    declare global {
      interface JQuery {
        foo (): JQuery
      }
    }

This is called declaration merging and allows for arbitrary declarations to be added to arbitrary interfaces just like in JavaScript you can add arbitrary fields to arbitrary objects. Cool, ain't it?

Also note that we use the global module. That's because if we're inside of another module then the original JQuery interface is a different one from the one defined in the global scope and things just don't go so well in that case.

Now let's use our new toy:

    $('div').foo()

Don't forget the new way of importing jQuery requires the allowSyntheticDefaultImports compiler option.

Happy coding!

Friday, September 1, 2017

Back to the roots: jQuery plugin authoring

The problem

The code you're writing using jQuery becomes unmaintainable and hard to read

The solution

Short of stop using jQuery (if that is an option you can use then go for it and use Vue.js instead) start by extracting reusable components/plugins from your code. Writing such a plugin is nothing hard - just a frame where you put your code in:

    (function($) {
      $.fn.myPlugin = function(options) {
        const defaults = {
          // TODO: add default values for options here
        }
        const settings = $.extend(defaults, options)
        // Either process each element in a loop
        return this.each(function() {
          // TODO: add the body of what the plugin should do
          // 'this' is the current element when iterating
        })
        // or operate on all elements at once using other functions
        // return this.css({ ... })
      }
    })(jQuery)

More on authoring jQuery plugins here.

With that in place you're ready to create whatever is used in multiple places like the grownups do and enjoy the pleasure of using method chaining even more!

    $('.my-component').myPlugin({ color: red });

Going Webpack!

It is said that once you go Webpack you never go back. If that's your case then you might want to create your plugins as separate modules and let Webpack put it all together nice and easy. To do that you define your plugin as a module that imports jQuery and exports nothing, then you import that module in your main application file.

    import $ from 'jquery'

    $.fn.myPlugin = function(options) {
      const defaults = {
        // TODO: add default values for options here
      }
      const settings = $.extend(defaults, options)
      // Either process each element in a loop
      return this.each(function() {
        // TODO: add the body of what the plugin should do
        // 'this' is the current element when iterating
      })
      // or operate on all elements at once using other functions
      // return this.css({ ... })
    }

In main.js

    import $ from 'jquery'
    import './my-plugin'

    $(document).ready(function() {
      // use your plugin here
      $('.my-component').myPlugin({ color: red });
    })

The easiest Webpack configuration that will work in this case is

    module.exports = {
      entry: __dirname + '/index.js',
      output: {
        path: __dirname + '/dist',
        filename: 'index.js'
      },
      devtool: 'source-map'
    }

In this case you can include the dist/index.js and you'll have all you need in one place. And that's it! Easy as a lion :)

And last but not least: don't name your plugin myPlugin. It's lame to do in anything else than a tutorial. Be explicit, use good names, be a good programmer!

Happy coding!

PS

Please be mindful that the function assigned to $.fn.myPlugin as well as the one that iterates over elements that have been passed on can't be arrow functions because jQuery sets the this element. Just remember that.

Friday, July 28, 2017

Getting started with Quasar on Android with Linux

The problem

As usual the problem is setting up the development environment for a project. This time it's going to be project using the Quasar framework and building the app for Android. This guide assumes a clean environment that does not contain anything (no Java, no Node no nothing).

Solution

First we need to have NodeJS installed. I install mine using the Node Version Manager (or nvm for short). Follow the instructions on https://github.com/creationix/nvm or simply enter the following commands:

$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash

At this stage you need to close the current terminal and open a new one for the installation to finish and then install the latest stable NodeJS like so:

$ nvm install stable

We'll need two more things: JDK and Gradle. I install mine using the SdkMan:

$ curl -s "https://get.sdkman.io" | bash

At this stage you need to close the current terminal and open a new one for the installation to finish and then install Java and Gradle like so:

$ sdk install java
$ sdk install gradle 2.14.1

The version of Gradle is important. Cordova works with specific versions so you might want to stick to 2.14.1 to avoid any issues

Next we need to have some basic tooling for NodeJS installed. This includes obviously the Quasar CLI but also Yarn and Cordova

$ npm install -g yarn quasar-cli cordova

Having all that installed we can now instantiate and build our first project

$ quasar init hello-world
$ cd hello-world
$ yarn
$ yarn build

At this stage we have have the hello-world type quasar application built. We can work on it as if it would be any other VueJS application by running

$ yarn dev

More on it in the official documentation for Quasar

What we need next is the Android SDK. This is the most troublesome part of all, but if scripted it doesn't seem to be all that hard. First you need to download and extract the Android SDK. To do that we'll use the unzip tool that understands the .zip archive format. Be patient. It takes forever to download and install all the components. On my connection it took almost 10 minutes!!!

$ sudo apt install -y unzip
$ mkdir -p ~/Android/Sdk
$ cd ~/Android/Sdk
$ wget -c https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
$ unzip -q sdk-tools-linux-3859397.zip
$ cd tools/bin
$ ./sdkmanager --update
$ ./sdkmanager emulator platform-tools
$ ./sdkmanager "build-tools;25.0.3"
$ ./sdkmanager "platforms;android-25"
$ ./sdkmanager "system-images;android-25;google_apis;x86"
$ ./avdmanager create avd --force \
               --name test \
               --package "system-images;android-25;google_apis;x86" \
               --tag google_apis \
               --device 'Nexus 4'

Starting the emulator takes some time - be patient!

$ ~/Android/Sdk/tools/emulator -avd test -skin 768x1280

After it is up we have a platform to run our Quasar application on. Let's use it to run our new and shiny app.

$ cd [your_project_folder]
$ quasar wrap cordova
$ cd cordova
$ ANDROID_HOME=~/Android/Sdk cordova platform add android
$ ANDROID_HOME=~/Android/Sdk cordova build
$ ANDROID_HOME=~/Android/Sdk cordova run

That's it! The application should be running on your virtual Android device. Of course you should make the ANDROID_HOME variable more permanent by adding it to your .profile or .bashrc startup files but I'll leave it up to you.

Have fun!