第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の概念とか、いろいろ勉強になった。

RubyWebCam非実在なこと by にしたかおさん

「わたしの趣味は車輪の再発明です」*3
ぐれーと。お知り合いになれた。

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
ガンガンいきます。

まとめ

いろいろ教えてくださった方々に、ありがとうございました。

*1:というかreadpartial一点狙いだったかも

*2:こちらは残念ながら実現しませんでした。okkezさん大変そうだった

*3:チャットで出てた「自分で作った車輪の再実装はダメだけどね」というのも良かった