=begin $Id: Design.rd.ja,v 1.7 2002/07/02 01:40:24 k Exp $ Copyright (C) 2001 KATAYAMA Toshiaki = BioRuby の開発方針とデザイン 新しくモジュールを追加する場合は CVS の以下のレポジトリに、内容に従って 適切に commit します。メーリングリストなどで contribute されたコードは 本人に committer になってもらうか、スタッフが commit します。 bioruby/ |-- README はじめに |-- install.rb インストーラ |-- COPYING |-- COPYING.LIB |-- bin/ アプリケーション |-- lib/ Ruby で書かれたライブラリ | `-- bio/ 配列など基本的なクラス | |-- data/ 生物学的な定数などデータそのもの | |-- db/ 各種データベースパーザ | |-- io/ データの入出力 | `-- appl/ 外部アプリの処理 |-- doc/ ドキュメント |-- ext/ C で書かれたライブラリ `-- sample/ サンプルコード == Ruby 部分についてのデザイン(bioruby/lib/bio/ 以下) これまでに行なわれた実装のデザインコンセプトと、今後の方針などについてま とめておきます。 BioRuby の全クラスは module Bio で囲むことで、名前空間を切り分ける事にし ます。 === lib/bio/ データ構造系 塩基やアミノ酸の配列、文献情報、遺伝子の location や feature、グラフなど の抽象データ構造を扱うクラスは lib/bio/ 直下に配置しています。これらは各 データベース・パーザなどからも直接呼ばれます。 他に、アライメントクラスなどの実装が必要かも知れません。また、生物種の系 統分類を表す Taxonomy クラスや GeneOntology などのクラスもあると便利かも 知れません。 === lib/bio/db/ データベース・パーザ系 使い方の簡単さ vs 実装の複雑さ、処理の早さ vs メモリの重さ、などバランス の問題で必ずしも良いとは言えない場合もありますが、lib/bio/db.rb では以下 のような独自のアイデア(すでに同様な試みがなされていたかどうかは調べてい ません)を採用しています。新しくパーザを追加する場合も参考にして下さい。 エントリ それぞれのデータベースにおける1つのデータ単位 例) GenBank なら LOCUS から // までの全ての行 デリミタ エントリとエントリを区切る文字列 例) GenBank なら // だけの行 タグ エントリの左端数カラムなどに記されるトップレベルの識別子 例) GenBank なら DEFINITION や FEATURES など フィールド タグ毎に意味のある単位をなす複数行からなるブロック 例) GenBank なら SOURCE に含まれる taxonomy までの行全部など BioRuby のデータベース・パーザは、1エントリ分の文字列を .new に渡す事で パースした結果を保持するインスタンスを返すデザインになっています。各デー タベースからエントリ単位のデータを得る方法は、手元のフラットファイルを使 う場合、ネット越しに DBGET や BioFetch した場合、NCBI から取ってきた場合、 など使い方によって様々ですが、どこからエントリを得たか (io) に関わらず、 パーザはエントリをパースする処理に専念できます。 最終的には全てのデータはあらかじめパースして tab 切りのデータに切り刻み、 MySQL などに突っ込んでしまってパースの必要がないような世界にしたいです。 BioPerl の BioPerl-DB に似ていると思いますが、BioRuby 標準の DataBase と いうことで BioRuby-DB (class BRDB) として開発を進めています。BioRuby プ ロジェクトも BioHackathon や BOSC に参加して OBDA の BioSQL などに準拠す るようになりましたので、今後は独自データベースが必要な部分は減っていくか もしれません。 * OBDA : Open Bio Sequence Database Access * (()) * (()) * BOSC : Bioinformatics Open Source Conference * (()) + データベース構造の抽象化 lib/bio/db/ 以下のモジュールは、lib/bio/db.rb を require し、少なくとも DB クラスを継承します。さらに、いくつかのデータベースは類似の構造を持っ ているため、とりあえず NCBI 型(KEGG も含む)、EMBL 型に分けて、パースの 際に共通で使えるメソッドを共有しようとしています。ただし、実際にはこれら の形式に当てはまらないデータベースも多いので、DB だけを継承する場合もあ るでしょう。 + フィールドに対する共通の API を定義 様々なデータベースでよく共通して現れるようなフィールドに対しては、db.rb 内にあるドキュメントで示される共通のメソッド名を使って、データを取りだせ るようにします。 このように、フィールドに対する共通のメソッド名が、各データベースのパーサ で適切に実装されていれば、データベース毎にメソッドを覚える必要が減ります。 例えば、データベースが異なっても「エントリの説明フィールド」取得のための メソッドは definition だろうと推測しやすくなります。 + 必要な時までパースを遅らせる(on-demand parsing) GenBank など複雑な構造のデータベースをパースする際にもスループットを良く するため、最初はフィールド毎に切り分ける前処理だけを行ない、特定のフィー ルド内のデータを要求するメソッドが呼ばれた時だけ実際にフィールド内のデー タを細かくパースする処理を行ないます。もちろんこの時パースされるのは、必 要なフィールドだけです。 + 一度パースした結果はキャッシュしておく 上記の on-demand parsing でパースした結果は、オブジェクト内にキャッシュ しておくので、2度目以降のメソッドコールでは保持しているパース済みのデー タを返します。これにより重いパースを繰り返すようなオーバーヘッドを無くし ています。 === lib/bio/data/ 定数、データ系 lib/bio/data/ 以下に、いくつかのデータ(アミノ酸の名前、KEGG での生物種 名、コドン表など)が定数として置かれています。 Bio::AminoAcid → アミノ酸の名前を表すハッシュ、など これらは、必要なクラスにアクセスメソッドを定義しても良いでしょう。 === lib/bio/io/ インターフェイス、IO 系 lib/bio/io/ 以下には、フラットファイル形式をエントリ単位で読み込むクラス、 ゲノムネットからデータ取得を行なう DBGET クラスと NCBI の PubMed による MEDLINE データ取得を行なう PubMed クラス、OBDA に準拠した BioRegistry, BioFetch, BioSQL などによるデータ取得を行うクラスなどがあります。 * flatfile.rb (実装済) # ローカルのファイルを GenBank フォーマットとして開く flatfile = Bio::FlatFile.open(Bio::GenBank, "genbank/gbest40.seq") # またはファイルは引数で与える Bio::FlatFile.new(Bio::GenBank, ARGF) # または IO で与える Bio::FlatFile.new(Bio::GenBank, IO.popen("gzip -dc nc1101.flat.gz")) # 最初のエントリを GenBank オブジェクトに gb = flatfile.next_entry # エントリごとに Bio::GenBank オブジェクトを生成 flatfile.each do |gb| puts gb.definition end === lib/bio/appl/ ツール系 FASTA(SSEARCH), [PSI-]BLAST, HMMER, CLUSTALW などの外部アプリを使った処 理を行います。ローカルな実行、http などによるリモート実行、結果のフォー マットなどを意識せずに、結果がパースされ格納された Report オブジェクトな どが返るような感じにしていきます。 * query となるオブジェクトに、アプリを実行するメソッドを実装(必要な引数 を与える) factory = Bio::Fasta.remote('fasta', 'genes') fa_res = f.fasta(factory) # f は Bio::FastaFormat オブジェクト fa_res = seq.fasta(factory) # seq は Bio::Sequence::AA オブジェクトなど * 新しく生成したファクトリに、サーバ、query、target などを設定して、アプ リを実行するメソッドを呼ぶ factory = Bio::Fasta.local(prog, target, opt) fa_res = factory.query(seq) などいくつかの実装方法が考えられます(Bio::Fasta, Bio::Blast では両系統 インプリしています)。 ファクトリやメソッドへのオプションの渡し方は、複雑な物はシンボルをキーに した hash にした方が良いかもしれません。もしくは名前つき引数待ち? res = a.query(:hoge => fuga, :hoge2 => fuga2) 今後他のアプリのインターフェイスも実装しながら仕様を決めていきたいと思 います。 == コーディングスタイル クラス設計、メソッド定義など全般において、KISS (keep it simple stupid) が 基本です。ただし、なんでもかんでも class に分解してしまえば良いわけでは ないでしょう。概念として一つのまとまりだと思えるものまで分解すればよく、 特に使い回しのきかないオブジェクトを生成する必要はないと思います。 技術的な事については、常に改善の余地があると思います。書き換える事により シンプルで分かりやすくなる場合、今後の開発がスムーズになると思われる場合 には、どんどん修正していきます。逆に、多少効率が良くなるとしても何をやっ ているか分かりにくくなる場合、必要性が高いと思える積極的な理由が見つから ない場合は採用を見合わせることもあると思います。いずれにしても、バージョ ン 1.0 までは下位互換性を犠牲にした変更も行なう予定です。 ソースコード中には日本語など非 ASCII の文字列は含まないようにします。 === ヘッダ部 ヘッダ部には、ファイル名、概要、著作権者、ライセンス、CVS の ID を書きます (下の例では ID の$を展開されないように全角にしていますが、実際は $ です)。 # # bio/hoge.rb - biological hoge class # # Copyright (C) 2000, 2001 KATAYAMA Toshiaki # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # $Id:$ # アプリケーションやサンプルコードを書く場合は、ruby のパスに依存しないよ うに1行目を #!/usr/bin/env ruby のように始めるのも良いでしょう。 === 本体 本体は module Bio で括り、必要なクラスを実装します。本体内では # を使っ てコメントをつけ、RD を使ったコメントはファイルの最後のドキュメント部ま では出てこないようにします。もし作成するクラスが bio/db/ 以下のデータベー スパーザ系だったら、bio/db.rb のドキュメントも参照して下さい。 require 'foo/bar' modlue Bio class Hoge # this method do hogehoge def hoge(fuga) @fuga = fuga # storing data fuga end def gege end end end RDoc を使えばクラスやメソッドの上に書かれたコメントから HTML などを生成 できるようです。今後 ri 用の出力もできるようになってきたら、採用するかも しれません。 === テストコード部 テストコードはクラス定義など本体部分のあとにをつけることにします。テスト コードの書き方はこれから検討していく必要がありますが、とりあえず以下のよ うな構文でテストコードを囲みます。 if __FILE__ == $0 # test code here end このおまじないのような構文は、ファイル hoge.rb がコマンドラインから % ruby hoge.rb などとスクリプトとして起動された場合に実行されますが、ライブラリとして require 'hoge' のようにロードされた場合には実行されないようにするための常套句です。 テストの方法は、Test::Unit が ruby の標準ライブラリとして添付されるよう になれば採用してもいいと思いますが、それまでは色々試したいと思います。テ ストに外部ファイルやネットワークなどが必要な場合どうするかとか、インストー ル前に動作確認としてテストを実行する方法なども検討する必要があります。 === ドキュメント部 これまで、各クラスについては、あまりドキュメント化されていませんでしたが、 今後はテストコードのあと、ファイルの最後尾に RD でメソッドなどのドキュメ ントを追加することにしていきます。 =begin = Bio::Hoge Hoge クラスの概要説明。 --- Bio::Hoge.new(fuga) クラスメソッドの説明。 --- Bio::Hoge#to_a メソッドの説明。 == Bio::Hoge::Fuga 内部クラス Hoge::Fuga の概要説明 --- Bio::Hoge::Fuga.new(fuga) クラスメソッドの説明。 --- Bio::Hoge::Fuga#to_a メソッドの説明。 =end ただし、 * ソースの中には ASCII 以外の文字を入れない方針のため、日本語のドキュメ ントは、この部分ではなく別ファイルに記述し、doc/ ディレクトリ以下に置 くなどします。この際、拡張子に .ja を付けて、ファイル名からも日本語と 分かるようにします。 * 前述のように、ソースコード本体内では RD によるドキュメントは禁止します。 理由は純粋にコードが読みづらくなるからです。ソース中で # を使って必要 なコメントを追加する事は歓迎します。(前述のように RDoc 準拠にするかも しれません) == 開発に参加するには BioRuby のウェブサイト (()) を見て、メーリングリ ストに参加するか、スタッフ に連絡してください。 =end