Weitere ähnliche Inhalte
Ähnlich wie 「モダンPerl入門」の入門 (20)
Kürzlich hochgeladen (11)
「モダンPerl入門」の入門
- 2. 自己紹介
• 韓 松熙(song)
• 韓国出身のPerlエンジニア
• Perl歴4年目のnewbieです!
• 荘野 和也(miniturbo)
• SBMカウンタの開発者
• Perl歴半年のnewbieです!
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 2
- 3. サービス紹介
• スクールガーディアン
• 世界初の学校裏サイト・ネットいじめ対策サービス
• インターネット上からネットいじめに関する書き込みなどを収集
し学校に対策を提案するサービス
• 元々手動でこなしていたサービスを自動化することに
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 3
- 4. プロジェクトの紹介
• スクールガーディアンツール
• 掲示板やプロフなどから特定の学校の学校裏サイトの情報を
収集するツールです
• 学校の先生が内容を確認できるように、サイト画面全体のスク
リーンショットや投稿データを取得できます
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 4
- 5. モダンPerl との出会い
2008年9月 2009年1月 2009年4月 2009年7月 2009年9月
モダンPerl Catalyst5.8 YAPC::Asia
入門発売 リリース モダンPerl
の入門の入門
モダン
Perl界隈 JPAセミナー#1
モダンPerlの現場
Catalyst
プロジェクト
企画 5.7ベースで Catalyst 5.8で開発
スケジュール 調査/設計
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 5
- 8. アプリケーションの分割
• Catalyst
• SchoolG::Client (エンドユーザ機能)
• SchoolG::Work (管理者機能)
• ジョブ
• SchoolG::Batch ( TheSchwartz Job )
• 共通
• SchoolG (共有モジュール/API)
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 8
- 9. フォルダ構造
app_root/
SchoolG-Client/
lib/
root/ CatalystApp
script/
template/
SchoolG-Work/
lib/
root/ CatalystApp
script/
template/
SchoolG-Batch/
lib/
script/ バッチ
t/
SchoolG/
config/
extlib/
lib/ 共通部分
script/
t/
template/
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 9
- 10. 実装
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 10
- 11. 使いたかった技術
• Catalyst 5.8 + Moose
• API
• Web::Scraper
• TheSchwartz
• local::lib
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 11
- 12. Catalyst 5.8
• local::libで運用
• 開発環境でCatalyst5.7と5.8を共存させたかった
• Catalyst::UpgradingのPODを参考にした
• Catalystをextendsしていた
• Pluginは極力使わないようにした
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 12
- 13. 最小限のPlugin
• ログイン関連
• Catalyst::Plugin::Authentication
• Catalyst::Plugin::Authorization::Roles
• Session関連
• Catalyst::Plugin::Session
• Catalyst::Plugin::Session::Store::Memcached
• Catalyst::Plugin::Session::State::Cookie
• その他
• Catalyst::Plugin::FillInForm
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 13
- 15. APIへの第一歩
• モダンPerl入門
これをより汎用的にして他のスクリプトからも呼べるようにするには、$c->model()
が呼び出された時にCatalyst依存のMyApp::Model::Userではなく単体で
動作するオブジェクトを返すようにしてしまえばいいのです。
「モダンPerl入門」 より引用
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 15
- 16. Modelの分離
• Controllerにロジックを書いていた時代
• Controllerの見通しが悪くなっていった
• Controllerからロジックを分離する
• ロジックはModelに切り出した
• 但し、ModelはCatalystに依存したまま
• Modelからロジックを分離する
• Model::Adaptorを利用してModelとロジックをマ
ッピングさせた
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 16
- 17. Modelの分離
• Model::Adaptorで一つ一つマッピングするの
は意外と大変
• Model::MultiAdaptorでつなぐようにした
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 17
- 18. Modelの分離(API)
package SchoolG::Work::Model::API::Logic;
use strict;
use warnings;
use parent 'Catalyst::Model::MultiAdaptor';
1;
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 18
- 19. Modelの分離(YAML)
Model::API::Logic:
package: 'SchoolG::API::Logic'
lifecycle: Singleton
except:
- 'Plugin::Filter'
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 19
- 20. Modelの分離はいったん完成
でも画面を書いていくうちに、
APIの中身がごちゃごちゃに…
これでAPI?
分離するだけは何かが足りない…
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 20
- 21. API?
• Wikipedia
Application programming interface (API) is an interface in computer
science that defines the ways by which an application program may request
services from libraries and/or operating systems
The API initialism may sometimes be used as a reference, not only to the
full interface, but also to one function, or even a set of multiple APIs
provided by an organization. Thus, the scope of meaning is usually
determined by the person or document that
communicates the information.
Wikipediaより引用
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 21
- 22. なら、APIは自分たちなりに
定義して
決めたらいいの?
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 22
- 23. 社内での議論(1)
• 分離したロジックはなぜごちゃごちゃになっている?
• APIモジュール群がうまく分類されていなくて、どこにどの処
理が書いてあるのか見失いやすくなった
• 結局ロジックを書く場所が変わっただけ?
• 分離したロジックを整理していかなければいけない
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 23
- 24. 社内での議論(1)
• WebアプリケーションはDB処理が多い
• 至るところで繰り返し呼び出されるDB処理(CRUD処
理)と、一連の処理の流れ(ビジネスロジック)を整理す
ることでAPIの見通しがよくなるのではないかと考えた
• それぞれどこに、どのように書くべきか?
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 24
- 25. 社内での議論(2)
A案:API≒CRUD処理派
SchoolG API CRUD処理
Logic ビジネスロジック
DBIC DBICの接続
B案:API≒ビジネスロジック派
SchoolG DBIC CRUD処理(DBICの接続)
API ビジネスロジック
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 25
- 26. 社内での議論(3)
• APIは隠ぺい化された機能
• CRUD処理も ビジネスロジックも、隠蔽化さ
れた機能として(どのコンテキストからも呼び
出せるように)実装していきたい
• つまりCRUD処理もビジネスロジックもAPI!
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 26
- 27. 社内での議論(4)
SchoolG API Data CRUD処理
最終
案
Logic ビジネスロジック
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 27
- 28. 実装してフィードバック、
また修正してフィードバック、
…
出来上がってきた!
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 28
- 29. API
API::Data
APIの構成 API::Logic
API
• API::Data
• DBへの接続
• SQLの発行
• API::Logic
• ビジネスロジック
• Dataを呼び出してデータを取得・整形
• API
• API::Jobqueue
• API::Plugin
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 29
- 30. API
API::Data
API::Logic
API
API::Data
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 30
- 31. API
API::Data
API::Data API::Logic
API
• 役割
• DBへの接続とSQLの発行を行う
• 子クラスにCRUD処理を記述する
• 機能
• レプリケーション管理
• コネクションプーリング
• 返値のデータ形式の統一
• 実装のポイント
• 子クラスはテーブル毎に作るようにした
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 31
- 32. API::DataとDBICのつなぎ方
package SchoolG::API::Data;
has _master_schema => (
is => 'rw',
required => 1,
isa => 'DBIx::Class::Schema',
default =>
sub {
# Singleton化した
SchoolG::DBIC::Singleton->instance->_master
}
);
sub master {
my ( $self, $table_name ) = @_;
$table_name ||= $self->_table_name;
return $self->_master_schema
->resultset($table_name);
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 32
- 33. API::Dataのデータの返し方
package SchoolG::API::Data;
# DBICのsearchは直接利用せずに親クラス(このクラス)のsearchを利用する
sub search {
my ( $self, $args ) = @_;
my $conditions = $args->{conditions};
my $options = $args->{options};
# searchの場合はslaveのschemaを利用
my $resultset =
$self->slave->search( $conditions, $options );
# DBICのオブジェクトではなくHashRefかArrayRefで返すようにした
$resultset->result_class
('DBIx::Class::ResultClass::HashRefInflator');
return
exists $options->{page} && exists $options->{rows}
? {
result => [ $resultset->all ],
pager => $resultset->pager
}
: [ $resultset->all ];
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 33
- 34. API
API::Data
API::Logic
API
API::Logic
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 34
- 35. API
API::Data
API::Logic API::Logic
API
• 役割
• 子クラスにビジネスロジックを記述する
• Catalyst のComponentにはビジネスロジックは極力書かない
• CRUD処理はAPI::Dataに記述し、API::Logicから呼び出す
• API::Logicの子クラスで行う処理の流れ
• Validation処理
• 一連のCRUD処理(トランザクション管理も含む)
• CRUD処理結果の加工
• 実装のポイント
• Catalystアプリケーション及びジョブの両方からAPI::Logicを呼び出
せるようにした
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 35
- 36. package SchoolG::API::Logic::ManageStaff;
extends 'SchoolG::API::Logic';
sub create {
my ( $self, $args ) = @_;
my $created_by = delete $args->{created_by};
my $validation = $self->validate(
$args->{columns}, 'create' );
return $validation unless $validation->{success};
delete $args->{columns}->{cancel};
delete $args->{columns}->{submit};
$args->{columns}->{staff_created_by} = $created_by;
$args->{columns}->{staff_updated_by} = $created_by;
my $txn = sub {
$self->data('MasterStaff')->create($args);
};
$self->txn($txn);
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 36
- 37. API
API::Data
API::Logic
API
API
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 37
- 38. API
API::Data
API::Logic
API API
• 役割
• API::DataとAPI::Logicの親クラス
• 機能
• API::DataとAPI::Logicのアクセサの提供
• API::DataとAPI::Logicで共通メソッドの提供
• 実装のポイント
• ジョブからAPIを経由してAPI::DataとAPI::Logic両方を簡
単に呼べるようにした
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 38
- 39. API
API::Data
APIの最終形 API::Logic
API
Catalyst からも ジョブからもAPIを呼べる
API
Data
Catalyst
Job DB
Logic
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 39
- 41. package SchoolG::Work::Controller::Manage::Staff::Create;
sub execute : Local {
my ( $self, $c ) = @_;
my $result = $c->model('API::Logic::ManageStaff')
->create(
{
columns => $c->req->params,
created_by => $c->user->staff_seq
}
);
$c->res->redirect( $c->uri_for('/manage/staff') );
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 41
- 42. package SchoolG::API::Logic::ManageStaff;
extends 'SchoolG::API::Logic';
sub create {
my ( $self, $args ) = @_;
my $created_by = delete $args->{created_by};
my $validation = $self->validate(
$args->{columns}, 'create' );
return $validation unless $validation->{success};
delete $args->{columns}->{cancel};
delete $args->{columns}->{submit};
$args->{columns}->{staff_created_by} = $created_by;
$args->{columns}->{staff_updated_by} = $created_by;
my $txn = sub {
$self->data('MasterStaff')->create($args);
};
$self->txn($txn);
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 42
- 43. package SchoolG::API::Data::MasterStaff;
use Moose;
extends 'SchoolG::API::Data';
sub BUILD {
my $self = shift;
$self->_table_name('MasterStaff');
$self->_table_prefix('staff');
$self->_default_conditions( {
staff_delete => 0,
staff_seq => { '>' => 1 }
} );
$self->_default_options(
{ order_by => [qw/staff_office/] }
);
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 43
- 44. package SchoolG::API::Data;
use Moose;
has _table_prefix => (
is => 'rw',
isa => 'Str',
);
sub create {
my ( $self, $args ) = @_;
my $prefix = $self->_table_prefix;
my $columns = $args->{columns};
my $result = $self->master->create($columns);
return $result ?
{ success => 1 } : { success => 0 };
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 44
- 46. package SchoolG::Batch::Job::CountTask;
sub work {
my ( $class, $job ) = @_;
my $api = SchoolG::API->new();
my $result = $api->logic('CountTask')
->get_profile_count( $job->arg );
$job->completed;
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 46
- 47. package SchoolG::API::Logic::CountTask;
extends 'SchoolG::API::Logic';
sub get_profile_count {
my ( $self, $args ) = @_;
undef my $counts;
return $counts if $args->{operation} == 3;
$counts->{count_profile} =
$self->data('DataProfile')->count_for_count_task(
{
school_seq => $args->{school_seq},
status => 0
}
);
return $counts;
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 47
- 48. package SchoolG::API::Data::DataProfile;
use Moose;
extends 'SchoolG::API::Data';
sub count_for_count_task {
my ( $self, $args ) = @_;
my $conditions;
$conditions->{profile_school_seq} =
$args->{school_seq};
$self->SUPER::count(
{ conditions => $conditions }
);
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 48
- 50. APIを設計しながら
• 機能単位の分割するのが難しかった
• ルールをすべて守るのが難しかった
• リファクタリング5回くらい!!!
• まとまってきれいにかけた
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 50
- 51. まとめ
• API設計
• CRUD処理はAPI::Dataに、ビジネスロジックは
API::Logicに分けることで見通しがよくなった
• Catalystからもジョブからも呼べるような構成となった
• Catalyst 5.8
• 今回CatalystのComponentではMooseの機能は使って
いない
• Mooseの機能をCatalyst内で使わない限りは5.7とほとん
ど同じように書くことができた
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 51
- 52. 今後に向けて
• 別のプロジェクトでも通用するのか試してみたい
• ガイアックスでは今回APIをこのように考えて実装し
てみました
• 「APIってこんな考え方もあるよ」などの意見があれ
ば是非教えて下さい
• パフォーマンス向上などこれからも新しいことに挑戦
していきます
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 52
- 53. 参考にさせていただきました
• Catalyst::Upgrading
• http://search.cpan.org/~flora/Catalyst-Runtime-
5.80011/lib/Catalyst/Upgrading.pod
• モダンPerlの世界へようこそ - 第6回
Catalyst::Upgrading:検証はお早めに
• http://gihyo.jp/dev/serial/01/modern-perl/0006
• Moosification: Catalyst 5.8に移行した際にちょっと
気づいた事。
• http://mt.endeworks.jp/d-6/2009/05/moosification-catalyst-
58.html
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 53
- 54. まだ終わってないです!
Perlじゃないって言われても
気にしません!
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 54
- 56. スクリーンショットの要件
• 学校裏サイトのスクリーンショッ
トが欲しかった
• サムネイルではなく、ページ全
体のスクリーンショットが必要
だった
• 長いページでもページ全体を
撮る必要があった
• 実装にあたって、2つのアプ
ローチがありました
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 56
- 57. アプローチ
• Xvfb案
• Xvfb(Xの仮想環境)にブラウザを立ち上げて、Xvfb
自体をキャプチャ!
• Canvas案
• FirefoxのCanvas要素でWebページをキャプチャ!
• まずはXvfbの実装から試してみることに!!
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 57
- 58. 案①
• Xvfbでキャプチャ!
• XvfbにFirefoxを起動し、 取得
X Window Dumpで 範囲
ファイルとして保存する
• …この案では、 Xの仮
想環境の高さまでしかペ
ージのキャプチャが取得
できない!
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 58
- 59. 案②
• Canvasでキャプチャ!
• Firefoxで、Canvas要素
へWebサイトを描画し、
それをファイルとして保
存する
取得
範囲
• …コンテンツ量の多いサ
イトで、メモリ不足になり、
キャプチャが取得できな
いときがある!
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 59
- 60. 案③
• Canvasでキャプチャ!
取得
• Canvas要素でスクリーン 範囲
ショットを取得
• 工夫として、撮影領域の
高さを固定し、分割して
キャプチャするようにした
• →高さを固定できるので、 取得
1回のキャプチャのメモリ 範囲
使用量の上限を固定でき
る
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 60
- 61. 結果…
• Canvas案を採用しました!
• この案をもとに、開いたページのスクリーンショットを
撮るFirefoxのアドオンを制作しました
• Canvasでスクリーンショットを取得
• XPCOMでディスクへファイルとして書き込み
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 61
- 62. 実装
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 62
- 63. CanvasでWebページを描画
• canvas要素を作成し、Webページを描画する
• toDataURL()を用いて描画したデータの
data:URLを取得する
var canvas = window.document.createElement(‘canvas’);
var ctx = canvas.getContext('2d');
// Webページの描画
ctx.drawWindow(window, X開始点, Y開始点,
X終了点, Y終了点, ‘rgb(0,0,0)’);
ctx.restore();
// 描画したデータをBASE64エンコードしたdata:URLを取得
var url = canvas.toDataURL(‘image/png’);
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 63
- 64. ファイルの保存
• nsIURIオブジェクトの生成
• nsILocalFileでファイルオブジェクトを生成
• nsIWebBrowserPersistでファイルに保存する
var Cc = Components.classes;
var Ci = Components.interfaces;
// URIオブジェクトの生成
var URI = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService).newURI(url, null, null);
// ファイルオブジェクトの生成
var file = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsILocalFile);
file.initWithPath(保存先のファイルパス);
// ファイルに保存
var wbp = Cc['@mozilla.org/embedding/browser/nsWebBrowserPersist;1']
.createInstance(Ci.nsIWebBrowserPersist);
wbp.saveURI(url, null, null, null, null, file);
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 64
- 65. 分割キャプチャ
以上の実装を、ブラウザの高さで
分割して、繰り返しキャプチャするようにした
var max = window.document.height;
var width = window.outerWidth;
var fromY = window.scrollY;
var height = window.innerHeight;
while (fromY <= max) {
var toY = (fromY + height) > max ?
max : fromY + height;
// キャプチャするメソッド
capture(window, width, fromY, toY);
if (fromY + height > max) break;
fromY = toY;
}
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 65
- 66. 実演
• デモをご覧ください
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 66
- 67. 参考にさせていただきました
• SCRAPBLOG : canvas要素によるWebペー
ジのスクリーンショット保存機能
• http://www.xuldev.org/blog/?p=37
• File I/O – MDC
• https://developer.mozilla.org/ja/Code_snippets/File_I
%2F%2FO
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 67
- 68. 最後に告知
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 68
- 69. 仲間募集中!
• ガイアックスでは、小さなチームで新しい技術を積極的に取り入れながら
自社プロダクトを開発しています。
• 一緒にチャレンジし、成長していく意欲のある開発者を募集中です。
9/17/2009 Copyright Since 1999 © GaiaX Co. Ltd. All rights reserved. 69