Contenu connexe
Similaire à 技術トレンディセミナー フレームワークとしてのTrac
Similaire à 技術トレンディセミナー フレームワークとしてのTrac (20)
技術トレンディセミナー フレームワークとしてのTrac
- 1. 技術 Trendy Seminar
フレームワークとしてのTrac
2009/03/03
エスエムジー株式会社
鈴木貴典
※資料中に記載の会社名および製品名は、各社商標または登録商標です。
※本資料の無断転載・複写を禁じます。
Copyright © SMG Co., Ltd. All rights reserved.
- 2. Hot Topic
1. 分散バージョン管理Git/Mercurial/Bazaar徹底比較(@IT)
http://www.atmarkit.co.jp/fjava/rensai4/devtool03/devtool03_1.html
最近は、分散バージョン管理が流行り!?
Gitは、はてなで標準として利用しているらしい
– 「株式会社はてなの開発戦略」(デブサミ2009 )
Mercurialは、本も出ている
– 「入門Mercurial Linux/Windows対応」(秀和システム)
2. Shibuya.trac 2009新年会の動画アップ(Shibuya.trac)
http://gihyo.jp/news/report/2009/02/0301
gihyo.jpで公開
2
Copyright © SMG Co., Ltd. All rights reserved.
- 3. 目次
1. Tracとは?
2. Tracのプラグイン
フレームワークとして見たTrac
3.
アークテクチャの詳細
4.
まとめ
5.
3
Copyright © SMG Co., Ltd. All rights reserved.
- 4. 1. Tracとは?
1. Tracの概要
ITS(Issue Tracking System)のひとつ
– Subversionの流行から、利用者が増えている
Pythonで開発されている
– Google App EngineがPythonをサポートしているので、今後、利用者
がさらに増える可能性あり
2. Tracの特徴
Subversionと簡単に連携できる
– リビジョン⇔チケットの相互関連付け
プラグインが豊富
Wikiも提供
Eclipse、VisualStudioなどからも連携可能
4
Copyright © SMG Co., Ltd. All rights reserved.
- 5. 2. Tracのプラグイン
1. Trac Hacksで公開
250個を超えるプラグインが公開
Trac Hacks自体もTracで構築
誰でも自由にプラグインを登録できる
2. アーキテクチャ
コンポーネントベースのアーキテクチャ
コンポーネントの1つ、もしくは複数から、
1つのプラグインとなる
詳しいチュートリアルはないが、他のプ
ラグインを参考にすれば、初めてでも開
参考:
発できてしまう簡単さ
Trac Component
Architecture
5
Copyright © SMG Co., Ltd. All rights reserved.
- 6. 3. フレームワークとして見たTrac
1. 優れたプラグインアーキテクチャ
基本は、必要な機能に合わせて、インタフェースを実装すれば、
Tracが必要なときに探し出して呼び出す
IoC(Inversion Of Control=制御の反転)の見本となる
2. ビューの分離
HTMLテンプレートを利用
テンプレートエンジンであるGenshiで実現
3. 簡単なコンフィギュレーションの管理
設定ファイル(trac.ini)の読み込み/書き込みも、メソッドを1つ
呼び出すだけ
カスタマイズ可能な設定を、ユーザに公開するのも簡単
6
Copyright © SMG Co., Ltd. All rights reserved.
- 7. 4. アークテクチャの詳細
インタフェースの実装
1.
2. HTMLテンプレート
3. Web API
4. Ticket API
5. DB API
6. Permission API
設定ファイルの読み込み/書き込み
7.
カスタマイズ可能な設定の公開
8.
今回は、アーキテクチャの有効性が分かりやすい一部のAPIについて、呼び出さ
れる側(プラグイン)から、フレームワークを見てみることにします。
Pythonでの実装ですが、フレームワークとしての考え方は、言語に依らず、参考
になります。
7
Copyright © SMG Co., Ltd. All rights reserved.
- 8. 【4. アーキテクチャの詳細】
インタフェースの実装
全てのクラスは、
class TicketTemplateModule(Component):
Componetntクラスを継承
implements(ITemplateProvider, IRequestFilter, ITemplateStreamFilter)
→Tracのプラグインとして認識
# ITemplateProvider method
def get_htdocs_dirs(self):
from pkg_resources import resource_filename
return [('ticketext', resource_filename(__name__, 'htdocs'))] ITemplateProvider
静的なコンテンツを取得する
# ITemplateProvider method
場合に利用する。
def get_templates_dirs(self):
from pkg_resources import resource_filename
return [resource_filename(__name__, 'templates')]
# IRequestFilter method
IRequestFilter
def post_process_request(self, req, template, data, content_type):
HTTPリクエストを処理する
if template == 'ticket.html':
add_script(req, 'ticketext/ticketext.js') 場合に利用する。
add_stylesheet(req, 'ticketext/ticketext.css')
return (template, data, content_type)
ITemplateStreamFilter
# ITemplateStreamFilter method
def filter_stream(self, req, method, filename, stream, data): ページにHTMLを埋め込み
if filename != 'ticket.html': たい場合に利用する。
return stream
インタフェースさえ実装すれば、良い構成になっている。
それだけ、メソッド粒度、引数などが洗練されているということ。
8
Copyright © SMG Co., Ltd. All rights reserved.
- 9. 【4. アーキテクチャの詳細】
HTMLテンプレート
<legend>チケットテンプレートの変更</legend>
<div class=quot;fieldquot;>
<label for=quot;typequot;>チケットの分類:</label><br />
<select id=quot;typequot; name=quot;typequot;>
def _process_read(self, req, page_param):
<div py:for=quot;type in template.typesquot;>
<option py:if=quot;type.selected”
ticket_types = [{
value=quot;${type.name}quot;
'name' : type.name,
selected=quot;selectedquot;>
'value' : type.value,
${type.name}
'selected' : (type.name == ticket_type)
</option>
} for type in ticket.Type.select(self.env)]
<option py:if=quot;not type.selectedquot;
value=quot;${type.name}quot;>
page_param['types'] = ticket_types
${type.name}
</option>
</div>
</select> レスポンスパラメータに、
サーバで指定された値を、
</div> HTML内で指定。 画面に表示される値を格納。
Beanのようなオブジェクトも、
利用できる。
ifやforなどの処理は、
テンプレートエンジンが解決
ロジックとビューの分離は一般的。
HTMLテンプレートにより、簡単にレイアウトが確認できる。
9
Copyright © SMG Co., Ltd. All rights reserved.
- 10. 【4. アーキテクチャの詳細】
Web API(画面の表示を処理する)
# INavigationContributor methods
def get_active_navigation_item(self, req):
これだけ実装すれば、
return 'importer'
タブメニューとして
def get_navigation_items(self, req): リンクが表示される。
if not req.perm.has_permission('IMPORT_EXECUTE'):
return
yield ('mainnav', 'importer',
html.a('インポート', href=req.href.importer()))
# IRequestFilter method
def post_process_request(self, req, template, data, content_type): 表示画面に、JavaScriptを
if template == 'ticket.html': 追加。
add_script(req, 'ticketext/ticketext.js')
add_stylesheet(req, 'ticketext/ticketext.css')
return (template, data, content_type) 表示画面に、CSSを追加。
決まったレイアウトになったり、決まった処理をするものは、簡単に利用できるように、
ユーティリティメソッドなどを用意しておく。
10
Copyright © SMG Co., Ltd. All rights reserved.
- 11. 【4. アーキテクチャの詳細】
Ticket API(チケットの変更に対する処理をする)
# ITicketChangeListener method
def ticket_created(self, ticket):
self.action = 'created'
self.ticket = ticket
def ticket_changed(self, ticket, comment, author, old_values):
self.action = 'changed'
self.ticket = ticket
self.comment = comment
self.author = author 追加、変更、削除の処理が発生した際に、
self.old_values = old_values
Listenerが呼び出される。
何らかの処理を実行する前後などで、
def ticket_deleted(self, ticket):
処理をフックしたい場合に利用する。
self.action = 'deleted'
self.ticket = ticket
そのシステムで中心的な処理の前後には、フックできるような仕組みを提供しておくと、
拡張が簡単になる。
11
Copyright © SMG Co., Ltd. All rights reserved.
- 12. 【4. アーキテクチャの詳細】
DB API(DBアクセスの処理をする)
DBアクセスが必要な処理であるが、
良く使われる処理であるため、
ユーティリティとして、値をメソッド1つで
取得できるようにしている。
def _delete_ticket(self, id):
major, minor = self._get_trac_version()
if major > 0 or minor >= 10:
ticket = Ticket(self.env,id)
SQLを簡単に発行。
ticket.delete()
→commitメソッドで、コミット完了。
else:
db = self.env.get_db_cnx()
cursor = db.cursor()
cursor.execute(quot;DELETE FROM ticket WHERE id=%squot;, (id,))
cursor.execute(quot;DELETE FROM ticket_change WHERE ticket=%squot;, (id,))
cursor.execute(quot;DELETE FROM attachment WHERE type='ticket' and id=%squot;, (id,))
cursor.execute(quot;DELETE FROM ticket_custom WHERE ticket=%squot;, (id,))
db.commit()
DBアクセスが必要な処理も、頻繁に呼び出されるような処理は、フレームワークとして、
APIを提供する。
ただし、トランザクションの設計は注意しておく必要がある。
12
Copyright © SMG Co., Ltd. All rights reserved.
- 13. 【4. アーキテクチャの詳細】
Permission API(権限に関する処理をする)
プラグインに実装されて
# IPermissionRequestor methods
いる権限を取得し、
def get_permission_actions(self):
yield 'TICKET_BATCH_MODIFY' 一覧へ表示する。
ユーザ毎に権限を設定
# ITemplateStreamFilter methods 権限を判定して、処理を分岐
def filter_stream(self, req, method, filename, stream, formdata):
させる。
if filename == 'query.html' and (
req.perm.has_permission('TICKET_ADMIN') or
req.perm.has_permission('TICKET_BATCH_MODIFY') ):
return stream | Transformer('//div[@id=quot;helpquot;]').before(self._generate_form(req, formdata) )
return stream
プラグイン毎に、権限を自由に設定することができる。
権限を一元管理する方法も考えられるが、プラグインアーキテクチャの場合は、
プラグインに権限処理の責務を持たせる。
13
Copyright © SMG Co., Ltd. All rights reserved.
- 14. 【4. アーキテクチャの詳細】
設定ファイルの読み込み/書き込み
self.run_server = self.config.getbool('ldap', 'autocomplete_run_server', False)
self.width = self.config.get('ldap', 'autocomplete_width', '400')
self.minChars = self.config.get('ldap', 'autocomplete_minchars', '1') 設定ファイルから、
self.delay = self.config.get('ldap', 'autocomplete_delay', '400') 設定値を読み込み。
デフォルト値を
「カテゴリ」+「項目」
# trac.ini 指定することも可能。
[ldap] で定義
autocomplete_run_server = True
autocomplete_width = 500
template_key = type_name.encode('utf-8') + '.template'
template = template.replace('¥r¥n', '¥n');
template = template.replace('¥n', self._LB);
self.config.set('ticketext', template_key, template)
enablefields_key = type_name.encode('utf-8') + '.enablefields' 設定値をセットし、
self.config.set('ticketext', enablefields_key, enablefields)
saveメソッドを実行する
だけ
self.config.save()
フレームワークだけでなく、システムには設定ファイルはつきもの。
頻繁に利用される処理であるため、ユーティリティで簡単にアクセスできるようにする。
14
Copyright © SMG Co., Ltd. All rights reserved.
- 15. 【4. アーキテクチャの詳細】
カスタマイズ可能な設定の公開
「カテゴリ」
「項目」
class DiscussionApi(Component):
「デフォルト値」
「説明文」
default_display = Option('discussion', 'default_display', 'tree',
を定義
'Default display mode of topic message list.')
forum_sort = Option('discussion', 'forum_sort', 'id', →Javaでいうところの、
'Column by which will be sorted forum lists.' + クラス変数みたいなもの
'Possible values are: id group name subject ' +
'time moderators description topics replies lasttopic lastreply')
クラスファイルを、
フレームワークが
自動で読み取り、
オプションとして表示。
設定値は、設定ファイ
ルに保存される。
ユーザがカスタマイズすることを可能にする設定値を定義している。
フレームワークが自動で読み取り、保存可能にしているため、プラグイン側では
定義のみで済んでしまう。
15
Copyright © SMG Co., Ltd. All rights reserved.
- 16. 5. まとめ
1. プラグインアーキテクチャとして、Tracのアーキテクチャ
は参考になる。
2. IoC(Inversion Of Control=制御の反転)は、フレーム
ワークとしての基本である。
3. ロジックとビューは分離する。
4. 頻繁に利用される処理は、ユーティリティなどを提供し、
簡単に利用できるようにする。
5. 権限、設定ファイルの管理など、共通機能に関しては、
全体に影響するため、特に早めにフレームワーク化し
ておく。
16
Copyright © SMG Co., Ltd. All rights reserved.