To highlight the functionality and power available through the Plugins framework, it is best to start with a concrete example of a simple, yet complete one. What follows is an example plugin for user authentication,
SimpleAuthenticationPlugin, broken into sections for explanation.
Anatomy of a Plugin
The first thing to do to create a Plugin is to define the plugin’s module, and include
Note: each class in our examples below must live inside the
SimpleAuthenticationPlugin module. We are merely omitting it here for conciseness.
Then we’ll need an inner
Plugin class inside our defined
SimpleAuthenticationPlugin module. This class is responsible for setting up the plugin’s configuration details. We can also define any other utility methods that might seem appropriate for the Plugin. For example, we’ve thrown in an
authenticate instance method, because it seemed as good of a place as any.
If we define a
Request module inside the plugin, it will automatically be included in
Praxis::Request. So to add a simple
current_user method to a Praxis
Request it is enough to define it like this.
Praxis::Controller are done by defining a
Controller module within the plugin. As in the case above, any code inside the module will be automatically included in
Praxis::Controller. Controller extensions typically want to register
around callbacks. The code below shows an example of that, registering a
before :action block for all controllers:
Our example authentication Plugin needs to allow designers to tag certain actions as requiring an authenticated user. A clean way to achieve that is to provide a new DSL method available within an
ActionDefinition. We can do that by adding a handy
requires_authentication method within the
ActionDefinition module of our plugin. Similarly, we can include a
decorate_docs hook to the
ActionDefinition module so that when documents are generated, this plugin has the chance to decorate the output for actions that
:authentication_required is true.
Enabling a Plugin
In order to use a plugin named
MyPlugin, you need to invoke the
use directive of the Praxis bootloader. To do so, you would typically have the following in your
As outlined above,
MyPlugin may be either a subclass of
Plugin or a module that includes
PluginConcern. In the latter case, Praxis will expect the module that contains a class named
The two main components of a plugin in Praxis are:
Plugin: The class that is instantiated for each use of the plugin (unless it includes
Singleton, in which case its
ActiveSupport::Concernmodule that encloses extensions to core Praxis classes.
In addition, plugins that use the
PluginConcern module may provide modules that should be included in the core Praxis classes. The modules must be named after the class they are to be included in to, at present the following classes are supported:
A plugin in Praxis must consist of a subclass of
Plugin, and that subclass may be enclosed in a module that has included
Plugin Bootstrapping and Configuration
Praxis processes your
Bootloader#use invocations while loading environment.rb and performs a few actions to load your plugin:
- The plugin module’s
setup!method is called. By default, that method will inject any relevant modules into the core Praxis classes, provided that is the first time the method has been called (in the event the plugin is used multiple times in one application).
- An instance of that plugin’s
Pluginclass is created and saved in the application’s set of plugins. Or, if that subclass is a
Singleton, then its
instanceis retrieved and used instead.
The configuration of the Plugin instance, however, is deferred until a separate
:plugin stage during bootstrapping. The
:plugin stage, contains three sub-stages:
:setup. Praxis will call your Plugins in this particular order by using the following callback methods:
prepare: The prepare phase will invoke the
Plugin#prepare_config!(node)method. This phase adds the plugin’s configuration definition to the provided
nodefrom the application’s own configuration. The
nodeparameter is an
Attributor::Structwhich can be enhanced with whatever attribute structure the plugin requires. Typically the code in
prepare_config!will pass a block to the
node.attributesmethod containing a structure of typed attributes. (see authentication example above)
load: The load phase is in charge of loading and returning the relevant configuration data (based on the structure defined in the previous phase). This is implemented in the
Plugin#load_config!method. This callback method is only responsible for returning the loaded configuration data. Praxis will internally take care of validating and saving such data onto the appropriate place in the application. There is a default implementation of this method in the base Plugin class. Such default method will automatically return the contents of the
:config_fileattribute of the plugin, assuming that is
YAML-parseable. So, for plugins that simply get their configuration through a single
YAMLfile, they can setup their
:config_filevariable appropriately and skip implementing the
load_config!method in the Plugin class.
setup: The last phase is
setup, and it allows the Plugin to perform any final initialization before the application’s code is loaded. This is implemented in the
Plugin#setup!method. The default implementation of this method is empty.
Note that these phases are invoked for all registered Plugins as a block, one phase after the other. In other words, all registered Plugins will go through the
prepare phase first, before they all move to the
load phase, and finally move onto the
Doc Browser Customization
It is possible to modify and enhance the Doc browser from plugins. To begin, you must register your plugin as extending the doc browser by calling
path will be the path to the directory where you store the assets for your plugin. The best place to call this is in the
Plugin#setup! method mentioned above. Once this is done, Praxis will automatically pick up your plugin’s components into its build system. This allows you to do the following:
If you place a
bower.json into your
dependencies field will be merged into a master
bower.json and then the dependencies will be automatically installed and linked into the doc browser.
Override or Add Templates
You may choose to override any of the Praxis builtin templates, simply place them at a matching path within a
views subdirectory and they will be automatically picked up.
Any code you provide will be loaded after the core Praxis doc browser, but before any of the user’s code. This will allow you to override any components from core you need as well as take advantage of any APIs exposed. You can also expose APIs to the user from your plugin. See the doc browser customization wiki for more details.
Provide SCSS Styles
path will be made available to the user’s
docs/styles.scss file, but will not be included automatically. Therefore it is up to you to instruct users to add any necessary imports to use styles you provide.
Add Other Assets
Any assets not mentioned above will be copied to the user’s
docs/output directory on build. This way you can provide images, fonts or other assets in a plugin.