2010-10-09

GAE アプリでユーザごとの設定を可能にする #2

今回のお題は、GAE が提供する「データストア」の使い方を調査すること。ただし、Blogger Glass に追加しようとしているユーザごとの設定機能に必要なのは、User.user_id() をキーとしてデータ実体を作成・抽出・更新する方法だ。複雑なクエリに関しては今のところ不要。

とりあえず、サンプルを作ったので貼っておく。

ログインしていなければ「Login」というリンクだけの画面が表示され、リンクをたどりログインすればユーザごとに設定した「Blog ID」とその更新日時が表示される。また、同時に「Blog ID」を入力するフォームも表示されているので、ID を入力し「Save Settings」ボタンを押せば、ID の値が更新される。つまり、Blogger Glass に追加しようとしている「ユーザごとの設定」機能の核となる部分はここにふくまれているわけだ。

このコードで肝心なのは、Settings クラスの定義と、def_settings 関数の内部で、User.user_id() の値をデータストアのキーとしてデータの実体(entity)を作成、あるいは抽出しているところだ。

参考文献

Programming Google App Engine
Dan Sanderson
Oreilly & Associates Inc ( 2009-11-15 )
ISBN: 9780596522728

User.user_id() をキーにする方法は Example 2-5 を参考にした。

関連リンク

関連記事

2010-10-08

GAE アプリでユーザごとの設定を可能にする #1

2010-10-08 時点の Blogger Glass では、アプリ固有の設定(例: スタイルシートの URL やブログ ID 等)は config.yaml というファイルに記述することになっている。

この方法の利点は、何と言ってもその手軽さにある。YAML (のサブセット)で書かれたファイルを読み込むのは単純で、ハードコードしたくない情報を簡単にプログラムから切り離すことができる。一方、欠点はと言えば、設定の変更にはアプリの再配備が必要なこと。プロトタイプとしてはこれでも悪くないが、ある程度アプリの機能が成熟してきたら、設定自体をアプリの機能として提供すべきだ。

そこで、Blogger Glass へ次に 追加する機能として「設定(Settings)」を選んだ。ブログ ID を「設定」で変更できるようになれば、LOG+REPO 以外のブログ(ただし Blogger で作ったものに限られる)を見られるようになる。

原則として個人が一人で使う iPhone のようなデバイス上のアプリとは異なり、ウェブアプリの設定は、ユーザごとに設定・保存できるものでなければならない。みんなが同じ URL でアクセスする以上、アプリはすべてのユーザが共有する。設定をユーザごとのものにしなければ、全ユーザが共有することになってしまう。そして、ユーザごとの設定にはユーザの識別が必要になる。

「設定」機能のデザイン(意匠と設計)に入る前に、GAE におけるユーザの識別について調べてみよう。

ユーザの識別

GAE アプリで、アプリを使っているユーザの識別するには GAE が提供する「ユーザーサービス」を利用する。

(「ユーザー サービスの使用」より)
サービスの 1 つであるユーザー サービスは、アプリケーションと Google ユーザー アカウントを連携させるものです。ユーザー サービスでは、ユーザーはすでに持っている Google アカウントを通じて、アプリケーションにログインすることができます。

ログインとログアウト

アプリを利用するユーザにログインを促す方法の 1 つは、リクエストハンドラで users.get_current_user() を使い、ユーザがログイン中であることを確認することだ。そしてログイン中でなければログイン画面にリダイレクトする。

(ユーザーサービス利用例)
from google.appengine.api import users 
[...snip...]
class MainHandler(webapp.RequestHandler):
    def get(self):
        user = users.get_current_user()
        if user:
            [...snip...]
            self.response.out.write("<div><a href='"
                                    + users.create_logout_url(self.request.uri)
                                    + "'>logout</a></div>")
        else:
            self.redirect(users.create_login_url(self.request.uri))

ログアウトに関しては、アプリ画面のどこかにログアウト用のリンクを置けば良い。これには users.create_logout_url()を使う。↑の例では、ログイン中であれば(つまり users.get_current_user() が有効なオブジェクトを返せば)、レスポンスにログアウト用の URL を出力している(アプリ画面の一部になる)。この例の場合、ログアウト用のリンクをクリックすると、(a) ログアウトが実行され、(b) このハンドラを起動した URL にリダイレクトされ、(c) ログイン中ではないからログイン画面にリダイレクトされ、(d) ログイン画面が表示されることになる。

特定の URL に対してログインを要求する

app.yaml に記述することで、特定のリクエスト(URL のパターン)に対してログインを強制することもできる。

(app.yaml の handlers 区画の記述サンプル)
handlers:
- url: /hello/
  script: hello.py
  login: required

- url: .*
  script: main.py

この設定では、/hello/ を開こうとすると、ログイン画面にリダイレクトされ、ログインしない限り、hello.py ハンドラが実行されることはない。

ハンドラ内でログイン画面にリダイレクトする方法と、app.yaml でリクエスト自体にログイン要求を設定する方法とは適宜使い分けることになる。たとえば、ログインをしなくとも使える機能(のハンドラ)についてはハンドラ内でログイン状態を識別し、必要に応じてリダイレクトする。一方、ログインしている状態でのみ使える機能(のハンドラ)に対しては、app.yaml で制限をかける。

Blogger Glass の場合だと、一覧表示や内容表示はログインしていなくても使えるが、新しく追加しようとしている「設定」はログインが必須になる。だから、前者はハンドラ内で識別とリダイレクトを記述し、後者にはapp.yaml でログインを必須に設定することになる。

User オブジェクト

実際のユーザの識別には User オブジェクトを利用する。

(「Google アカウント Python API 概要」より)
User オブジェクトからは、ユーザー アカウントが存在する限り、メール アドレスが変更されても一定である一意なユーザー ID を取得できます。この値は、データストアのエンティティ キーまたはプロパティ値として使用できます。

User.user_id() メソッドはユーザの一意で永続的な ID (型は str) を返す。

開発環境(SDK)でのログイン

ユーザーサービスを使ったアプリを開発するために、SDK にはログイン(とログアウト)をシミュレートするダミーのログイン画面が用意されている(→)。

上述の例のように self.redirect(users.create_login_url(self.request.uri)) でログイン画面にリダイレクトするとこの画面が表示され、ログインボタンを押すとアプリ画面に戻る。

次回予告

ログイン要求つきの設定画面(ビュー)を用意してユーザごとの項目を入力できるようになっても、それが保存できなければ意味がない。正確には、後続のリクエストを処理する際に参照できなければ役に立たない。つまり、リクエストハンドラ間で情報を共有する仕組みが必要になる。

そのための一番確実な方法は「データストア」を利用することだろう。ユーザごとの設定情報を永続化してしまえば、どのリクエストハンドラからも参照できる。情報を保存するハンドラと、参照するハンドラが別のサーバで実行されていたとしても問題ない。

次回は、GAE の提供する「データストア」の使い方を調べることから始める。

関連リンク

関連記事

2010-10-07

Blogger で作ったブログの記事の内容を表示する #3

前回のリファクタリング構想にしたがい、実装を行う。

表示内容を保持するオブジェクト

アプリ固有の情報を AppInfo オブジェクト、ビュー(前回まではページと呼んでいた)固有の情報を ViewInfo オブジェクトに収めることにした。ただし、ViewInfo オブジェクトはビューの種類(一覧表示と内容表示)ごとにサブクラス化して使う。

ビュー内の表示領域の構成と表示内容オブジェクトの対応

これら表示内容オブジェクトは src/info.py で定義している。

AppInfo

アプリ固有の情報を保持するため、すべてのビューで使われる。このオブジェクトが保持するのは、基本的に config.yaml で設定する情報だ。

AppInfo のプロパティ一覧
プロパティ名 説明
lang 生成される HTML に対する言語指定。具体的には html 要素の lang 属性の値を指定する。
title アプリのタイトル。html/head/title 要素の内容になることを想定している。
stylesheet 生成される HTML で使用されるスタイルシート。
home_url アプリのトップ URL。
ListViewInfo

一覧表示ビューで表示する情報を保持する。

ListViewInfo のプロパティ一覧
プロパティ名 説明
page 表示するページ番号。
total_pages 全ページ数。
content フィードにふくまれる entry 要素のデータ。実際には Entry (後述) のリストになっている。

ListViewInfo.content は (main.py で定義している) Entry のリストであり、Entry は以下のプロパティを持つ。

Entry のプロパティ一覧
プロパティ名 説明
title 記事のタイトル。
feedlink 個別記事のフィードへの URL。
PostViewInfo

内容表示ビューで表示する情報を保持する。

PostViewInfo のプロパティ一覧
プロパティ名 説明
title 記事のタイトル。
permalink Blogger の個別記事への permalink。
content 記事の内容となるテキスト。実際には Unicode 文字列になっている。

リクエストハンドラの変更

MainHandler (main.py で定義、一覧表示リクエストを処理する)、PostViewHandler (postview.py で定義、内容表示リクエストを処理する)ともに、template.render にわたすディクショナリに直接値を詰めるのではなく、AppInfo と ViewInfo (のサブクラス) オブジェクトを用意するように変えた。

その他の修正

前回のリファクタリング・プランにはなかったが、feed/core.py で定義していた Entry クラスを削除した。このクラスは Atom フィードにふくまれる entry 要素のデータを保持するためのものだが、単純にディクショナリ型オブジェクトとして保持することに変えたため不要になった。

またこれにともない、AtomFeedAtomPostFeed でも解析後にデータを保持する方法を修正している。

関連リンク

関連記事

2010-10-06

Blogger Glass をリファクタリングする

細かな機能の追加はまだ残っているが、Blgger Glass にとっての主機能となる 2 つの実装が完了した。すなわち、(a) 記事の一覧表示、(b) 個々の記事の内容表示、の 2 つだ。

細部の作り込みに移る前に全体の構造を見直しておきたい。つまり、今回のお題は、リファクタリングだ。

ウェブアプリの 2 層構造

Python で書いたフィルタを GAE に載せる #3」の「ウェブアプリのテンプレート」では、GAE でテンプレートシステムとして使う Django の(HTML 以外の)記述は、あくまでも HTML 中に配置するオブジェクトを出力するための補助、なのだと書いた。そして、この「配置するオブジェクト」を定義することが、プレゼンテーション層とバックエンド層を分離することになる、と。これは言い換えると、ウェブアプリを 2 つの層からなる多層構造として作る、ということになる。

(「テンプレートシステム入門 (2) 基礎編」より)
何らかの表示を伴うアプリケーションは、大きく「ビジネス層」と「プレゼンテーション層」の 2 つに分けることができます。

ビジネス層
何 (What) を表示するか? を司る層であり、メインプログラムが担当します。
プレゼンテーション層
どう (How) 表示するか? を司る層であり、テンプレートシステムが担当します。

仕組みとしては、ビジネス層であるメインプログラムがデータを用意し、それをプレゼンテーション層であるテンプレートシステムに渡すことでデータが表示されます

この記事では、わたしがバックエンド層と呼んだものをビジネス層と名付けており、その役割は「何(What)を表示するか? を司る層」としている。一方、「プレゼンテーション層」は「どう(How)表示するか? を司る層」だ。ビジネス層という名前は気に入らないが、それぞれに与えられた役割は明確でわかりやすい。

以上を踏まえ、Blgger Glass のプレゼンテーション層のデザインを考え直してみよう。

Blogger Glass のデザイン(意匠と設計): プレゼンテーション層

すでに書いたように、Blgger Glass の論理的な画面構成は右のようになる。「論理的な」と書いたのは、スタイルの定義は後回しにしているからだ。たとえば、App Header が Page Header や Contents の上部に表示されるかどうかは、スタイルの定義次第になる。つまり、この図は Blogger Glass で「何を(What)」表示するかの抽象的な構造だと言える。

一般に、個々の画面に結びついた機能によって表示される内容は変化するが、中には変化しないものもある。たとえば、アプリの名称は一覧表示画面であれ、内容表示画面でれ、あるいはユーザ設定画面であれ変化することはない。このような情報をアプリ情報と呼ぶ。一方、機能ごとに変化する内容をページ情報と呼び、その中でもメインとなる内容を特に主コンテンツと呼ぶことにする。

以下に、Blogger Glass が持つ 2 つの画面における各情報の具体的内容を示す。

Blogger Glass のプレゼンテーション要素
ページの種類 アプリ情報 ページ情報 主コンテンツ
一覧表示 アプリ名
アプリの URL
ページ番号
ページ切り替え
記事一覧
内容表示 アプリ名
アプリの URL
記事の作成日時
記事タイトル
ラベル
記事内容

これを元に、実際のテンプレートごとに表示するオブジェクト(のプロパティ)を決める。

テンプレートごとのオブジェクトプロパティ
template AppInfo PageInfo (actual content)
listview.html title (text)
home_url (text)
page (number)
total_pages (number)
content (list of Entry object)
pager (list of Page object)
entry.title (text)
entry.feedlink(text)
postview.html title (text)
home_url (text)
title (text)
content (text)
(text)

この表は、たとえば listview.html には AppInfo クラスがあり、そのインスタンすは titlehome_url というプロパティ(どちらも値はテキスト)を持っている、というように見る。また、listview.htmlPageInfo インスタンスでは、contentpager というプロパティは他オブジェクトのリストを参照している。

細かいことだが、ページ(page)という言葉は「ウェブページ」からの連想で付けたもので、ウェブアプリのプレゼンテーション要素としては適切とは言えない。むしろ、ビュー(view)と呼ぶべきかも。

関連リンク

関連記事

2010-10-05

Blogger で作ったブログの記事の内容を表示する #2

前回ののデザイン(意匠と設計)に沿って実装を行った。

実装概要

app.yaml (修正)

記事内容を表示させるためのリクエスト形式(/post/?id=<id>) に対応させるため、handlers区画に以下の記述を追加した。

(app.yaml より)
- url: /post/.*
  script: postview.py
postview.py と postview.html (新規追加)

postview.py が内容表示リクエスト用のハンドラ、postview.html は同ハンドラが使用するテンプレートになる。

postview.py で定義されている PostViewHandler は一覧表示用の MainHandler を一部書き換えたものになっている。共通部分のくくり出し等のリファクタリングは行っていない。

構造も論理も MainHandler とほとんど変わっていない。目立つ差異はフィードを抽出するためのクラスに記事内容専用の AtomPostFeed を使っていることだ。

現状、テンプレートはフィードから抽出した記事の内容を流し込むだけのものになっている。タイトル以外のメタ情報(作成日付やタグ等)は一切表示していない(フィードから抽出もしていない)。

feed/parser.py (修正)

変更点は、記事の内容 (feed/entry/content) を抽出するようになったことと、ブログの permalink と個別フィードへの URL を抽出するようにしたこと。

main.py と feed/core.py (修正)

内容表示リクエストでは、フィードの取得時に必要なポスト ID をリクエストパラメータとして受け取る。一方、一覧表示リクエストでは、リクエストパラメータとしてページ番号を受け取る(フィードを取得する際に start-index を指定する値に変換される)。この違いを吸収するため、それぞれに専用のフィードオブジェクトを用意することにした。従来からある AtomFeed を一覧表示用に、新しく追加した AtomPostFeed を内容表示用として使う。リクエストパラメータは、それぞれをインスタンス化する際に引数として渡すことにした。このため、フィードオブジェクトのインスタンス化と get メソッドの呼び出し形式が変わることになり、main.py を修正した。

core.py では、この他にも、フィードデータを保持する Entry クラスを修正(抽出するデータの種類が増加したため)、さらに一覧表示で各項目に張るリンクを、これまでの記事への permalink から、内容表示リクエストの形式に変更してある。

feed/post.py (新規追加)

コードの中身はほぼ AtomFeed からの流用になっている。いずれリファクタリングし、共通部分を BloggerFeed に押し出すつもりだ。

この AtomPostFeed で少し特殊なのは、内容表示リクエストの形式で /post/ というパラメータなしのパターンをサポートしたこと。この場合、ポスト ID を指定するタイプの URL からフィードを取得することはできない。そこで、ポスト ID が指定されなかった場合に限って、通常のフィード取得用の URL (一覧表示リクエストで利用しているもの) に max-results=1 を指定して取得することで対応した。

ファイルのリネーム

テンプレートへの postview.html の追加にともない、page.htmllistview.html とリネームした。今後、テンプレートを追加する際には、以下の名前規約にしたがう。

<表示するオブジェクト>view.html

今回、追加したテンプレートは記事("post")を表示するから postview.html、旧テンプレートは記事一覧("list")を表示するから listview.html となる。

その他の変更

今回から logging モジュールを使うことにした。sys.stderr に書き出していたエラーメッセージ(これもログに流れるようだが)を logging.error を使って書き換えている。

今後の展開

肝心の記事の内容そのものを表示することができるようになった。次は、記事のメタ情報を表示させることを考える。ここで言うメタ情報とは、作成日時、ラベルのことだ。

フィードには published に加えて updated という日時データもふくまれているので、更新日時も表示させても良い。

ラベルは複数指定可能なので、表示にも工夫の余地がある。まずは、辞書順にソートして並べて表示、で良いが。

関連リンク

関連記事

2010-10-04

Blogger で作ったブログの記事の内容を表示する #1

記事の一覧を表示するところまではできた。次は、各記事の内容を表示する機能を作り込む。

ユーザ体験

ユーザ体験はシンプル。単に記事一覧の項目をクリック(iOS デバイスなら、タップ)すると、(Blogger のブログに飛ぶ代わりに)記事の内容を表示する。

この機能(記事表示機能)が完成した後、Blogger Glass には (a) 記事一覧表示画面、(b) 記事内容表示画面の 2 つの画面ができることになる。

全体構想

画面デザイン(意匠)

記事表示画面に限らず、Blogger Glass の画面はこの(→)ようにする。やがては、サイドバー等についても組み込むつもりだが、現時点ではごくシンプルなものにしておく。

現状の記事一覧画面はこのデザインと少し異なっているが、記事表示機能の実装が終わったら、リファクタリングする予定だ。

今回作る記事表示画面では、「Contents」エリアに記事のコンテンツを流し込むだけにする。装飾等はなし。CSS によるスタイルもフォントの指定(ファミリー、サイズ)程度にしておく。

リクエストハンドラ

一覧表示、内容表示の両方を 1 つのリクエスト形式にまとめることもできる(例: リクエストパラメータで切り替える)。しかし、それではリクエストを処理するハンドラに余計な複雑さを持ち込むことになる。一覧表示と内容表示ではリクエストの目的が異なるのだから、素直に別形式のリクエストにする。当然、リクエストごとにハンドラを作る。

一覧表示用ハンドラ

従来のリクエストハンドラ。定義するファイル名、クラス名、ともに変更はしない。

変更するのは、記事のタイトルにリンクを張る部分。従来は、Blogger 上のブログの permalink をそのままリンクに使っていたが、これを内容表示用ハンドラへのリクエスト形式のものに変える。

内容表示用ハンドラ

新しく作るハンドラ。記事の内容を取得する。リクエストのパラメータとして、記事を識別する「何か」を受け取る。

テンプレート

テンプレートもリクエストハンドラ同様、2 種類用意する。一覧表示用と内容表示用だ。

調査

この機能の肝となるのは、各記事のコンテンツをどうやって取得するかだ。今のところ、ストレージは使わない方針で作っているので、内容表示リクエストを一覧表示リクエストとは別のリクエストとして受け付けるためには、フィードの取得もリクエストごとに独立して行わなければならない。

一覧表示のときと異なり、必要なフィードは特定の記事のものだけだ。特定の記事だけを指定することはできるだろうか?

Blogger の配信する Atom 形式のフィードには entry にふくまれる linkrel 属性の値が self になっているものがある。この linkhref が各記事ごとのフィードを取得するための URL になっているようだ。

つまり、記事一覧表示用のリクエストハンドラ内で、記事への URL を取得するために抽出している link 要素を rel="self" のものに変えれば、個別フィードを識別する情報を取り出せることになる。

ちなみに、個別フィードへの link は以下のような形式だ。実際には、blogIDpostID がブログごと、記事ごとの ID に置き換わっている。内容表示リクエストのパラメータとして、この postID を使うことにする。

<link rel='self' type='application/atom+xml' 
      href='http://www.blogger.com/feeds/blogID/posts/default/postID'/>

内部デザイン(設計)

リクエスト

リクエストハンドラが増えたことにともない、リクエスト形式も以下のように変更する。

リクエスト形式
リクエスト機能
/page=0 を指定した場合と同様。
/?page=<number>number で指定したページの一覧を表示する。
/post/最新の記事の内容を表示する。
/post/?id=<id>id で指定した記事の内容を表示する。
フィード解析の変更

内容表示用のリクエストハンドラも、個別フィードを取得し Atom 形式のデータとして解析しなければならない。このため、パーサとフィードをくるんだオブジェクトに手を入れる必要がある。コードで言うと、これ(↓)。

関連リンク

関連記事

2010-10-03

ウェブアプリのデバッグ

バグとは……

不具合、欠陥、障害。いくつかの呼び名があるが、プログラマにとっては「バグ」という名前が一番馴染み深い。そして「バグ」とは、プログラムに関わる誰かにとって何かがうまくいかない状態のことを言う。「誰か」(利害関係者)はユーザであることが多いが、他にもプログラマ自身のこともあるし、さらにはユーザでもプログラマでもない他の関係者(例えば発注者)の場合もある。「うまくいかない」というのは、言葉を変えれば「期待した通りにならない」ということだ。この「期待した通り」のことを一般に仕様と呼ぶ。

正しいとされる「期待」(仕様)が誰にとってのものであれ、実際のプログラムの振る舞いとの差異が指摘されてしまえば、それはバグになる。バグに対してプログラマが許される振舞いは次の 4 つ。

  1. バグを修正する。
  2. 「これは仕様(あなたの期待通り)のものです」と関係者を説得する。
  3. 他のプログラマに回す。
  4. 対処を先送りにする。

プログラマがどの対応を選ぶかは、彼または彼女の置かれた状況によって異なる。けれどたいていのプログラマは、それがどんなバグであれ、基本的には修正したいと考える。修正以外の道を選ぶとすれば、プログラマ自身の心情よりも周辺事情(迫る期日、労力の不足、...)がそれを許さないからであることが多い。

だから諸事情がそれを許せば、プログラマは 1. を選ぶ。そして、1. を選んだ瞬間から、デバッグが始まる。

デバッグの基本

デバッグには 2 つの側面がある。それは (a) バグを見つけること、(b) それを修正すること、の 2 つだ(→ 「Code Craft」p.206)。そしてデバッグの真髄は (a) にある。バグを見つけることさえできれば、修正は単純であることが多いからだ(そうでない場合、完全に修正するには設計変更をともなうことになり、それはもはや修正と呼ぶの域を超える)。

また、デバッグ作業を段階に分解すると以下のようになる(→ 「Code Craft」p.209-212 を参照)。

  1. 障害を認識する
  2. 障害を再現する
  3. 欠陥の所在を突き止める
  4. 問題を理解する
  5. テストを作成する
  6. 欠陥を修正する
  7. 修正できたことを立証する。

前半の 3 つが先の (a) に、残りが (b) に相当する。

同じことが「Code Complete」(下巻 p.96) では次のように書かれている。

  1. エラーを安定させる(確実に再現させる)。
  2. エラーの原因を特定する。
  3. 欠陥を修正する。
  4. 修正をテストする。
  5. 同様のエラーを探す。

多少の表現の違いはあるが、どちらも同じことを主張している。つまり、デバッグの段階は、(1) 再現可能な環境を構築すること、(2) その中でバグの原因となるコードを特定すること、(3) 修正を行うこと、最後に (4) 検証を行うこと、となる。

今回は、この中でも (2) の原因を特定するための方法について考えてみる。

ウェブアプリのデバッグ

バグの原因となっているコードの場所を突き止める場合、一番確実な方法は print 文などによって、おかしなことが起きる場所をしぼりこむことだ。

(「プログラミング作法」p.175)
プログラムが何をしているのかわからないときには、もっと多くの情報を表示させる文を追加するのが一番手軽で効率の良い手段となり得る。

ウェブアプリのようにコンソールを持たないプログラムの場合、プログラムに print 文を埋め込む方法は使えない(あるいは使うべきではない)。その代わりの手段がログだ。

ログを取る

GAE アプリでは、Python の標準ライブラリにあるロギングモジュールを使い、ログを記録することができる。

記録されたログは、ローカルの開発環境で実行している場合は GAE Launcher からアプリごとのログを表示させれば見ることができる。GAE の本番環境では管理コンソールからアクセスできるとのこと。

ログはプログラムの動作を知る上で何よりも重宝する。一方で、大量のログはアプリのパフォーマンスに影響を及ぼす可能性があるし、ストレージ等のリソースも消費する(GAE の場合はどうなんだろう?)。また、多すぎれば見る方にも負担になる。デバッグ用に一時的に埋め込んだログ出力は、デバッグ(と修正の検証)が終わったら、本番環境に配備する前に取り除く方が良い。

いちばん大事なこと

実は、デバッグにおいて何よりも忘れてはならない手法がある。それは記録を取ること。

これは「プログラミング作法」の p.178 でも「手がかりのない困難なバグ」に対処する方法の 1 つとして紹介されている。「Code Complete」下巻 p.102 でも、バグを見つけるためのヒントとして「メモ帳を用意して、試してみることをリストアップする」ことが挙げられている。

デバッグの作業中に、仮説を立てているのであれ、分割統治法でバグの範囲をしぼりこんでいるのであれ、あるいは同じ間違いを探しているときであれ、自分の記憶を信頼しすぎてはいけない。記憶は平気で嘘をつく。そもそもバグの大半は、ちょっとした見落しや、記憶違いから入り込むのだ。バグを潰す作業で同じことをやっていては、同じ間違いを見逃したり、別の間違いを潜り込ませることになりかねない。

あらかじめ作業リストを作って消していく方式でも良いし、思いついたことやったことを簡単にメモするだけでも良い。不確実な記憶に頼らず、確実な記録を残すこと。

参考文献

プログラミング作法
ブライアン カーニハン, ロブ パイク
アスキー ( 2000-11 )
ISBN: 9784756136497
おすすめ度:アマゾンおすすめ度

第5章がデバッグについての章になっている。取り上げられているバグの例は、(他の 2 冊とくらべて)具体的で読んでいておもしろい。ただし、載せられているコードの断片はいずれも C のものばかりなので、Ruby や Python でのデバッグにそのまま活かせるものではない。

Code Complete第2版〈下〉―完全なプログラミングを目指して
スティーブ マコネル
日経BPソフトプレス ( 2005-03 )
ISBN: 9784891004569
おすすめ度:アマゾンおすすめ度

この「コード・コンプリート」は「プログラミング作法」でデバッグに関する参考文献として挙げられている。第23章(下巻に収められている)がデバッグについての章になっている。ここに挙げた 3 冊のうちで「バグを見つける」ことに関する具体的な手順については、これがもっとも詳しい。

Code Craft ~エクセレントなコードを書くための実践的技法~
Pete Goodliffe
毎日コミュニケーションズ ( 2007-11-29 )
ISBN: 9784839921941
おすすめ度:アマゾンおすすめ度

第9章「謝りの検出」がデバッグについての章だ。3 冊のうちでは、バグおよびその原因の分類について詳しい。

関連リンク

関連記事