第42回 Ruby/Rails勉強会@関西にいってきた
だいぶ前(4/24)の話になってしまうんだけど、ruby関西勉強会の#42に行って来た。一応狙いはid:cuzicさんのIOクラスについて*1と、以前uuuu帰りのお食事のときにid:okkezさんに「TDDおしえてー」とワガママってみたのでそれ*2。大変久しぶりだったので、結構理解できない事ばっかりかなーとか思ってたんだけど、やっぱり理解できない事がたくさん><
IOクラスについて by cuzicさん
IOクラスの話は予想通り面白かった。ruby IOではOSに近い部分が再実装されていて、高レベル操作と見事に統合されている(混用できる)。socket系統とか使うときに便利そう。あと、その為に fcntlでわざわざフラグを変えていたりしてて、凄い。
rubyの objidとメモリallocの概念とか、いろいろ勉強になった。
Ruby 初級者向けレッスン by okkez さん
パートナーが就活で、、、とかいう時風的イヤな感じで、okkezさん一人でのレッスン。大変そうだった><
内容自体は4月ってこともあって本当に初心者向け(異論あると思われます)。いろんなことをざっと流す感じだった。
課題があったのでとりあえず聞きながらやってみた。良くありそうなローマ数字変換をIntegerにつける、というやつ。ついでなので、課題についていた例を使ってそのまま Test::Unitの勉強をしてみた。
、、、と、言っても非常に簡単で、良くあるテストな感じ。
require "test/unit" require "to_roman" class RomanTest < Test::Unit::TestCase def setup end def test_old_roman assert_equal("IIII", 4.to_old_roman) assert_equal("CXV", 115.to_old_roman) end def test_roman assert_equal("IV", 4.to_roman) assert_equal("IM", 999.to_roman) end end
まあそのまま。でこれを実行。
to_roman自体はこんな感じで書いた。ちゃんと課題を読んでなくて、Fixnumにつけてしまった。
class Fixnum @@candidate = [[1000, "M"], [500, "D"], [100, "C"], [50, "L"], [10, "X"], [5, "V"], [1, "I"]] @@subst = @@candidate.reverse def to_old_roman v = self ret = "" @@candidate.each {|n, ch| c = v / n if c > 0 ret << ch * c v -= c * n end } ret end def to_roman(level = 1) v = self ret = "" @@candidate.each {|n, ch| if v > 0 c = v / n if c > 0 ret << ch * c v -= c * n end @@subst.select {|i, t| i < n}.each {|s, subch| c = (n - (v/s)*s) /s if 0 < c and c <= level and (n - s*c) != s ret << subch*c + ch v -= (n - s*c) end } end } ret end end
実行してみたら普通に
s210-063% ruby romantest.rb Loaded suite romantest Started .. Finished in 0.001021 seconds. 2 tests, 4 assertions, 0 failures, 0 errors, 0 skips
まあそりゃそうか・・・意図的に変えてみてエラーを出した。
Loaded suite romantest Started .F Finished in 0.000914 seconds. 1) Failure: test_roman(RomanTest) [romantest.rb:14]: <"IM"> expected but was <"Ip">. 2 tests, 4 assertions, 1 failures, 0 errors, 0 skips
ちなみにTDDでは最初にエラーになるように未実装のコードで実行して、「テストの正しさ」を検証するのが正しいやり方みたい。順番逆DAYO><。他にもMutation Testingなるものもあるそうな。これはコードの方をある規則で変化させて(例えば文をカットしてしまうとか、論理結合子を変えてしまうとか)その結果 pass/failが変わらないテストを「こいつダメダメ」と考える手法らしい。いろいろあるなあ。
あとからokkezさんに添削された。Test::Unitは エラーがあるとそのテストメソッドはそこで打ち切る(まあ普通そうだよね)。なので、1メソッド1テストにすべきとの事。確かにそうしないとエラー数が分からない。
で、後から minitestを覚えたので結果としてこうなった。
require "minitest/unit" require "to_roman" MiniTest::Unit.autorun class TestToRoman < MiniTest::Unit::TestCase def test_old_roman_4 assert_equal "IIII", 4.to_old_roman end def test_old_roman_115 assert_equal "CXV", 115.to_old_roman end def test_old_roman_9 assert_equal "VIIII", 9.to_old_roman end def test_old_roman_45 assert_equal "XXXXV", 45.to_old_roman end def test_old_roman_49 assert_equal "XXXXVIIII", 49.to_old_roman end def test_old_roman_999 assert_equal "DCCCCLXXXXVIIII", 999.to_old_roman end def test_roman_115 assert_equal "CXV", 115.to_roman end def test_roman_4 assert_equal "IV", 4.to_roman end def test_roman_9 assert_equal "IX", 9.to_roman end def test_roman_45 assert_equal "VL", 45.to_roman end def test_roman_49 assert_equal "IL", 49.to_roman end def test_roman_999 assert_equal "IM", 999.to_roman end end
なんかいけてないんだけど、とりあえず覚えたってことで。。。
RSpecも勉強始めたら @Masahitoさんに凄い良いとこ教えてもらった。
http://d.hatena.ne.jp/t-wada/20100228/p1
ガンガンいきます。
まとめ
いろいろ教えてくださった方々に、ありがとうございました。