Devel::REPLのプラグイン(1)

めも。


Devel::REPLの、プラグインの呼び出しには(たぶん)2種類あって、
1つめがDevel::PERLに定義されてるメソッドをhookする(before,after,aroundする)。
こっちのタイプは単純にMoose::Roleを使ってる。
Plugin::Colorsとかだと、

package Devel::REPL::Plugin::Colors;

use Moose::Role;
...
around print => sub {
  my $orig = shift;
  my $self = shift;
  print {$self->out_fh} color($self->normal_color);
  $orig->($self, @_);
  print {$self->out_fh} color('reset');
};

みたいな感じでDevel::REPL->print()にhookしてる。

このaroundとかが、どこでDevel::REPL->print()に設定されるかというと、
MooseX::Object::Pluggable->_load_and_apply_role()の中の、$role->meta->apply($self)の部分。

package MooseX::Object::Pluggable;
...
sub _load_and_apply_role{
    my ($self, $role) = @_;
    die("You must provide a role name") unless $role;

    eval { Class::MOP::load_class($role) };
    confess("Failed to load role: ${role} $@") if $@;

    carp("Using 'override' is strongly discouraged and may not behave ".
         "as you expect it to. Please use 'around'")
        if scalar keys %{ $role->meta->get_override_method_modifiers_map };

    $role->meta->apply( $self );
    return 1;
}

$roleには"Devel::Plugin::Colors"とか、プラグインクラス名が入ってて、
$role->metaはMoose::Meta::Role。たぶんuse Moose::Roleするとexportしてくれるのかな。んでapply()する。
Moose::Meta::Role->apply()は既存のインスタンスとかクラスとかにMixin(?)するメソッドで、
定義されてるメソッドだけじゃなくてaroundとかbeforeとかも一緒にやってくれる。


わすれないようにサンプル。

use strict;
use warnings;

package Foo;

sub new { bless {}, shift }
sub hello { print "hello\n" }

package Foo::Plugin::Hello;
use Moose::Role;

before 'hello' => sub {
  print "before hello\n"; 
};

sub buy {
  print "buy\n"
}

package main;

my $foo = Foo->new;
Foo::Plugin::Hello->meta->apply($foo);
$foo->hello();
$foo->buy();

#結果
before hello
hello
buy

まあこんな感じでDevel::REPLのメソッドが呼ばれるたびに、Pluginのaroundやらbeforeやらが呼ばれるみたい。

古いentryからDedupedしつつ1件ずつPublishするyaml

「サンプルコードによるPerl入門」を、最初の記事から順にメールで読みたいなあと思って書いてみた。
昨日のPlugin::Expressionを使ってます。

global:
  ...
plugins:
  - module: Subscription::Config
    config:
      feed: http://d.hatena.ne.jp/taraburo/rss2

  - module: Expression
    config:
      hooks:
        hook: update.feed.fixup
        expression: |
          my ($self, $context, $args) = @_;
          my $dedup = Plagger::Rule->new({
              module => 'Deduped',
              path => '/path/to/deduped.db',
          });
          my @entries = grep {
              $dedup->{deduper}->is_new($_);
          } $args->{feed}->entries;
          my $last_entry;
          if (@entries) {
              $last_entry = pop @entries;
              $dedup->{deduper}->add($last_entry);
              $context->log(debug => "deduper add entry: " . $last_entry->title);
          }
          $args->{feed}->{entries} = $last_entry ? [ $last_entry ] : [];

  - module: Publish::Gmail
    config:
      ...

こんなんしなくてもPluginとRule組み合わせればもっとスマートにいけるような気がするけど気にしない。


最初はFilter::ReverseとFilter::Ruleでいけるかと思ったんだけどなあ。

  - module: Filter::Reverse
  - module: Filter::Rule
    rule:
      - module: RecentN
        count: 1
      - module: Deduped

RecentNで1件に削ってそれをDedupedしてくれるわけじゃないんですね。

Plagger::Plugin::Expression

既存のPluginじゃ上手く処理できないんだけど、新しくPlugin書くほどでもないよなーって時に。

package Plagger::Plugin::Expression;
use strict;
use base qw( Plagger::Plugin );

sub init {
    my $self = shift;
    $self->SUPER::init(@_);

    $self->rule_hook($self->conf->{rule_hook});

    my $hooks = $self->conf->{hooks} or return;
    $hooks = [ $hooks ] unless ref $hooks eq 'ARRAY';

    for my $hook (@$hooks) {
        my $callback = sub {
            eval $hook->{expression};
            Plagger->context->error($@) if ($@);
        };
        push @{$self->{hooks}}, $hook->{hook}, $callback;
    }
}

sub register {
    my ($self, $context) = @_;
    $context->register_hook(
        $self,
        @{$self->{hooks}},
    );
}

1;

__END__

=head1 SYNOPSIS

    - module: Expression
      config:
        rule_hook: smartfeed.entry
        hooks:
          - hook: smartfeed.init
            expression: Plagger->context->log(debug => 'smartfeed.init')
          - hook: smartfeed.entry
            expression: my ($self, $context, $args) = @_; $context->log(debug => $args->{entry}->title)

    - module: Expression
      config:
        hooks:
          hook: update.feed.fixup
          expression: Plagger->context->log(debug => 'update.feed.fixup')
=cut

QIQ試してみた

前々から気になってたQIQを試してみました。

とりあえずPlaggerのrun_hookっぽい感じで。

<?php
class Pluggable {
    function loadPlugin($plugin, $to) {
        new $plugin($this, $to);
    }

    function registerHook() {
        foreach (func_get_args() as $hooks) {
            foreach ($hooks as $hook => $closure) {
                $this->hooks[$hook][] = $closure;
            }
        }
    }

    function runHook($hook) {
        foreach ($this->hooks[$hook] as $closure) {
            $closure();
        }
    }
}
// Pluginぽいの
class Pluggable_Plugin_Hello {
    function __construct($p, $to) {
        $this->to = $to;

        $self = $this; // $this は static $this で取り込めないようなので
        $p->registerHook(
            array(
                'hello_hook' => function() { echo "hello\n"; }
            ),
            array(
                'buy_hook' => static function() {
                    static $self;
                    $self->buy();
                }
            )
        );
    }

    function buy() {
        echo "buy {$this->to}\n";
    }
}

んで実行してみる。

<?php
$p = new Pluggable;
$p->loadPlugin('Pluggable_Plugin_Hello', 'id:taraburo');
$p->runHook('hello_hook');
$p->runHook('buy_hook');
hello
buy id:taraburo

PHPでも変態になれそう。。。

str_transliterate()

Matzにっき(2008-03-15)で知ったのだけれど、
PHP6の新しい関数str_transliterate()がおもろい。

$php -r "echo str_transliterate('taraburo', 'Latin', 'Hiragana');"

たらぶろ

うへー。


次の5.3でも名前空間や新しいsyntax追加だというし、最近はPHPも動きが活発になってきたなあ。

PHP5.3や6は下記でダウンロードできる。メモ。
http://snaps.php.net/

$php -r "echo str_transliterate('PHP6 ha sugoiyo!', 'Latin', 'Hiragana');"
ぷへぷ6 は すごいよ !
(´・ω・`)


PHP6のunicodeのpdf見つけた。あとで読む。
http://www.gravitonic.com/downloads/talks/fosdem-2008/php-unicode-i18n.pdf

Zend Debugger

PDT、PHP5.2.3(win)、ローカルのApacheで動かすまで。
なんかどのサイトみても設定とかばらばらではまった。。。


http://downloads.zend.com/pdt/server-debugger/から、
ZendDebugger-5.2.10-cygwin_nt-i386.tar.gzをダウンロード。
(ZendDebugger-5.2.12-cygwin_nt-i386.zipはなんでか動かなかった)


アーカイブを解凍したら、
5_2_x_comp\ZendDebugger.dllを
C:\PHP5\ext\ZendDebugger.dllとかにコピー(コピーする場所はどこでもOK)


php.iniに設定書く

zend_extension_ts="C:\PHP5\ext\ZendDebugger.dll" // コピーしたdllのパス
zend_debugger.allow_hosts=127.0.0.1
zend_debugger.expose_remotely=always


Apache再起動


PHPエクスプローラからPHPファイルを右クリック>デバッグPHP WEBページでデバッグ開始

require require require...

うはw こんなんできるんか。
return1.php

<?php
return 'return2.php';

return2.php

<?php
return 'return3.php';

return3.php

<?php
return 'end.php';

end.php

<?php
return 'Hello World!';

start.php

<?php
echo require require require require 'return1.php';

結果

$php start.php
Hello World!


ほかに思いつくのは

<?php
echo print print print print print `echo`;

とかでしょうか。