Jesse Clark’s Blog

Dynamically-generated Cakefile Tasks

Published

Cake is a task-runner for CoffeeScript.

It can be useful to invoke external scripts from cake, if they are written in another language or simply don't fit in the Cakefile. Suppose we have a scripts directory populated with various text-based executables, and we want to automatically define a cake task for each of those scripts, with an appropriate description, like so:

$ ls scripts
build.py
clean.sh

$ cake
cake build                    # build the project
cake clean                    # remove built files

$ cake build --help
build.py: usage info...

We can do this in the Cakefile by reading each script and extracting its first comment, then defining a task with that comment as its description:

# Define tasks for any scripts in the `scripts` directory
for basename in fs.readdirSync('./scripts') then do (basename)->
    return if basename[0] is '.'
    filename = "./scripts/#{basename}"
    contents = fs.readFileSync(filename, 'utf8')
    title = basename.replace(/\..*?$/, '')
    # use the first block comment in the file as its description
    description = ''
    for line, i in contents.split(/[\r\n]+/)
        if line.match(/^#!/) then continue
        if line.match(/-\*- mode/) then continue
        if line.match(/^\s*(#|\/\/|$)/)
            description += line.replace(/^\s*(#|\/\/)\s*/, ' ').trim()
            continue
        # stop reading when we encounter non-comment text
        break

    task(title, description, ->
        # pass command-line arguments directly in to the script
        index = process.argv.indexOf(title)
        args = process.argv.slice(index+1)
        child_process.spawn(filename, args, {
            stdio: 'inherit'
            env: env
        }).on('exit', (code, signal)->
            process.exit(code)
        )
        # prevent `cake` from invoking further tasks based on those arguments
        global.invoke = (->)
    )
Read on →

Conditional Live-Reload with Metalsmith CLI

Published

Metalsmith is a popular static site generator.

The metalsmith command-line interface allows a build process to be defined by a single JSON file. If you want to vary the build process in different situations, you need to write a script that duplicates much of the logic of the CLI. It's possible to avoid duplication by requiring the CLI binary from another script. This gives us an opportunity to modify the config with conditional or non-serializable data:

var config = require('./metalsmith.json');
// provide a formatting function for use in templates
config.metadata.formatDate = function(date, format) {
    return require("moment")(date).format(format || "MMMM Do, YYYY");
}
// only enable live-reloading when requested
if (process.env.AUTO_RELOAD) {
    config.plugins['metalsmith-watch'] = { livereload: 35729 };
    config.metadata.customHTML = '<script src="http://localhost:35729/livereload.js?snipver=1"></script>');
}

// run the standard CLI
require('metalsmith/bin/metalsmith');

This lets us use the same general config for both batch processing and live-reloading, which would otherwise be incompatible.

node build.js                  # terminates after building
AUTO_RELOAD=1 node build.js    # keeps running until killed
Read on →

Bad Joke Eel

Published

Read on →

My phone location history

Published

Extracted with http://petewarden.github.com/iPhoneTracker/

Read on →

Lady Gaga wearing nothing but zippers

Published

Read on →

Homemade record player

Published

p67.mov Watch this video

We rolled a sheet of paper into a cone, attached a sewing needle to the tip with candle wax, and held it up to a record spinning on a bicycle wheel.

Read on →

Candy cigarettes

Published

Read on →

Eye Pad Two

Published

Read on →

I fell off the wagon

Published

Read on →

Whose keychain is this?

Published

I found this under my couch. It has probably been there for years.

 Page 1 | Next Page »