Skip to content
gregpabian edited this page Sep 4, 2014 · 8 revisions

Bender server part is based on broadway that introduces Inversion of control mechanism to increase modularity, allow easy application extensibility and better composition.

Using broadway's API, you can create custom plugins that have full access to application`s core and modules.

Plugin types

There are five basic types of plugins defined:

However, you can create a plugin that does not fit to any of the above groups. You can also create a plugin that combines 2 or more of the above, benderjs-jquery plugin is a good example of that approach - it consists of a pagebuilder and a testbuilder.

Naming convention

All Bender plugins should be named according to the following convention:

bender-<plugin type>-<plugin name>

Exceptionally, for plugins that don't fit in any of the above types, you can skip plugin's type and name it in the following way:

bender-<plugin name>

Structure of a plugin

Bender plugins are distributed as NPM packages. This means, they should consist of at least three files:

  • index.js - source code of a plugin
  • package.json - metadata used by NPM
  • README.md - plugin's description

Plugins utilize broadway API. For a valid plugin you need to specify at least the name and the attach function.

module.exports = {
    
    name: 'bender-pluginType-pluginName',
    
    attach: function () {
        // plugin's core
    },
    
    init: function () {
        // plugin's initialization
    }
};

Explanation:

  • name - name of the plugin, should follow naming convention.

  • attach - function called when bender.use() method is executed. It's being called in Bender's context, which means by using this you can refer to bender application, extend it and access it's modules, methods and properties.

    Example:

    module.exports = {
        name: 'plugin-customPlugin',
        
        attach: function () {
            var bender = this;
            
            // extend bender with custom plugin's API
            bender.customPlugin = {
                foo: function () {
                    // do some stuff
                },
                
                bar: 12345
            };
        }
    };
  • init - function called when bender.init() method is executed. At this stage all of the modules should be already attached to bender application. This function is also executed in Bender's context. It accepts optional done callback that can be used in case the initialization of the plugin is asynchronous.

    Example:

    module.exports = {
        name: 'plugin-customPlugin',
        
        attach: function () {
            var bender = this;
            
            // extend bender app with custom plugin's API
            bender.customPlugin = {
                foo: function () {...},
                fooAsync: function (callback) {
                    // do some async stuff and execute the callback on some point
                    callback();
                }
            };
        },
        
        init: function (done) {
            var bender = this;
    
            bender.customPlugin.foo();
            bender.customPlugin.fooAsync(done);
            // this will execute 'done' to indicate that the initialization process is complete
        }
    };

Custom plugin configuration

If the plugin requires some additional configuration from a user, it can access to Bender's configuration and options defined in bender.js file through bender.conf object.

Example:

In bender.js configuration file:

{
    applications: {...},
    
    customOption: 'foo',

    tests: {...}
}

In the plugin's code:

module.exports = {
    name: 'benderjs-customPlugin',
    
    attach: function () {
        var bender = this;
        console.log(bender.conf.customOption); // 'foo'
    },
    
    init: function () {
        var bender = this;
        console.log(bender.conf.customOption); // 'foo'
    }

};

Please note: some of the configuration options names are reserved for Bender core, so in case you really need to use them, please avoid modifying them.

Listening to events

Bender application inherits from EventEmitter2 and uses its API to manage events. There are several events you can listen to with your custom plugin.

Example:

module.exports = {
        name: 'plugin-customPlugin',
        
        attach: function () {
            var bender = this;
            
            // extend bender with custom plugin's API
            bender.customPlugin = {
                foo: function (arg1, arg2) {
                    // do some stuff
                }
            };
        },
        
        init: function () {
            var bender = this;

            // attach custom listener for 'eventName'
            bender.on('eventName', bender.customPlugin.foo);
        }
    };

Dependency check

Some of the plugins may depend on others. To make sure all of the dependencies are available, use bender.checkDeps API.

checkDeps(<current_plugins_name>, <dependency_1_name>, <dependency_2_name> ...)

Example:

module.exports = {
    name: 'benderjs-customPlugin',
    
    attach: function () {
        var bender = this;

        bender.checkDeps('customPlugin', 'benderjs-jquery', 'benderjs-otherCustomPlugin');
        
        bender.otherCustomPlugin.method();
    }
};

If one or more dependencies are missing, following message will appear in the console and Bender will be stopped:

error: Missing module: <dependency_name>
error: Module <plugin_name> requires: <list_of_dependencies>

Custom plugin files

Some of the plugins may require loading additional files in the test context. To mark which files should be shared by Bender server, use files field in the plugin definition.

Example:

var path = require('path'),
    files = [
        path.join(__dirname, 'custom-script.js'),
        path.join(__dirname, 'custom-styles.css')
    ];

module.exports = {
    name: 'bender-testbuilder-custom',
    
    files: files,
    
    build: function (data) {
        // add a custom script to the test context if a custom flag is truthy
        if (data.customPlugin.foo) {
            data.parts.push(
                '<head><script src="' + files[0] + '"></script></head>'
            );
        }

        return data;
    }
};

Clone this wiki locally