-
Notifications
You must be signed in to change notification settings - Fork 3
Writing a Plugin: Directives
It is about Casket v1.
Join the Casket Community Forum to chat with other Casket developers!
This page describes how to write a plugin that registers a new directive for the Casketfile.
Directives can do things like run code when the server starts or shuts down, change server configuration, etc. For example, a common thing is to use a directive to configure and inject a middleware handler into an HTTP server.
- Create a new Go package
- Implement a Setup Function
- Order your Directive
- Plug in your Plugin (applies to any plugin)
Casket plugins are Go packages. Create a new one that imports the casket
package and registers your plugin. Let's create one called "gizmo" that is specific to the HTTP server only:
import "github.com/tmpim/casket"
func init() {
casket.RegisterPlugin("gizmo", casket.Plugin{
ServerType: "http",
Action: setup,
})
}
The name of your directive plugin is also the name of the directive. It must be unique! It should be one word, lowercased. This is an important simplicity and usability convention.
Most directives apply only to a specific type of server. For example, directives for the "http"
server type such as gzip
and fastcgi
configure and inject a middleware handler. These kinds of plugins typically need to import the package of the relevant server type.
Some directives don't pertain to a specific type of server. For example, tls
is a directive that any server type can use to take advantage of Casket's powerful TLS capabilities, and startup
and shutdown
run commands when a server starts/stops, no matter what type of server it is. In that case, the ServerType
field can be left empty. In order to use these kinds of directives, server types must be coded to support them specifically.
The Action field of the casket.Plugin
struct is what makes a directive plugin unique. This is the function to run when Casket is parsing and executing the Casketfile.
The action is simply a function that takes a casket.Controller and returns an error:
func setup(c *casket.Controller) error {
return nil
}
Next, we look at how to use this Controller to execute your directive.
After Casket has parsed the Casketfile, it iterates each directive name (in the order prescribed by the server type) and calls the directive's setup function every time it encounters the directive name. It is the responsibility of the setup function to parse the directive's tokens and configure itself.
The Controller struct makes this quite easy. Notice that the type definition embeds casketfile.Dispenser
. If we expect a line in the Casketfile such as:
gizmo foobar
We can get the value of the first argument ("foobar") like so:
for c.Next() { // skip the directive name
if !c.NextArg() { // expect at least one value
return c.ArgErr() // otherwise it's an error
}
value := c.Val() // use the value
}
You parse the tokens present for your directive by iterating over c.Next()
which is true as long as there are more tokens to parse. Since a directive may appear multiple times, you must iterate over c.Next()
to get all the appearances of your directive and consume the first token (which is the directive name).
See the godoc for the casketfile
package to learn how to use the Dispenser more fully, and take a look at any other existing plugins for examples.
The last thing you have to do is tell the server type where in the process to execute your directive. This is important because other directives may set up more primitive configuration that you rely on, so the order that the directives are executed cannot be arbitrary.
Each server type has a list of strings where each item is the name of a directive. For instance, see the HTTP server's list of supported directives. Add your directive to the list in the proper place.
Finally, don't forget to import your plugin's package! Casket must import your plugin to register and execute it. This is usually done within run.go at the end of the import
section:
_ "your/plugin/package/here"
Please Note: The _
before the package name is required.
That's it! Build casket with your plugin, then write a Casketfile with your new directive to see it in action.
If you are writing an HTTP middleware, continue to the next page.