Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

API for plugin framework in Lua

Tags:

plugins

lua

I am implementing a plugin system with Lua scripts for an application. Basically it will allow the users to extend the functionality by defining one or more functions in Lua. The plugin function will be called in response to an application event.

Are there some good open source plugin frameworks in Lua that can serve as a model?

In particular I wonder what is the best way to pass parameters to the plugin and receive the returned values, in a way that is both flexible and easy to use for the plugin writers.

Just to clarify, I am interested in the design of the API from the point of view of the script programming in Lua, not from the point of view of the hosting application.

Any other advice or best practices related to the design of a plugin system in Lua will be appreciated.

like image 540
alexrs Avatar asked May 30 '11 19:05

alexrs


2 Answers

Lua's first-class functions make this kind of thing so simple that I think you won't find much in the way of frameworks. Remember that Lua's mantra is to provide minimal mechanism and let individual programmers work out policy for themselves.

Your question is very general, but here's what I recommend for your API:

  • A single plugin should be represented by a single Lua table (just as a Lua module is represented by a single table).

  • The fields of the table should contain operations or callbacks of the table.

  • Shared state should not be stored in the table; it should be stored in local variables of the code that creates the table, e.g.,

    local initialized = false
    
    return {
       init = function(self, t) ... ; initialized = true end,
       something_else = function (self, t) 
                          if not initialized then error(...) end
                          ... 
                        end,
       ...
    }
    
  • You'll also see that I recommend all plugin operations use the same interface:

    1. The first argument to the plugin is the table itself
    2. The only other argument is a table containing all other information needed by the operation.
    3. Finally, each operation should return a result table.

    The reason for passing and returning a single table instead of positional results is that it will help you keep code compatible as interfaces evolve.

In summary, use tables and first-class functions aggressively, and protect your plugin's private state.

like image 179
Norman Ramsey Avatar answered Oct 18 '22 06:10

Norman Ramsey


The plugin function will be called in response to an application event.

That suggests the observer pattern. For example, if your app has two events, 'foo' and 'bar', you could write something like:

HostApp.listeners = {
   foo = {},
   bar = {},
}
function HostApp:addListener(event, listener)
   table.insert(self.listeners[event], listener)
end
function HostApp:notifyListeners(event, ...)
   for _,listener in pairs(self.listeners[event]) do
      listener(...)
   end
end

Then when the foo event happens:

self:notifyListeners('foo', 'apple', 'donut')

A client (e.g. a plugin) interested in the foo event would just register a listener for it:

HostApp:addListener('foo', function(...)
   print('foo happened!', ...)
end)

Extend to suit your needs.

In particular I wonder what is the best way to pass parameters to the plugin and receive the returned values

The plugin just supples you a function to call. You can pass any parameters you want to it, and process it's return values however you wish.

like image 41
Mud Avatar answered Oct 18 '22 06:10

Mud