Plagger::Plugin::Publish::InfoseekMail
PlaggerのPlugin作ってみた。
取得したフィードをInfoseekのwebメールに送信。
package Plagger::Plugin::Publish::InfoseekMail; use strict; use warnings; use base qw( Plagger::Plugin ); our $VERSION = '0.0.1'; use Plagger::Mechanize; use Encode; sub rule_hook { 'publish.feed' } sub register { my($self, $context) = @_; $context->register_hook( $self, 'publish.feed' => \¬ify, ); } sub init { my $self = shift; $self->SUPER::init(@_); $self->conf->{username} or Plagger->context->error("username is required"); $self->conf->{password} or Plagger->context->error("password is required"); $self->conf->{to} ||= $self->conf->{username}; } sub notify { my($self, $context, $args) = @_; return if $args->{feed}->count == 0; my $cfg = $self->conf; my $feed = $args->{feed}; my $uri = 'http://email.www.infoseek.co.jp/'; my $username = $cfg->{username}; my $password = $cfg->{password}; my $to = $cfg->{to}; my $subject = $feed->title || '(no-title)'; my $body = $self->templatize('infoseek_notify.tt', { feed => $feed }); $context->log(info => "Sending $subject to $to"); # トップページ取得 my $mech = Plagger::Mechanize->new; $mech->get($uri); # ログイン $mech->submit_form( form_name => 'login', fields => { username => $username, password => $password, }, ); # メール作成画面へ移動 $mech->follow_link(url => $uri); $mech->submit_form( form_number => 3, ); # 送信 $mech->form_name('tform'); $mech->set_fields( sto => $to, ssbj => $self->to_eucjp($subject), sbdy => $self->to_eucjp($body), ); $mech->click_button(number => 1); } sub to_eucjp { my ($self, $str) = @_; $str = encode('eucjp', $str); return $str; } 1;
YAMLはこんな感じ。
- module: Publish::InfoseekMail config: to: 送信先メールアドレス(指定しない場合はusername) username: Infoseekのメールアドレス password: パスワード
ほとんどPublish::Gmailのコピペ。
とりあえず送信はできました。
※ちゅういがき※
エラー処理とかしてません。
enclosureよくわからんかったので省いた。
テキストメールで送信しちゃうのでHTMLタグがそのまま表示されます。
rule_hookて何をするところなのかわかってません。
WebService::RakuAPIのソース読んだ(2)
ソースコードの中でこれいいなと思ったのが、
Resultクラスでやってる処理。
no strict 'refs'; *{String::CamelCase::decamelize($_)} = \&$_ for @Fields;
型グロブ使わずにやるならnewの中にこんな感じの処理をいれなきゃいけない。
my @accessors; foreach (@Fields) { my $accessor = String::CamelCase::decamelize($_); $self->{$accessor} = $self->{$_}; push @accessors, $accessor; } $class->mk_accessors(@accessors);
少し勉強になった。
WebService::RakuAPIのソース読んだ
勉強がてらRakuAPIのモジュール作ったわけですが、
伊藤さんのコードはどんな感じかな〜と読んでみました。
まず、知らないモジュールをいくつか発見。
- Class::ErrorHandler
- Readonly
- URI
- String::CamelCase
さっそくCPANに行ってこれらのモジュールを探してみる。
Class::ErrorHandler
どうやらエラーメッセージ用アクセサ?っぽい。
メソッドはセッタのerror($message)とアクセサのerrstr。
継承させてつかうみたい。
Readonly
use constantな働きをするモジュール。
use constantでは、
- 配列とハッシュにはちょっと特殊な書き方しなくちゃいけない。
- レキシカルスコープにできない。
- コンパイル時にしか働かない?(Works only at compile time)
- オーバーライド可能。
なのを、Readonlyではその辺カバーしてくれるらしい。
「Perlベストプラクティス」に載ってるモジュールだそうな。
URI
これいいね。
今まで知らんかったのが残念。
my $uri = URI->new('http://rakuapi.ddo.jp/api'); $uri->query_form( keyword => 'PSP', sort => 'new', genre => 'book', ); print $uri;
ってやると、
http://rakuapi.ddo.jp/api?keyword=PSP&sort=new&genre=book
こんな感じでURI生成してくれます。
超便利。
String::CamelCase
これもなかなか便利そう。
文字列を特定の形式に変換してくれます。
camelize($str)は、'some_keyword'を'SomeKeyword'に。
decamelize($str)は、'SomeKeyword'を'some_keyword'に。
wordsplit($str)は'some_keyword'を(some, keyword)、
もしくは'SomeKeyword'を(Some, Keyword)に。
モジュールの使い方だけでも大変勉強になりました。
続きは明日。
ウェブ進化論:梅田望夫
- 作者: 梅田望夫
- 出版社/メーカー: 筑摩書房
- 発売日: 2006/02/07
- メディア: 新書
- 購入: 61人 クリック: 996回
- この商品を含むブログ (2353件) を見る
RakuAPI.pm
なんかいろいろおかしかったので修正。
package RakuAPI; use strict; use warnings; use XML::TreePP; use base qw(Class::Accessor::Fast); __PACKAGE__->mk_accessors(qw/tpp base_url/); sub new { my $class = shift; my $self = { tpp => XML::TreePP->new(force_array => ['Result']), base_url => 'http://rakuapi.ddo.jp/api?keyword=', }; return bless $self, $class; } sub search { my ($self, $keyword, $option) = @_; my @params; if ( $option ) { foreach ( keys %{$option} ) { my $param = $_ . '=' . $option->{$_}; push @params, $param; } } $keyword =~ s/(\W)/'%' . unpack('H2', $1)/eg; my $reqest_url = $self->base_url . $keyword; $reqest_url .= '&' . join('&', @params) if ( $option ); my $parsed_xml = $self->tpp->parsehttp(GET => $reqest_url); my @results; foreach my $result( @{$parsed_xml->{ResultSet}->{Result}} ) { push @results, RakuAPI::Result->new($result); } return @results; } 1; package RakuAPI::Result; use strict; use warnings; use base qw(Class::Accessor::Fast); sub new { my ($class, $result) = @_; my $self = $class->SUPER::new($result); $class->mk_accessors(keys %{$result}); return bless $self, $class; } 1;
まだいろいろおかしいような気ガス。
一応動く。
use strict; use warnings; use RakuAPI; my $app = RakuAPI->new; my @results = $app->search('PSP', {sort => 'high'}); foreach my $result ( @results ) { print $result->Title, "\n"; print $result->Url, "\n"; }
上の実行結果。
新品!カルピジャーニ ソフトクリームフリーザー AES403PSP/S http://item.rakuten.co.jp/recyclemart/cpjheaaa19010n/ 新品!カルピジャーニ ソフトクリームフリーザー AES403PSP/S http://www.rakuten.co.jp/recyclemart/451778/452834/593927/#693600 新品!カルピジャーニ ソフトクリームフリーザー AES261PSP/S http://item.rakuten.co.jp/recyclemart/cpjheaaa19000n/ 新品!カルピジャーニ ソフトクリームフリーザー AES261PSP/S http://www.rakuten.co.jp/recyclemart/451778/452834/593927/#693494 新品!カルピジャーニ ソフトクリームフリーザー 191BAR/PSP http://item.rakuten.co.jp/recyclemart/cpjheaaa19040n/ 新品!カルピジャーニ ソフトクリームフリーザー 191BAR/PSP http://www.rakuten.co.jp/recyclemart/451778/452834/593927/#694419 中古:カルピジャーニ ソフトクリームフリーザー(ソフトクリーマー) AES-403PSP http://item.rakuten.co.jp/recyclemart/cpjhe95a21650u/ SBDB001 【25%OFF!】 スプリングドライブ SEIKO PRPSPEX[プロスペックス] http://www.rakuten.co.jp/iget/655089/727155/655180/661745/#618078 ●MUSE RESEARCH Receptor XT http://www.rakuten.co.jp/ikebe/443913/505946/622909/#633263 SBDS001 【25%OFF!】 SEIKO PRPSPEX[プロスペックス] http://www.rakuten.co.jp/iget/655089/727155/655180/656917/#613772
同一ファイル内にクラス複数作る時の書き方はあれでいいのかな。
Class::Accessor
よく
use base qw(Class::Accessor::Fast); __PACKAGE__->accessors(@accessors);
ってみかけるけど、
package Accessor; use base qw(Class::Accessor::Fast); sub new { my ($class, $hash_ref) = @_; $class->mk_accessors( keys %{$hash_ref} ); return $class->SUPER::new($hash_ref); }
こんなクラス作って、これを継承させたら
ハッシュリファレンスつっこむだけでアクセサ付のオブジェクト作れるなあと思ったりした。
あんま使い道なさそうだけど。