libdrizzleのruby拡張ライブラリ

http://d.hatena.ne.jp/tokuhirom/20090612/1244764189

ちょっと面白そうだったので、軽く調べた感じだと今のところなさそうだったので、上でやってることができるぐらいに作ってみた。

ちょっと時間があまりなくてまだベンチマークとかはとったりできてない。mysqlplusとかと速度比較してみると面白いのかなと思っている。

http://github.com/walf443/net-drizzle-ruby/

第7回Gotanda.rb

もう何回目か数えるのもめんどくさくなってきましたが、今日もひたすら前回の続きをしてました。

Rackの上で書いていると、

def action
  return not_found if cond1
  return not_found if cond2
  return not_found if cond3
  [ 200, { 'Content-Type' => 'text/plain' }, 'Found' ]
end

def not_found
  [404, { 'Content-Type' => 'text/plain' }, 'Not Found' ]
end  

みたいなコードを書きたくなるときがありますが、not_foundメソッドを呼ぶときは必ずreturnを呼ばないとそこで処理が終らずに下の処理が実行されてしまいます。

ここまではまぁ当たり前の話なのですが、cond1, cond2, cond3までチェックするという部分が複数のアクションで共通だったりするとメソッドにくくりたくなります。

def check
  return not_found if cond1
  return not_found if cond2
  return not_found if cond3
end

def action1
  check
  [ 200, {'Content-Type' => 'text/plain' }, 'Found' ]
end

def action2
  check
  return not_found if cond4
  [ 200, {'Content-Type' => 'text/plain' }, 'Found' ]
end

こういうふうにしたときに、not_foundは大域脱出できるようなメソッドとして作ってないと、action1では200が結果として返ってしまいます。

ということでこんなふうにしてみました。

class ActionError < Exception; end
class NotFound < ActionError; end

def action
  check
  [ 200, {'Content-Type' => 'text/plain' }, 'Found' ]
rescue ActionError => e
  e.message
end

def not_found!
 raise NotFound, [ 404, { 'Content-Type' => 'text/plain' }, 'Not Found' ]
end

例外オブジェクトは文字列を渡すことになっているのですが、いちおうそれ以外も渡せるのを利用してます。

前似たようなことをやろうとしたときに、例外だと何かに引っかかってcatch..throw方式を試していた記憶があるんですが、ちょっと思い出せないのと、throwはSymbolで投げるので、内部的に使ってるライブラリとかとsymbolが万が一被るとやっかいなことになりそうなので、例外方式がよいのかなといった感じです。

not_found!とメソッド名をふつうのメソッドのフローとは違うとわかるようにしています。(キモいと言われそうですが、本当はNotFoundにしようかと思っていたのですが、NotFoundだけだと定数と解釈されてしまうのでNotFound()とカッコをつけないといけなくて、目立つのでそれでもいいかなと思ったのですが慣習に従ってみた次第です)

YARVの気持ちに触れる方法

ふとYARVの気持にふれてみようと思ったんですが、どうやるんだったかよく忘れてしまうのでメモ。

yoshimi% ruby -e 'puts RubyVM::InstructionSequence.compile("(0..100).each {|i| }").disasm'
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
== catch table
| catch type: break  st: 0002 ed: 0012 sp: 0000 cont: 0012
|------------------------------------------------------------------------
0000 trace            1                                               (   1)
0002 trace            1
0004 putobject        0..100
0006 send             :each, 0, block in <compiled>, 0, <ic>
0012 leave
== disasm: <RubyVM::InstructionSequence:block in <compiled>@<compiled>>=
== catch table
| catch type: redo   st: 0000 ed: 0001 sp: 0000 cont: 0000
| catch type: next   st: 0000 ed: 0001 sp: 0000 cont: 0001
|------------------------------------------------------------------------
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
[ 2] i<Arg>
0000 putnil                                                           (   1)
0001 leave
yoshimi% ruby -e 'puts RubyVM::InstructionSequence.compile("for i in (0..100) do; end").disasm'                                                                          
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
== catch table
| catch type: break  st: 0002 ed: 0012 sp: 0000 cont: 0012
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] i          
0000 trace            1                                               (   1)
0002 trace            1
0004 putobject        0..100
0006 send             :each, 0, block in <compiled>, 0, <ic>
0012 leave            
== disasm: <RubyVM::InstructionSequence:block in <compiled>@<compiled>>=
== catch table
| catch type: redo   st: 0005 ed: 0006 sp: 0000 cont: 0005
| catch type: next   st: 0005 ed: 0006 sp: 0000 cont: 0006
|------------------------------------------------------------------------
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
[ 2] ?<Arg>     
0000 getdynamic       *, 0                                            (   1)
0003 setlocal         i
0005 putnil           
0006 leave 

VMがRubyVMに去年の6月ごろに変わっていたらしい。

追記:

twitter経由で中田さんに--dump=insnsオプションを教えてもらった。

yoshimi% ruby --dump=insns -e 'for i in 1..10 do end'                                                                                                                    [ ~/project/ruby/todoit master ]
== disasm: <RubyVM::InstructionSequence:<main>@-e>======================
== catch table
| catch type: break  st: 0002 ed: 0010 sp: 0000 cont: 0010
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] i          
0000 trace            1                                               (   1)
0002 putobject        1..10
0004 send             :each, 0, block in <main>, 0, <ic>
0010 leave            
== disasm: <RubyVM::InstructionSequence:block in <main>@-e>=============
== catch table
| catch type: redo   st: 0006 ed: 0007 sp: 0000 cont: 0006
| catch type: next   st: 0006 ed: 0007 sp: 0000 cont: 0007
|------------------------------------------------------------------------
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1] s3)
[ 2] ?<Arg>     
0000 getdynamic       *, 0                                            (   1)
0003 setdynamic       i, 1
0006 putnil           
0007 leave 

ちなみにinsnsって覚えられなさそうだなぁと思って何度か打ち間違えましたが、insとか短くてもマッチすればOKっぽい。(ただし将来的にその挙動が保証されるかは怪しい気もする)

1.9.1のmanを見ても載ってないということは開発者向けの隠しオプションという扱いなのかな。trunkを見てみると、dumpオプションは載っていて説明はDO NOT USEと書いてあるけど、ケースによってはこの出力は役に立つこともありそうだなぁと思ったりはする。(確かにそこまで必要なことはほとんどないけど)

他にもdumpに指定できるやつで便利そうなのはどんなのがあるんだろうか。

実行しているrubyインタプリタを使って別のrubyスクリプトを実行する

ベンチマークをまとめて走らせるプログラムなどを書いているようなときは、
system('/usr/bin/ruby some_script.rb')

とか書きがちなのですが、
/usr/bin/rubyと固定で書いてると、
別のところにある1.9.1とかでふと実行してしようと思ったときにスクリプトを書きかえないといけないのでめんどくさいです。

require 'rbconfig'

ruby_cmd = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
system("#{ruby_cmd} some_script.rb")

と書いておけば、起動したrubyのパスで実行させることができるので、1.9.1で動くかとかも楽にチェックできますね。

# Windowsとかはうまく対応できてないかもしれません。

# ふとそういえば、rakeでもやってたような気がして、rakeのrubyメソッドの定義を見てみると大本はこんなふうになってて、

  RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']).
    sub(/.*\s.*/m, '"\&"')

subとかしてるのは何でしょうねぇ。ディレクトリ名に空白が入っていた場合とかかなぁ。もしそうだとしたらgsubしないといけなさそうな気も。

追記:

id:otnさんのコメントで気づいたのですが、\&はリファレンスマニュアルとかざっと調べた感じだと載ってないような気もしますが、どうやら後方参照の一つで、おそらく組み込み変数$&と対応してるっぽい。つまりマッチさせた文字列全体なのでこのsubは空白があった場合に二重引用符で全体を囲うという意味らしいです。ひとつでもあったときに全体に適用するので、gsubでなくてsubということですね。

追記:

String#gsubの項目に説明が書いてあることを教えていただきました。正規表現の後方参照ではなくて、sub/gsubの特殊文字と呼ぶ方が正しいですね。

クラス内で使いたい関数を明示的にincludeできるようにする

rubyで次のようなコードはわりとよくあると思います。

# some/utils.rb
module Utils
  module_function

  def html_escape str
    # ...
  end

  def html_unescape str
    # ...
  end
end

# some/more_utils
module MoreUtils
  module_function

  def some_func
  end
end

# some/foo.rb
class Foo
  include Utils
  include MoreUtils

  def initialize
    html_escape 'html'
  end
end

自分はこのコードが嫌な点が3つあって、

  • html_escapeがどこで定義されているかsome/foo.rbを見ただけではわからない
  • Fooクラスにはhtml_unescapeは不要なのにFooの中から呼べてしまい、しかもpublic_instance_methodになってしまう。
  • Fooクラスにもescapeというメソッドが他にも存在しているので別名にしたい

というようなことが少し気にかかります。

これを気にするのであれば、明示的にモジュール名を書けばよいという話もありますが、あまりにも頻度が多いとそれはそれでコードが読みにくくなってしまうのがなんともといった感じです。

そこで、ここをより明示的に書けるようにするにはどうしたらいいんだろうということで書いてみました。

http://gist.github.com/77523

module FunctionExporter
  def export context, *args
    parent_mod = self
    
    if args.size == 1 && args.first.kind_of?(Hash)
      args.first.each do |original_name, want_name|
        context.module_eval %{
          want_name = "#{want_name}"
          define_method want_name do |*params|
            #{parent_mod}.__send__ "#{original_name}", *params
          end
    
          if want_name =~ /^_/
            private want_name
          end
        }
      end
    else
      args.each do |func|
        context.module_eval %{
          func = "#{func}" 
          define_method func  do |*params|
            #{parent_mod}.__send__ func, *params
          end
        }
      end
    end
  end
end   
      
module FunctionImporter
  def import_function mod, *funcs
    mod.export self, *funcs
  end
end   

if $0 == __FILE__
  # 対象となるようなモジュールはmodule_functionで定義してあるのが前提
  module Utils
    extend FunctionExporter

    module_function
   
    def escape str
      str
    end
   
    def unescape str
      str
    end
  end

  # 一番単純な使い方。第2引数以降にFooの中で使いたいメソッドを並べる
  class Foo
    Utils.export self, :escape, :unescape

    def initialize
      p escape('hoge')
    end
  end

  # 上記の例と同様。selfを渡すのがちょっとカッコわるいのでシンタックスシュガーとしてFunctionImporterを使ってるだけ。
  class Bar
    extend FunctionImporter
    import_function Utils, :escape, :unescape

    def initialize
      p escape('hoge')
    end
  end

  # Hashで渡すと、keyのメソッドをvalueの名前として使えるようになる
  # _で始まるメソッドはprivateメソッドになる
  class Baz
    Utils.export self, {
      :escape => :_escape,
      :unescape => :my_unescape
    }

    def initialize
      p _escape('hoge')
    end
  end

  Bar.new
  begin
    Baz.new._escape('hoge')
  rescue Exception => e
    warn e
  end
  Baz.new.my_unescape('hoge')
end

ついmodule_evalでやってしまいましたが、こういうgemないかなぁと思って検索してみたらmodule-importというgemがみつかりました。

こちらは、

  • importがKernel空間に直接定義されているということと、
  • 別名で使えるようにするなどの機能がない ( githubのtrunkなどをみると別名で書ける機能もあるっぽい )

といった点が個人的には不満ですが、元のモジュールをdupして不要なメソッドをundefしまくってからincludeする実装になっているので、自分の手法に比べ、

  • 最終的にはincludeで入れているので元モジュールでmodule_functionしておく必要がない
  • 元モジュールで定義されていないメソッドがあればimport時に例外を出してくれる
  • Fooクラス内でのメソッド呼び出しのコストがないので実行時では速そう

といったあたりが優れているなぁと思いました。

探してみれば、過去に散々議論されてるネタなような気もします。

追記:

コードを一通り書き直してgemにしました。

gem install function_importerでインストールできます。

使い方などは特に変わってはいないですが、ドキュメントなどはこちらを見てください。 https://github.com/walf443/function_importer/tree

nestした構造のprocを作る

# ok.
bl1 = proc { p "called!!" }
  #=> #<Proc:0x00636a14@(irb):1>
 
bl2 = proc { [1, 2].each(&bl1) }
  #=> #<Proc:0x00633788@(irb):2>
 
bl2.call
"called!!"
"called!!"
  #=> [1, 2]
 

ふつうは上記のような挙動をするが、

bl = proc { p "called!!" }
  #=> #<Proc:0x006369b0@(irb):1>
 
bl = proc { [1, 2].each(&bl) }
  #=> #<Proc:0x006337d8@(irb):2>
 
bl.call
SystemStackError: stack level too deep
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
... 6167 levels...
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):2:in `each'
        from (irb):2
        from (irb):3:in `call'
        from (irb):3

これは、procが実行されるときには、同じスコープ内でblが書きかわっているため、無限ループしてしまう。

これがどういうときに必要になってくるかというと、

procs = [ proc { p "called!!" } ]
  #=> [#<Proc:0x00636028@(irb):1>]
 
3.times do |i|
 procs[i + 1] = proc { (0..1).each(&procs[i]) }
end

のようなループの中でprocを作りその中で動的に作ったprocをつくるような場合だったりする。

いちおうevalを使うと、うまく実行させることもできる。

procs = [ proc { p "called!!" } ]
  #=> [#<Proc:0x0062979c@(irb):9>]
 
3.times do |i|
  procs[i + 1] = eval <<-EOE
    proc { [1, 2].each(&procs[#{i}]) }
  EOE
end
  #=> 3
 
procs.last.call
"called!!"
"called!!"
"called!!"
"called!!"
"called!!"
"called!!"
"called!!"
"called!!"
  #=> [1, 2]

しかし、evalはなるべくなら使いたくなかったので、#rails-tokyo@freenodeで聞いてみると、id:moroさんに教えてもらえた。

3:11 walf443: http://gist.github.com/20388
23:12 walf443: proc内でprocを実行させまくろうとしたら上のような罠にハマったのですが、
23:12 walf443: もっと良い回避方法を御存じの方いらっしゃいませんか?
23:13 morohashi: 同じ名前なのが行けないんじゃないでしたっけ
23:14 walf443: ですね。同じ名前なのがまずいのでevalでむりやり回避してます
23:15 morohashi: bl1とbl2の中身は無関係なんですか? なんつーか、プログラマブルに作れます??
23:17 walf443: うーん。とループの中でprocを順番にネストした構造にしたいというのが目的ですかね。
23:18 morohashi: finder = [{:cond=>"hoge"},{:cond=>"fuga"].inject(proc{ find(1) }){|p,c| with_scope(c, &p) }
23:18 walf443: プログラマブルに作れるというのは意味がちょっとよくわかってないです
23:18 morohashi: とか昔書いた気が。
23:18 morohashi: あー、ループの中で、というのであってると思います。
23:19 morohashi: こんな感じで解決するのなら私が推測した問題があってたということで
23:20 walf443: with_scopeってactivesupportでしたっけ?
23:20 morohashi: あーこれはRails 2.0のころに書いたコードです。named_scope入るまえ。
23:21 morohashi: named_scopeはこれをメソッドチェーンで出来るようになってる凄い子
23:21 walf443: ふむ.
23:24 walf443: injectだと一時変数がブロックローカルスコープ内におさまっているので、名前の重複を起こす心配がないので大丈夫なのかな。
23:24 walf443: ちょっとinjectで試してみます.
23:31 walf443: うーん。 http://gist.github.com/20388
23:31 walf443: 最後の一回しか実行されてないっぽいですねぇ。。。
23:47 morohashi: (0..2).inject(proc{p :called}){|pr,i| proc{(0..i).each(&pr)} }[]
23:47 morohashi: でいっぱい動きますよ
23:48 walf443: 一番最後みたいに、
23:48 walf443: http://gist.github.com/20388
23:48 walf443: メソッド呼びだしにすると動きますね。
23:48 morohashi: てか [0..1]
23:49 morohashi: じゃなくて[0..i]か
23:50 morohashi: [ 0..1 ].each(&b)でなく(0..1).each(&b)じゃないでしょうか。
23:50 walf443: うはw
23:51 walf443: Range派じゃないのがバレたw
23:51 morohashi: www
23:52 walf443: このパターンのときはinjectを使わざるをえないということは肝に明じておきますw
23:52 walf443: morohashi++
23:54 walf443: ちなみにblogのエントリにしようと思うのですが、IRCのログ出してもOKですかね? > morohashiさん
23:56 morohashi: もちろん
23:56 walf443: あざっす

injectは使うとコードは一見してわかりにくくなるし、eachで実行するのにくらべて非常に遅くて、最近はあまり好きじゃなかったのですが、この場合はinjectを使ってブロックローカルスコープを作り出すのがよさそうですね。

id:moro++

札幌Ruby会議01にいってきた

jpmobileでいつもパッチなどを送らせていただいてるid:darashiさんや、wassr上でよくやりとりしてるid:h-sbtさんなど、札幌の面々はいろいろふだんからつきあいがあり、実際に会いたかったので、札幌にいってきた。

wassrでの位置情報の地図はこんな感じ

以下非常に長いふつうの日記。

1日目

新千歳空港と札幌の間は数駅なのでそんなに遠いとは思っていなかったが、普通に遠かった。

駅で島田さんが歓迎してくださって、id:kakutaniさんとid:darashiさんいちおしのPicanteというお店。

http://soupcurry.info/shop/7

id:mrknさんがお店の席をとっておいてくださった。

ついてそうそうid:kakutaniさんがビールを頼む。
ちょっとして、id:t-wadaさんが合流。

おいしくて、量もすごくあるのでおなかもいっぱいになった。ごはんを食べたあとは、各々ホテルにチェックインなどをしないといけないので一旦解散した。

id:mrknさんがにホテルの名前を言っただけで場所を知っていてすぐわかったので非常に助かった。

チェックインしたあとはホテルでだらだらコード書いてたり、cucumberの予習や会場設営のustをながめてたりした。

前夜祭よりはかなり早めにでて次の日の会場などを探してうろうろするがかなり迷ってしまう。

仕方がないので島田さんに電話してなんとか前夜祭の会場にたどりつく。

Ruby札幌は女性の方も多かったので驚いた。木戸さんに青森の話などを聞く。

ここでの舟盛りが大量ですごくおいしかった。

店を出たところでなぜかバナナを配っていた。

ホテルに帰ると腹いっぱいで苦しくて眠れなくて何もせずにねころがっていた。

2日目

朝おきて、Ruby会議の開始が12:00からと結構遅かったので、その日の二次会で行われるという予定らしいものの仕込みを作っていた。

つい夢中になっていて気がついたら時間がなかったので急いで出てなんとか間にあった。

スイーツタイムは、ひととおりは食べてみようかなと思っていろいろ食べまわっていたが、他の人は遠慮がちに食べていたのでもう少しスイーツ(笑)としての嗜みが必要だったのかもしれない。

id:mrknさんが用意した南幌キムチがスイーツの中で明らかに浮いていたが好評だった。

id:elimさんが会場をustしてあるいていた。

artonさんの話

windows環境ならではのrubyを対応させる上での苦労話など。arton節が炸裂して、ircが盛りあがっていた。

えっちなしばたさんの話

自分はあまりtDiaryを触らずにrubyをやってる人なので、その歴史的な話などはあまり知らなくておもしろかった。興味を失わず継続的に開発を進めることは本当に大変で、自分などは特に飽きっぽく作って放置しているプロジェクトがいくつかあるのでそれらもたまには手を入れないとなぁと反省した。

1.9移行計画は、tDiaryのようなアプリケーションはEncodingが非常に重要なところなので、1.8でも動くように保ちつつ、1.9に移行させるのはなかなか大変な作業だろうなと思った。

うまく壊さずに進めるためにはid:t-wadaさんの東京Ruby会議01のおはなしのように、

  • tDiaryの今の挙動をトレースできるようなスクリプトを書く。
  • 挙動がかわらないようにリファクタリングしていじりやすい構成にする
  • Encoding周りで1.9と1.8系のコードを分離する

といった手順を踏んでくしかないかなぁと思ったり。(tDairyの中身をよくしらないので憶測ですが)

せきさんの話

天丼パターン重要。dRuby入門だと思っていたらdRuby実装入門だった。講義についていけずに落ちこぼれたのが非常に悔しい。
まさかのid:darashiさんとの講師交代。

ちなみに既にbuzztterではRindaは動いていないとのうわさw

まいむぞうさんの話

http://gemspec.info/ の話。

gemからメタデータを吸いだしたり、全gemをダウンロードして展開してみたりとかを最近ちょっとやってみたので、かなり興味深いはなしで、そのあとの懇親会やスイーツタイムなどでいろいろ情報交換をしたりさせてもらった。

id:kwatchさんのはなし

http://github.com/kwatch/motto-mysql/tree/master のはなし。

とみたまさひろさんのmysql-rubyをベースに機能を追加したもの。mysql-rubyはDateTime型のデータなどを文字列でしか返してくれないのでオブジェクトに変換したりする機能を提供してくれるらしい。

個人的にも今薄いORMっぽいものを作りたい衝動があるので、割と似たようなものを作るか、このライブラリをラップしてあつかえるようにした何かを作るかもしれない。

さらに http://github.com/oldmoe/mysqlplus/tree/master をベースにすると1.9.0.5にも対応できてかつ早くなるんではないかなと思った

id:mrknさんのはなし

https://github.com/mrkn/ruby-measure/tree は賢い数値単位変換を実現するためのかなりおもしろいライブラリで、既にRuby札幌の勉強会のustの発表を見ていて興味が湧いたのでパッチなどを投げたりしていた。

折をみて色々話をしてみたが、今後の課題としてあるらしい、各単位のクラス化、単位の多次元化や、そのインターフェースなどはなかなか難しい。

個人的にもちょっと色々やってみて良さそうな方法を模索してみようかと思っている。

id:t-wadaさんのはなし

5分のLTなのに非常に内容の濃い話で、それをネタに懇親会で議論しつづけてももちそうな話だった。

なるべく実装に依存しないテストというのは、抽象的になりがちで、中身を見たときにそこで何をやろうとしているのかがすんなりわかるようなテストにするのはなかなか難しいなぁと思います。

ただ冗長すぎると、どんなテストがあるのかわからず、他の人が手を入れることになったりすると、あっというまにレガシーなコードになってしまうなぁと。

テストを消すという発想と、カバレッジがかぶっているときは消すことができるという仮説はおもしろいなと思いました。

id:kakutaniさんの話

などに関しては気になりながらもあまりキャッチアップできてないので、あとで実際につかってみる。

話の内容としては、しばたさんの話を進めるための一つの提言だった。

一つ問題があるとすれば、使おうとしているツールが今のところ1.9対応してないところだろうか。

rspecは1.9で動かないコードが入っていてこれまで何度かパッチが出ていたような気がするが、作者はあまり1.9に興味はないっぽい。

ただし、rspecに関してはmspecである程度は代替できる。rcovは非常に便利で素晴しいツールですが、1.9だと動かない。ただし、1.9からだとruby本体にカバレッジ測定用のAPIがあるのでそちらを使うことはできるような気もする。

なのでひととおりやってしまってから考えてもよいかもしれない。

http://github.com/kakutani/testable_tdiary/ は主に変更されてるのはmaster以外のブランチなので注意


その後、片付けを手伝って発表者でもないのに打ち上げに参加させていただいた。

id:kakutaniさんが前来たときに山のように刺身が盛られていて食いきれないといっていたお店にいった。実際、敵は化け物だった。

あえなく撃沈した。自分たちのテーブルはid:cojiさんが早々に手巻き寿司セット、味噌汁などの流行を作り出していた。

カニむきに定評のあるドラ娘が活躍していた。

二次会は、バーのようなところで、プロジェクターを移してLTをやろうということだった。

いがいがさん( http://iddy.jp/profile/igaiga/ )のQuartzComposerでタイマー+IRCを実現する話は、本番のLTで発表しなかったのがもったいないくらいに面白いはなしだった。

id:kwatchさんのネタは九州で発表するやつらしいのでここでは割愛しておく。

自分は、 http://d.hatena.ne.jp/tokuhirom/20081018/1224344782 の話などruby界隈でももっと盛りあがるとおもしろいかなぁと思って前々からやろうとは思っていたのですが、

http://github.com/walf443/classx-pluggable

vimゼンした。実際のところはまだまだ作りかけで、APIなどはまだまだ変わる余地がいろいろある。

そのあとは、スープカレーのお店。このお店の名前はわすれてしまった。

( 追記: SHO-RINというお店だそうです。 http://soupcurry.info/shop/47 )

id:kwatchさんがまいむぞうさんの辛さ20のやつを間違って食べてしまい、大変なことになってしまっていたのと、フリップフロップ演算子の話で盛りあがっていた。

フリップフロップ演算子はその場にいた人のほとんどが知らないという非常にマニアックな機能で、

x = 0; 10000.times { if (x == 4)..(x == 10); p x; end; x += 1; }
4
5
6
7
8
9
10
  #=> 10000

こんなもの。

応用すると、 http://jarp.does.notwork.org/diary/200810c.html#200810211

のようなことができる。

プロゴルファーになりたい人はぜひとも知っておきたいテクニックだ。

3日目

id:darashiさん、id:mrknさん、しまださん、tmaedaさん( http://tmaeda.s45.xrea.com/td/ )、いがいがさん、ドラ娘、高橋会長と自分で札幌観光をした。

まずはデパ地下でスイーツをキメて、スープカレーを食べた。
北海道のスープカレーは確かにどこも非常においしい。(もちろんマスターid:darashiさんの推薦のお店にいっていることも大きいのだけど)

そうすると雨が降ってきたので、ぶらぶらしながらJRタワーの展望台へ。

その後は喫茶店を探しながらうろうろしたが、雨と日曜日なこともあってか大人数はちょっと入れなかった。

個人的には、id:darashiさんとgitのrebaseとmergeの違いについてが話せてよかった。

地下の広場で座りながら話をして、電車に乗って帰った。

まとめ

本当にほどんど面識がないにもかかわらず、Ruby札幌の人には非常に親切にしていただいて、OSSの人同士のつながりのようなものの素晴しさを再実感できて感動しました。

普段からオンラインで色々議論していてなかなか伝わらずにもどかしい部分などを実際にHackathlonのようなことで伝えられなかったのはちょっと残念かなぁと思っています。

会場の設備、片づけは、もっとみんなでやってもよかったと思う。色々とコミュニケーションを取る必要ができるし、コミュニティってそういうものだと思うのです。発表者と聴衆とスタッフというようにはっきり別れてしまうのは時には必要なことなのですが、壁をつくってしまいがちなところもあったりするような気もします。

あと札幌発表の人が少なかったのも少し残念でした。

というようにあれこれ書いていますが、非常に綿密に計画が練られていて、様々なことに挑戦し、Ruby札幌は素晴しいと思います。これからの発展に期待します。

本当にお世話になったしまださん、ありがとうございました。