Barhandles

Handlebars in reverse

A couple of months ago, I coded a document template solution mixing Markdown with Handlebars and a couple of extensions to both Markdown and Handlebars. These templates will be evaluated at runtime, but in order to be able to author them, I created a simple JSON/Markdown/Handlebars editor, that allows you to author a data structure on the left pane, author the template (using Handlebars and Markdown) on the middle pane, and have a live preview of the merged template on the right pane.

The setup is working pretty good. That is, if you're really careful. Handlebars doesn't check if all the variables you're referring to are actually defined by the data structure, and as a result, more than once we were pointing at variables that were actually no longer / never getting passed in. It turns out that without any type of validation, you easily introduce invalid references.

And then, a while ago, I started noticing that the data structures we were passing were always out of sync. Even though many templates had similar data requirements, we somehow always encoded it in different data structures, making maintenance a nightmare.

Which left me wondering if it would be possible to extract a schema from the Handlebars template, to be able to check the data structures upfront. It appeared somebody tried it before, but it was based on an old version of Handlebars. So I coded my own solution, called Barhandles.

What does it do?

Barhandles takes your template and extracts variable references from it, reconstructing partial references you will find in the template into full references, addressing from the root of your data structure.

So let's take this as an example:

{{#with foo}}
{{bar}}
{{/with}}

If this is your template, then – first of all – Barhandles offers a callback based approach for visiting all these references.

template = '''
{{#with foo}}
{{bar}}
{{/with}}
'''

Barhandles = require 'barhandles'
Barhandles.extract template, (ref) -> console.info(ref.join('.'))
# Producing:
# foo.bar 

However, this might not be ideally suited for validating your data structures. So Barhandles also offers a way to extract an entire schema from Handlebars. This is a schema using its own little language, not based on something that actually does the validations, but it's rich and small enough to turn it into your schema language of choice.

Barhandles.extractSchema template
# Producing:
# {
#   "_type": "object",
#   "foo": {
#     "_type": "object",
#     "bar": {
#       "_type": "any"
#     }
#   }
# }

In our project, we are taking this and turn it into Joi validation, using this function:

rejoice = (schema) ->
  switch schema._type
    when 'object'
      validations = {}
      keys = _.filter _.keys(schema), (key) -> key isnt '_type'
      _.each keys, (key, index) ->
        validations[key] = rejoice(schema[key]).required()
      Joi.object().keys(validations).unknown(true)
    when 'array'
      Joi.array().items(rejoice schema['#'])
    when 'any'
      Joi.any()