Documentation
¶
Overview ¶
Package slingshot contains a plugin manager built on top of the standard plugin package. Plugins are loaded using the Load function, which takes the filesystem path to the compiled plugin and a map of parameters. The Load function uses the plugin package to load the plugin and invokes its SlingshotInit function, which must be declared to take as arguments a Slingshot interface object and the parameters (declared as a map from string to generic interface). The SlingshotInit function is then expected to use the Slingshot object's Register method, passing it a namespace, a key, the plugin object itself, and zero or more plugin options.
The slingshot package divides plugins up into namespaces. The namespaces should be unique for the application, e.g., "github.com/klmitch/slingshot". If the application requires multiple namespaces for different types of plugins, those namespaces should have a common prefix, e.g., "github.com/klmitch/slingshot/hooks" and "github.com/klmitch/slingshot/drivers".
Plugins are further divided up by a "key"; this is again a simple string, and its use will depend on how the application uses the plugin. For instance, in driver-style plugins, the key identifies the driver, while in hook-style plugins, the key names the hook to be triggered.
Plugins come in many varieties. The simplest to understand is perhaps the driver pattern plugin, where a driver is specified by a name. The desired driver may then be retrieved by simply calling GetPlugin with the appropriate namespace and driver name; this retrieves only the first-registered plugin with the given name. Hook-pattern plugins, on the other hand, expect to have all plugins with the same key invoked whenever the hook is triggered; to implement this pattern, call GetAllPlugins with the appropriate namespace and hook name, then iterate over the returned list.
A more complex pattern is the extension pattern. In this pattern, again, all the plugins are invoked in order, but each plugin calls the next plugin, and has the opportunity to process its return value. To make this easier to accommodate, the slingshot package provides the IterPlugins utility function, which encapsulates the list of plugins in an object with a Next method returning the next plugin. With this, an extension function could be written like so:
func Extension(nextPlug *PluginIter, finalFunc func() error) { next := nextPlug.Next() if next == nil { return finalFunc() } return callExtension(next, nextPlug, finalFunc) }
In this example, callExtension is assumed to be a function that calls the plugin function; more on that below.
Some applications may have built-in plugins. For instance, a driver-style plugin may provide a mock driver. Such plugins can be registered directly using the Register function, which has the same calling convention as the Register method of the Slingshot object passed to the plugin initialization function.
What is a plugin? The GetPlugin and GetAllPlugins functions ultimately return a PluginMeta object. This object contains more than just the plugin itself; it contains a large amount of metadata, such as the filesystem path to the plugin, the plugin's base filename, and the namespace and key of the plugin. Additional options can be passed to the Register function to set a name, a plugin version, a license description, or documentation. An integer APIVersion can also be specified, which could be used to allow an application to still function with plugins implementing an older version of the plugin's interface. Finally, any arbitrary metadata can be provided, which could be used by the application for additional complex filtering when iterating over the list of plugins.
The Plugin element of the PluginMeta object is the actual plugin passed to the Register function. This is declared as a generic interface, so this may be anything, from a simple string to a function to an object implementing an interface. The callExtension function described above could thus be something of the form:
func callExtension(plug *PluginMeta, nextPlug *PluginIter, finalFunc func() error) { plug.Plugin.(func(*PluginIter, func() error) error)(nextPlug, finalFunc) }
Finally, the slingshot package contains full-featured mocks, built on the "github.com/stretchr/testify/mock" mocking package. The MockRegistry type allows mocking the slingshot plugin registry. This mock can be installed for the use of functions such as GetPlugin by using SetRegistry from a test function like so:
defer SetRegistry(SetRegistry(mockReg))
The MockSlingshot type can be passed to a plugin initialization function. Finally, there is a MockNamespace type, which has a Name element to set the return result of the Namespace method call.
For more information on using the mocks, check out the documentation for "github.com/stretchr/testify/mock".
Index ¶
- Constants
- Variables
- func Load(path string, params map[string]interface{}) error
- func Register(namespace, key string, plugin interface{}, opts ...PluginOption)
- type MockNamespace
- type MockRegistry
- func (reg *MockRegistry) Get(namespace string, create bool) (Namespace, bool)
- func (reg *MockRegistry) GetAllPlugins(namespace, key string) ([]*PluginMeta, bool)
- func (reg *MockRegistry) GetPlugin(namespace, key string) (*PluginMeta, bool)
- func (reg *MockRegistry) Load(path string, params map[string]interface{}) error
- func (reg *MockRegistry) Register(namespace, key string, plugin interface{}, opts ...PluginOption)
- type MockSlingshot
- type Namespace
- type PluginIter
- type PluginMeta
- type PluginOption
- type Registry
- type Slingshot
Constants ¶
const SlingshotInit = "SlingshotInit"
SlingshotInit is the name of the plugin initialization function that will be looked up.
Variables ¶
var ( ErrIncompatInit = errors.New("Incompatible plugin initializer") ErrInitPanic = errors.New("Plugin initializer paniced") )
Errors that may be returned
Functions ¶
func Load ¶
Load loads a plugin and instructs it to register its plugin points with the Slingshot registry.
func Register ¶
func Register(namespace, key string, plugin interface{}, opts ...PluginOption)
Register is for registering a "core" plugin--that is, a plugin that is implemented within the code of the application, rather than one loaded from an external file using the plugin package.
Types ¶
type MockNamespace ¶
MockNamespace is a mock object for Namespace.
func (*MockNamespace) Add ¶
func (ns *MockNamespace) Add(key string, plugin *PluginMeta)
Add adds a new plugin descriptor under the given key.
func (*MockNamespace) Get ¶
func (ns *MockNamespace) Get(key string) (*PluginMeta, bool)
Get returns the first plugin descriptor for the given key. If there are no descriptors for that key, the second value will be false.
func (*MockNamespace) GetAll ¶
func (ns *MockNamespace) GetAll(key string) ([]*PluginMeta, bool)
GetAll returns all the plugin descriptors for the given key. If there are no descriptors for that key, the second value will be false.
func (*MockNamespace) Namespace ¶
func (ns *MockNamespace) Namespace() string
Namespace returns the namespace string. The Name element of the MockNamespace must be set for this method to work.
type MockRegistry ¶
MockRegistry is a mock object for Registry.
func (*MockRegistry) Get ¶
func (reg *MockRegistry) Get(namespace string, create bool) (Namespace, bool)
Get gets a specified namespace from the registry. If the namespace doesn't have any entries and create is false, the second value will be false.
func (*MockRegistry) GetAllPlugins ¶
func (reg *MockRegistry) GetAllPlugins(namespace, key string) ([]*PluginMeta, bool)
GetAllPlugins gets all the plugin descriptors for the designated namespace of the registry. If the namespace doesn't have any entries for the designated key, the second value will be false.
func (*MockRegistry) GetPlugin ¶
func (reg *MockRegistry) GetPlugin(namespace, key string) (*PluginMeta, bool)
GetPlugin gets a specified plugin from the designated namespace of the registry. If the namespace doesn't have any entries for the designated key, the second value will be false.
func (*MockRegistry) Load ¶
func (reg *MockRegistry) Load(path string, params map[string]interface{}) error
Load loads a plugin and instructs it to register its plugin points with the Slingshot registry.
func (*MockRegistry) Register ¶
func (reg *MockRegistry) Register(namespace, key string, plugin interface{}, opts ...PluginOption)
Register is for registering a "core" plugin--that is, a plugin that is implemented within the code of the application, rather than one loaded from an external file using the plugin package.
type MockSlingshot ¶
MockSlingshot is a mock object for Slingshot.
func (*MockSlingshot) Register ¶
func (sling *MockSlingshot) Register(namespace, key string, plugin interface{}, opts ...PluginOption)
Register is for registering a "core" plugin--that is, a plugin that is implemented within the code of the application, rather than one loaded from an external file using the plugin package.
type Namespace ¶
type Namespace interface { Namespace() string Get(key string) (*PluginMeta, bool) GetAll(key string) ([]*PluginMeta, bool) Add(key string, plugin *PluginMeta) }
Namespace describes a Slingshot namespace.
type PluginIter ¶
type PluginIter interface {
Next() *PluginMeta
}
PluginIter describes a plugin iterator.
func IterPlugins ¶
func IterPlugins(plugins []*PluginMeta) PluginIter
IterPlugins is a constructor for a PluginIter. Given a list of plugins, as returned by Namespace.GetAll, it returns an objects whose Next method will return each plugin in turn.
type PluginMeta ¶
type PluginMeta struct { Path string // Path to the plugin Filename string // Basename of the plugin Namespace string // Namespace the plugin exists in Key string // The key the plugin belongs in Plugin interface{} // The actual plugin. Name string // The name the plugin identifies as Version string // The version of the plugin License string // Text describing the plugin license Docs string // Documentation for the plugin APIVersion int // The API version of the plugin Meta map[string]interface{} // Additional metadata }
PluginMeta contains the metadata for a registered plugin. This includes such things as the filename, the namespace (string), the key, the plugin version, the API version, or anything else the plugin may register.
func GetAllPlugins ¶
func GetAllPlugins(namespace, key string) ([]*PluginMeta, bool)
GetAllPlugins gets all the plugin descriptors for the designated namespace of the registry. If the namespace doesn't have any entries for the designated key, the second value will be false.
func GetPlugin ¶
func GetPlugin(namespace, key string) (*PluginMeta, bool)
GetPlugin gets a specified plugin from the designated namespace of the registry. If the namespace doesn't have any entries for the designated key, the second value will be false.
type PluginOption ¶
type PluginOption func(meta *PluginMeta)
PluginOption is an option function that can be passed to the Plugin.Register method.
func APIVersion ¶
func APIVersion(version int) PluginOption
APIVersion sets the version of the plugin API. This is a number that can be used by the application to determine how to call the plugin, if the plugin interface has been evolved.
func License ¶
func License(license string) PluginOption
License sets the license descriptor for the plugin.
func Meta ¶
func Meta(key string, value interface{}) PluginOption
Meta allows setting any number of other pieces of metadata, which can be used by the application as desired.
func Name ¶
func Name(name string) PluginOption
Name sets the name of the plugin, which may be distinct from the filename or key depending on the needs of the application.
type Registry ¶
type Registry interface { Get(namespace string, create bool) (Namespace, bool) GetPlugin(namespace, key string) (*PluginMeta, bool) GetAllPlugins(namespace, key string) ([]*PluginMeta, bool) Register(namespace, key string, plugin interface{}, opts ...PluginOption) Load(path string, params map[string]interface{}) error }
Registry describes the Slingshot registry.
func SetRegistry ¶
SetRegistry sets the registry used by the top-level functions to the specified value, returning the original value. This is intended for use by library callers to allow for mocking out the registry using the MockRegistry type.
type Slingshot ¶
type Slingshot interface {
Register(namespace, key string, plugin interface{}, opts ...PluginOption)
}
Slingshot is used to carry additional data through the plugin's initialization routine to the Register method.