Plagger実行中にconfigを対話的に入力するプラグイン

パスワードが必要なプラグインを使う場合、config.yamlにパスワードを直接書いたりするようなのですが、対話的に入力させたいなあと思って実行中にパラメータを入力できるプラグインを作ってみました。(既にあったら悲しい)

使い方

例えばCustomFeed::Mixiの場合、

plugins:
  - module: CustomFeed::Mixi
    config:
      email: email@example.com
      password: password
      fetch_body: 1
      show_icon: 1
      feed_type:
        - RecentComment
        - FriendDiary
        - Message
  - module: Publish::Debug

のように書きますが、以下のようにすることでconfig.yamlを書き換えずに実行時にemailとpasswordを入力させることができます。

plugins:
  - module: CustomConfig
    config:
      - ReadLine:
          prompt: "What is your Mixi's email? [email@example.com] "
          name: email
      - ReadPassword:
          prompt: "What is your Mixi's password? [password] "
          name: password
  - module: CustomFeed::Mixi
    config:
      email: email@example.com
      password: password
      fetch_body: 1
      show_icon: 1
      feed_type:
        - RecentComment
        - FriendDiary
        - Message
  - module: Publish::Debug

CustomConfig内のconfigで指定されたプラグインが上から順に処理されます。ReadLineでは一時停止し一行入力待ちとなります。そこで入力した値が次のmodule(上の例ではCustomFeed::Mixi)のconfigに上書きされます。何も入力しなければ上書きしません(CustomFeed::Mixiでの値がデフォルト値となります)。promptが指定されている場合、その文字列は入力待ちに入る直前に標準エラー出力に印字されます。
ReadPasswordはReadLineのエコーバック無し版です。

気になること

  • 既に同じようなモジュールがあるかも知れません(致命的)。
  • 各Pluginのconfは独立しているため、$context->{plugins}を引っ張ってきて変なことをしています。
  • 適切なカテゴリが見つからなかったため、Plagger::PluginにCustomConfigを掘ってます。
  • CustomConfigのconfigを「password: ReadPassword」みたいな感じにしたかったのですが、設定キーが"password"を含んでいると、Plagger::Plugin#do_walk,Plagger#rewrite_configによって書き換えられてしまうため、少し直感的ではない設定方法にしています。
  • テストを書いていません。

ソース(*.pmのみ抜粋)

Plagger/Plugin/CustomConfig.pm
package Plagger::Plugin::CustomConfig;
use strict;
use warnings;
use version; our $VERSION = qv( (qw$Revision: 190 $)[1] / 1000 );

use Carp;
use English qw(-no_match_vars);
use UNIVERSAL::require;

use base qw(Plagger::Plugin);

sub register {
    my ( $self, $context ) = @_;

    $context->register_hook( $self, 'plugin.init' => \&init );
    return;
}

sub init {
    my ( $self, $context ) = @_;

    foreach my $i ( 0 .. @{ $context->{plugins} } ) {
        if ( ref $context->{plugins}->[$i] ne __PACKAGE__ ) {
            next;
        }
        $i++;
        my $plugin = $context->{plugins}->[$i];    # $plugin is the next plugin.
        foreach my $ref ( @{ $self->conf } ) {
            my $module = __PACKAGE__ . q{::} . ( keys %{$ref} )[0];
            my $config = ( values %{$ref} )[0];
            $module->require or croak $EVAL_ERROR;
            my $value = $module->run($config);
            if ( defined $value ) {
                $plugin->conf->{ $config->{name} } = $value;
            }
        }
    }
    return;
}

1;
Plagger/Plugin/CustomConfig/ReadLine.pm
package Plagger::Plugin::CustomConfig::ReadLine;
use strict;
use warnings;
use version; our $VERSION = qv( (qw$Revision: 190 $)[1] / 1000 );

use English qw(-no_match_vars);
use Term::ReadKey;

sub run {
    my ( $class, $config ) = @_;

    if ( exists $config->{prompt} ) {
        print {*STDERR} $config->{prompt};
    }

    my $value;
    eval {
        $value = ReadLine(0);
        chomp $value;
    };
    if ($EVAL_ERROR) {
        return;
    }

    return $value;
}

1;
Plagger/Plugin/CustomConfig/ReadPassword.pm
package Plagger::Plugin::CustomConfig::ReadPassword;
use strict;
use warnings;
use version; our $VERSION = qv( (qw$Revision: 190 $)[1] / 1000 );

use English qw(-no_match_vars);
use Term::ReadKey;

sub run {
    my ( $class, $config ) = @_;

    if ( exists $config->{prompt} ) {
        print {*STDERR} $config->{prompt};
    }

    my $value;
    eval {
        ReadMode('noecho');
        $value = ReadLine(0);
        chomp $value;
    };
    ReadMode(0);
    if ($EVAL_ERROR) {
        return;
    }

    return $value;
}

1;