Home

Awesome

Refract

Build Status

Refract is a command-line utility to reshape objects to a template.

// object
{
    "id": "5768", 
    "customer": "Stijn Debrouwere", 
    "subtotal": 200, 
    "handling": 10 
}
// template
{
    "id": "parseInt id", 
    "uid": "dasherize customer", 
    "tax": "subtotal * 0.21", 
    "total": "subtotal + handling + tax"
}
// result
{
    "id": 5768, 
    "uid": "stijn-debrouwere", 
    "tax": 42, 
    "total": 252
}

Think of it as destructuring assignment on steroids.

Installation

Install refract using the NPM package manager which comes bundled with node.js.

npm install refract-cli -g

This will make the refract command globally available on your system. You can also use the refract-cli module in node.js, as detailed below.

Usage

Refract takes data and modifies it according to a template.

Templates are JSON or YAML objects containing keys and expressions. Each expression will be evaluated and the resulting object will be returned.

There are many ways to specify a template:

optiondescriptionexample
--templatea path to a YAML or JSON template file--template template.yml
--stringa string with interpolations--string 'hello #{name}'
--template-stringa template string--template-string 'head: uppercase title'
--applya list of mapping functions--apply title:uppercase

While --template is recommended for most purposes, --apply works really well for quick refractions, as do --string and --template-string. They save you the bother of needing to have a refraction template on disk.

--template

refract order.json --template template.json

--string

With --string you pass a simple string. Output can become dynamic by including placeholders for interpolation.

refract bio.json --string 'Hello #{name}! You are #{age - 20} years over twenty.'

--template-string

With --template-string, you pass the actual template as a string of YAML of JSON instead of a path to a template file.

refract order.json --template-string 'total: subtotal + tax'
{
    "total": ..., 
}

--apply

Use --apply to apply a helper function to a field. The helper function will receive the current value of the field as its only argument.

refract order.json --apply total:parseInt,title:titleize

Mapping fields with --apply is considerably less flexible than using templates, but when all you need to do is clean up or modify a couple of fields in a straightforward way, it does the trick.

Use --update to keep fields that were not transformed in the output.

Expressions

Refract will evaluate every value in the template object as CoffeeScript code, or if that fails, as a string with string interpolation. Every expression is then replaced with its result.

Variables:

book.author

Math:

handling + subtotal * 1.21

String interpolation:

Dr. #{name.first} #{name.last}

Plain strings:

Hello world.

Explicit strings:

'Hello world.'

Arrays:

[1, 2, 3]

Methods:

participants.join ', '

Functions:

titleize permalink

Because, in this limited environment, almost all valid JavaScript is also valid CoffeeScript, you can get away with just writing expressions in JavaScript if you'd prefer:

{
    // CoffeeScript
    "slug": "(slugify title).toUpperCase()", 
    // JavaScript
    "slug": "slugify(title).toUpperCase();"
}

Helpers

Available context includes the object itself as well as numerous helper functions loaned from underscore and underscore.string. Please take a look at their documentation to find out which helper functions are available to you.

Because the keys of your data object can sometimes override helpers (for example, you might have a where key that clashes with underscore's where function), underscore functions are also available under the _ namespace, underscore.string functions under the _.str namespace.

// template that calls titleize with and without namespacing
{
    "slug": "dr-livingstone-i-presume", 
    "title": "titleize slug", 
    "alternative-title": "_.str.titleize slug"
}
// resulting in two identical titles
{
    "title": "Dr Livingstone I Presume", 
    "alternative-title": "Dr Livingstone I Presume"
}

Additional features

Merging the refraction with the original object

By default, only the evaluated fields from the template object will be included in the output. Specify --update if you would like to merge this output with the original object. If the template should only be used to fill in missing values, instead use --missing.

Editing JSON in-place

Refract prints to stdout. If you'd like to modify the original JSON file instead, use --in-place. For obvious reasons, this only works if a file was specified on the command line rather than piped in over stdin.

Iterating over multiple objects

Refract can also iterate over an array of objects. Use the --each option.

Normalizing keys

Renaming the fields of an object is easy:

// object
{
    "firstName": "Belle"
}
// template
{
    "first-name": "firstName"
}

However, if you'd like all fields of an object to adhere to the same standard, this field-per-field approach gets cumbersome really fast. The --normalized option provides a handy shortcut. You can normalize key names using any underscore.string function, e.g. --normalized underscored. The most useful ones are probably:

Normalization happens before refraction, so refraction expressions should refer to variables by their new normalized names, not their original ones.

Custom helper functions

You can add your own helper functions. This should be a regular .js or .coffee file that can be require'd in node.

For example, your helpers.js might look like:

exports.toCelcius = function (fahrenheit) {
    return (fahrenheit - 32) * 5 / 9;
}

And then you can use that helper with

refract data.json \
    --apply temperature:toCelcius \
    --helpers helpers.js

Usage from node.js

var refract = require('refract-cli');

var obj = {
    count: '137'
}
var template = {
    count: 'parseInt count'
}

// refract with only JavaScript builtin functions
// like `parseInt` and `Math.max` as helpers
var newObj = refract(template, obj);

// include underscore and underscore.string
// as helpers
var _ = require('underscore');
var context = _.extend({}, obj, refract.defaultHelpers)
var newObj = refract(template, context);

// keep original fields too, not just refracted ones
_.extend(newObj, obj);

Future