The ability to easily add reusable functionality to a framework is one of the most important features. Plugins in Adhearsion 2.0 have been completely rebuilt to better suit the new structure and allow them to provide a wider variety of features. Controller methods, initializer code, specific configuration, rake tasks and included generators all are possible with the new plugin classes. In this first post we will be generating a plugin using the CLI and looking through the resulting code.
Components in Adhearsion 1.x
The previous release of Adhearsion was deeply tied to the concept of a dialplan and related contexts, like the dialplan itself or event handling code. That brought the component architecture to simply define a series of methods that were available in some or all of the contexts. Components, however easy to create and use, were limited in scope and very difficult to properly test. Adhearsion 2.0 completely removes support for components in favor of plugins.
What is an Adhearsion 2.0 Plugin?
A plugin, in Adhearsion as in many other Ruby frameworks, simply represents a collection of code, usually in the form of modules used as mixins to CallControllers. The library is packaged as a gem to facilitate its use, reuse, and sharing with the community. In addition to providing classes and modules, a plugin can bring a series of extra functionalities that will be demonstrated below.
Anatomy of a Plugin
The easiest way to create a skeleton plugin is to use the Adhearsion command "ahn generate".
By running the following ahn generate plugin GreetPlugin
a directory named greet_plugin will be created in the current working directory. The plugin itself, being a gem, can reside anywhere, unlike components that needed to be inside the application directory. The output from this command should show the files being created, like this:
create greet_plugin create greet_plugin/lib create greet_plugin/lib/greet_plugin create greet_plugin/spec create greet_plugin/greet_plugin.gemspec create greet_plugin/Rakefile create greet_plugin/README.md create greet_plugin/Gemfile create greet_plugin/lib/greet_plugin.rb create greet_plugin/lib/greet_plugin/version.rb create greet_plugin/lib/greet_plugin/plugin.rb create greet_plugin/lib/greet_plugin/controller_methods.rb create greet_plugin/spec/spec_helper.rb create greet_plugin/spec/greet_plugin/controller_methods_spec.rb
Gem Plugin Structure
The .gemspec file contains information on your plugin, required dependencies and other necessary data. Enter your contact information, the name and description of your plugin and list any dependencies in greet_plugin.gemspec to have a fully functional gem. The README is customarily formatted in Markdown and its use is strongly encouraged to help people understand how to use your plugin. The Rakefile contains tasks that pertain to the plugin gem itself, such as running unit tests. Note that it is separate from adding tasks to Adhearsion applications; this will be covered later.
Plugin Files
The entry point for the plugin, as usual with gems, resides in lib/greet_plugin.rb. It is mainly composed of requires for the plugin classes and modules. When adding functionality to a plugin, it will need to be require
'd here to be available. Plugins are namespaced by package name to avoid conflicts.
# lib/greet_plugin.rb module GreetPlugin require "greet_plugin/version" require "greet_plugin/plugin" require "greet_plugin/controller_methods" end
In this example Adhearsion plugin:
version.rb
contains the current version number for the plugin, and is used during packaging.plugin.rb
contains the hooks into the Adhearsion framework that are called when the plugin is loaded by the Adhearsion application.controller_methods.rb
contains a module that gets mixed into the base CallController class, making its methods available to all calls running in Adhearsion.
# lib/greet_plugin/plugin.rb module GreetPlugin class Plugin < Adhearsion::Plugin # Actions to perform when the plugin is loaded # init :greet_plugin do logger.info "GreetPlugin has been loaded" end # Basic configuration for the plugin # config :greet_plugin do greeting "Hello", :desc => "What to use to greet users" end # Defining a Rake task is easy # The following can be invoked with: # rake greet_plugin:info # tasks do namespace :greet_plugin do desc "Prints the PluginTemplate information" task :info do STDOUT.puts "GreetPlugin plugin v. #{VERSION}" end end end end end
In plugin.rb
there are three important blocks shown. The first is the #init
block which is invoked by Adhearsion when the plugin is first loaded. In our case, all this does is write an informational message to the log showing that the plugin was, in fact, loaded. The second is the #config
block that registers configuration options with the Adhearsion framework. This is important because it allows your users to easily discover the possible configuration options for your plugin by simply running rake config:show
within their Adhearsion applications. It also allows you to document the configuration options and set default values. The third block is the #tasks
block, which registers Rake tasks to be available within the Adhearsion application. In this case it adds a Rake task called greet_plugin:info
that prints the version number of the plugin.
Plugin Methods: #init and #run
The #init
method defines code that is run when the plugin is loaded. Every plugin goes through two separate phases before it is ready to run. The plugin first gets initialized through #init
, which sets up any basic requirements or configuration. Later, when the Adhearsion framework has booted, the #run
block is called to start the plugin. An example of using the #init
and #run
methods might be an IRC plugin. In the #init
method, the IRC class is instantiated and configured, but no connection to the server is made. Then in #run
the actual connection is opened and the service begins. Both methods are optional, but if they are defined, the mandatory arguments are the name of the plugin as a symbol and a block to provide the code to be run. A plugin can also request to be initialized before or after another plugin by name, using the :before and :after options passed as an hash to #init
and/or #run
.
Plugin Methods: #config
The #config
block allows a plugin to define configuration values in a customizable and self-documenting way. Every configuration line has the key, followed by a default value, optionally followed by a :desc key to allow for a description. rake config:show
in an application directory will display all config keys provided by the core and the plugins, with their descriptions.
A config line can also validate supplied values with a transform:
max_connections 5, :desc => "Maximum number of connections to make", :transform => lambda { |v| v.to_i }
The :transform will be used to modify the configuration value after it is read from the end-user's setting.
Plugin Methods: #tasks
The #tasks
method allows the plugin developer to define Rake tasks to be made available inside an Adhearsion application. Task definitions follow Rake conventions.
What Next?
In this post we have covered the basic tasks necessary to create a new plugin, identify it to Rubygems with metadata, and provide hooks for loading into Adhearsion applications. In the next post, we will cover how your code may be used by Adhearsion applications as well as testing for plugins.