Net::REPL::Client: Allow read/print portions to be overridden separately.
[thrasher.git] / perl / lib / Thrasher / Plugin.pm
blob64e89018bc7c7f3db1937744a90862b4cf6c8ff7
1 package Thrasher::Plugin;
2 use strict;
3 use warnings;
5 =pod
7 =head1 NAME
9 Thrasher::Plugin - manage the plugins to the component
11 =head1 DESCRIPTION
13 Thrasher::Plugin implements the code to manage the plugins to
14 Thrasher::Component. Plugins are loaded by "use"ing the corresponding
15 module, which will call the appropriate "register_plugin" method
16 within it.
18 A Plugin adds a given capability to the component, adding support for
19 the appropriate IQs and and other necessary codes. Protocols then
20 declare which plugins they support, and the Component will load the
21 appropriate plugins. Protocols indicate plugins by their name in
22 the Plugin directory.
24 If plugins need other plugins (such as when the Avator code needs the
25 PEP plugin), you can simple "use" the prerequisites in your module.
27 Plugins call the following function in their module:
29 =cut
31 use Thrasher::Log qw(log);
33 use Carp qw(confess);
35 use Thrasher::Callbacks qw(:all);
36 use Thrasher::Constants;
38 use base 'Exporter';
40 our @EXPORT_OK = qw(register_plugin unregister_plugin
41 method_for_iq_namespace
42 supported_features
43 callbacks);
44 our %EXPORT_TAGS = (all => \@EXPORT_OK);
46 # This is filled out as we go, to stay in sync with the spec
47 my %QUERY_FUNCTIONS;
49 sub method_for_iq_namespace { $QUERY_FUNCTIONS{$_[0]}->{$_[1]}->{$_[2]}; }
51 # This is a list of all features we support. This may be changed by
52 # 'use'ing various additional bits of functionality.
53 my %SUPPORTED_FEATURES;
54 my @SUPPORTED_FEATURES_SORTED;
56 # Features always supported:
57 register_plugin({features => [
58 $Thrasher::Constants::NS_CHATSTATES,
59 ]});
61 register_callback('plugins_changed', 'default', \&update_supported_features);
63 sub update_supported_features {
64 @SUPPORTED_FEATURES_SORTED = sort keys %SUPPORTED_FEATURES;
67 sub supported_features { @SUPPORTED_FEATURES_SORTED; }
69 sub register_plugin {
70 my $plugin_data = shift;
72 # A plugin can define:
73 # A set of IQ handlers by namespace
74 # A set of features that this plugin enables
76 if ($plugin_data->{client_iq_handlers}) {
77 while (my ($namespace, $type_more) =
78 each %{$plugin_data->{client_iq_handlers}}) {
79 while (my ($type, $handler) = each %$type_more) {
80 $QUERY_FUNCTIONS{'client'}->{$type}->{$namespace} = $handler;
85 if ($plugin_data->{component_iq_handlers}) {
86 while (my ($namespace, $type_more) =
87 each %{$plugin_data->{component_iq_handlers}}) {
88 while (my ($type, $handler) = each %$type_more) {
89 $QUERY_FUNCTIONS{'component'}->{$type}->{$namespace} = $handler;
94 if ($plugin_data->{features}) {
95 for my $feature (@{$plugin_data->{features}}) {
96 $SUPPORTED_FEATURES{$feature} = 1;
100 if ($plugin_data->{callbacks}) {
101 while (my ($type, $callbacks) =
102 each %{$plugin_data->{callbacks}}) {
103 while (my ($name, $callback) = each %$callbacks) {
104 register_callback($type, $name, $callback);
109 callbacks('plugins_changed');
112 # This is to make testing easier, so we can pop features in and out.
113 # This should never be called in running code.
114 sub unregister_plugin {
115 my $plugin_data = shift;
117 if ($plugin_data->{client_iq_handlers}) {
118 while (my ($namespace, $handler) =
119 each %{$plugin_data->{client_iq_handlers}}) {
120 delete $QUERY_FUNCTIONS{client}->{$namespace};
124 if ($plugin_data->{component_iq_handlers}) {
125 while (my ($namespace, $handler) =
126 each %{$plugin_data->{component_iq_handlers}}) {
127 delete $QUERY_FUNCTIONS{component}->{$namespace};
131 if ($plugin_data->{features}) {
132 for my $feature (@{$plugin_data->{features}}) {
133 delete $SUPPORTED_FEATURES{$feature};
137 if ($plugin_data->{callbacks}) {
138 while (my ($type, $callbacks) =
139 each %{$plugin_data->{callbacks}}) {
140 while (my ($name, $callback) = each %$callbacks) {
141 unregister_callbacks($type, $name);
146 callbacks('plugins_changed');
149 # Plugins are designed to load themselves via "use" and make
150 # themselves Just Work (TM), so you should not need to use this
151 # method in real code. However, the test protocol would sometimes like
152 # to use a certain plugin for testing purposes, or behave differently
153 # when the plugin is loaded, without actually "use"ing the plugin
154 # itself, and possibly affecting other test scripts not expecting that
155 # plugin. Thus, this method will tell the test protocol whether the
156 # given plugin is loaded. Hopefully this makes it clear why real code
157 # should never use this.
158 sub _has_plugin {
159 my $plugin_name = shift; # the part after "Thrasher::Plugin::"
160 return $INC{'Thrasher/Plugin/' . $plugin_name . '.pm'};