root/trunk/plagger/lib/Plagger.pm

Revision 104 (checked in by miyagawa, 14 years ago)
  • Added IRC publish plugin. Fixes #46
  • Renamed publish.add_feed into publish.feed
  • Property svn:keywords set to Id Revision
Line 
1 package Plagger;
2 use strict;
3 our $VERSION = '0.5.1';
4
5 use 5.8.1;
6 use Carp;
7 use Data::Dumper;
8 use File::Find::Rule;
9 use YAML;
10 use UNIVERSAL::require;
11
12 use base qw( Class::Accessor::Fast );
13 __PACKAGE__->mk_accessors( qw(conf update subscription plugins_path) );
14
15 use Plagger::Date;
16 use Plagger::Entry;
17 use Plagger::Feed;
18 use Plagger::Subscription;
19 use Plagger::Template;
20 use Plagger::Update;
21
22 sub active_hooks {
23     my $self = shift;
24     my @hooks= keys %{$self->{hooks}};
25     wantarray ? @hooks : \@hooks;
26 }
27
28 sub context { undef }
29
30 sub bootstrap {
31     my($class, %opt) = @_;
32
33     my $self = bless {
34         conf  => {},
35         update => Plagger::Update->new,
36         subscription => Plagger::Subscription->new,
37         plugins_path => {},
38     }, $class;
39
40     my $config;
41     if (-e $opt{config} && -r _) {
42         $config = YAML::LoadFile($opt{config});
43         $self->{conf} = $config->{global};
44     } else {
45         croak "Plagger->bootstrap: $opt{config}: $!";
46     }
47
48     local *Plagger::context = sub { $self };
49
50     $self->load_plugins(@{ $config->{plugins} || [] });
51     $self->run();
52 }
53
54 sub load_plugins {
55     my($self, @plugins) = @_;
56
57     if ($self->conf->{plugin_path}) {
58         for my $path (@{ $self->conf->{plugin_path} }) {
59             my $rule = File::Find::Rule->new;
60                $rule->file;
61                $rule->name( qr/^\w[\w\.]*$/ );
62             my @files = $rule->in($path);
63
64             for my $file (@files) {
65                 next if $file =~ /\W(?:\.svn|CVS)\b/;
66                 my $pkg = $self->extract_package($file)
67                     or die "Can't find package from $file";
68
69                 (my $base = $file) =~ s!^$path/!!;
70                 $self->plugins_path->{$pkg} = $file;
71             }
72         }
73     }
74
75     for my $plugin (@plugins) {
76         $self->load_plugin($plugin) unless $plugin->{disable};
77     }
78 }
79
80 sub extract_package {
81     my($self, $file) = @_;
82
83     open my $fh, $file or die "$file: $!";
84     while (<$fh>) {
85         /^package (Plagger::Plugin::.*?);/ and return $1;
86     }
87
88     return;
89 }
90
91 sub load_plugin {
92     my($self, $config) = @_;
93
94     my $module = delete $config->{module};
95     $module =~ s/^Plagger::Plugin:://;
96     $module = "Plagger::Plugin::$module";
97
98     if (my $path = $self->plugins_path->{$module}) {
99         eval { require $path } or die $@;
100     } else {
101         $module->require or die $@;
102     }
103
104     $self->log(info => "plugin $module loaded.");
105
106     my $plugin = $module->new($config);
107     $plugin->register($self);
108 }
109
110 sub register_hook {
111     my($self, $plugin, @hooks) = @_;
112     while (my($hook, $callback) = splice @hooks, 0, 2) {
113         # set default rule_hook $hook to $plugin
114         $plugin->rule_hook($hook) unless $plugin->rule_hook;
115
116         push @{ $self->{hooks}->{$hook} }, +{
117             callback  => $callback,
118             plugin    => $plugin,
119         };
120     }
121 }
122
123 sub run_hook {
124     my($self, $hook, $args) = @_;
125     for my $action (@{ $self->{hooks}->{$hook} }) {
126         my $plugin = $action->{plugin};
127         if ( $plugin->rule->dispatch($plugin, $hook, $args) ) {
128             $action->{callback}->($plugin, $self, $args);
129         }
130     }
131 }
132
133 sub run {
134     my $self = shift;
135
136     $self->run_hook('subscription.load');
137
138     for my $type ($self->subscription->types) {
139         for my $feed ($self->subscription->feeds_by_type($type)) {
140             $self->run_hook("aggregator.aggregate.$type", { feed => $feed });
141         }
142     }
143
144     for my $feed ($self->update->feeds) {
145         for my $entry ($feed->entries) {
146             $self->run_hook('update.entry.fixup', { feed => $feed, entry => $entry });
147         }
148         $self->run_hook('update.feed.fixup', { feed => $feed });
149     }
150
151     $self->run_hook('update.fixup');
152
153     $self->run_hook('smartfeed.init');
154     for my $feed ($self->update->feeds) {
155         for my $entry ($feed->entries) {
156             $self->run_hook('smartfeed.entry', { feed => $feed, entry => $entry });
157         }
158     }
159
160     for my $feed ($self->update->feeds) {
161         for my $entry ($feed->entries) {
162             $self->run_hook('publish.entry.fixup', { feed => $feed, entry => $entry });
163         }
164         $self->run_hook('publish.feed', { feed => $feed });
165     }
166
167     $self->run_hook('publish.finalize');
168 }
169
170 sub log {
171     my($self, $level, $msg) = @_;
172     my $caller = caller(0);
173     chomp($msg);
174     warn "$caller [$level] $msg\n";
175 }
176
177 sub error {
178     my($self, $msg) = @_;
179     my($caller, $filename, $line) = caller(0);
180     chomp($msg);
181     die "$caller [fatal] $msg at line $line\n";
182 }
183
184 sub dumper {
185     my($self, $stuff) = @_;
186     local $Data::Dumper::Indent = 1;
187     $self->log(debug => Dumper($stuff));
188 }
189
190 sub template {
191     my $self = shift;
192     $self->{template} ||= Plagger::Template->new($self);
193 }
194
195 1;
196 __END__
197
198 =head1 NAME
199
200 Plagger - Pluggable RSS/Atom Aggregator
201
202 =head1 SYNOPSIS
203
204   % plagger
205
206 =head1 DESCRIPTION
207
208 Plagger is a pluggable RSS/Atom feed aggregator. See
209 L<http://plagger.org/> for the latest code, development community and
210 bug tracking.
211
212 =head1 AUTHOR
213
214 Tatsuhiko Miyagawa E<lt>miyagawa@bulknews.netE<gt>
215
216 See I<AUTHORS> file for the name of all the contributors.
217
218 =head1 COPYRIGHT
219
220 This library is free software; you can redistribute it and/or modify
221 it under the same terms as Perl itself.
222
223 =head1 SEE ALSO
224
225 L<http://plagger.org/>
226
227 =cut
Note: See TracBrowser for help on using the browser.