Back to topic list

Abstractors

Abstractors are awesome. They provide a way to manipulate context data before rendering, such as applying markdown, or fetching external content, for example twitter post content based on id.

Usage

Let's start with a context file like this:

{
  doc: 'lets have some __bold__ text'
}

To replace the __bold__ with <strong>bold</bold> we will create a doc.js file in /app/abstractors folder that will look like this:

var abstractor = function () {}

abstractor.prototype.init = function(context) {
    return new Promise(function(resolve, reject) {

        // initialize abstractor
        resolve()
    })
}

abstractor.prototype.abstract = function(context) {
  return new Promise(function(resolve, reject) {

    // will hide the generatod context from admin interface
    context['$marked_doc_hidden'] = true

    // will replace __strong__ to <strong>strong</strong>
    context.marked_doc = context.doc.replace(/__([^*]+)__/g, '<strong>$1</strong>')

    // abstract directive
    return resolve()

  })
}

module.exports = new abstractor()

simple enough, this will add a ready to be used marked_doc to our context like this:

{
  doc: 'lets have some __bold__ text',
  marked_doc: 'lets have some <strong>bold</strong> text'
}

Hint: The basename of an abstractor's file should exactly match the name of a field you want to abstract. E.g. if you have a field called rss_feed and you want to process it, you will need an /app/abstractors/rss_feed.js file.

Use cases

Abstractors are used on this very page to add markdown by using marked. Apart from this I used abstractors to fetch twitter post based on id.

Pro tip: abstractors work great with arrays of objects, templatitators and deeply nested structures too without the need to change abstractor's behaviour. So, if you want to display a series of sections with marked text in each, you can simply make an array of objects containing doc field each.

Initialization

Every abstractor need to export two functions: init() and abstract(). Init runs only once per enduro render event, while abstract run for every key named as the abstractor.

Abstractors and multilingual support

If you want your abstracted fields to be translated too, you should expand your code to add language-specific fields in your context files. The code may look like this:

var abstractor = function () {}

abstractor.prototype.init = function(context) {
    return new Promise(function(resolve, reject) {

        // initialize abstractor
        resolve()
    })
}

abstractor.prototype.abstract = function(context) {
  return new Promise(function(resolve, reject) {

    // will hide the generatod context from admin interface
    context['$marked_doc_hidden'] = true

    // will replace __strong__ to <strong>strong</strong>
    context.marked_doc = context.doc.replace(/__([^*]+)__/g, '<strong>$1</strong>')

    // add support for locales
    if ('$doc_de' in context) {
        context.$marked_doc_de = context.$doc_de.replace(/__([^*]+)__/g, '<strong>$1</strong>')
    }

    // abstract directive
    return resolve()

  })
}

module.exports = new abstractor()
Back to topic list

Shout out to pexels and freepik