ADAMKDEAN

Invalidate require cache

Published Friday, 1 July 2016

CommonJS, the module format that Node.js uses, caches modules the first time that you require them. In a sense, all required Node.js modules are singletons. Sometimes, this isn't so good. For example, an application library might reload a configuration file everytime it starts up. If you need to run tests, and perhaps change the configuration file each time, then you're going to need to be able to have a fresh require() for each test.

To do this, you need to delete the module from require.cache:

delete require.cache[require.resolve('./path/to/file')]

The following example will show this working:

date.js:

var date = new Date()

module.exports = exports = function (msg) {
  msg = msg || 'n/a'
  return `Generated on ${date.toString()} with: ${msg}`
}

index.js:

var date = require('./date')

console.log(date('first'))

setTimeout(function () {

  delete require.cache[require.resolve('./date.js')]

  date = require('./date')
  console.log(date('2 seconds later'))

}, 2000)

Output:

adam@macbook:test $ node index.js
Generated on Fri Jul 01 2016 15:49:11 GMT+0100 (BST) with: first
Generated on Fri Jul 01 2016 15:49:13 GMT+0100 (BST) with: 2 seconds later

Semantic commit messages

Published Friday, 1 July 2016

I have decided to adopt the following rules for semantic commit messages.

feat: add hat wobble  
^--^  ^------------^  
|     |  
|     +-> Summary in present tense.  
|  
+-------> Type: chore, docs, feat, fix, refactor, style, or test.

Although I sometimes only see chore, docs, feat, and fix, I do think refactor, style and test are useful too.

More examples on behalf of mutewinter:

chore: add Oyster build script  
docs: explain hat wobble  
feat: add beta sequence  
fix: remove broken confirmation message  
refactor: share logic between 4d3d3d3 and flarhgunnstow  
style: convert tabs to spaces  
test: ensure Tayne retains clothing

Code should be self-documenting, but commit messages should allow a developer to completely understand what that patch does. The developer should be able to implement the changes again without looking at the code, just from the commit message. fixed pesky bug doesn't quite cut it.

base64 decode explained

Published Monday, 2 May 2016

This is part 2 of a series on base64 encoding/decoding, based on a kata I recently completed at codewars. I've used base64 a lot but never have I delved into it enough to understand exactly what goes on. So I took the time to explain via inline comments. I hope you enjoy reading it as much as I enjoyed writing it. Part 1 - base64 encode explained.

String.prototype.fromBase64 = function () {
  const base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef' +
                      'ghijklmnopqrstuvwxyz0123456789+/';
  let result = '',
      encoded = '';

  // step 1. convert base64chars into reverse lookup object
  const base64inv = {};
  for (let i = 0; i < base64chars.length; i++)
    base64inv[base64chars[i]] = i;

  // step 2. remove any characters that are not base64 or padding
  const base64regex = new RegExp(`[^${base64chars}=]`, 'g');
  encoded = this.replace(base64regex, '');

  // step 3. replace padding at the end with A (remember, A equals zero)
  const onePadding = encoded.charAt(encoded.length - 1) === '=';
  const twoPadding = encoded.charAt(encoded.length - 2) === '=';  
  const padding = onePadding ? twoPadding ? 'AA' : 'A' : '';
  encoded = encoded.substring(0, encoded.length - padding.length) + padding;

  // step 4. iterate over the encoded string, four characters at a time
  for (let i = 0; i < encoded.length; i += 4) {

    // step 5. convert the four base64 characters into 6-bit numbers using
    //         the base64 character -> 6-bit number map above
    const dn = base64inv[encoded.charAt(i)];
    const en = base64inv[encoded.charAt(i + 1)];
    const fn = base64inv[encoded.charAt(i + 2)];
    const gn = base64inv[encoded.charAt(i + 3)];

    // step 6. convert these four 6-bit numbers into  one 24-bit number
    //
    // if you remember from before, we split a 24-bit number into four 6-bit numbers:
    //
    // e.g.   00001111 00000101 00001010
    // to     |----||- ---||--- -||----|
    //         d     e      f      g
    //
    // d =    00000000 00000000 00000011
    // e =    00000000 00000000 00110000
    // f =    00000000 00000000 00010100
    // g =    00000000 00000000 00001010
    //
    //
    // we need to left shift them (<<) so that they all line up
    // 
    // 00000000 00000000 00XXXXXX (we have four of these)
    // DDDDDDEE EEEEFFFF FFGGGGGG (we want one of these)        
    const d = dn << 18;      // DDDDDD00 00000000 00000000
    const e = en << 12;      // 000000EE EEEE0000 00000000
    const f = fn << 6;       // 00000000 0000FFFF FF000000
    const g = gn;            // 00000000 00000000 00GGGGGG
    const n = d + e + f + g; // DDDDDDEE EEEEFFFF FFGGGGGG (yay!)

    // step 7. split this 24-bit number into three 8-bit (ASCII) characters
    //
    // if you remember, we had three of these (8-bit): 00000000
    // and we actually wanted one of these (24-bit): 00000000 00000000 00000000
    //
    // to get this, we shift the first number 16 bits left, and the second, 8 bits left:
    //
    // 00000000 <------- -------- first char << 16
    //          00000000 <------- second char << 8
    //                   00000000 third char (no shift)
    //
    // so now we want to reverse this and reclaim our three 8-bit (ASCII) characters,
    // we can do this by shifting the numbers back over to the right, and applying a
    // 255 value logical AND (&) bit mask to ignore anything in the 16 bits on the left
    //
    // e.g.   00001111 00000101 00001010
    // to     |------| |------| |------|
    //         a        b        c
    //
    // >>> 16 00000000 00000000 00001111
    // & 255  00000000 00000000 11111111
    // a =    00000000 00000000 00001111
    //                            
    // >>> 8  00000000 00001111 00000101
    // & 255  00000000 00000000 11111111
    // b =    00000000 00000000 00000101
    //
    // noop   00001111 00000101 00001010
    // & 255  00000000 00000000 11111111
    // c =    00000000 00000000 00001010
    const a = (n >>> 16) & 255;
    const b = (n >>> 8) & 255;
    const c = n & 255;

    // step 8. turn these three 8-bit numbers into ASCII characters, and append to result
    result += String.fromCharCode(a, b, c);
  }

  // step 8. finally, remove any padding that was previously added to make this a multiple of 3
  return result.substring(0, result.length - padding.length);
};

base64 encode explained

Published Monday, 2 May 2016

This is from a kata I recently completed at codewars. I've used base64 a lot but never have I delved into it enough to understand exactly what goes on. So I took the time to explain via inline comments. I hope you enjoy reading it as much as I enjoyed writing it. Part 2 - base64 decode explained.

String.prototype.toBase64 = function () {  
  const base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef' +
                      'ghijklmnopqrstuvwxyz0123456789+/';

  let plaintext = this,
      result = '',
      padding = '';

  // step 1. ensure that the string is a multiple of three chars
  if (plaintext.length % 3 > 0) {
    for(let count = 0; count < 3; count++) {
      padding += '=';
      plaintext += '\0';
    }
  }

  // step 2. iterate over the input string, three chars at a time
  for (let i = 0; i < plaintext.length; i += 3) {
    // step 3. take three 8-bit (ASCII) chars, and store
    //         them as one single 24-bit number
    //
    // 00000000 8-bit (1 byte)
    // 00000000 00000000 00000000 24-bit (3 bytes)
    //
    // we'll bitshift the first number two bytes (16 bits)
    // we'll bitshift the second number one byte (8 bits)
    // and we'll pop the other number into the third byte
    //
    // 00000000 <------- -------- first char << 16
    //          00000000 <------- second char << 8
    //                   00000000 third char (no shift)    
    const a = plaintext.charCodeAt(i) << 16;
    const b = plaintext.charCodeAt(i + 1) << 8;
    const c = plaintext.charCodeAt(i + 2);
    const n = a + b + c;

    // step 4. separate this 24-bit number into four 6-bit numbers
    //
    // we'll do this by shifting to the right (with zero pad >>>)
    // and then doing a logical AND (&) to strip everything after 
    // the first (right most) six bits
    //
    // e.g.   00001111 00000101 00001010
    // to     |----||- ---||--- -||----|
    //         d     e      f      g
    //
    // >>> 18 00000000 00000000 00000011
    // & 63   00000000 00000000 00111111
    // d =    00000000 00000000 00000011
    //                            
    // >>> 12 00000000 00000000 11110000 
    // & 63   00000000 00000000 00111111
    // e =    00000000 00000000 00110000
    //
    // >>> 6  00000000 00111100 00010100 
    // & 63   00000000 00000000 00111111
    // f =    00000000 00000000 00010100
    //
    // noop   00001111 00000101 00001010
    // & 63   00000000 00000000 00111111
    // g =    00000000 00000000 00001010
    const d = (n >>> 18) & 63;
    const e = (n >>> 12) & 63;
    const f = (n >>> 6) & 63;
    const g = n & 63;

    // step 5. use the four 6-bit numbers as indices to pluck
    //         chars from our char array above, and add to the
    //         result string, before continuing with the loop
    //
    // this works because a 6-bit number is 0-63, and we have
    // 64 characters above. Therefore, we can pluck out chars
    result += base64chars[d];
    result += base64chars[e];
    result += base64chars[f];
    result += base64chars[g];
  }

  // step 6. finally, we'll remove the zero pad we added above
  //         and add the actual padding, then return this string
  return result.substring(0, result.length - padding.length) + padding;
};

List all files changed in last commit

Published Monday, 8 February 2016

List all files changed in the last commit by using git diff-tree on HEAD.

git diff-tree --no-commit-id --name-only -r HEAD

For example, if you changed README.md in your last commit:

adam@macbook:project (master) $ git diff-tree --no-commit-id --name-only -r HEAD
README.md
 
Showing posts 1-5 of 182