Skip to content

Writing a Plugin: Directives

Jason Chu edited this page Oct 25, 2020 · 1 revision

This page is obsolete.

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.

  1. Create a new Go package
  2. Implement a Setup Function
  3. Order your Directive
  4. Plug in your Plugin (applies to any plugin)

1. Create a new Go package

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,
	})
}

Directive Name

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.

Server Type

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.

Action (The "Setup Function")

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.

2. Implement the Setup Function

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.

3. Order your Directive

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.

4. Plug in your Plugin

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.