1 |
=head1 NAME |
2 |
|
3 |
SuikaWiki::Plugin - SuikaWiki: WikiPlugin common interface |
4 |
|
5 |
=head1 DESCRIPTION |
6 |
|
7 |
This module provides WikiPlugin interface implementation. |
8 |
|
9 |
There are two interface: class methods collection and WikiPlugin object. |
10 |
Class methods collection provides some useful facilities to be |
11 |
called by WikiPlugin module and/or by contexts using WikiPlugin. |
12 |
|
13 |
This module is part of SuikaWiki. |
14 |
|
15 |
=cut |
16 |
|
17 |
package SuikaWiki::Plugin; |
18 |
use strict; |
19 |
our $VERSION = do{my @r=(q$Revision: 1.16 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r}; |
20 |
|
21 |
=head1 CLASS METHODS |
22 |
|
23 |
=over 4 |
24 |
|
25 |
=item $formatter = SuikaWiki::Plugin->formatter ($category_name) |
26 |
|
27 |
Returns an instance of formatter whose category (context) is |
28 |
$category_name. |
29 |
|
30 |
=cut |
31 |
|
32 |
{my %Formatter; |
33 |
sub formatter ($$) { |
34 |
my $t = $_[1]; $t =~ tr/-/_/; |
35 |
unless ($Formatter{$t}) { |
36 |
$Formatter{$t} = new SuikaWiki::Plugin::xml_formatter |
37 |
-category_name => $t; |
38 |
} |
39 |
return $Formatter{$t}; |
40 |
}} |
41 |
|
42 |
{my %Formatter; |
43 |
sub text_formatter ($$) { |
44 |
my $t = $_[1]; $t =~ tr/-/_/; |
45 |
unless ($Formatter{$t}) { |
46 |
$Formatter{$t} = new SuikaWiki::Plugin::text_formatter |
47 |
-category_name => $t; |
48 |
} |
49 |
return $Formatter{$t}; |
50 |
}} |
51 |
|
52 |
{my %Formatter; |
53 |
sub boolean_formatter ($$) { |
54 |
my $t = $_[1]; $t =~ tr/-/_/; |
55 |
unless ($Formatter{$t}) { |
56 |
$Formatter{$t} = new SuikaWiki::Plugin::boolean_formatter |
57 |
-category_name => $t; |
58 |
} |
59 |
return $Formatter{$t}; |
60 |
}} |
61 |
|
62 |
=item $package = SuikaWiki::Plugin->module_package ($plugin_name) |
63 |
|
64 |
Returns package name of the plugin module named $plugin_name. |
65 |
|
66 |
=cut |
67 |
|
68 |
## NOTE: Fix mkplugin2.pl when modify this function. |
69 |
sub module_package ($$;%) { |
70 |
my ($self, $module, %opt) = @_; |
71 |
my $pack = $SuikaWiki::Plugin::Registry::Info{$module}->{module_name}; |
72 |
if ($pack) { |
73 |
return $pack; |
74 |
} elsif (not $opt{allow_undef}) { |
75 |
throw SuikaWiki::Plugin::error |
76 |
-type => 'PLUGIN_NOT_FOUND', |
77 |
module => $module, |
78 |
-object => $self, method => 'module_package'; |
79 |
} else { |
80 |
return undef; |
81 |
} |
82 |
} |
83 |
|
84 |
=item SuikaWiki::Plugin->load_directory ($dir1, $dir2,...) |
85 |
|
86 |
Loads plugin modules in specified directories. |
87 |
|
88 |
=cut |
89 |
|
90 |
sub load_directory ($;@) { |
91 |
my $self = shift; |
92 |
for my $dir (@_) { |
93 |
opendir PDIR, $dir or throw SuikaWiki::Plugin::error |
94 |
-type => 'PLUGIN_DIRECTORY_CANT_OPEN', |
95 |
file => $dir, |
96 |
message => $!, |
97 |
-object => $self, method => 'load_directory'; |
98 |
for (grep {substr ($_, -3) eq '.pm'} readdir PDIR) { |
99 |
eval { require $dir.'/'.$_ }; |
100 |
if ($@) { |
101 |
throw SuikaWiki::Plugin::error |
102 |
-type => 'PLUGIN_COMPILE_ERROR', |
103 |
file => $dir.'/'.$_, |
104 |
message => $@, |
105 |
method => 'load_directory', -object => $self; |
106 |
} |
107 |
} |
108 |
closedir PDIR; |
109 |
} |
110 |
} |
111 |
|
112 |
sub load_file ($@) { |
113 |
my $self = shift; |
114 |
for (@_) { |
115 |
eval { require $_.'.pm' }; |
116 |
if ($@) { |
117 |
throw SuikaWiki::Plugin::error |
118 |
-type => 'PLUGIN_COMPILE_ERROR', |
119 |
file => $_.'.pm', |
120 |
message => $@, |
121 |
method => 'load_file', -object => $self; |
122 |
} |
123 |
} |
124 |
} |
125 |
|
126 |
=back |
127 |
|
128 |
=head1 WIKIPLUGIN MANAGER OBJECT |
129 |
|
130 |
This part implements WikiPlugin manager object of SuikaWiki WikiPlugin |
131 |
implementation model, second edition. |
132 |
|
133 |
Note that class methods such as C<< ->load_directory >> |
134 |
and C<< ->formatter >> as also available as methods of WikiPlugin object, |
135 |
although result of those operation is still "global". |
136 |
|
137 |
WikiPlugin manager object is usually instantiated by C<init_plugins> |
138 |
method of Wiki implementation object (C<SuikaWiki::Implementation> module). |
139 |
|
140 |
$wiki = <SuikaWiki::Implementation instance>; |
141 |
$wiki->init_plugins; |
142 |
$plugin_manager = $wiki->{plugin}; |
143 |
|
144 |
=over 4 |
145 |
|
146 |
=item $p = SuikaWiki::Plugin->new (wiki => $WIKI) |
147 |
|
148 |
Constructs new instance of WikiPlugin manager |
149 |
|
150 |
=cut |
151 |
|
152 |
sub new ($;%) { |
153 |
my $class = shift; |
154 |
bless {@_}, $class; |
155 |
} |
156 |
|
157 |
sub exit ($) { |
158 |
my $self = shift; |
159 |
delete $self->{wiki}; |
160 |
$self->{exited} = 1; |
161 |
} |
162 |
|
163 |
sub DESTROY ($) { |
164 |
my $self = shift; |
165 |
$self->exit unless $self->{exited}; |
166 |
} |
167 |
|
168 |
=item $p->{wiki} |
169 |
|
170 |
Wiki implementation $p is associated with |
171 |
|
172 |
=back |
173 |
|
174 |
=cut |
175 |
|
176 |
package SuikaWiki::Plugin::xml_formatter; |
177 |
require Message::Util::Formatter::Node; |
178 |
our @ISA = 'Message::Util::Formatter::Node'; |
179 |
require Message::Markup::XML::Node; |
180 |
|
181 |
sub ___rule_def ($) { |
182 |
$SuikaWiki::Plugin::Rule{$_[0]->{-category_name}} || {}; |
183 |
} |
184 |
|
185 |
sub replace_option () {+{ |
186 |
-class => 'Message::Markup::XML::Node', |
187 |
}} |
188 |
|
189 |
package SuikaWiki::Plugin::text_formatter; |
190 |
require Message::Util::Formatter::Text; |
191 |
our @ISA = 'Message::Util::Formatter::Text'; |
192 |
|
193 |
sub ___rule_def ($) { |
194 |
$SuikaWiki::Plugin::Rule{$_[0]->{-category_name}} || {}; |
195 |
} |
196 |
|
197 |
package SuikaWiki::Plugin::boolean_formatter; |
198 |
require Message::Util::Formatter::Boolean; |
199 |
our @ISA = 'Message::Util::Formatter::Boolean'; |
200 |
|
201 |
sub ___rule_def ($) { |
202 |
$SuikaWiki::Plugin::Rule{$_[0]->{-category_name}} || {}; |
203 |
} |
204 |
|
205 |
package SuikaWiki::Plugin::error; |
206 |
require Message::Util::Error; |
207 |
our @ISA = 'Message::Util::Error'; |
208 |
|
209 |
=head1 EXCEPTIONS |
210 |
|
211 |
=over 4 |
212 |
|
213 |
=item PLUGIN_COMPILE_ERROR |
214 |
|
215 |
Something wrong while loading WikiPlugin module file C<file>, |
216 |
because of C<message>. |
217 |
|
218 |
=item PLUGIN_DIRECTORY_CANT_OPEN |
219 |
|
220 |
WikiPlugin directory C<file> cannot be opened, because of C<message>. |
221 |
|
222 |
=item PLUGIN_NOT_FOUND |
223 |
|
224 |
WikiPlugin module C<module> not found. |
225 |
|
226 |
=cut |
227 |
|
228 |
sub ___error_def () {+{ |
229 |
PLUGIN_COMPILE_ERROR => { |
230 |
description => q(%t(name => method);: %t(name => file);: %t(name => message);), |
231 |
}, |
232 |
PLUGIN_DIRECTORY_CANT_OPEN => { |
233 |
description => q(%t(name => method);: %t(name => file);: %t(name => message);), |
234 |
}, |
235 |
PLUGIN_NOT_FOUND => { |
236 |
description => q(%t(name => method);: WikiPlugin module "%t(name => module);" not loaded), |
237 |
}, |
238 |
}} |
239 |
|
240 |
=back |
241 |
|
242 |
=head1 SEE ALSO |
243 |
|
244 |
<IW:SuikaWiki:SuikaWiki:WikiPlugin> |
245 |
|
246 |
C<lib/SuikaWiki/Plugin/*>, |
247 |
C<misc/pluging/*> |
248 |
|
249 |
=head1 LICENSE |
250 |
|
251 |
Copyright 2002-2003 Wakaba <w@suika.fam.cx> |
252 |
|
253 |
This program is free software; you can redistribute it and/or |
254 |
modify it under the same terms as Perl itself. |
255 |
|
256 |
=cut |
257 |
|
258 |
1; # $Date: 2004/06/03 06:38:48 $ |