Blog

Blog Archives - 6 Record(s)

Year: '2015' - Month: '6'

24
June 2015

Gavin Pickin

Post Conference - Session Review - How do I Write Testable Javascript?

Conferences, Dev.Objective, Javascript, jQuery, Unit Testing

dev.Objective() has come and gone, and like my Cordova Hooks session, I wanted to write a review of the session, and provide some additional information for those who attended, or those who wanted to attend. The conference was a blast, lots of great info, lots of great people, one of my highlights of the year... so many talented people, and whether you are in the sessions, or in the hallways, there is always something to learn. I submitted this session because although there is a lot of great information on testing, sometimes the hardest steps are the first steps, and most people are live and breathe testing, forget the first few steps, and how many stumbling points they ran across from. Hopefully this session will help everyone get started with Javascript testing.

I did mean to write this sooner, but I got carried away with the deep dives on each of my Cordova Hooks, so I'll try and split the next few posts 50/50 with a little of both. I think the session and the slides gave the users a lot of useful information, I wanted to dive deeper into some of the topics, to make it clear for those who didn't attend, or maybe some of those who did attend, but will not be able to actually put this into action until later, and essentially be starting from ground zero.

I have a Mini Website setup for this session, which includes the Powerpoint, and PDF of the slides, and links to various relevant information, like the github repo for my spaghetti code refactor, and these blog posts, as I release them. Please bookmark my mini site and refer back as needed:

http://www.gpickin.com/devobj2015/testablejavascript/

Session Review

I was really concerned with this session, I knew Javascript is a hot topic and thought there would be a good number of people attending, and I wanted to make sure I delivered value to each and every person. This session was aimed at the real beginner in testing, I hoped that came across well in the description of the session. In short, a lot of pressure, I just hoped it lived up to the expectations.

We were in the Cedar room, alongside the hallway, seating approx 50, I thought if I got half the room, that would be a good session. Before the session was supposed to start, I was passing chairs from the last Panel session held in that room to several people forced to sit on the floor in the back of the room. It was packed, it was awesome to have that many people show up to hear what I had to say... but that just meant more pressure to deliver... a lot of people showing up is one thing, will a lot of people get what they wanted out of it?

The first thing I said to the group, after offering the extra chairs, I asked what levels of experience the audience had with Testing Javascript. It was an expectedly low number of hands shown. I expanded that to see how many test their server language code, and more raised their hand, but still surprisingly low number of developers. I think a lot of people realize the value of testing, but its hard to START testing, and hopefully this session helped the attendees to get started.

1 hour later, the end of the session was upon us, and I was still trying to squeeze in a little more, we heard the clattering of dishes in the hallway, so I said :

"Thank you to everyone who came, as you can hear, its lunch time already, if you want to head to lunch already, I completely understand, if you want to wait to see my show you how to get Grunt Watch and Grunt Jasmine running in Sublime, I'd love to keep going and show you that too."

To my surprise, only 2-3 got up and headed to lunch, but the rest stayed and listened as I continued. That was the true sign to me, that I had given a good session, and they wanted to hear more. Even the sounds and smells of lunch were not enough to drag them out of my session. Of course, they might have all being really "NICE", but I don't believe that many people would put my feelings ahead of their own lunch lol.

After showing the Sublime Package Control Package Manager in action, getting Grunt and Jasmine running in minutes, we all went to lunch. While packing up, a couple of attendees came up, and chit chatted with me, and even installed the Grunt Plugin right there while I bagged up my cables. We carried our conversations over to lunch, and several people hit me up in the hallways, and on twitter to go a little deeper. If you have questions, feel free to reach out, I'm not the best, but I'll do what I can to help.

I was really happy with the way the session turned out, and I think that everyone got some value out of the session. I know the quick comparison of the testing frameworks was valuable, and showing the tools like Grunt Watch and Grunt Jasmine vs Node Jasmine really made it seem more approachable.

One of the big pieces of value, IMHO, was taking the Spaghetti Code example, and going through step by step, to boil the code down, and slowly make it better and better. Slowly cleaner up the code, extracting functions, adding tests, step by step. The process even showed some missteps so you could see how I changed my mind halfway through the process. I know its not perfect code, but its more about the process, and I wish I had the opportunity to work at this level with people on a regular basis. 

I have shown that to members of our user groups, and I want to break this down in a blog series, or maybe even a screencast, because it's really good to see how people break down their code, how they refactor. Let me know if you would like to see this? The code itself, is on my mini site, and on my github, each step, is broken down into branches, so you can check in and check out branches, to see whats changed.

Thanks to all of you who attended, I enjoyed giving the talk, and hope you all got what you wanted out of it.
Bookmark my mini site, as I add more content to my blog, whether it be blogs, or videos, I'll make sure to update that mini site page so all the relevant content is there.

Mini site: http://www.gpickin.com/devobj2015/testablejavascript/

18
June 2015

Gavin Pickin

ColdFusion 12 Pre-Release - Are you ready for the next version of ColdFusion?

CFML Language, CFML Server, ColdFusion Install Repo

For those of you who missed Adobe at dev.Objective()... don't worry, Adobe is going to be all over the map now... as they are starting the Pre-Release program for ColdFusion 12. They opened a survey so if you want to get in on the Pre Release Program, you can do so today.

If you have not been on a pre-release program before, its a pretty cool experience. You can fill out a survey to go on the list, and if you are selected (not sure how choosey they really are) then you get a special login to the Adobe Beta area. You will have access to the pre-release, a special forum for discussing the product, and any bugs / issues with the app, and might be able to shape the software on the way out the door. Of course you will need to agree to a NDA, to make sure you don't leak top secret information, but its an interesting process, and if you have some time, or are curious, I recommend you get involved.

This version is supposed to be DEVELOPER centric, with focus on making everything better for the developer, after CF11 was all cfclient, and a few language constructs thrown in there for fun.

If you are interested, jump in and fill out the survey here: https://www.surveymonkey.com/r/LPS7YYP
ColdFusion's Blog post about it here: http://blogs.coldfusion.com/post.cfm/pre-release-for-the-next-version-of-coldfusion

17
June 2015

Gavin Pickin

Cordova Hooks - Deep Dive into my Jasmine Unit Test Hook written in NodeJS

Android, Cordova / Phonegap, Dev.Objective, Ionic, IOS, Javascript, Mobile Development

After my session about Dev.Objective() called “Getting your Hooks into Cordova - Workflows and Build Helpers”, I decided I should do a deep dive into each of my Hooks I discussed in my presentation, and today we’ll look at my Jasmine Unit Tests Hook, written in NodeJS.

My presentation mini site is available here on my blog at http://www.gpickin.com/devobj2015/cordovahooks/

As I add more blog posts about this topic, I will continue to update the mini site.
The minisite has links to the gists of all the hooks I had, the slides and links to the few blog posts I did find on the topic.

For more information on what hooks are, please refer to my Session Review and Overview here: Post Conference - Session Review - Getting your hooks into Cordova- Workflows and Build Helpers

The next hook in my hybrid mobile workflow with Cordova, is my Jasmine Unit Test hook.

This hook is setup to run in the BEFORE_BUILD.

Unit testing is one of those Development Lifecycle phases that most people are aware of, majority of people know why they should do it, and fewer people actually do it. I believe there is a invisible barrier that most people hit when deciding to implement Unit Testing, and the main reasons are it is time consuming and because people think its hard. Running your unit tests automatically, takes a lot of the “HARD” and “TIME” out of it.

My other session I gave at dev.Objective() was “How do I write Testable Javascript” so Unit Testing is up my alley you might say. Our organization fights some of those same battles, but with Cordova and Hooks, I saw the perfect opportunity to automate the unit tests on every build. Link to that presentation below.

I searched and searched, but I couldn’t find any existing Jasmine Hooks out in the world already, so I decided it would be a good idea to dive into Node, and try and figure out how it all works and write my own. 

Was that a good idea? 
At first, I thought it was, then for a while, I was unsure, but eventually, I got it to work, and I felt so much better about getting my hands dirty, diving down the rabbit hole that is NodeJS.

There are essentially 2 ways to run Jasmine, Jasmine which is the Node version, and then Jasmine with PhantomJS, a headless browser to give you access to a DOM etc, which is required for DOM related tests etc. 

This hook is setup with Node Jasmine, so its perfect for Service level objects, testing business logic. If you want to test more, you would need the PhantomJS version, which I will have to write later and add as a follow up post.

First I installed the NPM module
#: npm install Jasmine

That creates a folder called node_modules, and installs Jasmine, and its dependencies, if you open that folder up, it should look something like this.

SCREENSHOT 2

Then you have to init Jasmine to create the folder structure which has the spec files, and the config file, to tell Jasmine what files to process.
#: jasmine init

SCREENSHOT 1

As you can see from the sceenshot, which includes a support folder, than has the Jasmine.json. This is what it has in it

{
    "spec_dir": "spec",
    "spec_files": [
         "**/*[sS]pec.js"
    ],
    "helpers": [
        "helpers/**/*.js"
    ]
}

 

Jasmine’s convention is that it runs any JS file in the spec directory (or subdirectory) that has the suffix spec.js or Spec.js. You can also place helpers in the helper folder, or any of its subdirectories. You can edit this as needed, and then when you run Jasmine from the CLI, or through Cordova as a hook, it will run the files specified in this file.

Next, I tracked down how the module actually was run, copied the code into a hook, and edited it, until I managed to successfully pass all the right information, and have Jasmine output the information I needed. Lucky for you, I figured that all out, so here is the code, below.

https://gist.github.com/gpickin/e6ce95a1d1bf7c98da30

#!/usr/bin/env node
 
var path = require('path');
var Command = require('../../node_modules/jasmine/lib/command.js');
var Jasmine = require('../../node_modules/jasmine/lib/jasmine.js');
var thePath = path.join( path.resolve(), 'www');
 
var jasmine = new Jasmine({ projectBaseDir: thePath });
var examplesDir = path.join(__dirname, '..', 'node_modules', 'jasmine-core', 'lib', 'jasmine-core', 'example', 'node_example');
var command = new Command(thePath);
 
console.log('Running Jasmine Tests');
command.run(jasmine, [] );

 

Lets break it down.

#!/usr/bin/env node

Cordova Hooks always need to know what type of Hook it is, so this tells Cordova its a node hook.

 

var path = require('path');

Require the path module, so we can use it for folder / path functions.

 

var Command = require('../../node_modules/jasmine/lib/command.js');

Require the command module, so we can run the Jasmine core from the command line. This takes care of input, output to the console etc.

 

var Jasmine = require('../../node_modules/jasmine/lib/jasmine.js');

Require the Jasmine core module… this does all the hard work.

 

var thePath = path.join( path.resolve(), 'www');

We set a variable with the path to where our BASE folder should be, in cordova projects, its inside the WWW folder.

 

var jasmine = new Jasmine({ projectBaseDir: thePath });

Next we initialize a new jasmine runner, passing in the project base path.

 

var examplesDir = path.join(__dirname, '..', 'node_modules', 'jasmine-core', 'lib', 'jasmine-core', 'example', 'node_example');

I assume this is defining the examples directory, not sure why I left this in here, if its not necessary. I should remove it and see what happens

 

var command = new Command(thePath);

Create a new Command Runner.

 

command.run(jasmine, [] );

Run jasmine with the command runner.

Not too complicated, but there was a few little things I had to tweak to get it working.
Now, all you have to do is install Jasmine, copy this into a hook file in the before_prepare folder, make it executable, and then every time you run build, it will run.

After my JS Hint hook has fired, and succeeded, then my Unit Tests run with Jasmine. If the unit tests all pass, the build continues, otherwise the process ends, with relevantfailing test information. Success or failure, Jasmine outputs number of specs, execution time etc.

Failures

Running command: /Users/gavinpickin/Dropbox/Apps/myapp/hooks/before_prepare/05_jasmine.js /Users/gavinpickin/Dropbox/Apps/myapp
/Users/gavinpickin/Dropbox/Apps/cstctraining/www
Running Jasmine Tests
/Users/gavinpickin/Dropbox/Apps/myapp/www/spec/attendeeServiceSpec.js

Started
FFFFF

Failures:
1) Player should be able to play a Song
  Message:
    ReferenceError: newAttendeeService is not defined
  Stack:
    ReferenceError: newAttendeeService is not defined
        at Object.<anonymous> (/Users/gavinpickin/Dropbox/Apps/myapp/www/spec/attendeeServiceSpec.js:6:23)
  Message:
    ReferenceError: player is not defined
  Stack:
    ReferenceError: player is not defined

 

Success

Running command: /Users/gavinpickin/Dropbox/Apps/myapp/hooks/before_prepare/05_jasmine.js /Users/gavinpickin/Dropbox/Apps/myapp
/Users/gavinpickin/Dropbox/Apps/myapp/www

Running Jasmine Tests
/Users/gavinpickin/Dropbox/Apps/myapp/www/spec/loginServiceSpec.js

Started
…

3 specs, 0 failures
Finished in 0.006 seconds


I need to submit this hook/module to the nom registry, I will post about that process when I do that.
I will also write a hook for a PhantomJS version, to allow you to write tests that call browser DOM or interact with libraries like jQuery.

Link to the gist on github: https://gist.github.com/gpickin/e6ce95a1d1bf7c98da30
More information on Jasmine: http://jasmine.github.io/
Link to my Cordova Hooks mini site http://www.gpickin.com/devobj2015/cordovahooks/
Link to my “How do I write Testable Javascript” Presentation Mini Site http://www.gpickin.com/devobj2015/testablejavascript/

10
June 2015

Gavin Pickin

How to solve Authorize.net CFHTTP SSL issues with CFML Engines like Railo and Lucee

CFML Language, Lucee, Migrating to Railo, OpenSSL, Server Admin, Techie Gotchas

There was a lot of noise about CFHTTP not working when Authorize.net was planning on updating their SSLs in May this year. Will Genovese, one of the good people at CF WebTools, posted ‘ColdFusion 8 & 9 CFHTTP still works with Authorize.net’  about early testing and confirmed that ColdFusion 8 and 9, and even some of their customers using 7 had this working. Of course, just the other day, some of my customers, running on Railo / Lucee started to see some issues. This is how I solved those problems.

If you have dealt with SSLs in ColdFusion before, you know its a pain in the butt, dealing with the key tool, importing into the right keystone. If you are using ColdFusion, you might not know that there is a great CF Admin extension, called Cert Manager which makes life easy. It gives you a simple UI to see all the certs, and makes working with them easy.
CertMan from RiaForge - http://certman.riaforge.org/

If you are on Railo / Lucee, you’ll know they have a really slick SSL Certificates page. You can put in the domain name of the Cert you need, and it will go out to the inter webs, and suck down the Cert for you. When I tried to do this with  secure.authorize.net cert, Railo just times out eventually.

The problem is… if there is an issue with something up the chain… then how do you fix it? You have to go back to the Keytool and the Keystore.
Its not hard to actually add a key to the keystore, but where is the keystore, which key do you need, and how do you find it?

I use SSL Shopper’s SSL checker tool for all my SSL testing, it makes sure the chain is complete, and gives you a good number of details at the same time. Using this tool in the past, I debugged some strange browser issues, some that complain about certs, while others wouldn’t. This tool helped me identify which certs needed corrections, and I could easily fix them.

https://www.sslshopper.com/ssl-checker.html#hostname=secure.authorize.net

I have more information in previous posts located here: ‘Techie Gotcha - SSL Certificate Problems with Apache and Issuer Chains” 

Using this tool, I knew which certs to download and from who.

Since the SSL Chain are all Entrust SSLs, I downloaded them from their website, here: Entrust's Website http://www.entrust.net/developer/
This was left in the comments of the post from Wil, which made it even easier to find. I downloaded all of the certs in the chain, but most of them were already in the keystone, so I’ll save you the headache.

Download the entrust_ec1_ca.cer

Once you have that, and its on your server, you need to locate your keystore.
For Railo, it seemed like it was in {railo}/jke/jre/lib/security/cacerts
Depending on your install, it might be elsewhere, but if you can find your JRE folder, its usually relative to that in lib/security/cacerts

When you run the command below, it will ask you for the password, if you haven’t changed it, the password will be ‘changeit’, so you should probably do that sometime soon.

Run the command (on CentOS i needed to be root since root owned the files)
keytool -import -keystore /pathto/railo/jdk/jre/lib/security/cacerts -alias entrustec1ca -file /path/to/your/cert/entrust_ec1_ca.cer

  1. Enter your password when prompted
  2. Confirm yes, you want to trust this cert, and it will be added to the keystore.
  3. Once added, you can return to Railo Admin, and try to get the SSL cert again, and this time, you should see it will not time out, and it will show something like this.

 

Again thanks to Will Genovese’s post at: http://www.trunkful.com/index.cfm/2015/5/26/ColdFusion-8--9-CFHTTP-still-works-with-Authorizenet which got me started on the right path.

Hope this helps anyone else out there battling this issue.

04
June 2015

Gavin Pickin

Cordova Hooks - Deep Drive into my JS Hint Hook - written in NodeJS

Android, Conferences, Cordova / Phonegap, Ionic, IOS, Javascript, Mobile Development, Node.js

After my session about Dev.Objective() called “Getting your Hooks into Cordova - Workflows and Build Helpers”, I decided I should do a deep dive into each of my Hooks I discussed in my presentation, and today we’ll look at my JS Hint Hook, written in NodeJS.

My presentation mini site is available here on my blog at http://www.gpickin.com/devobj2015/cordovahooks/

As I add more blog posts about this topic, I will continue to update the mini site.
The minisite has links to the gists of all the hooks I had, the slides and links to the few blog posts I did find on the topic.

For more information on what hooks are, please refer to my Session Review and Overview here: Post Conference - Session Review - Getting your hooks into Cordova- Workflows and Build Helpers

The next hook in my hybrid mobile workflow with Cordova, is my JS Hint hook.

This hook is setup to run in the BEFORE_BUILD.

Have you ever been working on your app, save your progress, switch to the command line, Cordova build your platform, the in xcode run the app, wait for the app to push to your phone, then try out the new feature, it doesn't work, so then you spend 15 minutes debugging the problem to realize you missed a semi colon, or a curly bracket?

I have, way too many times, so I decided I wanted to Lint my code... and save myself the brain numbing frustration of an absent minded bug.
Note: Of course I could have my IDE do that, but this is nice to catch any file, not just the ones I am working in.

I have used JS Lint before, Douglas Crockford's Linter for Javascript, but like me, someone else decided that it went too far. JS Hint was born from JS Lint, with the idea that syntax and best practices be the focus, not style. I used JS Hint whenever possible.

More info on JS Hint: http://jshint.com/about/
More info on JS Lint: http://www.jslint.com/

This was my first look into Cordova Hooks, I wanted to find a way to run JS Hint on every build I ran, so I could catch those silly mistakes, and save debugging for the real bugs. So I found this article by Nic Raboy called ‘Minifying Your App’s Source Code’ located on the Ionic blog http://blog.ionic.io/minifying-your-source-code/

I followed the instructions, and had the npm modules installed, and the hook installed, and once I remembered the magic trick with Cordova hooks, make them executable, my App was hinting my code on every build, in a few minutes.

You need two modules for this hook, jshint and async. You can install them like this:

$ npm install jshint
$ npm install async

 

Then you need to download the hook file from the Gist below, and put the file in the before_build folder (change the number to change priority), make it executable, and then modify the folders you would like to Hint on every build.

What does a successful build look like?

$ cordova build ios

Running command: /Users/gavinpickin/myapp/hooks/before_prepare/02_jshint.js /Users/gavinpickin/Dropbox/Apps/myapp
Linting www/js/services/attendeeDAO.js
Linting www/js/index.js
File www/js/services/attendeeDAO.js has no errors.
-----------------------------------------
Linting www/js/services/attendeeService.js
File www/js/index.js has no errors.
-----------------------------------------
File www/js/services/attendeeService.js has no errors.
-----------------------------------------
Linting www/js/services/attendeeSyncService.js
File www/js/services/attendeeSyncService.js has no errors.

-----------------------------------------
Linting www/js/services/auditDAO.js
File www/js/services/auditDAO.js has no errors.

 

What does a bad build look like?

$ cordova build ios

Running command: /Users/gavinpickin/Dropbox/Apps/myapp/hooks/before_prepare/02_jshint.js /Users/gavinpickin/Dropbox/Apps/myapp
Linting www/js/services/attendeeDAO.js
Linting www/js/index.js
Errors in file www/js/services/attendeeDAO.js
1:1 -> Expected an assignment or function call and instead saw an expression. -> funct ion newAttendeeDAO() {
1:6 -> Missing semicolon. -> funct ion newAttendeeDAO() {
1:7 -> Expected an assignment or function call and instead saw an expression. -> funct ion newAttendeeDAO() {
1:10 -> Missing semicolon. -> funct ion newAttendeeDAO() {
1:27 -> Missing semicolon. -> funct ion newAttendeeDAO() {
-----------------------------------------
File www/js/index.js has no errors.

 

The URL for this Hook is https://gist.github.com/gpickin/04a7acfc4907f3ed27f6

Remembering, I didn’t write this one, lets look at the file. If you don’t care about how it works, no problem… just edit the foldersToProcess array, and ignore the rest.

#!/usr/bin/env node

First we need to let Cordova know this is a NodeJS file.

 

var fs = require('fs');
var path = require('path');
var jshint = require('jshint').JSHINT;
var async = require('async');

Next, we include the modules we’ll be using. fs filesystem and path are the standards, but we also list the 2 we installed for this hook here as well.

 

var foldersToProcess = [ 'js', 'js/services' ];

This is where we set the folders we want JS Hint to lint. This hook currently is a flat directory only hook, so if you have subfolders, like services in this example, under js, we need to specify them both. We could use a lesson learned from a previous hook to recursively walk the file system, but this is not modified (at this time).

 

foldersToProcess.forEach(function(folder) {
    processFiles("www/" + folder);
});

This starts the process, looping over the foldersToProcess array, and for each of those elements, it calls processFiles with the full path.

 

function processFiles(dir, callback) {
    ...
}

Next is the processFiles function, which runs through the directories we want to process, and then calls another function from there, ‘lintFile()’ that actually lints the file, and outputs the associated console debugging info. Lets look into processFiles more

 

var errorCount = 0;
fs.readdir(dir, function(err, list) {
    if (err) {
        console.log('processFiles err: ' + err);
        return;
    }
    … //continue in the processFiles()
}

Here we set an error counter, and then read the directory in question, if there is an error, we log it to the console, and return, otherwise keep processing this directory.

 

async.eachSeries(list, function(file, innercallback) {       //1
    file = dir + '/' + file;                                 //2
    fs.stat(file, function(err, stat) {                      //3
        if(!stat.isDirectory()) {                            //4
            if(path.extname(file) === ".js") {               //5
                lintFile(file, function(hasError) {
                    if(hasError) {                           //6
                        errorCount++;
                    } 
                    innercallback();
                });
            }
            else {
                innercallback();
            }
        }
        else {
            innercallback();
        }
    });
}, function(error) {            // 7
    if(errorCount > 0) {
        process.exit(1);
    }
});
  1. Next, we loop through the list of files in that directory.
  2. Get the full path of the file
  3. Get the file system stats using fs.stat() on the full path
  4. If it is a directory then call the callback to exit to the loop
  5. If it is not a directory, and the file extension is js, lint the file by calling lintFile() on the file.
  6. If the callback is called with an error, we add to the error count, regardless we call the inner callback to exit to the loop
  7. When the async series is done… if there is an errorCount of more than 0, then we will exit the process… which means this hook stops the WHOLE build process.

Now, lets look into the lintFile()

function lintFile(file, callback) { 
    console.log("Linting " + file);                    //1
    fs.readFile(file, function(err, data) {            //2
        if(err) {                                      //3
            console.log('Error: ' + err); 
            return; 
        } 
        if(jshint(data.toString())) {                  //4
            console.log('File ' + file + ' has no errors.');      //5
            console.log('-----------------------------------------'); 
            callback(false);                           //6
        } 
        else {                                         
            console.log('Errors in file ' + file);     //7
            var out = jshint.data(),                   //8
                errors = out.errors; 
            for(var j = 0; j < errors.length; j++) {   //9
                console.log(errors[j].line + ':' + errors[j].character + ' -> ' + errors[j].reason + ' -> ' + errors[j].evidence); 
            } 
            console.log('-----------------------------------------'); 
            callback(true);                            //10
        } 
    }); 
}
  1. First output to the console we are linting this file
  2. Use fs.readFile to read the file. 
  3. Following the Node convention, if there is an error, the first argument will not be null, and we want output an error and exit early.
  4. Next, we hint the file by calling JSHint on data.toString()…
  5. If the return of that function is true, we output there was no errors,
  6. Then we call the callback with false, saying no errors found
  7. If the result of linting the file is false, then we output that there is an error in the file. 
  8. We pull the JSHint data out with jshint.data()
  9. Then we loop through the errors, outputting the errors to the screen.
  10. We finish by calling the callback with true, telling the hook we have an error, and then the hook knows to output the errors, and stop the cordova build (as we discussed at the end of the async earlier).

I love this hook, and I will use it in all my projects for all the time it saves me. Its not just the automatic bug identification, it saves all the time that getting the app takes to get to your phone. Awesome. 

Thanks again to those who contributed to this... and thanks for reading

Nic Raboy : ‘Minifying Your App’s Source Code’ on the Ionic Blog http://blog.ionic.io/minifying-your-source-code/
The URL for this Hook is https://gist.github.com/gpickin/04a7acfc4907f3ed27f6
More info on JS Hint: http://jshint.com/about/
More info on JS Lint: http://www.jslint.com/

01
June 2015

Gavin Pickin

Cordova Hooks - Deep Drive into my Cordova Cleanup Hook - written in NodeJS

Android, cfObjective, Conferences, Cordova / Phonegap, Dev.Objective, Ionic, Javascript, Mobile Development, Node.js

After my session about Dev.Objective() called “Getting your Hooks into Cordova - Workflows and Build Helpers”, I decided I should do a deep dive into each of my Hooks I discussed in my presentation, and today we’ll look at my Cordova Cleanup Hook, written in NodeJS.

My presentation mini site is available here on my blog at http://www.gpickin.com/devobj2015/cordovahooks/

As I add more blog posts about this topic, I will continue to update the mini site.
The minisite has links to the gists of all the hooks I had, the slides and links to the few blog posts I did find on the topic.

For more information on what hooks are, please refer to my Session Review and Overview here: Post Conference - Session Review - Getting your hooks into Cordova- Workflows and Build Helpers

The next hook in my hybrid mobile workflow with Cordova, is my Cordova Cleanup hook.

This hook is setup to run in the BEFORE_BUILD.
This hook is written with NodeJS, so it will work on MacOSX and Windows, and this hook is useful with iOS, Android, Windows, any Hybrid Mobile Platform. 

This script is based upon one originally posted on Nic Raboy’s Blog called “Hooks With Apache Cordova Mobile Applications
I modified this hook to make it behave slightly differently, which I will explain as we walk through the code.

This is a simple script that cleans up those annoying operating system files like .DS_Store and Thumbs.db etc. It could be configured to clean up more files by adding to a simple switch statement.
Lets dive into the code.

The hook is found online as a github gist: https://gist.github.com/gpickin/218068bf525a8f02d2a0

 

#!/usr/bin/env node

First line, we need to learn the Cordova CLI build system know how to interpret this hook. This line shows it is a Node script. Remember to make the script executable, otherwise your hook will not be run.

 

// Original : https://blog.nraboy.com/2015/01/hooks-apache-cordova-mobile-applications/
// Modified by Gavin Pickin - 05/09/15

Next we have a comment, showing where I got the original code, and when I last modified this script. 

 

var foldersToProcess = [
    "js",
    "css"
];

Here we have an array of folders we wish to search for the files we wish to clean up. Originally this script was written to only check those directories, but I made a simple change, to recursively search through subfolders as well. We will see how that works later in the file.
To use this hook, you do not need to edit anything below this point, unless you are adding more FILE types to search for.

 

console.log('-----------------------------------------');
console.log('Cordova Cleanup Starting');

Next we output to the console so when debugging the build, we can see when this hook starts processing.

 

foldersToProcess.forEach(function(folder) {
    processFiles("www/" + folder);
});

Now we want to loop over each element in the foldersToProcess array, calling a function for each element, prcoessFiles, passing in the path we want to process the files for.

 

function processFiles(dir) {
    ...
}

The rest of the file is a big function, the meat of the file… processFiles function.

 

fs.readdir(dir, function(err, list) {
    …
}

First we read the directory passed into the function… using fs.readdir, passing in a function which is called with Node’s convention, err and data… in this case, the list of files.

 

if(err) {
    console.log('processFiles err: ' + err);
    return;
}

Again, a Node convention, check for an Error, if you find one, perform an early exit, otherwise, continue through the function.

 

list.forEach(function(file) {
    ...
}

Next we loop through each element of the file list from the directory. 

 

file = dir + '/' + file;
fs.stat(file, function(err, stat) {
    ...
}

Next, we save the full path to the file in a var ‘file’ and then pass that into fs.stat().
fs.stat() returns a Stat object with information of the file system object. More information can be read on that here. https://nodejs.org/api/fs.html#fs_class_fs_stats

 

if(!stat.isDirectory()) {
    …
}
else {
    processFiles( file );
} 

Next, we check if the element in the directory we’re looping over is not a directory. If it is a directory, we recursively call processFiles(). This is one of the additions I made. If this element is not a directory, then we enter the IF statement (shown below).
 

switch(path.basename(file)) {
    case ".DS_Store":
        fs.unlink(file, function(error) {
            console.log("Removed file " + file);
        });
    break;
    case "Thumbs.db":
        fs.unlink(file, function(error) {
            console.log("Removed file " + file);
        });
    break;
    default:
        //console.log("Skipping file " + file);
    break;
}

Here is the switch that does the work. The expression is path.basename(file) which gets the file name (separated from the path)… and if it matches one of the cases, it removes the file from the system. This could be made to perform any action you require on a file to file basis. Switch statements are fast, so that is simple way to solve this problem, but you could alter this to use regex, or something similar.

That’s that. Simple script, but nice little addition to the work flow. 

During the presentation, we had a pretty open conversation, and we discussed other uses of a script like this. We thought maybe you might keep your PSD files in your images folder, alongside the jpg and png files, for simplicity, and sharing, but you obviously don’t want to deploy hundreds of mb of PSD files. Maybe a hook like this would be helpful in this case?

This runs before build so this happens in the source folder, so when you delete the files, it removes the files from the source www folder, before the build process moves the files to the relevant platform folders. So if you delete psd files it will delete the PSD files from the source www folder.
If you set the hook file in a after_build, you can target the platforms www folder and clean up the PSD files after the fact, and leave the PSDs in the source www. This is exactly how the Uglify hook works (that we will look at in another blog post), it does not minify and uglify anything from the source files, only the files in the platform folders.

The hook is found online as a github gist: https://gist.github.com/gpickin/218068bf525a8f02d2a0

Any ideas on other uses for a hook like this? Let me know.
Have a good one.

Blog Search