Skip to content

coutego/pluggable

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pluggable

What is pluggable

Pluggable is a nano framework (<70 lines of code) implementing a plugin architecture for Clojure(Script) applications.

Pluggable is very general. It supports the creation of plugin architectures, but there is little imposed in how to actually do it. The only requirement is that a plugin is a map with a key ‘id’ and, optionally, a key ‘loader’ pointing to a function that can load other plugins later in the list of plugins. A plugin can optionally refer to dependencies to other plugins (:deps), which then are loaded before the plugin that defines the dependency (but only once). Circular dependencies are automatically detected.

Plugins can define “extensions”: new keys with arbitrary meanings, which are available to plugins loaded afterwards. An application built in this style is simply a collection of plugins, each one having access to the functionality of the plugins defined beforehand and defining new functionality and extension points for the plugins loaded afterwards.

There is a special plugin (root-plugin) which is loaded by default and offers some standard functionality, as the ability to define extension points.

A plugin architecture allow for the initialization code to be completely declarative, rather than imperative. By standardizing how the initialization of the application code is done, it becomes much easier to reuse code between applications.

Plugin based architectures don’t make sense in very small applications. Also, it’s impossible to understand how this library can be used in practice without a good example, which must be realistic. The two previous facts mean that in order to give a meaningful example of how to use the library, a non trivial application should be used. I plan to write a realistic mid-size application using this library as an example of how this approach can be used in real world scenarios.

To get a taste of how a plugin for a part of an application can look like when using this library, we can see an example. This is a plugin definition for a router plugin for SPAs:

(def plugin
  {:id         ::routing
   :beans      {:main-component [vector current-page ::router :ui-page-template]
                ::router        {:constructor [create-router]
                                 :mutators    [[init-router ::routes]]}}
   :extensions [{:key     ::home-page
                 :handler ext-handler-home-page
                 :doc     "Defines the home page (route) of the application,
                           replacing any previously defined home page"}
                {:key     ::routes
                 :handler ext-handler-routes
                 :doc     "Adds the given list of routes to the routes of the application"}]
   ::routes    [["/about"
                 {:name ::about-page
                  :view about-page}]]
   ::home-page ui-home-page})

This plugin defines two extension points (::routes and ::home-page) and defines default values for them (a plugin can already use its own extensions). Other plugins can use these extension points to overwrite the home page or add other routes to the application. The code that process this extension points is defined in the ext-handler-home-page and ext-handler-routes functions (code not shown here).

If you wonder what the :beans key means, this is a key defined by another plugin not shown here that defines these extension points to allow plugins to define ‘beans’ to be processed by the Injectable library. That library is not a requirement for Pluggable, but I personally use the two together.

Why

Because it’s a lot of fun to develop applications as a series of plugins.

From a less subjective point of view, we have seen repeatedly than, sooner than later, an application incurres on a “growing tax”, where adding new functionality becomes increasingly costly and working with that codebase is painful.

The standard strategy to deal with this problem is to change projects, jobs or career paths. That works for individuals, but normally not for projects or organisations. These usually follow the strategy to adopt modularity. A plugin architecture is a form of modularity optimised for making application extensivility easy.

I became familiarized with this style of development when launching the project Monodevelop, which started as a port of the #Develop IDE to Linux. The plugin architecture of #Develop made the port easy to do, one module at a time, and the code easier to understand.

All big commercial applications use some kind of plugin architecture. The main reason is to allow third party developers to add functionality to the platform even without access to the source code. But open source software also usually embraces that style, in some cases in a radical style where all the functionality of the application is implemented via plugins (e.g. Eclipse).

My main reason to adopt this style of development is that it’s a lot of fun to develop like this.

How

This technique comes from the object oriented world, even if it can be applied with a functional flavor. The object orientation of the philosophy is not bad in itself:

https://www.youtube.com/watch?v=HSk5fdKbd3o

Pluggable targets the set up of the application state/configuration and stops there. It is therefore purely functional. Nevertheless, the constructed state/configuration can be arbitrarily rich and contain any kind of elements.

This nano framework consists in one function, that takes an initial value for the map and a list of plugins and returns the transformed map. Conceptually, Pluggable is a function ‘f’ which takes a collection and an initial state and applies each of the plugins to transform the state:

(f state plugins) -> state

This means that Pluggable is a simply a reducer.

Its power comes from the fact that every plugin can define its own extension points so other plugins can tap into them and also define their own extension points. But that a story for another day.

About

Plugin library for Clojure

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published