2013年8月10日土曜日

TDDBC参加レポート

TDD Boot Camp Tokyo 2013-07に参加して来ました。

「おい7月の話じゃねーか」と言いたくなりますがぐっと堪えて参加レポートでも書こうと思います。

基調講演


基調講演のまとめはめんd僕が改めて書くよりも素晴らしいまとめをされている方がいらっしゃるので、いくつか紹介させて頂いて終わろうと思いますw

http://makopi23.blog.fc2.com/blog-entry-96.html
http://taichiw.hatenablog.com/entry/2013/07/29/003642

ペアプロ大会


やっぱりTDDBCのいいところは、発表聞いて終わりじゃなくて、実践までがセットになっているところだなーと改めて思いました。

僕のペアもなかなか変わったペアだったので、この記事もペアプロの話メインにしようかと。
ちなみに、今回のお題は「飲み物自動販売機 Ver 2.0」でした。

で、改めてtogetter見なおしてみたんだけど、


なんか基調講演始まる前から実はもう予定調和っぽくなってるねw


というわけで、@razonさんとScalaでペアプロさせていただきました。@razonさんありがとうございます!

で、当日書いたコードがこちら。

https://github.com/shizone/tddbc201307

今回の記事ではこのコードの解説でもしてみようと思います。

「やっぱオブジェクト指向はダメだね」


というセリフが出たシーンがこのペアプロのハイライトでした。

Scalaでやるということで、コーディングスタイルとしてオブジェクト指向に寄るか関数型に寄るか選べるわけです。で最初僕らはオブジェクト指向の方を選びました。
最初のうちはうまく行ってたのですが、在庫管理を始めたあたりからテスト書くのに「モック必要じゃね」とか「他の機能もいくつか先に実装しないとテスト書けないよね」とかいうオブジェクト指向TDDあるあるにハマり始めて、イライラしてきました。

で、「よし、やっぱり関数で行こう」となったわけです。

状態(内部変数)の排除


まずはVendingMachineという型をつくります。1行だけです。



「class」という名前がついているので紛らわしいですが、これはオブジェクト指向でいうクラスとは全く違うものです。
「total」や「stock」という変数は再代入不可能になるので、VendingMachineはimmutableであり*1、状態を持ちません。
つまり型VendingMachineは現実世界のオブジェクトのテンプレートとしてのクラスではなく、ある瞬間の自販機の状態を表現するスナップショットとしての単なるデータにすぎません。

処理を関数で記述


オブジェクト指向の場合は、例えば「10円を入れる」という処理はVendingMachineクラスのメソッドとして表現し、内部変数を書き換えるという実装をするわけですが、関数型の場合は、処理を行う前のスナップショットから処理を行った後のスナップショットを計算する関数を記述します。



もちろん「関数を返す関数」を作ることもできるので、「コインを入れる」という関数はこのような表現が可能です。



関数のテストを書く


ちょっとその前に、テストを書きやすくする補助関数も作っておきます。
初期状態を取得するinitと、パイプライン演算子*2を。



パイプライン演算子の実装の方はちょっと高度なので今回は詳しく書きませんが、よく見るパターンです。Scala2.10で入った新しい機能、implicit classとvalue classを使って、メソッド「|>」を記述しています。
意味合い的にはただ単に関数適用の記述を楽にするだけのものです。VendingMachine型を引数とする関数fを、値vmに適用する際に、普通は「f(vm)」と書くところを、「vm |> f」と書けるようにするだけ。「VendingMachine => VendingMachine」という型の関数を連続で適用していく場合に、普通なら「h(g(f(vm)))」となるところが「vm |> f |> g |> h」となって素敵です。

さて、ここまで準備すると、テストコードがどうなるか。

「初期状態の自動販売機に100円玉を入れて、次に10円玉をいれると、投入金額合計は110円になる」というテスト、こうなります。
(当日はScalaTestを使いましたが、僕はSpecs2の方が慣れてるのでSpecs2式に書きます)



まとめ


「TDDBC当日のペアプロ大会で、Scala組はこんな感じのコードを書いてました」という紹介をしてみました。
一部Scala黒魔術が顔を見せたりしてはいるものの、簡潔で読みやすいテストコードが書けるよーという様子を感じて頂ければと思います。

実は「関数型はUnit Testと相性がいい」という僕の持論もあるのですが、その話はまた今度。



*1: ちなみにScalaではMap型も(特にmutableなものを使うことを明示しない限り)immutableです。
*2: パイプライン演算子はもともと「F#」由来の素敵な演算子です。ちょうどいいページが見つからなかったのでテキトーにググってくださいwちなみにScalazに実装されています。

2013年8月7日水曜日

Gitについて書いておく

※ 本記事の内容は「僕の個人的な考え」であって、正解とは限りません。予めご了承ください。あと、長いです。

能書き


ここ半年ぐらい、僕のまわりでは「Gitに移行しようぜー」的な波が押し寄せてきてる。前職の職場も、今の職場も、大学時代の友達も。一方僕は、学生時代(3年前ぐらい?)にたまたま偶然Gitに首突っ込んだので、もう既にGitがないと生きていけない体になってしまっているという立場。ということで、Gitについて思うところを僕なりに一回文章にしてみようと思う。
ここに書くのはGit入門ではなく、「Gitの基本コマンドは覚えて使えるけど、どうやったらGitっぽくつかえるか、Gitの恩恵を得られるか掴みきれてない」ぐらいの習熟度の方に参考になるような内容の予定。もちろんここに書いてあることに従えと言いたいわけではないので、違う意見の場合はぜひコメントなど頂ければ幸いですし、Git勉強中の方は1つの意見として参考にしていただければと思う。

SVNとの違い


SVNなど、一世代(あるいはもっと)前のVCSからGitに移ってくる場合に、SVNとGitの違いという観点からGitを理解しようとすることは多いと思う。そのアプローチ自体は良いとして、1つ問題がある。SVNとGitの違いの話をするときに大抵の場合、まず最初に「中央管理型か分散型か」を挙げられてしまうことだ。もちろんそれは重要な違いではあるのだが、その違いが重要なケースは稀だ。Linuxプロジェクトのように(開発者の人数という意味で)大規模なプロジェクトの管理をする立場になったら考えることであって、一開発者の立場なら、「へー」ぐらいの理解から始めれば十分だ。(もちろんこれから挙げていくGitの特徴の根本にあるのは「分散型」の恩恵なので、深堀りするときには勉強しよう)

というまえがきを置いた上で、僕はSVNとGitとの違いとして、「ブランチの粒度の大きさ」を挙げたい。

Gitでは、とにかくブランチを細かくたくさん切るのだ。たった1行の修正のために1つのブランチを切るなんてことも決して珍しくはない。
一例として、今僕は会社でソロ開発してんだけど、7/1から7/25までの1ヶ月弱の間に切ったブランチの数カウントしたら、120だった。たぶん、SVNでは想像できない数なんじゃないかな。

というわけで、これからブランチを細かく切ると何が嬉しいのかってところを掘り下げて行くことにする。

帽子をかぶり分けるためにブランチを切る


開発の世界では「帽子をかぶり分ける」という言い回しをよく使う*1のでここでも引用させてもらった。
つまり、今、機能Aの開発をしているのか、バグBを直しているのか、処理Cのパフォーマンス向上のための改修をしているのか。どの作業をやっているのか明確にした上で、そのことだけを考える。そのためにブランチを切るのである。

例えば、開発してると、以下の様な場面に出くわすことがあるだろう。

  • 機能Aの開発中に、機能Aとは関係ないバグBを見つけた
  • 機能Cを作っていたが、機能Dを先に作っておく必要があることに気づいた
  • コメントが気に食わない、タイプミスが気になる、リファクタリングしたい、等々

このような場面でそのまま本能の赴くままに手を加えてはいけない。あれもこれも一緒に実装しようとすると、だいたい何か考慮漏れがあって、バグる。そこで、「このブランチではこの作業だけをやる」というルールを自分に言い聞かせて、頑なに守るのだ。ブランチの切り替え(git checkout)が帽子のかぶり換え。定量的にはわかりにくいかもしれないが、帽子のかぶり分けの効果は、スピードや品質の向上にかなり有効だと思う。
ちなみに、「今どの帽子をかぶってるか=今どのブランチにいるか」がすぐわかるようになってると、脳の切り替えには有効だ。なので、手慣れたシェルがあれば、是非、プロンプトに「今いるブランチ名」を表示するように設定しておこう。
また、あとで少し触れるが、マージ作業や、後から変更履歴を確認するときなんかにも、「機能Aのブランチでは必ず機能Aの実装だけが行われている」ことはとても有効だ。

"じゃあ、「今のブランチとは関係ないバグB」や「先に作らなきゃいけない機能D」はどうするの?"
答えはこう。

  1. issue tracking systemにissue切る(まずは何も考えずにissue切る。これやらないと、忘れるよ。)
  2. 関係ないバグBは放置。あとでやろう。
  3. 先に作らなきゃいけない機能Dが見つかったら、今のブランチのことはひとまず忘れて、機能D用ブランチを切ろう。


できる限り狭い範囲だけを考えればいいように影響範囲を限定しながらソースコードに手を加えるのは、開発の基本。ブランチの切り方も、この考え方にきっちり従っていこう。

粒度の話


ここまでの話をより深くイメージするには、「機能A」ってのはどれくらいの粒度なのよってのがあると良いと思うので、ここで触れておこう。

僕は1つのfeatureブランチは、だいたい以下の様な大きさに収まるように、という方針で切っている。(もちろん状況によるので必ずしも全てのブランチがそうなっているということはないが。)

ブランチ単位のdiffをとったとき、出てくる各diffが何を意図して行われた変更か、すぐに想像できる程度

ソースレビューなんかを実施する文化で開発したことのある方だと想像しやすいと思うが、ちょっとdiffの量が増えると、すぐ「必死にdiffを読み込まないと意図が把握できない」という状況になってしまう*2。そうならない程細かくブランチを切るのだ。

なぜそこまで細かくするか。

繰り返しになるが、それだけ「考える範囲を限定する」ことによってスピードと品質の高い開発をしようというのが一点。

そしてもうひとつは、「マージのため」という視点だ。VCSの利用において、一番事故につながりやすい作業が「マージ」だ。できるだけマージはしたくないという意図はわかるが、Gitの考え方は逆方向で、「マージの回数は増えても、安全なマージをする」だ。まず単純に、diffが少なければ競合しにくい。マージってのはブランチ単位でするものだから、ブランチが小さければ小さいほど競合しにくいという至極単純な理屈。
しかし競合は起きるものであり、いかに競合を正しく解決するかも考えなければいけない。正しく競合解決するために必要なものは「コード変更の意図を正しく把握すること」だと僕は思っている。ブランチの規模は「diffを見て意図をすぐ読み取れる」程度にしておくと、競合した時にお互いの変更をどのように組み合わせるのが正しいか把握しやすい。この辺りは文章では伝わりにくいかもしれないので、是非手を動かして実感して欲しい。


CIとの相性


あともう一つ、ブランチを細かく切るということは、CI(継続的インテグレーション)との相性が良いということにも言及しておきたい。

ビルドを回すには、最新のコードが「動くもの」になっている必要があるが、複数featureが混ざった長いブランチで開発していると、「動く状態」のタイミングが訪れにくい。「機能Aの実装は終わったけど機能Bが中途半端に実装開始している状態」コミットとかね。そうなると、結局全機能実装し終わるまでテストを回すことができなくなって、それって結局CIでもなんでもないということになる。

それに対して、あくまで最小単位の機能でブランチを切り、その機能だけが新たに動く状態になった時点でマージするというのがGitのスタイルになる。マージ先になるブランチには、機能が出来上がるたびに、プロダクトが動く状態でマージされていくので、このブランチはCIツールに監視させるブランチにピッタリだ。
(A successful Git branching modelをイメージして書いてます。これで言う、「develop」ブランチは、CIのためにあると僕は思っている。)

なんかGitの使い方というより、ブランチの切り方の話してない?


仰るとおり。

あくまでブランチの切り方が大事だと思っているのは確か。そして、ツールの機能としてはSVNでもこのブランチの切り方を実践することはできるだろう。だけど、このブランチの切り方を実践するにはGitじゃないとダメなんだ、これが。
なぜなら、この開発スタイルになってくると、ものすごい頻度でブランチ切るし、diff見るし、マージすることになる。SVNだとそれらのコマンドを打つたびにリポジトリサーバと通信が走り、待たされる。でもGitならこれらのコマンドはすべてローカルリポジトリで完結するから、一瞬で結果が表示される。だから分散リポジトリ型のVCSが、Gitがイイ。

一度Gitに慣れてしまってから「もうSVNには戻れない」って言う人は、そのサーバと通信してる数秒×何千回の時間浪費に戻れないという面が大きいのだと思う。

まとめ


というわけで何が言いたかったかというと、僕は別に「Gitのこの機能が良い!」という風には全然考えてない。「CVSからSVNに移行したときのように、SVNからGitに移行した時も打つコマンドが変わるだけ」というGitの使い方では意味が無いのだ。
では何がすごいのか。それは、「ちっちゃいブランチを切る」ことから始まる新しい開発スタイル*3だと僕は思っている。



*1: TDDでは「テストを書いてコンパイルを通す」「テストをグリーンにする」「リファクタリング」のステップがあり、明確に作業を分離する。例えば「コンパイルを通す」のステップで実装(テストを通す)を考えてはいけない。このように今どの作業をしているのか明確にし、その作業のことだけを考えるというスタイルを「帽子をかぶり分ける」と表現する。
*2: 「そして形式だけのソースレビューが行われて、レビューの効果が出るわけもなく、バグる」までが予定調和。
*3: ゆーてもGit自体そこまで新しいというわけではないので「新しい開発スタイル」は言いすぎかもしれないが、少なくともSVNベースとは違う開発スタイルになる。

2012年10月20日土曜日

heroku+SendGrid+Play2+JavaMailでメールを送る

今回は、heroku上にホスティングしたPlayframeworkベースのアプリケーションからメールを送信してみようというお話です。
※この記事は「heroku上でPlayframework2のwelcomeページを表示させることはできた」という前提で進めます

herokuからメールを送る


まず、そもそもherokuからどうやってメールを送るの?というところから。

手段としては、外部にSMTPサーバを用意するってのが一番単純で、ググると「herokuからGMailのSMTP叩く」みたいなのは出てきます。出てくるんですが、これやるにはそのSMTPの認証情報を直書きしてpushしなきゃいけないので、「できるんだけど扱いにくいなぁ」という印象でした。うまくやる方法あるのかな?あったら教えて下さい。

で、今回はherokuアドオンに頼ることにします。

https://addons.heroku.com/sendgrid

SendGridというアドオンがあったので使ってみることにしました。
1日200通まで無料の「Starter」プランで送れるそうです。どんなメールを送るかによりますが、とりあえず試すには十分すぎるのでこれを使います。
コマンドでもWebからでもいいので、SendGrid Starterをサクっとアプリケーションに追加しちゃってください。

このSendGridというアドオンはどのような仕組みでメールを送っているかというと、

  • SMTPサーバ自体は外部にある
  • 認証情報をアドオンから環境変数に渡してくれる
  • あとはSMTP叩くだけ

という至極シンプルなものです。

これだけ書けばどうやってメール送るかは想像できるかもしれませんが、一応ひと通り書いていきます。

認証情報をPlayアプリケーションに渡す


herokuでPlayframework2ベースアプリケーションを動かすにはProcfileってのを作ったと思うので、これを編集して、環境変数をJavaシステムプロパティとしてアプリケーションに引渡します。DBを使わないメールのみ送れる一番単純なアプリだとこんな感じ。

web: target/start -Dhttp.port=${PORT} ${JAVA_OPTS} -Dmail.smtp.host=smtp.sendgrid.net -Dmail.smtp.port=587 -Dmail.smtp.auth=true -Dmail.smtp.user=${SENDGRID_USERNAME} -Dmail.smtp.pass=${SENDGRID_PASSWORD}

※1行です

SENDGRID_USERNAME、SENDGRID_PASSWORDという環境変数で認証情報が渡ってくるので、適当なプロパティ名つけて渡してやりましょう。mail.smtp.host、mail.smtp.port、mail.smtp.authについては、別にここで渡さなきゃいけないってことはないけども、設定値をハードコーディングしない的な意味でも、どうせJavaMailでPropertiesオブジェクトが必要だからこの方が楽という意味でも、システムプロパティとして渡してやった方が良いかと思います。

JavaMailでメール送信


あとはメール送るだけです。今回はJavaMail使います。

http://www.oracle.com/technetwork/java/javamail/index.html

もちろんmavenリポジトリ上にあるので、project/Build.scalaに1行追加すれば依存設定完了。

val appDependencies = Seq(
  // Add your project dependencies here,
  "javax.mail" % "mail" % "1.4.5"
)

で、あとはJavaMailから、システムプロパティの値を使ってメールを送信してやるだけ。



※エラーハンドリングとかWebアプリ自体の認証とかせず、メール送信部分だけを記述しています

JavaMailの送信手順をScalaで記述しただけで、工夫とか何もしてませんけどね。
普通にこれで送信できます。

ね、簡単でしょ?

2012年8月28日火曜日

lift-jsonのextractで痒いところに手を伸ばす

lift-json便利ですね。Play2に組み込まれてるJsonも「型クラスかっけー」って感じで好きですけど、Playを使わないところではやっぱりlift-jsonがデファクトっぽい感じになってるかなーと思います。
で、lift-jsonでJsonからモデルにマッピングするにはどうするのかなーと思って検索してみると、大抵こちらに行き着きます。

Lift Json の case class への変換がとても便利な件 | scalaとか・・・

これ、すっげー便利なんですけど、実際に使ってみると、そのままじゃうまくいかない場面が出てくるんですよ。
なので、今回は「うまくいかない場面」 = 「痒いところ」に手を伸ばしてみます。

うまくいかない場面1: case objectへのマッピング


例えばこんな風に、Javaでいう列挙型みたいなモデルを作った場合。



で、この3つにそれぞれ0,1,2が割り当てられていて、Jsonでは例えば
{ carrier: 0 }
といった表現がされているとします。

この場合は、この型に変換する機構をlift-jsonは持っていないので、作ってやる必要があります。



このようにSerializerを作っておくと、Formatsに組み込むことができるので、以下のように自作Serializerを組み込んだ新しいFormatsを使ってextractできます。





うまくいかない場面2: Jsonの構造がちょっと違う


例:楽天ウェブサービスの市場商品検索2

これ、返りのJsonが以下のような構造をしています

{
  xxx: yyy
  ...
  Items: [
    {
      Item: {
      }
    },
    {
      Item: {
      }
    }
  ]
}

なぜかItemsとItemだけ頭文字が大文字なのかとか、ItemsのArrayの中身がItemそのものではなく、Itemというフィールドを持ったオブジェクトなのかとかツッコミたい感じです。ツッコミたいだけならいいんですけど、デフォルトのextract一発変換に頼ろうと思うとこういうのが結構ネックになるんですよね。
これ、「うまくいかない場面1」と同じようにSerializerを作っちゃってもいいんですけど、他にもたくさんの要素を含んでいて、ほんの一部だけを修正するためにSerializerを作るのはめんどうです。なので、こういう場合はJsonAST側を都合の良いようにいじっちゃった方が早いです。

変換にはtransformを使います。こいつ、再帰的にJsonASTを操作してくれるので、簡単にASTをいじれます。
詳しくはlift-jsonのソース見ちゃったほうが早いです。



こんな感じ。

2012年7月24日火曜日

永続化の前後をクラスで表現する

※本エントリの内容は「たぶんコレでいいんじゃない?むしろ違ったら教えて下さい」的な内容です。

DBにエンティティを永続化するというシチュエーションを考える。
例えばPersonというエンティティとしよう。



これをDBに保存するなら、PrimaryKeyが必要だ。ま、普通はIDを付けるでしょう。





さて、問題は、「ID採番前のPersonはどう表現するの?」という話。

案1. id = 0 とか、負の数を「採番前」ということにする

冗談です。

案2. idの型をOptionにする



確かに「IDの有無」を表現はできてるが、これだと例えば「これから採番するPersonのリスト」のような型が作りにくいです。

案3. オブジェクト指向っぽく行けばいいんじゃない?

いろいろ考えたんだけど落ち着いたのがここ。あんまカッコイイ答えじゃないんだけどね。
ただ、「case classにする」ってところだけは守るという制約でScalaで書こうと思うと、やっぱりちょっと考えないといけない。



で、こうなった。id以外のメンバは何回も書いてるのが正直気に入らない。でも、使うときは多分これが一番気持ちよく使えると思う。

もっと良い書き方募集。

2012年7月17日火曜日

Windowsガジェット環境でのsubstr

Windowsガジェット(最初はVistaサイドバーガジェットとか呼ばれてたもの)って、もはや生みの親であるMicrosoftからも「Windows8が出たら要らないよね」って言われちゃったかわいそうな子だけれども、でもなんだかんだ言ってもWindows7使ってるうちは何かと便利なツールなわけで、結構馬鹿にできないと思うんです。

というわけで、to-banからJSON引っ張って今日の"当番"をWindowsガジェットに表示させておくと捗るぞ、と思って手を出した。

が、すっげーアホくさいところでハマったので、記録しておく。

それがString.substr関数。

これ、第一引数に負の数を渡すと、現在の標準では文字列の後ろから数えてくれるんだけど、Windowsガジェット環境(や超古いブラウザ)では負の数を渡すと何もしてくれなくなる。



厄介なのは、IE9が入ってるWindows環境でも、ガジェット上ではガジェット環境での挙動を示すということ。「IEで動けばガジェットでも大体動くだろー」とタカをくくってるとハマる(体験談)

2012年7月16日月曜日

PlayでurlFormEncodedなデータを受け取る

Playでformからの入力を受け取る場合は、ファイルアップロードが必要な場合を除けば、大抵の場合はurlFormEncodedを通してデータを受け取る事になると思う(Ajaxとかいう解はとりあえず考えない)。例えば以下の様なコードになる。



言うまでもなく、Requestからデータを取り出す部分が無駄に長い。
これは、urlFormEncodedのbodyの型がMap[String, Seq[String]]だから。もちろんプロトコル上はこの型が妥当なのだが、大抵の場合はValueは1つだけ値があれば十分なので、Seqから1個取り出すのが冗長に感じられるわけだ。

じゃあ、ということで、以下の様な関数を作ってやればとりあえず短くはなる。



短くはなるんだけど、ただ単純に処理を切り出しただけ。使ってみるとわかるけど、あんまりScalaっぽくない。
せっかくScala使ってるんだから、型クラス使いたいよね!

というわけで、こうなった。



paramOpt関数がControllerから呼び出す関数。
こいつはimplicit parameterでExtractor[T]を受け取っていて、そのExtractorのextract関数を利用してデータ抽出を行う。
paramOpt関数自信も型パラメータを持っていて、そこに指定された型に合ったExtractorが選択されて使われるわけだ。
extractorByみたいな関数も用意しておけば、IntExtractorの様に、既存のExtractorと型変換用関数を組み合わせて新たなExtractorを構成するのも簡単だ。

Extractor[T]を使って最初のPersonの例を書きなおすとこうなる。



スッキリ!



追記:

extractorByの中身、何をやってるかって言うと、A => Option[B] と B => Option[C] を組み合わせて A => Option[C] を作ってるんだよね。で、


って呟いたら、反応されてた。



やっべ、kleisliわかんねぇ。勉強します。。。