How to customize MorganJS

MorganJS is easy to install and works nicely out of the box.

{[.old-setup | 1.hilite(=javascript=) ]}

Here is what it looks like. Highlighted HTTP status codes are quite useful.

Screen Shot 2015-11-08 at 14.44.07

Thankfully, it’s possible to customize MorganJS by adding tokens, which are template symbols, like this:

{[ .current-user | 1.hilite(=javascript=) ]}

which can be later used like this:

{[ .middle-setup | 1.hilite(=javascript=) ]}

to produce something like this:

Screen Shot 2015-11-08 at 14.52.16

Uh-oh!! Where are my colors?

I delve into MorganJS code…

{[ .morgan | 1.hilite(=javascript=) ]}

As you may have noticed, the above code is hard to understand and quite hard-coded too.

  • Hard-coded because, even if the ´dev´ template is documented as ´:method :url :status :response-time ms – :res[content-length]´, it’s really embedded into the code and mixed up with extraneous bits rather than being declared into some option and used like any other MorganJS template is.
  • Hard to understand because the function object is being used as a cache for its own executions which entail a compilation step whose raison d’être I still have to grasp. I could be wrong, but this one could be a clear example of over-engineering.

However my biggest disappointment was that there is no way of reusing the colored ´:status´ token nor the coloring functionality, neither directly, by calling a method, nor indirectly, by copy-pasting some code. A total fail. 🙁

Googling “terminal colors” I eventually got to this Unix StackExchange answer, which I used to write this:

{[ .color-factory | 1.hilite(=javascript=) ]}

A nice collateral about my ´ColorFactory´ function is that I can use it also in the console like this:

{[ .console-log | 1.hilite(=javascript=) ]}

to get something like this:

Screen Shot 2015-11-08 at 16.12.17

Finally, I was able to customize Morgan with this:

{[ .morgan-factory | 1.hilite(=javascript=) ]}

and use it like this:

{[ .new-setup | 1.hilite(=javascript=) ]}

to get something like this:

Screen Shot 2015-11-08 at 16.51.54

 

How to improve filters with promises

I had been programming a filters setup for the node API of a MEAN stack app.

Having this ´User´ model:

// user.model.js (complete)

var mongoose = require('mongoose');

var schema   = new mongoose.Schema({
    name: String,
    admin: Boolean
});

module.exports = mongoose.model('User', schema);

It allowed a ´User´ controller like this:

// user.controller.js (complete)

var fields = [
    'name', 
    function (admin) { 
        return !!admin.length; 
    }
];

var Item = require('./user.model');
var Controller = require(global.absPath + '/app/shared/CRUD.controller');
module.exports = Controller(Item, fields);

The meaning should be straightforward: copy the ´name´ field as is and make the ´admin´ field a proper boolean. That was made possible by this:

// CRUD.controller.js (excerpt)

module.exports = CRUD_Controller;

function CRUD_Controller(Item, fields) {
    //...
    function Create(req, res) {

        var item = new Item();

        CopyFields(fields, req.body, item);

        item.save(function(err) {

            if (err) {
                return res.send(err);
            }

            res.json({
                message: 'Item created!'
            });

        });

    }


    function CopyFields(fields, data, item) {

        (fields || []).forEach(function(field) {

            switch (typeof field) {

                case 'string':
                    item[field] = data[field];
                    break;

                case 'function':
                    var matches = String(field).match(/^functions*(s*(w+)s*)/);
                    if (!(matches && matches[1])) {
                        console.log('Expected a function with only one argument.');
                        return;
                    }
                    var name = matches[1];
                    item[name] = field(data[name]);
                    break;

            }

        });

    }
    //...
}

Then I wanted to add a ´password´ field to the ´User´ model. For storing it I decided to go with Strong Password Hashing with Node.js Standard Library. Properly translated to JavaScript and slightly tweaked I got this:

// hash.js (complete)

var crypto = require('crypto');

module.exports = Hash;

return;

function Hash(options, callback) {

    // Default options.plaintext to a random 8-character string
    if (!options.plaintext) {
        return crypto.randomBytes(8, function(err, buf) {
            if (err) {
                return callback(err);
            }
            options.plaintext = buf.toString('base64');
            Hash(options, callback);
        });
    }

    // Default options.salt to a random 64-character string (512 bits)
    if (!options.salt) {
        return crypto.randomBytes(64, function(err, buf) {
            if (err) {
                return callback(err);
            }
            options.salt = buf.toString('base64');
            Hash(options, callback);
        });
    } 

    // Default options.iterations to 10k
    if (!options.iterations) {
        options.iterations = 10000;
    }

    // Default options.digest to sha1
    if (!options.digest) {
        options.digest = 'sha1';
    }

    crypto.pbkdf2(options.plaintext, options.salt, options.iterations, 64, options.digest, function(err, key) {
        if (err) {
            return callback(err);
        }
        options.algorithm = 'PBDFK2';
        options.key = key.toString('base64');
        callback(null, options);
    });

}

So my ´User´ model became this:

// user.model.js (complete)

var mongoose = require('mongoose');

var schema   = new mongoose.Schema({
    name: String,
    password: {
        algorithm:  String,
        digest:     String,
        iterations: Number,
        salt:       String,
        key:        String
    },
    admin: Boolean
});

module.exports = mongoose.model('User', schema);

Have you noticed that the ´Hash´ function relies on the asynchronous´crypto.pbkdf2´ function? That’s just standard, so I wasn’t going to use the synchronous version on a second thought.

Then my problem was:

How do I make these filters work with deferred values?

Ta-da! Promises:

// user.controller.js (complete)

var Promise = require('es6-promise').Promise;
var fields = [
    'name', 
    function (password) { 
        return new Promise(function (resolve, reject) {
            var Hash = require(global.absPath + '/app/components/auth/hash');
            Hash({plaintext: password}, function (error, result) {
                if (error) {
                    reject(Error(error));
                } else {
                    delete result.plaintext;
                    resolve(result);
                }
            });
        });
    }, 
    function (admin) { 
        return !!admin.length; 
    }
];

var Item = require('./user.model');
var Controller = require(global.absPath + '/app/shared/CRUD.controller');
module.exports = Controller(Item, fields);

To make that work I had to change a bit the ´CRUD´ controller.

The first change was to separate the filtering from the assignment, so that I could later use the ´Promise.all´ method which allows to synchronize promises and values as well. That implied to pass from a ´CopyFields´ function which filters and assigns each value in turn to a ´FilterFields´ function which filters all values at once, thus making the assignments directly in the ´Create´ function.

// CRUD.controller.js (broken excerpt) 
 
module.exports = CRUD_Controller; 
 
function CRUD_Controller(Item, fields) { 
    //... 
    function Create(req, res) {

        FilterFields(fields, req.body, function (fFields) {
            var item = new Item();

            fFields.forEach(function (fField) {
                item[fField.name] = fField.value;
            });

            item.save(function(err) {

                if (err) {
                    return res.send(err);
                }

                res.json({
                    message: 'Item created!'
                });

            });
        });

    }


    function FilterFields(fields, data, callback) {

        Promise
            .all((fields || []).map(Filter))
            .then(callback)
            .catch(function (error) {
                console.log(error);
            });


        function Filter(field) {
            var result;

            switch (typeof field) {

                case 'string':
                    result = {
                        name: field,
                        value: data[field]
                    };
                    break;

                case 'function':
                    var matches = String(field).match(/^functions*(s*(w+)s*)/);
                    if (!(matches && matches[1])) {
                        console.log('Expected a function with only one argument.');
                        return;
                    }
                    result = {
                        name: matches[1],
                        value: field(data[matches[1]])
                    };
                    break;

            }

            return result;
        }

    }
    //... 
}

The second change was to add a needed special treatment for my promises. You may have noticed that, in the ´case ‘function’:´ above, ´result.value´ can be a promise BUT that won’t make ´result´ a promise itself!! So the code above wouldn’t work yet, because it would complete ´Promise.all´ before getting the hashed password. Finally, I got this:

// CRUD.controller.js (working excerpt)

module.exports = CRUD_Controller; 
 
function CRUD_Controller(Item, fields) { 
    //... 
    function Create(req, res) {

        FilterFields(fields, req.body, function (fFields) {
            var item = new Item();

            fFields.forEach(function (fField) {
                item[fField.name] = fField.value;
            });

            item.save(function(err) {

                if (err) {
                    return res.send(err);
                }

                res.json({
                    message: 'Item created!'
                });

            });
        });

    }


    function FilterFields(fields, data, callback) {

        Promise
            .all((fields || []).map(Filter))
            .then(callback)
            .catch(function (error) {
                console.log(error);
            });


        function Filter(field) {
            var result;

            switch (typeof field) {

                case 'string':
                    result = {
                        name: field,
                        value: data[field]
                    };
                    break;

                case 'function':
                    var matches = String(field).match(/^functions*(s*(w+)s*)/);
                    if (!(matches && matches[1])) {
                        console.log('Expected a function with only one argument.');
                        return;
                    }
                    result = {
                        name: matches[1],
                        value: field(data[matches[1]])
                    };
                    if (stuff.isPromise(result.value)) {
                        var promise = new Promise(function (resolve, reject) {
                            var name = result.name;
                            result.value.then(function (value) {
                                resolve({
                                    name: name,
                                    value: value
                                });
                            }).catch(function (error) {
                                reject(Error(error));
                            });
                        });
                        result = promise;
                    }
                    break;

            }

            return result;
        }

    }
    //...
}

The added lines make ´result´ a promise if ´result.value´ is one: ´result´ will eventually resolve to the expected result. BTW, the ´stuff.isPromise´ method is the classical ´object.then && typeof object.then == ‘function’´.

How to run WordPress tests in VVV using WP-CLI and PHPStorm 8

Some time ago I wrote an analogous reminder: How to run WordPress tests in VVV using PHPStorm 8. Today I can do it again. And I really mean can instead of a simpler and also possible must. In fact, only some minutes ago I was able to solve this issue.

In my mentioned article, I explained how to do everything for the ´wordpress-develop´ site. It is what allows you to develop the WordPress core, for example for fixing a bug. And with the help of VVV / Vagrant / VirtualBox, setting up the environment is almost a painless task.

At some point I decided it was time to start developing the new version of my very old Enzymes plugin. I thought that it would make a lot of sense to take advantage of the ´wordpress-default´ site which VVV provides too, so I checked out my plugin code from wordpress.org, and, for some days, I could think and write my new code in a PHPStorm project, as I use to.

When I needed to debug an intermittently failing test, I recalled that I had written a reminder about that, and happily read it and applied all those steps again… to no avail. Oops.

Of course there had to be some difference. The first one I could think of was that before I had used the ´phpunit.xml.dist´ configuration file that came packaged with VVV, while now I was trying to use the ´phpunit.xml´ generated by the ´wp scaffold plugin-tests´ command. However it didn’t seem the right issue here.

What absolutely I could not understand was why I could run my PHPUnit tests smoothly from the command line but not from PHPStorm, not even following my own fabulous how-to instructions that had worked so well before. It drove me crazy, and more so because I couldn’t solve it.

From time to time I got back to having a need for debugging my tests, and tried to configure PHPStorm again, but never succeeded.

Today I also tried an alternative I found on the PHPStorm issue tracking site. WI-25653: Path mapping not considered when running PHPUnit tests with remote interpreter… I really wanted the solution outlined in the comment to be the one, but I couldn’t make that work either.

I was very sad. It had to be the solution, because that was almost exactly the problem I had:

Screen Shot 2015-02-12 at 22.25.29

Of course the problem I had was that PHPStorm was trying to access a path in my hosting environment (OSX) thinking it was a path in the hosted environment (Ubuntu). In fact my ´phpunit.xml´ file not only was very real, but I had previously selected it with PHPStorm itself !!

Screen Shot 2015-02-12 at 22.33.05

So I deleted that field, unchecked the Use alternative configuration file, and clicked on that last icon on the right… Just because.

Screen Shot 2015-02-12 at 22.03.27

It opened the PHPUnit dialog, which I knew very well already.

Screen Shot 2015-02-12 at 22.38.44

Well, better to look at the remote configuration.

Screen Shot 2015-02-12 at 22.40.24

Then, all of a sudden, I noticed something that I saw many many times before.

Screen Shot 2015-02-12 at 22.44.43

I checked the Default configuration file and clicked on the button on the right. It opened this:

Screen Shot 2015-02-12 at 22.50.27

Tah-Dah!! That’s it: Remote browsing!!

So I selected my ´phpunit.xml´ file.

Screen Shot 2015-02-12 at 22.53.51

Lastly, the dialog for the PHPUnit / Remote configuration looked like this:

Screen Shot 2015-02-12 at 22.54.14

After the click on OK I had this:

Screen Shot 2015-02-12 at 23.06.09

Simple to solve issue this one. Just refresh the Run/Debug Configurations dialog by checking and unchecking the Use alternative configuration file. And we’re ready to go:

Screen Shot 2015-02-12 at 23.09.31

Wow ! Almost there. Notice how the configuration file is working perfectly fine now.

Hm… I saw this piece of path ´/tmp/wordpress-tests-lib´ in the ´bootstrap.php´ file… right.

Screen Shot 2015-02-12 at 23.22.49

What is the value of ´WP_TESTS_DIR´ when I execute PHPUnit from the CLI?

Screen Shot 2015-02-12 at 23.26.54

If I put that in my Run/Debug Configurations dialog?

Screen Shot 2015-02-12 at 23.30.10

Alleluia, alleluia, alleluia.

Screen Shot 2015-02-12 at 23.32.30

(The backtrace is part of my stuff.)

Actually, no! It’s not working YET !!!

I can run and debug, I mean, I can click on the run and on the debug buttons and PHPStorm completes without any difference with respect to what I have when I run PHPUnit from the CLI. When I get reds in the CLI, I also get reds in PHPStorm, and when I get greens in the CLI, I also get greens in PHPStorm.

BUT there is no way to make PHPStorm see my breakpoints !! It simply ignores them.

To be fair, PHPStorm tells me something. There is an x on the breakpoint icon. Hovering, it displays this message: “File path is not mapped to any file path on server. Edit path mappings to fix the problem.” Very cryptic, or very wrong. I remember that I set my path mappings before.

Screen Shot 2015-02-13 at 1.34.29

So??

Clicking on Validate remote environment I got this:

Screen Shot 2015-02-13 at 1.39.07

Well, not exactly, because this is what it’s supposed to look like when it’s fixed 🙂 In my case, the Deployment server was empty and its selection list was empty too, so there was no other option than to click on the top-right button.

Same as above, this is what the Deployment / Connection panel is supposed to look like when it’s fixed.

  1. The Type must be ´SFTP´.
  2. The SFTP Host and Root path must be respectively ´local.wordpress.dev´ and ´/vagrant/www/wordpress-default´ which are the host name and the root directory for the ´wordpress-default´ site.
  3. Upload/download project files / Test SFTP connection… and Browse files on server / Open must work fine.
  4. I decided to set the Name equal to the SFTP Host (easier for me).
  5. User name and Password are both ´vagrant´, as per VVV setup.
  6. Autodetect does not work. For me it got ´/home/vagrant´ but that didn’t work (it didn’t make much sense either…).
  7. Last, but not least: check Save password.

And now the infamous Deployment / Mappings panel.

Screen Shot 2015-02-13 at 1.57.56

Here you could have a couple of entries: if so, remove one and make your panel look like the one above.

  1. Local path must be the path corresponding to the Root path on the Connection panel, thus ´~/dev/wp/vvv/www/wordpress-default´.
  2. Deployment path… must be the path corresponding to Local path, but considering the Root path on the Connection panel, thus ´/´.
  3. Web path… well… I think this one should be the same as the previous one, according to how all other fields are configured.

Lastly, after clicking the Validate Remote Environment / Validate button, I got:

Screen Shot 2015-02-13 at 2.22.23

Is that right? No idea, but at least it doesn’t say it’s wrong either :D. Funny, no OK button here… use Cancel.

Will it work now? NO!

There is yet another step, which (luckily?) I could find in this forum thread: PHPStorm 8 / Vagrant / Xdebug.

The crucial last point is… to tell PHPStorm to mind that whatever we were doing in the last couple of hours we really meant it. And how do we do it? Like this.

Open again the dialog for the PHPUnit / Remote configuration (I showed it above) and click on the top-right button, next to the Interpreter selection box.

Screen Shot 2015-02-13 at 1.17.54

Once again, this is how the Interpreters dialog is supposed to look like when it’s fixed. In my dialog I had selected Remote / Vagrant, but of course, the correct option is… Remote / Deployment configuration. And make sure Test connection really works.

Now… NOW IT DOES WORK !!

Screen Shot 2015-02-13 at 2.38.02

After using it a few hours, here is a better configuration, to have path mappings also for debugging into the WordPress code that my testing bash file downloads and installs: ´bash bin/install-wp-tests.sh test-db root root localhost latest´.

First, Deployment / Connection.

Screen Shot 2015-02-13 at 16.31.35

And then, Deployment / Mappings.

Screen Shot 2015-02-13 at 16.32.03

It works fine because my ´wordpress-default´ site is actually the latest stable version, so anything in ´/tmp/wordpress´ and in ´~/dev/wp/vvv/www/wordpress-default´ corresponds to each other one to one.