libflitter/views/ViewEngineUnit.js

/**
 * @module libflitter/views/ViewEngineUnit
 */

const path = require('path')
const Unit = require('../Unit')

/**
 * The view engine unit is responsible for registering the view engine
 * with the underlying Express installation. It also defines a function
 * for instantly creating route handlers from view names. This unit sets
 * Pug as the view engine, though that would be relatively easy to alter.
 * 
 * @extends module:libflitter/Unit~Unit
 */
class ViewEngineUnit extends Unit {

    /**
     * Instantiate the unit class. Resolves the fully-qualified path to the views directory.
     * @param {string} [views_dir = './app/views'] - path to the views directory
     */
    constructor(views_dir = './app/views'){
        super()

        /**
         * The fully-qualified path to the views directory.
         * @type {string}
         */
        this.directory = path.resolve(views_dir)
    }
    
    /**
     * Function that modifies and returns resources passed to it.
     * 
     * @typedef {Function} ViewEngineUnit~ResourceModifier
     * @function
     * @param {*} items - either a collection of Model instances, or a single Model instance to be modified
     * @returns {*} - some modified version of the items. This is usually stored to be passed to the view.
     */
    
    /**
     * A collection of resource specifications.
     * This is used by the {@link module:libflitter/views/ViewEngineUnit~ViewEngineUnit#view} method to retrieve resources and pass them to a view.
     * 
     * @typedef {Object} ViewEngineUnit~ResourceSpec
     * @property {string} model - the Flitter canonical name of the model to retrieve
     * @property {"one" | "all"} fine - if "one", only the first instance of the model matching the criteria will be retrieved. If "all", then all instances matching the criteria will be retrieved.
     * @property {Object} criteria - Find criteria to be passed to the model's find() or findOne() functions. See the Mongoose docs for more info.
     * @property {string} name - accessor name for the resource. This will become the variable name used to access the resource from w/in the view.
     * @property {module:libflitter/views/ViewEngineUnit~ViewEngineUnit~ResourceModifier} modifier - allows the modification of the resource before it is passed to the view
     */
    
    /**
     * Render a view to the provided response. Passes through arguments and retrieves the specified resources for the view.
     * @param {Express/Response} response - the Express response to be served
     * @param {string} view_name - the Flitter canonical name of the view to be served
     * @param {Object} args - collection of arguments to be passed directly to the view
     * @param {module:libflitter/views/ViewEngineUnit~ViewEngineUnit~ResourceSpec[]} resource_list - array of resource specifications to be retrieved and passed to the view
     * @returns {Promise<*>}
     */
    async view(response, view_name, args = {}, resource_list = {}){
        view_name = view_name.replace(/:/g, '/')
        let resources = {}

        for ( let find_key in resource_list ){
            const find = resource_list[find_key]
            let returns;

            if ( find.find === 'one' ){
                returns = await model(find.model).findOne(find.criteria)
            }
            else {
                returns = await model(find.model).find(find.criteria)
            }

            returns = await find.modifier(returns)

            resources[find.name] = returns
        }
        
        args = {...args, ...resources}
        
        return response.render(view_name, args)
    }

    /**
     * Loads the unit. 
     * Binds the view helper function to the global context and configures Express to use the Pug view engine.
     * @param {module:libflitter/app/FlitterApp~FlitterApp} app - the Flitter app
     * @param {module:libflitter/Context~Context} context - the unit's context
     * @returns {Promise<void>}
     */
    async go(app, context){

        app.global.bind('view', this.view)
        

        /*
         * Set the underlying express view engine to use Pug.
         */
        app.express.set('view engine', 'pug')
        app.express.set('views', this.directory)
        
    }

    /**
     * Get the name of the unit.
     * @returns {string} "views"
     */
    name() {
        return "views"
    }

    /**
     * Get the directories provided by this unit.
     * Currently, "views" mapped to {@link module:libflitter/views/ViewEngineUnit~ViewEngineUnit#directory}.
     * @returns {{views: string}}
     */
    directories() {
        return {
            views: this.directory
        }
    }

    /**
     * Get the fully-qualified path to the migrations provided by this unit.
     * @returns {string}
     */
    migrations(){
        return path.resolve(__dirname, 'migrations')
    }
}

module.exports = ViewEngineUnit