radarpost uses a very basic plugin system to rig together different components. The rigging is housed in the top-level plugins module.
A plugin can be any python object, which of course includes classes, functions as well as any value (read: anything)
plugins are attached to a particular 'slot' identified (usually) by a string.
>>> import plugins
>>>
>>> MY_PLUGIN_SLOT = 'my.plugin'
>>> plugins.register(42, MY_PLUGIN_SLOT)
Great, 42 is now a part of the plugin slot 'my.plugin' and anything that uses 'my.plugin' will
to get things plugged into a slot, use the 'get' function of the plugins module.
>>> for plugin in plugins.get(MY_PLUGIN_SLOT):
>>> print plugin
42
slot: mailbox.DESIGN_DOC_PLUGIN
This plugin slot accepts a dictionary representing a design document. Objects in the slot are added as design documents to all mailboxes that are created / synced.
eg:
- DESIGN_DOC = {
'_id': '_design/mailbox', 'views': {
- 'messages_by_timestamp': {
- 'map':
""" function(doc) {
- if (doc.type == 'message') {
- emit(doc.timestamp, {'_rev': doc._rev});
}
} """,
# ... }}} plugins.register(DESIGN_DOC, DESIGN_DOC_PLUGIN)
slot: <Type>.SUBTYPE_PLUGIN
This plugin is used to register subtypes of a Document class, eg a FeedSubscription subtype of Subscription. It allows the correct subclass to be constructed when retrieving a mixed set of results from a view, or retrieving a Document by id without knowledge of its specific subtype.
The slot accepts functions that can create subtypes given a subtype name. If a plugin cannot handle a particular subtype, it should return None. A Document type with subtypes should inherit from mailbox.DowncastDoc and declare SUBTYPE_PLUGIN (the plugin id) and SUBTYPE_FIELD (a field in the document that is used to select the subtype and passed to the plugin functions)
eg:
Subscription looks something like this:
class Subscription(DowncastDoc):
type = TextField(default=SUBSCRIPTION_TYPE) subscription_type = TextField()
# ...
SUBTYPE_PLUGIN = 'radar.mailbox.subscription_subtype' SUBTYPE_FIELD = 'subscription_type'
# a FeedSubscription subtype is registered like this:
@plugins.plugin(Subscription.SUBTYPE_PLUGIN) def create_feedsub(typename):
- if typename == FEED_SUBSCRIPTION_TYPE:
- return FeedSubscription()
return None
slot: config.CONFIG_INI_PARSER_PLUGIN
This plugin id is used to register configuration validators / manipulators that can check and parse the types of configuration values read from the configuration ini file.
the parser should accept a dict and raise an exception if any illegal configuration is found. The parser is free to manipulate the configuration as needed.
eg:
# # parse the http port as an integer # @plugins.plugin(CONFIG_INI_READER_PLUGIN) def http_port_conf(config):
- if 'http.port' in config:
- config['http.port'] = int(config['http.port'])
slot: main.COMMAND_LINE_PLUGIN
This plugin id is used to expose administrative commands available at the command line as 'radarpost <command>' and in the shell as 'cmds.<command>'.
The plugin accepts classes with the interface below, but many commands may be simplified by inheriting from main.BasicCommand. see cli.basic.ShowConfigCommand for a basic example.
class SomeCommand(object):
command_name = "<name used on command line and shell>" description = "<a brief description of the command used in help>"
- def __init__(config):
- """ called to initialize the command with the current configuration which defines the context in that the command will run in. """
- def print_usage():
- """ Called to print out an informative description of the command and its arguments. Used for example during help, when an invalid argument has been given or when an argument is missing. """
- def setup_options(parser):
- """ called to set up any commmand line arguments specific to this command. parser is an optparse OptionParser. """
- def run(args, options):
""" called when invoked from the command line, should parse the command line options in any way needed and invoke self.__call__
args - any non-flag arguments specified at the command line options - the result of the optparse options parsing. """
- def __call__(...):
- """ called to invoke the actual command. This is exposed directly in the shell. """
slot: agent.plugins.SUBSCRIPTION_UPDATE_HANDLER
Register methods that handle updating subscriptions.
return True if the subscription was handled, False if the subscription was not handled. throw exceptions for errors.
method signature is: update(mailbox, subscription, config) => bool
eg:
@plugin(SUBSCRIPTION_UPDATE_HANDLER) def update_twizzle(mb, sub, config):
- if sub.subscription_type != 'twizzle':
- return False
# contact twizzle.org # ... return True
slot: web.api.controller.ATOM_RENDERER_PLUGIN
This slot represents plugins that can render a Message into an Atom entry.
Most types can be handled by just creating a template called radar/atom/entry/<message_type>.xml
Specifically, the slot is filled with callables that accept a Message and a Request, and produce a zero argument callable returning the text of an atom entry representing the Message. If the Message cannot be handled, None should be returned. eg:
@plugin(ATOM_RENDERER_PLUGIN) def _render_empty(message, request):
- if message.message_type != 'empty':
- return None
- def render():
- return "<entry></entry>"
return render
slot: web.radar_ui.controller.HATOM_RENDERER_PLUGIN
This slot represents plugins that can render a Message into an HAtom entry (html)
Most types can be handled by just creating a template called radar/hatom/entry/<message_type>.html
Specifically, the slot is filled with callables that accept a Message and a Request, and produce a zero argument callable returning the html representing the Message. If the Message cannot be handled, None should be returned. eg:
@plugin(HATOM_RENDERER_PLUGIN) def _render_empty(message, request):
- if message.message_type != 'empty':
- return None
- def render():
- return '<div class="hentry"></div>'
return render