| 1 |
package Plagger::Plugin::Store::DBIC; |
|---|
| 2 |
use strict; |
|---|
| 3 |
use warnings; |
|---|
| 4 |
use base qw/Plagger::Plugin Class::Accessor::Fast/; |
|---|
| 5 |
|
|---|
| 6 |
use Algorithm::Diff; |
|---|
| 7 |
use UNIVERSAL::require; |
|---|
| 8 |
|
|---|
| 9 |
our $VERSION = '0.02'; |
|---|
| 10 |
|
|---|
| 11 |
__PACKAGE__->mk_accessors(qw/schema/); |
|---|
| 12 |
|
|---|
| 13 |
=head1 NAME |
|---|
| 14 |
|
|---|
| 15 |
Plagger::Plugin::Store::DBIC - Store feeds in DBI |
|---|
| 16 |
|
|---|
| 17 |
=head1 SYNOPSIS |
|---|
| 18 |
|
|---|
| 19 |
$ sqlite3 /path/to/plagger.db < sql/plagger.sqlite.sql |
|---|
| 20 |
|
|---|
| 21 |
- module: Store::DBIC |
|---|
| 22 |
config: |
|---|
| 23 |
schema_class: Plagger::Schema::SQLite |
|---|
| 24 |
connect_info: [ 'dbi:SQLite:/path/to/plagger.db', ] |
|---|
| 25 |
|
|---|
| 26 |
=head1 DESCRIPTION |
|---|
| 27 |
|
|---|
| 28 |
Store DBI |
|---|
| 29 |
|
|---|
| 30 |
=head1 METHODS |
|---|
| 31 |
|
|---|
| 32 |
=head2 register |
|---|
| 33 |
|
|---|
| 34 |
=cut |
|---|
| 35 |
|
|---|
| 36 |
sub register { |
|---|
| 37 |
my ( $self, $c ) = @_; |
|---|
| 38 |
|
|---|
| 39 |
unless ( $self->conf->{schema_class} and $self->conf->{connect_info} ) { |
|---|
| 40 |
$c->error('schema_class and connect_info are required'); |
|---|
| 41 |
} |
|---|
| 42 |
|
|---|
| 43 |
$self->conf->{schema_class}->require |
|---|
| 44 |
or $c->error( |
|---|
| 45 |
qq/Can't load schema class "@{[ $self->conf->{schema_class} ]}", $@/); |
|---|
| 46 |
|
|---|
| 47 |
$self->schema( $self->conf->{schema_class} |
|---|
| 48 |
->connect( @{ $self->conf->{connect_info} } ) ); |
|---|
| 49 |
|
|---|
| 50 |
$c->register_hook( $self, 'publish.feed' => \&store, ); |
|---|
| 51 |
} |
|---|
| 52 |
|
|---|
| 53 |
=head2 store |
|---|
| 54 |
|
|---|
| 55 |
=cut |
|---|
| 56 |
|
|---|
| 57 |
sub store { |
|---|
| 58 |
my ( $self, $c, $args ) = @_; |
|---|
| 59 |
|
|---|
| 60 |
# feed |
|---|
| 61 |
my $feed = $self->schema->resultset('Feed')->find_or_new( |
|---|
| 62 |
{ link => $args->{feed}->link, |
|---|
| 63 |
# type => $args->{feed}->type, |
|---|
| 64 |
} |
|---|
| 65 |
); |
|---|
| 66 |
|
|---|
| 67 |
my $data; |
|---|
| 68 |
for my $attr (qw/url title description language author updated/) { |
|---|
| 69 |
$data->{$attr} = $args->{feed}->$attr if defined $args->{feed}->$attr; |
|---|
| 70 |
} |
|---|
| 71 |
|
|---|
| 72 |
# xxx |
|---|
| 73 |
$data->{image} = $args->{feed}->image->{url} if $args->{feed}->image; |
|---|
| 74 |
$data->{updated} ||= eval { $args->{feed}->entries->[0]->date } || Plagger::Date->now; |
|---|
| 75 |
|
|---|
| 76 |
for my $attr (keys %$data) { |
|---|
| 77 |
$feed->$attr( $data->{$attr} ) if !$feed->$attr or $feed->$attr ne $data->{$attr}; |
|---|
| 78 |
} |
|---|
| 79 |
$feed->insert_or_update if !$feed->in_storage or $feed->is_changed; |
|---|
| 80 |
|
|---|
| 81 |
# feed meta |
|---|
| 82 |
$feed->metas->delete_all; # need more hack |
|---|
| 83 |
for my $k ( keys %{ $args->{feed}->meta } ) { |
|---|
| 84 |
$feed->add_to_metas( { keyid => $k, value=> $args->{feed}->meta->{$k} } ); |
|---|
| 85 |
} |
|---|
| 86 |
|
|---|
| 87 |
# feed tag |
|---|
| 88 |
my @feed_tags = map { $_->name } $feed->tags; |
|---|
| 89 |
my $feed_diff = $self->diff( \@feed_tags, $args->{feed}->tags ); |
|---|
| 90 |
|
|---|
| 91 |
$feed->tags( { name => $feed_diff->{deleted} } )->delete_all |
|---|
| 92 |
if @{ $feed_diff->{deleted} }; |
|---|
| 93 |
|
|---|
| 94 |
for ( @{ $feed_diff->{added} } ) { |
|---|
| 95 |
$feed->add_to_tags( { name => $_ } ); |
|---|
| 96 |
} |
|---|
| 97 |
|
|---|
| 98 |
|
|---|
| 99 |
# entry |
|---|
| 100 |
for my $feed_entry ( @{ $args->{feed}->entries } ) { |
|---|
| 101 |
my $entry = $self->schema->resultset('Entry')->find_or_new({ |
|---|
| 102 |
feed => $feed->id, |
|---|
| 103 |
link => $feed_entry->link, |
|---|
| 104 |
}); |
|---|
| 105 |
|
|---|
| 106 |
for my $attr (qw/title author summary date body rate/) { # todo: icon |
|---|
| 107 |
$entry->$attr( $feed_entry->$attr ) |
|---|
| 108 |
if defined $feed_entry->$attr && (!$entry->$attr or $entry->$attr ne $feed_entry->$attr); |
|---|
| 109 |
} |
|---|
| 110 |
$entry->insert_or_update if $entry->is_changed; |
|---|
| 111 |
|
|---|
| 112 |
# meta |
|---|
| 113 |
$entry->metas->delete_all; |
|---|
| 114 |
for my $k ( keys %{ $feed_entry->meta } ) { |
|---|
| 115 |
$entry->add_to_metas( |
|---|
| 116 |
{ keyid => $k, value => $feed_entry->meta->{$k}, } ); |
|---|
| 117 |
} |
|---|
| 118 |
|
|---|
| 119 |
# tag |
|---|
| 120 |
my @entry_tags = map { $_->name } $entry->tags; |
|---|
| 121 |
my $entry_diff = $self->diff( \@entry_tags, $feed_entry->tags ); |
|---|
| 122 |
|
|---|
| 123 |
$entry->tags( { name => $entry_diff->{deleted} } )->delete_all |
|---|
| 124 |
if @{ $entry_diff->{deleted} }; |
|---|
| 125 |
|
|---|
| 126 |
for ( @{ $entry_diff->{added} } ) { |
|---|
| 127 |
$entry->add_to_tags( { name => $_ } ) if $entry->id; # xxx |
|---|
| 128 |
} |
|---|
| 129 |
} |
|---|
| 130 |
} |
|---|
| 131 |
|
|---|
| 132 |
=head2 diff |
|---|
| 133 |
|
|---|
| 134 |
=cut |
|---|
| 135 |
|
|---|
| 136 |
sub diff { |
|---|
| 137 |
my ( $self, $old, $new ) = @_; |
|---|
| 138 |
|
|---|
| 139 |
my $result = { |
|---|
| 140 |
added => [], |
|---|
| 141 |
deleted => [], |
|---|
| 142 |
}; |
|---|
| 143 |
|
|---|
| 144 |
my $diff = Algorithm::Diff->new( $old, $new ); |
|---|
| 145 |
while ( $diff->Next ) { |
|---|
| 146 |
next if $diff->Same; |
|---|
| 147 |
|
|---|
| 148 |
if ( !$diff->Items(2) ) { |
|---|
| 149 |
push @{ $result->{deleted} }, $diff->Items(1); |
|---|
| 150 |
} |
|---|
| 151 |
else { |
|---|
| 152 |
push @{ $result->{added} }, $diff->Items(2); |
|---|
| 153 |
} |
|---|
| 154 |
} |
|---|
| 155 |
|
|---|
| 156 |
$result; |
|---|
| 157 |
} |
|---|
| 158 |
|
|---|
| 159 |
=head1 AUTHOR |
|---|
| 160 |
|
|---|
| 161 |
Daisuke Murase <typester@cpan.org> |
|---|
| 162 |
|
|---|
| 163 |
=head1 COPYRIGHT |
|---|
| 164 |
|
|---|
| 165 |
This program is free software; you can redistribute |
|---|
| 166 |
it and/or modify it under the same terms as Perl itself. |
|---|
| 167 |
|
|---|
| 168 |
The full text of the license can be found in the |
|---|
| 169 |
LICENSE file included with this module. |
|---|
| 170 |
|
|---|
| 171 |
=cut |
|---|
| 172 |
|
|---|
| 173 |
1; |
|---|
| 174 |
|
|---|