Create React App Custom Templates “cannot find module `cra-template-[template-name]`”

After creating a Create React App custom template and trying to create a project from it locally (npx create-react-app cratest --template file:./cra-template-[template-name]), Create React App failed with a cannot find module error:

cannot find module cra-template-[template-name]

Rather than copy/pasting the package.json from the docs, I’d created my package.json with npm init which by default sets the value of the main property to index.js.

Changing main to template.json, as it is in the Create React App docs was the fix.

More for self reference than anything else.

What’s my altitude?

I’ve been wanting to put something like this together for myself for a while and I finally found myself with a couple of hours to spare today. The main reason is that I have to calibrate my GPS watch with my current altitude to be able to get accurate elevation readings using the watch’s barometer. So to be able to quickly get my current elevation based on map data for my location I thought this would be quite handy (at least whenever I’ve mobile reception).

It’s a little overkill but it’s built in React. There are even a couple of token tests in there – mainly for the sake of using Jest snapshots which I’ve not used up until this point.

It’s a simple little app that takes your location from navigator.geolocation.getcurrentposition, fires it off to the google elevation service (for now at least) which returns your elevation in metres.

It was a bit of a ball-ache setting up the Google API, there’s a lot of configuration to be done in their control panel to make sure you can get requests through and initially I just tried hitting their elevation API directly not realising this was blocked by CORS so I had to use their Google maps Javascript API.

I’m going to look for an alternative API to google’s for getting the elevation as they are charging £0.005 per request. It’s fine for now – when I signed up to their cloud platform I was given ~£220 ($300) free credit. I should be good for about 44000 requests which I’d be surprised to ever come close to.

I had a quick look and came across open elevation but their open API was very slow to the point of unusable. I may look at firing up a Digital Ocean droplet to host my own instance but that’s for a later date (if at all). If anyone has any other suggestions, please do let me know.

It’s all hosted on an S3 bucket. I’m still trying to get my head around AWS so it was a nice little project to have a play with that too.

You can take a look at WHAT’S MY ALTITUDE? and see the code on my Github (if you want to try this out make sure you set up your .env file as per the .README).

How much does it cost to row an ocean?

TL;DR £12,026.87 per person

This is slightly cheaper than it should have been because the boat sank so we didn’t have to ship it home (every cloud and all that). Though we could have probably sold a lot of the gear listed below to recoup quite a bit of money back.

We were a team of 4, travelling from the UK to California to compete in the 2014 (first ever) Great Pacific Race. A race from Monterey in California to Waikiki in Hawaii. See here, and here

ocean rowing rescue
Good times

We kept meticulous record of all our expenses so they could be split evenly. I’ve pasted the records below so you can get an idea of where the money went.

Note: Where there are repeating items, it means that multiple people bought the same thing, either for themselves or we needed multiple of them or we all chipped in for the same item. All prices are in GBP (we did a rough conversion before adding US purchases to the spreadsheet to keep things simple)

(See the section below for thoughts on fund raising).

ItemPriceNotes
Race entry£16,950
Boat rental£8750probably should have spent more here!
T-Shirts£55
Boat Storage£90
MISC STATIONERY£10
Event Application£25
White Board£20
Flights£100
Tarpaulin – to protect boat£10
MSR cooker£110
Batteries£125
Gaviscon£13
BP Monitor£16
St John£3
Oximeter£18
Quik Clot£17
Thermometer£2
NHS 3 month prescription charge£29
Misc 1st Aid + Baby wipes£19
SAM SPLINT£7.75
Eye wash pods£3
Blister plaster£3
Plasters£3
Safety pins£1.89
FOIL BLANKET£1.35
Tuf cuts£3
Scissors£2
Hand sanitizer£6
med gloves£5
Scalpels£5
Mag glass£2
Micropore tape£3
Reli pad£2
Tubi grip D£4
tUBI GRIP e£4
tUBI GROP F£4
Finger splints£5
Finger gauze£3
Triangle bandage£1.89
Burn gauze£3.2
Eye pads and bandage£4.5
Alcohol pads£1.79
Non adherent dressing pads£1.57
Poole fuel£35
Padding bandage£7
Ankle support£2.59
Soffban£4.24
Assorted wound dressing£10.5
10x Chapstick£5.6
Arron£250
Snacks£195
watermaker motor, test, fix and demo + FUEL£410
Chocolate£30
Shipping fuel£40
Cushion£14
Shipping£434
Spoons£2.9
Sam’s Food£642.29
Colin’s Food£642.29
Weather books£28
Para Anchor for Daryl and Team£125
Fishing gear for Daryl and Team62.5
Headbands, watches etc£32
Custom broker & printing£19
drogue£60
velcro£13.5
west marine£6
tools£38
Second boat storage£80
1 x Jetboil£78.5
Tow Bar£224
Banner, business cards, stickers£94.56
Number plate for trailer x2£31.98
Fuel – transporting boat to Falmouth£105
Fuel – driving to Cornwall for training£80
Multimeter£6.99
Crocodile clips£2.5
Survival Suits£682.74
More dry bags£65
Active Radar Reflector£330
2x headlamps£62.98
5x packs wheel bearings (for seats)£39.9
2x deck knives£29.64
4x emergency blankets£2.72
sponges scourers duck tape sealant gun unibond rags£30.72
6x Henderson hatches£139.69
radar reflector mount£25
8x wheels£21.34
batteries£90
inverter power adapter£17.99
cigarette lighter extension£5.07
hex key set£4.49
52pc tool set£21.99
small cable ties£2.79
lg cable ties£3.79
screw driver set£12.99
fuel for towing to and from weston super mare£40
Work on boat (absolute aquank)£1138.23Genius play on words there
shipping fuel£25
shipping£434
jetboil gimbal and spare mounting bracket£157.2
food£642.29
solas tape£35
fuel£13
home depot£108.39
west marine£124.65
walgreens£20
sweat bands, watch et al£25
cup holders£6.5
nuts and books£5
multi tool£6.5
1x Jetboil£78.5
Flyers£70
Charts£85
Row grips£80
Sat phone airtime£362
Drogues£170
Paid Daryl for casters£210
Shipping£434
Shakes£142
ROHO cushions£171
Hot sauces for food£8
Water sprayer£13
Car hire 1£239
Car hire 2£47.4
Car hire 3£87
CostCo 1£10.52
CostCo 2£186
CostCo 3£90
Sheepskin cover£18
Berger meal£14.9
Gerry meal£9
Ace Hardware 1£40
Ace hardware 2£10.8
2 gas canisters£7.5
USB to serial cable£17
Ziplock bags£3.6
Duck tape£3.2
Jetboil Flash x1£79.94
Fire Steel Flint Spark£4
Stanley FatMax Retractable Utility Knife£10.45
Plastic Padding Marine Epoxy Filler£15.3
Whistle£1.45
Stanley Blade Dispenser£3.28
Fire Blankets x2£9.43
Strobe Light£31.34
Batteries x5£43.83
Pair of Oars & x4 spare locks£365.73
Manual Watermaker – Jim£78
Diesel – 2 trips ttl£70
Liferaft Hire£320
Hostel – Monterey£34.4
Boat cleaning detergent£10
LED Light for Cabin£10
Shipping UK -> US£434
Adventure Food – 3 for 2 offer – 6x meals£19.5
x10 Pacific Rowers T-Shirts£69.89
Food£642.28
Quest Bars£28.72
Gas£80
Urinal£4.26
Little Snitch£22.72
Mail2Sail£90
REI – Gas for Jetboil£95
Walmart – Peanuts, Ziplock Bags etc.£95
Saline wash, lighters, sharpies£36.66
Safeway – Seasick food£22
Costco – Final stuff£23
Drugstore – Antibac Gel, self Adhering Tape, tape for blisters£11.65
Walgreens – Final stuff£20.82
Home Depot – Buckets etc£38
Boat transportation – Monterey Boat Works£169.86
Boat transportation – Truck driver fees£126.15
Wine for Bob£7
2nd round of boat modifications – Monterey Boat Works£830
Flares & cradle to Hawaii£492.14
LW Radio£31.75
Clothes Pegs£3
Epoxy/Sharpies etc£26
Flights£803
Yachmaster course£365
VHF£115
Sea Survival£135
First Aid£110
Jacket£45
Ipod£70
PLB£200
Trouser£150
Dentist£50
Flights£821.95
Flight Date change£40
Yachtmaster£292.5
plotter and divider£25
Sea Survival£150
VHF£115
VHF Licence£60
Aquapac stuff – Waterproof duffel. SLR case£100.5
First Aid course£99
plb£205
ipod£165
yha membership£15
doctors£20.02
dentist£48
Courses£625
Flight£600
PLB£205
Foul weather Jacket£200
Salopettes£60
Insurance for first aid course£25
Waterproof duffell bag 25l£30
Rain jacket£50
Polyprop top£16
Sunglasses£45
Dentist£275
Flights£747
Yachtmaster£292.5
Plotter/Divider£25
Sea Survival£150
VHF£115
VHF License£60
First Aid£99
PLB£199.95
US Visa – 6 Mths£115
Walkie Talkies£49.99
YHA Membership£15
Dentist£110
Doctor£15
Will£24.99
Gear frm Blacks£14
Yachtmaster postage£9.64
Lithium Batteries x3 for Camera£13.77
Pole for Go Pro£39.99
HH jacket£80

Fund raising

When you see all the boats in the race pictures and footage plastered with sponsor logos, it’s very easy to assume that you’ll be able to get some kind of corporate sponsorship to cover the costs of your row. Even the race literature received before we’d signed up had a stat claiming along the lines of “on average, crews get 70% of their costs covered by sponsorship”.

Sadly, this was not the case for us or the vast majority of the crews in our race of that we have encountered (ocean rowing is a small community).

In our race of 13 crews, we were only aware of 1 who had had major sponsorship – one of the crew’s employers had bought them the boat and paid the entry fee – and 2 others that had been given ~10k each from a sponsor – nothing to scoff at by any means but still a fraction of the overall costs.

If you have immediate connections then absolutely explore these but, if like 99% of ocean rowers, you are between the ages of 16-70, male or female and from a western country your time spent sending emails and making cold calls to North Face, Gillette and every booze company under the sun (you know, because Talisker Whisky sponsosrs the Atlantic race) would be much better spent at a second job behind your local bar or scanning groceries.

I can’t emphasize this enough, we contacted 315 (we kept track of all of this too) companies with witty scripts and tailored emails and recieved 0 positive responses!

It’s hugely demoralising to spend every evening and weekend for months and months writing emails and calling companies and getting zero response.

In the end I took out a large (£10k) loan which I paid off over the next 3 years.

Equipment is, however, possible to come by and I would recommend going this route. We were able to get a job lot of sunscreen, waterproof luggage and various items of clothing. Most of the time they wanted nothing in return. I’m happy to share the names of the (mainly small British) companies privately for those interested.

Space Beer Cave is now available on iOS devices

Space Beer Cave

A few months back, I released my mobile game “Space Beer Cave” into the Google Play Store. I had full intention of following this immediately with an iOS release, but running a 128Gb Macbook Air with no space left on the SSD prevented me from installing the behemoth that is Xcode.

It took about 6 months, but I finally pulled my finger out and cleared some space from my drive and it is now in the App Store. It’s completely free with no ads or catches. You can download it here.

The game was built in a day using the Phaser.js framework and all packaged up with cocoon.js. I use Phaser quite heavily in my day job and absolutely love it for how easy it is to quickly put together something cool.

Now Space Beer Cave is in the bag, I’m currently working on another mobile game, tentatively titled “Space Bookah”. It uses spot gravity and orbital mechanics (to try and sound fancy). The tricky game mechanics are in place and I now just need to flesh it out and create all the levels and story. This is going to be a more complex game than Space Beer Cave so will be a much lengthier process to get the game out around my full time job, social life and training.

I made a mobile game!! Introducing Space Beer Cave

Space Beer Cave

I’m very late posting about this and any readers of my blog (mainly those who I am regularly connected with) will likely already be aware, but I made a game!

In my day job, I’ve been working extensively with the Phaser framework to build a mobile slot game. With my new found knowledge aquired through the game, I wanted to see if it was possible to make a game for Android and iOS in a day. It turns out I could (mostly).

I had a very basic concept for the game. I wanted base the game around mechanics of Flappy Bird ie a basic 1 tap control. This is a proven format for simplicity and enjoyment. I also wanted a retro 8-bit feel with a sci-fi theme. Being a fan of sideways scrollers (and beer) the idea of Space Beer Cave was born.

I started the development of the game on a Sunday, with the laptop on my knees at around 10am in bed, moving to my dining table around lunchtime to continue.

Long story short, after about 10hrs of development (though I’d guess about 40% of this time was spent sourcing and creating the graphics and sound effects) my game was complete and running how I wanted it in my browser and device simulators. Which just left the unknown task of getting it wrapped as an app.

Phaser’s forums are full of talk of Ludei’s Cocoon.js as a common way of packaging HTML5 games and apps into Android and iOS compatible apps so, the following weekend, I set about to make my game an Android app. Though a relatively simple process (and I’m still not exactly sure how I manage to achieve it with SSLing, Zip Aligning and other steps) it took me about 6hrs before I had a .pkg that I could submit to the Play Store. You can download the game to your Android device here.

I’m still to package the game for iOS so iPhone and iPad users will have to wait a while (my excuse at the time was that there wasn’t enough space on my MBA’s 128Gb SSD, though since resetting the machine, I no longer have that excuse).

Performance isn’t as great as I would like it to be. The main culprit for this is the physics applied to the ceiling and floor of the cave. Each of the individual blocks has physics applied to them, where I could get away with just the outer blocks. This is a lot of extra and unnecessary calculating that needs to be done on every update cycle. I will get round to sorting that at some stage 😉

Launching iOS and Android simulators with Node.js

I wanted a script to open iOS and Android device simulators and open their browsers to a URL (my karma.js test URL) so wrote one in node.js, using child_process to execute bash commands to get the job done.

It’s far from perfect and will probably explode if you don’t have devices already installed, but it’s more than suitable for my needs. This is a post for self reference more than anything.

It:

  • Gets the list of all available iOS devices and picks the fourth one in the list (position 3 in the array generated from the text output) (at the time of writing, on my system this is the iPhone 5)
  • Executes the command to load the selected iOS emulator
  • Gets the list of all available Android AVDs (Android Virtual Devices) and selects the first one (unlike iOS devices through xcode, AVDs have to be manually installed in Android Studio so it can be a pain if you don’t have more than 1 set up)
  • Executes the command to load the AVD
  • Starts a loop to check if the AVD has loaded yet – it does this by checking the AVD’s bootanim property. If this is ‘stopped’ we know the device has loaded (I couldn’t find a better way of detecting device load. If anyone has one, please let me know)
  • Once the Android emulator has loaded – we can assume the iOS emulator has also loaded as the AVD always takes much longer to load – we execute the command to unlock the screen on the AVD.
  • We execute the commands to launch the browser to our required URL.
var process 		= require('child_process'),
	url 			= "http://192.168.10.126:9876",
	iosdevice, bootChecker;
 
process.exec('xcrun simctl list', function(error, stdout, stderr){
	var splitRows = stdout.split("\n");
	iosdevice = splitRows[3].split(" (")[0];
	launchIOSDevice();
});
 
process.exec('~/Library/Android/sdk/tools/android list avd', function(error, stdout, stderr){
	var splitRows = stdout.split('\n'),
		deviceObj = {};
	// console.log(splitRows);
	splitRows.forEach(function(row){
		var splitRow = row.split(':');
		deviceObj[splitRow[0].replace(/\s/g, '')] = splitRow[1];
	});
	console.log(deviceObj.Name.replace(/\s/g, ''));
 
	process.exec('~/Library/Android/sdk/tools/emulator -avd ' + deviceObj.Name.replace(/\s/g, ''));
 
	isAndroidEmulatorBooted(function(response){
		if (!response){
			kickOffBootChecker();
		} else {
			unlockAndLoadURL();
		}
	});
 
});
 
function launchIOSDevice(){
	console.log('xcrun instruments -w "'+iosdevice+'"');
	process.exec('xcrun instruments -w "'+iosdevice+'"', function(error, stdout, stderr){})
}
 
function kickOffBootChecker() {
	bootChecker = setInterval(function(){
		isAndroidEmulatorBooted(function(response){});
	},1000);
}
 
function isAndroidEmulatorBooted(callback){
	process.exec('~/Library/Android/sdk/platform-tools/adb shell getprop init.svc.bootanim', function(error, stdout, stderr){
 
		if (stdout.toString().indexOf("stopped")>-1){
			clearInterval(bootChecker);
			unlockAndLoadURL();
			return callback(true);
		} else {
			console.log('loading Android emulator ...');
			return callback(false);
		}
	});
}
 
function unlockAndLoadURL(){
	//unlock the device
	console.log('unlocking the android emulator');
	process.exec('~/Library/Android/sdk/platform-tools/adb shell input keyevent 82');
 
	//gotourl
	console.log('launching the url on android');
	process.exec('~/Library/Android/sdk/platform-tools/adb shell am start -a android.intent.action.VIEW -d ' + url);
 
	console.log('android emulator takes longer to load so we can be sure the ios emu has loaded. launch url there too');
	process.exec('xcrun simctl openurl "'+iosdevice+'" ' + url);
}

Running Karma on iOS and Android Simulators with Appium

At my day job I’ve been tasked with improving our webapp testing strategy. This need came about after a bug was introduced in one of our webapps and not picked up by our current testing process. The bug was related to inconsistencies in the implementation of the Javascript date API between iOS Safari and Android Chrome browsers.

We currently employ a (pretty loose) TDD workflow for our Backbone webapps. Writing our specs with Jasmine and using Grunt as our task runner. This is all integrated into our Jenkins CI. The Jasmin specs are all run though an instance of Phantom in Jenkins via the (one of many) grunt task(s) when we push our code up and release though Jenkins. The build either passes or fails.

From investigating all the various options and frameworks, all signs pointed to Karma. Karma would give us:

  • Seamless integration into our current work flow
  • Immediate feedback – you write a test for a new piece of functionality, hit save and it immediately tells you it’s failed. Write the functionality, hit save and it tells you right away if it passes your test.
  • Testing on multiple real devices

It was a fairly painless process to set Karma up using the official docs and get my tests running on the browsers on my dev environment (Safari, Chrome, Canary, Firefox, Phantom) and there are plenty of resources online for this so I won’t go into this here. What I did have issues with, especially with the lack of resources online, was running my specs on mobile devices.

I came across an iOS launcher plugin that had looked promising to get me going with iOS Safari but it’s not been touched by the developer in a couple of years and after a bit of experimentation discovered that doesn’t appear to work with node versions > 0.10. At the time of writing, I’m working with 0.12.

More research led me to Selenium, a browser automator. From there, I came across Appium which uses the same WebDriver protocol as Selenium but also enables you to automate mobile browsers. Download the Appium desktop app here

To interact with Appium, there is the Karma webdriver launcher (npm install karma-webdriver-launcher).

The idea is pretty simple (but the config is a little fiddly):

  • Make sure you have xcode with iOS simulator installed and the Android Development Studio with at least 1 AVD (android virtual device) set up.
  • Fire up the Appium desktop app to start an Appium server on the machine with the virtual devices (a mac for iOS testing and windows/linux/mac for Android testing). This can be the same machine as you run your Karma instance or a machine accessible by it.
  • Configure your karma.conf.js file to let it know where your virtual devices are
  • Run your tests and Karma will pipe the tests out to the Appium server with the WebDriver API. Appium will then fire up the emulators and run the tests in the specified browsers.

karma.conf.js

Karma will launch the browers to localhost:9876 (or whatever other port you set it to) so if running your Karma instance on a different machine to your test machine, you will want to let your emulated browsers where to go to run the tests. Obviously localhost on the test machine is of no use if localhost there doesn’t have your Karma instance. You can simply do this by specifying the hostname in your karma.conf.js. ie:

hostname: '192.168.10.126',

You will need to declare your custom launchers for your emulators too. In my case, this looks like this:

customLaunchers: {
    'iOS-Safari' : {
        base: 'WebDriver',
        platformName: "iOS",
        deviceName: 'iPhone 5',
        config: webdriverConfig,
        browserName: 'Safari',
    },
    'Android' : {
        base: 'WebDriver',
        platformName: "Android",
        deviceName: 'Android',
        config: webdriverConfig,
        browserName: 'Browser',
    }
},

webdriverConfig just contains the IP and port of the Appium server. In my case:

var webdriverConfig = {
    hostname: '192.168.10.126',
    port: 4723
}

Here is my complete karma.conf.js:

// Karma configuration
// Generated on Thu Aug 06 2015 11:58:03 GMT+0100 (BST)
var webdriverConfig = {
    hostname: '192.168.10.126',
    port: 4723
}
module.exports = function(config) {
 
  config.set({
 
    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',
 
    browserNoActivityTimeout: 1000000,
 
    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine', 'requirejs'],
 
    // list of files / patterns to load in the browser
    files: [
      'test/test-main.js',
      {pattern: 'environment-config.js', included: false},
      {pattern: 'environment-config.json', included: false},
      {pattern: 'js/**/*.*', included: false},
      {pattern: 'test/jasmine/spec/**/*.spec.js', included: false},
    ],
 
    junitReporter: {
        outputDir: 'reports/karma',
        outputFile: undefined,
        suite: ''
    },
 
    // list of files to exclude
    exclude: [
    ],
 
    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },
 
    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress', 'junit'],
 
    // web server port
    port: 9876,
 
    hostname: '192.168.10.126', //points to accessible Karma server
 
    // enable / disable colors in the output (reporters and logs)
    colors: true,
 
    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,
 
    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,
 
    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['iOS-Safari', 'Chrome'],
 
    customLaunchers: {
        'iOS-Safari' : {
            base: 'WebDriver',
            platformName: "iOS",
            deviceName: 'iPhone 5',
            config: webdriverConfig,
            browserName: 'Safari',
        },
        'Android' : {
            base: 'WebDriver',
            platformName: "Android",
            deviceName: 'Android',
            config: webdriverConfig,
            browserName: 'Browser',
        }
    },
 
    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true
 
  })
}

Appium Android Config

Screen Shot 2015-08-20 at 16.34.33

 

You can get the device name and platform version of your installed AVDs with the command:

~/Library/Android/sdk/tools/android list avd

 

This will give you something that looks like this:

Screen Shot 2015-08-20 at 16.39.37

Nothing changed in advanced settings except you must put in your Android SDK path so Appium knows where to find the SDK.Screen Shot 2015-08-20 at 16.34.50

Appium iOS Config

Screen Shot 2015-08-20 at 16.34.58

Appium General Config

The server address here is the address and port of the Appium instance (note in my example, Appium and Karma are running on the same machine under different ports but as mentioned above, you can have the Appium server running on an entirely different machine as long as it is accessible by the Karma service)

Screen Shot 2015-08-20 at 16.35.05

Next steps

I’ve not yet solved the running of iOS and Android tests simultaneously on the same machine but I am currently trying to solve this by setting up a Linux VM on the test machine to run the Android simulator so I can pipe to 2 machines effectively and test on both at the same time.

Also to tackle is running the tests on Chrome on the Android simulator. Currently I’m only testing on the “stock” browser. Desktop Chrome tests will most likely cover me in every single instance, though for completeness I want to get this solved.

Error on “npm install gulp –save-dev”

Using a PC at work so unable to reap the benefits that I get from CodeKit 2 on my mac at home, I’ve mean meaning to get Grunt or Gulp into my workflow.

Always one to jump on a bandwagon, I decided to choose Gulp over Grunt as the newer task runner seems to be gaining traction over the warthog. A sucker for a logo, I also prefer the Gulp logo.

Starting a new project this afternoon, I decided to spend the final hours of the working week getting set up and accustomed to Gulp.

Our dev server at the office is running Ubuntu 11.10 so it was a bit of a struggle updating dependencies to actually get node.js installed. A bit of brainstorming with Sam (read: Sam, the unix genius, took the reins did it for me) and node was installed.

Following the setup instructions I hit a hurdle when trying to install Gulp to my project folder. With the command:

npm install gulp --save-dev

I was getting a lengthy error:

npm WARN package.json [email protected] No repository field.
npm WARN package.json [email protected] No README data
npm WARN package.json [email protected] No README data
npm ERR! Error: EACCES, chown '/path/to/dir/htdocs/node_modules/gulp/package.json'
npm ERR!     at Error (native)
npm ERR!  { [Error: EACCES, chown '/path/to/dir/htdocs/node_modules/gulp/package.json']
npm ERR!   stack: 'Error: EACCES, chown \'/path/to/dir/htdocs/node_modules/gulp/package.json\'\n    at Error (native)',
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   path: '/path/to/dir/htdocs/node_modules/gulp/package.json',
npm ERR!   fstream_finish_call: 'chown',
npm ERR!   fstream_type: 'File',
npm ERR!   fstream_path: '/path/to/dir/htdocs/node_modules/gulp/package.json',
npm ERR!   fstream_class: 'FileWriter',
npm ERR!   fstream_stack:
npm ERR!    [ '/usr/local/lib/node_modules/npm/node_modules/fstream/lib/writer.js:308:19',
npm ERR!      '/usr/local/lib/node_modules/npm/node_modules/graceful-fs/polyfills.js:143:7',
npm ERR!      'Object.oncomplete (evalmachine.:93:15)' ] }
npm ERR!
npm ERR! Please try running this command again as root/Administrator.

npm ERR! System Linux 3.0.0-12-generic-pae
npm ERR! command "node" "/usr/local/bin/npm" "install" "gulp" "--save-dev"
npm ERR! cwd /path/to/dir/htdocs
npm ERR! node -v v0.13.0-pre
npm ERR! npm -v 1.4.26
npm ERR! path /path/to/dir/htdocs/node_modules/gulp/package.json
npm ERR! fstream_path /path/to/dir/htdocs/node_modules/gulp/package.json
npm ERR! fstream_type File
npm ERR! fstream_class FileWriter
npm ERR! fstream_finish_call chown
npm ERR! code EACCES
npm ERR! errno -13
npm ERR! stack Error: EACCES, chown '/path/to/dir/htdocs/node_modules/gulp/package.json'
npm ERR! stack     at Error (native)
npm ERR! fstream_stack /usr/local/lib/node_modules/npm/node_modules/fstream/lib/writer.js:308:19
npm ERR! fstream_stack /usr/local/lib/node_modules/npm/node_modules/graceful-fs/polyfills.js:143:7
npm ERR! fstream_stack Object.oncomplete (evalmachine.:93:15)
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR!     /path/to/dir/htdocs/npm-debug.log
npm ERR! not ok code 0

After A LOT of trouble shooting, we discovered the issue was caused by npm being unable to change of the owner of package.json because the drive the site resided on (a mounted NAS) was in NTFS format. Rather than pull my hair out and try and find a work around, I moved the site to the main drive and everything was gravy.

CodeKit 2 replacing hostname instances with preview URL

After settings up a new project for a quick static build using CodeKit 2, I noticed some strange (and buggy) behaviour an interesting feature.

It appears that when previewing a project through CodeKit 2’s preview, it replaces all instances of the hostname specified in the project’s “External Server Address” with the preview URL.

In my case, I’d set up my host in MAMP Pro as “GWAC”, pointing to this in CodeKit 2’s
“External Server Address”

mampScreen Shot 2014-09-09 at 8.49pm

 

Wherever the string “GWAC” appears in my files, it is replaced with CodeKit’s preview URL. ie:

Screen Shot 2014-09-09 at 8.53pm

becomes:

Screen Shot 2014-09-09 at 8.54pm

 Update

This is expected behaviour. Makes sense:

 

PHP Recursive Function to Build a Site Tree / Site Map

A problem faced fairly regularly is the need to generate a site tree either for a menu or a site map.

Assuming your page structure in your database is along the lines of:

intPageId strPageName intParentId
1 Top Level Page X 0
2 Top Level Page Y 0
3 Child Page X 1
4 Grandchild Page X 3
5 Grandchild Page Y 3

Giving you a visual structure of:

Site tree structure

I’m a developer, not a designer. Don’t judge me

You can achieve this fairly simply in with the following PHP recursive function to get your page structure into an array. Note, I have used the Laravel query builder in this example as the project I wrote this for was a Laravel project, but this can be swapped out with a query builder from whatever framework you are using or with PHP’s PDO (or any of the lesser PHP APIs for working with databases).

function generateSiteTree($startAt)
{
	if ($children = \DB::table('cms_pages')->where('intParentId', '=', $startAt)->get())
	{
		$thisLevel = array();
		foreach ($children as $child) 
		{
			$thisLevel[$child->intPageId] = $child;
			$thisLevel[$child->intPageId]->children = generateSiteTree($child->intPageId);
		}
		return $thisLevel;
	}
 
}
 
$tree = generateSectionSiteTree(0);
 
 
print_r($tree);

That’s it.

Bonus recursive function

The project I wrote this function for required me to be able to reel off the site tree only for the top level parent of the current page, regardless of however deep you currently are.

This is easily achieved with this function:

function recurseToFindSectionParent($pageId)
{
	$current = \DB::table('cms_pages')->where('intPageId', '=', $pageId)->first();
 
	if ($current->intParentId == 0) //we've reached the top of the chain
	{
		return $current->intPageId;
	}
	else //keep going
	{
		return recurseToFindSectionParent($current->intParentId);
	}
}
$topParentID = recurseToFindSectionParent($currentPageId);

You can now feed the ID of the top level parent into the generateSiteTree function:

$tree = generateSiteTree($topParentID);