Plugins

Plugins allow developers to share pre-built functionality, that can be used in any of your Projects. Or simply to just extend your Coronium 2 system.

How To Make One

A plugin is simply a regular Lua module. A plugin has access to the cloud and lower-level ngx namespaces. You can include additional 3rd party modules, by using the libs folder (see below).

A plugin!

--/plugins/devspace/plugin.lua
local plugin = {}

function plugin.doSomething( arg )
  return 'Better not '..arg..' with me!'
end

return plugin

Done.

Where They Live

Plugins reside in the /home/cloud/plugins directory. Unless you don't plan on sharing your Plugin(s), you should place them in a "developer" namespace, like so:

/plugins
  /develephant
    eightball.lua

Plugins have full access to the cloud namespace.

The libs Folder

Inside the plugins folder is a directory called libs. Inside this directory, you can place any 3rd party libraries that you are using with your plugins.

/plugins
  /libs
    time.lua

You can require these libs in your plugins like so:

--/plugins/develephant/eightball.lua
local time = require('time')

local m = {}

function m.getTime()
  return time.now()
end

return m

Using Plugins

You can use any plugins in your project api, like so:

--/projects/super/api.lua
local api = cloud.api()

--Use the developer namespace for the plugin.
local eightball = require('develephant.eightball')

function api.get.hello( input )
  local answer = eightball.shake()

  return "Hello" .. input.name .. '. Your answer was ' .. answer
end

return api

Developing Plugins

A plugin acts much like a controller and should take in data, process it, and output it back to the original calling user api.lua. The original calling api should handle all downstream rendering.

Plugins should be reusable across any project, and as such, should be decoupled. In other words, the plugin should know nothing about the api calling it, nor should it store project specific data internally.

REMEMBER: A plugin should never send data downstream on its own. Always pass the data back to the calling api using a return statement.

Scope

One important piece to building plugins is understanding their scope within a users api.lua file. Once a plugin has been require'd into a users api.lua, the scope of the plugin is the same scope as the api.lua file.

An example to illustrate:

--/projects/demo/api.lua
local api = cloud.api()

local myplugin = require('namespace.plugin')

function api.get.confirm( input )
  local res, err = myplugin.doSomething()

  if not res then
    return cloud.error( err )
  end

  return res, cloud.HTML
end

return api

The plugin...

--/plugins/namespace/plugin.lua
local _m = {}

function _m.doSomething( input )

  -- We are in the api.lua "scope" now, we can path tpl directly.
  -- In this example we are loading /projects/demo/tpl/index.tpl
  -- as the project is "demo" (see api.lua above).
  local str, err = cloud.template.render('index.tpl', input)

  --check our output, and return properly (res, err)
  if not str then
    return nil, 'could not render'
  end

  return str, nil
end


return _m

Again, we are not returning data downstream, just back to the calling api.lua file.

Returns and Errors

Every method in Coronium should follow a specific return pattern. This should be counted on by any developer using your plugin. Do not waiver from this pattern.

It's very simple. Every method must return a result, or an error. These are always returned as two values, for example:

-- Example #1
-- Successful result
return res, nil

-- Example #2
-- Unsuccessful result
return nil, 'some type of error message'

So, the result (if any) is always the first return value, and the error (if any) is the second value.

At no time should both the result and error be populated. One must contain a nil value, based on the process outcome.

Consuming Data

If your plugin needs access to incoming data, make sure to include this in your method signature. At no time should a plugin directly access any incoming request data.

For example:

--/plugins/namespace/plugin.lua
local _m = {}

function _m.doSomething( input )
  local email = input.email

  return 'your email is ' .. email, nil --< res, err
end

return _m

And in api.lua

--/projects/demo/api.lua
local api = cloud.api()

local myplugin = require('namespace.plugin')

function api.post.confirm( input )
  -- Be lazy. Pass it all.
  local res, err = myplugin.doSomething( input )

  if not res then
    return cloud.error( err )
  end

  return res
end

return api

More Tips

Don't try to be fancy in your plugins. Simply process your data and pass it back to the api.lua to handle. In other words, don't try to encode, rename, or manipulate the downstream message packet.

You can return any valid Lua type from your plugins.

All content output flags should be handled in the calling api.lua.