ちゃんと書こうとするといつまでも完成しないような気がしたので、小出しにすることにしました*1。
groongaのリファレンスマニュアルは英語版と日本語版を用意しています。英語版ページには同じ内容の日本語版ページへのリンク、日本語版ページには同じ内容の英語版ページのリンクを用意しています。各ページの上のところにある国旗画像がそのリンクです。以下のように国旗画像で言語を表現しています。
これはSphinxのテーマを使って実現しています。そのやり方について説明します。
以下のようなディレクトリ構成になっているとします。en/html/以下にはsphinx -Dlanguage=en ...
で生成したHTMLがあり、ja/html/以下にはsphinx -Dlanguage=ja ...
で生成したHTMLがあります。
. |-- en | `-- html | | ... HTMLがたくさん | `-- index.html `-- ja `-- html | ... HTMLがたくさん `-- index.html
この場合、ja/html/index.htmlでは../../en/html/index.htmlへリンクを張ればよいことになります。
conf.pyとテーマのlayout.htmlを設定します。
まず、conf.pyで以下のように設定し、-Dlanguage=...
で指定した言語をテーマ内で参照できるようにします。
conf.py:
html_context = {"language": language}
次に、テーマ内でlanguageの値を参照してリンクを生成します。groongaのようにページの先頭の方にリンクを入れる場合は以下のようにします。
layout.html:
{% extends "default/layout.html" %} {% block header %} <ul> {%- if language != "en" %} <li><a href="{{ pathto('../../en/html/', 1) }}{{ pathto(pagename, 0, '.') }}">English</a></li> {%- endif %} {%- if language != "ja" %} <li><a href="{{ pathto('../../ja/html/', 1) }}{{ pathto(pagename, 0, '.') }}">日本語</a></li> {%- endif %} </ul> {{ super() }} {% endblock %}
pathto(pagename, 0, '.')
でトップページから現在のパスへの相対パスを取得している所がポイントです。Sphinxのドキュメントにはこのやり方が書いていませんでしたが、Sphinxのソースを読んでみたらできそうだったのでこうしました。
languageを参照して出力内容を変えるという手法は他のものにも応用できそうですね。
groongaのリファレンスマニュアルで実現している、他言語ページへのリンクを自動生成する方法を紹介しました。Sphinxで複数言語用のドキュメントを生成する場合に利用してみてください。
*1 まとまっていなくて、後から参照しづらいのではないかという点が心配です。
先月お知らせした通り、本日、本社を移転しました。明日から新オフィスでの業務開始となります。
以前のオフィスよりも2倍以上広くなり、ホワイトボード周りを広々使えるようになりました。
クリアコードの設立日は2006年7月25日なので、今月からちょうど6期目に入りました。フリーソフトウェアをビジネスにして5年間存続でき、そして、新しいオフィスで6期目をスタートできることをとても嬉しく思います。
日本Ruby会議2011の3日目の「テスティングフレームワークの作り方」の準備をしていますが、30分だと詰め込み過ぎになってしまうので、話さないことを事前に書いておきます。それは、テストを抽象化するためのAPIの違いです。
RSpecとtest-unit 2でのAPIの違いというと、class UserTest < Test::Unit::TestCase
とdescribe User
やassert
とshould
の違いの方が目に付きますが、抽象化するためのAPIにもツールの特徴が出ています。抽象化するためのAPIはテストの量が増えてくると必要になる大事な機能です。ここでは、その中でも「テストを共有するAPI」について考えます。
まず、ツールの考え方について確認し、その後、それぞれのツールでどのようなAPIになっているかをみます。
まず、それぞれのツールの考え方について確認しましょう。
RSpecでの書き方を見る前に、RSpecがどういうことを実現するためのツールとして開発されているかを確認しましょう。
BDD is an approach to software development that combines Test-Driven Development, Domain Driven Design, and Acceptance Test-Driven Planning. RSpec helps you do the TDD part of that equation, focusing on the documentation and design aspects of TDD.
[RSpec Documentationより引用]
ざっくり訳すと、
BDDはテスト駆動開発とドメイン駆動設計と受け入れテスト駆動計画づくりとATDPを合わせたソフトウェア開発の方法で、RSpecはそのうちのテスト駆動開発の部分だけをお手伝いしますよ。もう少し言うと、テスト駆動開発の特徴であるドキュメントと設計の部分を特に重視しています。
となります*1。
ドキュメントと設計(とRSpecというツール名)を合わせて考えると、仕様をテストとして実行できる形にしながら開発を進めたいのではないかという解釈ができます。そうすると、仕様とテストを一緒に書きやすいAPIを目指しているはずです。
test-unit 2はxUnit系のテスティングフレームワークです。Rubyで書かれたコードのテストをRubyで書けることを重視しています*2。そのため、Rubyとしてテストを書きやすいAPIを目指しています。
それでは、このような考え方を持つツールはどのようなAPIを提供するかを見てみましょう。
RSpecでテストを共有する場合はit_behaves_like
を使います。以下は、shared examples - Example Groups - RSpec Coreにあるコードです。「別途記述した動作通りに動くこと」と読めるAPIになっていますね。期待した動作を取り込むのではなく、参照しているように読めるところがポイントです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
require "set" shared_examples "a collection" do let(:collection) { described_class.new([7, 2, 4]) } context "initialized with 3 items" do it "says it has three items" do collection.size.should eq(3) end end describe "#include?" do context "with an an item that is in the collection" do it "returns true" do collection.include?(7).should be_true end end context "with an an item that is not in the collection" do it "returns false" do collection.include?(9).should be_false end end end end describe Array do it_behaves_like "a collection" end describe Set do it_behaves_like "a collection" end |
test-unit 2でテストを共有する場合は共有したいテストを書いたModule
をinclude
します。こちらは「テストの実装を共有する」と読めるAPIになっていますね。RubyではModule
は実装を共有する手段として提供されているため、それをそのまま「テストを共有」するために使っていることがポイントです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
require "set" gem "test-unit" require "test/unit" module CollectionTests def collection @collection ||= collection_class.new([7, 2, 4]) end def test_initilize assert_equal(3, collection.size) end def test_include_true assert_true(collection.include?(7)) end def test_include_false assert_false(collection.include?(9)) end end class ArrayTest < Test::Unit::TestCase include CollectionTests def collection_class Array end end class SetTest < Test::Unit::TestCase include CollectionTests def collection_class Set end end |
テスティングフレームワークの作り方からもれた話題のひとつである「 RSpecとtest-unit 2の考え方の違いとそれが『テストを共有するAPI』にどう現れているか」をみてみました。
考え方としてRSpecとtest-unit 2のどちらがよいかではなく、自分がやろうとしている作業にはどちらが合っているかを考えるべきです。仕様としても使えるテストとRubyとして書けるテストのどちらが必要か・重要かを考えます。
例えば、すでにある仕様書や要望リストを実現するために作業している場合はRSpecの方が作業に合っているかもしれません。仕様っぽいAPIである程度Rubyと切り離して作業することにより、仕様を意識しながら作業を進めることができます。
そうではなく、内部で使うためのもので外部とのインターフェイスとなっていない部分であれば、test-unit 2の方が合っているかもしれません。RubyのそのままのAPIを使ってテストを書くため、仕様としてどうかということよりも、Rubyのプログラムとしてどのように動くのがよいかという部分に集中できます。
日本Ruby会議2011で「テスティングフレームワークの作り方」について話してきました。前に人前で話したのが2月のフクオカRuby大賞だったので半年くらい人前に出ていなかったのですね。
発表内容のこととRuby会議のことについて書いておきます。
数年前なら自分がよいと思っている技術的なことを「これかっこいいでしょ!」みたいな感じで話したかったのですが、最近は自分がよいと思っている開発スタイルをまだそれを知らない人に「こういうスタイルもあるんだよー」と伝えたくなってきました*1。そのため、今回の発表内容は「自分が開発しているときに無意識のうちに考えていること」になりました。
発表内容についてざっくりまとめるとこんな感じになります。
キーワードは「判断基準」にしました。
「判断基準」はここ数年で意識することになったキーワードで、今まで無意識でやっていたことを改めて考えるきっかけになっていました。今のところ、1回の話で伝えられる程には自分の中で整理できているわけではなく、何度も繰り返し伝えていくことでしか伝えきれないと感じています。今回は1回だけの機会になるので伝えきれる気がしなかったのですが、せめてきっかけくらいになればいいなぁと思い、伝えようとしてみました。
本当は一緒に開発をしながら伝えていきたいことです。現状だとクリアコードに就職するしか方法がないのですが、別の方法でもなにか機会があるといいなぁと思っています。
Ruby会議ではセッションを聞かずにロビーなどをぶらぶらしながら誰かと立ち話をするのが好きでした。ふだんは直接会えない人と初めて会ったり久しぶりに会ったりできるのがRuby会議でした。会ったことがない人同士を引き合わせるのも好きでした。
今回のRuby会議ではクリアコード関係者が4人も壇上で話していました。クリアコードができた頃はこんなことになるとは思ってもいませんでした。とても嬉しいことです。
Ruby会議じゃない場所でもまた会えるといいなぁと思います。
*1 でも、まだ、「これかっこいいでしょ!」という内容を話すときの方が楽しいです。
しばらく人前で話す予定がないので、講演者が本当に話したかったスピーチを残しておきます。
実は、日本Ruby会議2011で話したことは札幌Ruby会議02で話したことと札幌Ruby会議03で話しかけたことの続きでした。話の流れも札幌Ruby会議02と同じ流れにしました。一番伝えたいことは話しの真ん中に持っていき、その後に実例を伝えるという流れです。
札幌Ruby会議のころは「伝えること」について考えていました。
私がプログラミングを始めたのは大学のころ*1で、世間で活躍しているプログラマーよりだいぶ遅いです。でも、プログラミングが好きでたくさんプログラムを書いてきました。今では、プログラミングをはじめて10年くらい経ち、だいぶ上手くなってきました。でも、このままだとよくないなぁと思うようになりました。
私は独学でプログラミングを学んできました。本を読んだりコードを書いたり、他の人のよいところを盗んだり。でも、他の人も同じようにやる必要はないなぁと思うようになりました。自分がよいとわかったことを厳選して伝えられて、その分、他の人がもっと別のことをできるなら、そっちの方がいいなぁと思ったのです*2。
そのため、札幌では自分がプログラミングで大事だと思っていることを伝えようとしました。
それから、普段でもいろいろ伝えてみました*3。 そしたら、伝えるだけでも足りないと思うようになりました。「魚をあげるんじゃなくて、魚の捕り方を教えなよ」では足りないのです。これでは、魚を捕れる人は増えるかもしれませんが、「魚の捕り方を教えなよ」という人は増えないのではないかと思うようになりました。「伝えること」だけではなく「伝えることを伝えること」をしなければいけないのではないか。
「伝えることを伝えること」は日本Ruby会議2011では落とした話題ですが、拾ってくれた人がいて嬉しかったです*4。いつかリベンジしたい話題です。
まだ、「伝えること」も伝えきれていないので、「伝えることを伝えること」はもう少し先の話になると思いますが、実現できたらいいなぁと思っています。まずは、クリアコード内で「伝えること」をまとめて、ここで公開しようとしているところです*5。公開したものが誰かの役にたつといいなぁ。
日本Ruby会議2011のことはもう書いたので、ここに書くことは技術的な話に戻そう*6と思っていました。でも、日本Ruby会議2011関連のWeb日記を読んでいたら、「書いておかないと」と思ったので、書いてしまいました*7。
*1 今、ざっと読みなおしてみたら札幌で話したこととか練馬で話したことと同じことを話していました。変わっていないですね。
*2 ソフトウェアに関してはプログラミングをはじめたころからそんな風に思っていた気がします。自分がよいと思ったソフトウェアがあったら他の人にも「これを使いなよ」と渡せるとよいなぁと思います。だから、自由に使えるソフトウェアが好きなんだと思います。
*3 そして、いろいろうまくいきませんでした。
*4 参考文献を教えてもらいました。手元に用意したので、そっちも少し調べてみようと思います。
*5 自由に利用できるようにする予定です。
*6 RDocとYARDとI18Nの話とか。
*7 ここでは「思いました。」という風には書かないようにしているのですが、Ruby会議関連の話題のときだけは勝手に「特別に使ってよい」ことにしています。