たろマーク (はてなブックマーク)
-
[ ruby ][ rails ][ cms ] RESTful なのいいな
-
[ ar ] 楽しそう。宝物を置かせてもらったお店とかは宣伝になったりするのかな。
-
[ yokohama.pm ][ waf ]
-
[ ruby ]
■ perl で Observer パターン
Ruby で Observer パターンをやってみたところで、Perl でもやってみたくなったので書いてみました。お題はコードの世界に載っているテキスト表示の時計です。
ruby にある observer モジュールは include で mixin して使うので、Perl では Moose の Role を使って実装してみます。(モダン Perl 入門にもデザパタの章で Observer パターンがありますが、あえて ruby の observer モジュールを模してみます。)
で、これが Observable な Role です。これは時計の心臓部 Tick の Role なので Tick::Role::Observable としました。
package Tick::Role::Observable;
use Moose::Role;
has observer => (
is => 'rw',
default => sub { {} },
);
has state => (
is => 'rw',
default => 0,
);
sub is_changed {
my ($self) = @_;
return $self->state();
}
sub changed {
my ($self) = @_;
$self->state(1);
}
sub notify_observers {
my ($self, @args) = @_;
return unless $self->is_changed;
$self->state(0);
foreach my $package ( keys %{$self->observer} ) {
eval {
$self->observer->{$package}->update(@args);
};
if ( $@ ) {
confess $@;
}
}
return 1;
}
sub add_observer {
my ($self, $obj) = @_;
my $package = ref $obj;
confess $obj . 'is not object'
unless $package;
confess $package . 'can not update'
unless $obj->can('update');
$self->observer->{$package} = $obj;
}
1;
このモジュールを with 'Tick::Role::Observable'; するとそのモジュールに Observer な振る舞いが付与されます。
元のお題の時計モジュールはこんな感じ。
package Tick;
use Moose;
with 'Tick::Role::Observable';
__PACKAGE__->meta->make_immutable();
no Moose;
use DateTime;
use DateTime::TimeZone;
use Time::HiRes qw(sleep gettimeofday);
sub start {
my ($self, @args) = @_;
my $tz = DateTime::TimeZone->new( name => 'Asia/Tokyo' );
while (1) {
my $now = DateTime->now( time_zone => $tz );
$self->changed();
$self->notify_observers($now->hour, $now->minute, $now->second);
# micro second 単位のズレを調整
sleep 1.0 - (gettimeofday)[1] / 1000000.0;
}
}
1;
Tick->new->start すると1秒ごと sleep するループに陥ります。
1秒ごとに changed メソッドで更新フラグを立てて、notify_observers メソッドで監視者のオブジェクトに更新を通知します。
では、監視者として時刻をテキスト表示するオブジェクトを用意します。
package TextClock;
use Moose;
__PACKAGE__->meta->make_immutable();
no Moose;
sub update {
my ($self, @args) = @_;
local $| = 1;
printf "\e[8D%02d:%02d:%02d", @args;
}
1;
Observable なモジュールの監視者になるには update メソッドが必要です。
notify_observer メソッドで更新通知される際には update メソッドが呼ばれるからです。
なので、 TextClock モジュールにも update メソッドを用意します。
引数には notify_observer メソッドに渡された引数が全部渡ってきます。
さて、これで時計の心臓部である Tick モジュールと TextClock モジュールができました。この二つを監視者と被監視者としてスクリプトにまとめてみます。
use Tick; use TextClock; my $tick = Tick->new(); $tick->add_observer( TextClock->new() ); $tick->start();
add_observer に update メソッドを持ったオブジェクトを渡してあげると、監視者と被監視者の関係が簡単にできます。
このスクリプトを実行すると現在時刻がテキストで表示され、1秒ごとに更新されます。
肝は接点が add_observer メソッドだけなところですね。
時計の表示を変えたくなったら update メソッドを持ったオブジェクトを作って add_observer してあげるだけで良くなります。(とコードの世界に書いてあった)
自分が普段使ってる言語で書き直せたらそれなりに理解できてるかしら?
日経BP出版センター
売り上げランキング: 44091

Rubyに導入された思考法翔泳社
売り上げランキング: 18356

perl経験者は読んで損はしない
Perl中級者におすすめしたい■ irc.lazy-people.org#project に thanksbot を復帰させました。
少し間が開いてしまいましたが、irc.lazy-people.org#project にありがとうを伝える thanksbot を放流しました。
この bot に対してありがとうを伝えると http://lazy-people.org/#ありがとう新着順 の一覧に載ります。まぁ、単純なボットですね。
このチャンネルには元々 thanksbot がいたのですが、lazy-people.org Xen 化作戦失敗以降復帰させていなかったのでした。
旧 thanksbot は mojunc という tomyhero 製 Web API を使っていたのですが、これを復帰させるのがめんどうだったので Wedata を使うことにしました。サーセン >_<
Wedata の使い方はこれで良いのかも謎w
そして、せっかくだからと覚え立ての ruby を使って書くことにしました。
IRC の bot 化には Net::IRC::Client を使い、Wedata への投稿には、こちらのモジュールを使わせていただきました。オリジナルの wedata.rb は、JsonParser に依存していたのですが、gem でインストールできる JSON を使うように少し修正しています。(修正したコード)
新規追加しかしないから wedata.rb は大げさだったかも。
#!/usr/bin/env ruby
# encoding: utf-8
$LOAD_PATH << "lib"
require 'rubygems'
require 'net/irc'
require 'wedata'
require 'observer'
require 'yaml'
require 'pp'
thanks_config = YAML.load_file('config.yaml')
class ThanksBot < Net::IRC::Client
include Observable
def initialize(*args)
p args
@channel = args[2]
args.delete_at 2
super
end
def on_rpl_welcome(m)
post JOIN, @channel
end
def on_privmsg(m)
if match = m[1].match(/^thanksbot:\s*(.*)/)
changed
notify_observers('', match[0])
post NOTICE, m[0], "#{m.prefix.nick}: #{match[0]}"
end
end
end
class ThanksDB < WedataDatabase
def update(name, message)
create_item(name, { 'message' => message })
end
end
thanksbot = ThanksBot.new(thanks_config['irc']['host'], thanks_config['irc']['port'], thanks_config['irc']['channel'],thanks_config['irc']['nicks'])
thanksdb = ThanksDB.new('thanks', thanks_config['wedata_api'])
thanksbot.add_observer(thanksdb)
thanksbot.start
ソースはここにあります。
Net::IRC::Client を継承した ThankBot と WedataDatabase を継承した ThanksDB は ovserver ライブラリを使って observer パターンで動作をひもづけてます。まつもとゆきひろ コードの世界を読んでいて、これを試して見たかったというのが大きいかもw
たぶんなにげに初めて書いた ruby プログラムのような気がします。
既存のライブラリを組み合わせただけでお手軽でした。ありがとうありがとう!
オライリージャパン
売り上げランキング: 13443

基本書にふさわしい本
rubyマスターになるために
鬱にされる鬱然たるRuby教科書
For Rubist日経BP出版センター
売り上げランキング: 40925

Rubyに導入された思考法





