Rails の select() 系ヘルパーはイケてないと声を大にして言いたい

背景

いつも思うが、Rails の select() 系ヘルパーメソッドってわかりにくい。
初心者泣かせ。

具体例(1)

参考文献: マニュアル

select() の例:

### eRuby
<%= select("post", "person_id",
           Person.all.collect {|p| [ p.name, p.id ] },
           { :include_blank => true }) %>

### HTML
<select name="post[person_id]">
  <option value=""></option>
  <option value="1" selected="selected">David</option>
  <option value="2">Sam</option>
  <option value="3">Tobias</option>
</select>

( select() では [p.name, p.id] の順番なのに )

具体例(2)

collection_select() の例:

### eRuby
<%= collection_select("post", "person_id",
                      Person.all, :id, :name,
                      {:prompt => true}) %>

### HTML
<select name="post[person_id]">
  <option value="">Please select</option>
  <option value="1" selected="selected">David</option>
  <option value="2">Sam</option>
  <option value="3">Tobias</option>
</select>

( collection_select() では :id, :name の順番になってる! 紛らわしい! )

問題点

  • 使い方がわかりにくい
    • 引数の数が多いため
  • よく似たメソッドが複数存在
    • ちょっとずつ異なる用途ごとに別々のメソッドが用意されているため

改善案

  • select() がブロックをとるようにする。
<%= select(....) do |t|
      @items.each {|x| t.option(....) }
    end %>

改善例(1)

### 改善前
<%= select("post", "person_id",
           Person.all.collect {|p| [ p.name, p.id ] },
           { :include_blank => true }) %>

### 改善後
<%= select("post", "person_id") do |t|
      t.option(nil, '-')
      Person.all.each {|p| t.option(p.id, p.name) }
    end %>

(:include_blank とか覚える必要なし)

改善例(2)

### 改善前
<%= collection_select("post", "person_id",
                      Person.all, :id, :name,
                      {:prompt => true}) %>

### 改善後
<%= select("post", "person_id") do |t|
      t.option(nil, 'Please select')
      Person.all.each {|p| t.option(p.id, p.name) }
    end %>

(:prompt とか覚える必要なし)

効果

  • 使い方がわかりやすい
    • select() の引数とブロックとで役割を分担するため
    • select() の引数の数が減るため
  • 1 つのメソッドに統一できる
    • 用途が少しずつ異なっても、ブロックの記述で吸収できる
    • 少しだけ異なる用途ごとにメソッドを用意する必要がない

今後の課題

  • optgroupタグのサポート Done
  • HTMLエスケープ
    • Rails3の仕様にあわせるべき?
  • Rails plugin 化
    • めんどいなー
    • 正直、Rails には疲れた

まとめ

  • Rails の select() 系ヘルパーはイケてない
    • 使い方がわかりにくい
    • よく似たメソッドが複数存在する
  • select() がブロックを取るように改善
    • 使い方がわかりやすくなる
    • 1 つのヘルパーメソッドだけで済む
    • ブロック万歳!

Java屋さんのコメントがレベル高すぎて困る

Velocity/JSPが遅い件について、Java屋さんからびっくりするようなコメントをもらった。


最初のコメントは、よく意味がわからなかった。

はなこ 2010/06/04 14:37
JSP 勘違いしてない?

JSP を初めて読み込むと、開発サーバーによって Java ソース コードに変換され、その Java ソースが Java バイトコードコンパイルされます。Java ソースとコンパイル済みのクラスは、一時ディレクトリに保存されます。元の JSP ファイルに変更を加えると、JSP が自動的に再生成されてコンパイルされます。

JSP の使用 - Google App Engine - Google Code
http://code.google.com/intl/ja/appengine/docs/java/gettingstarted/usingjsps.html

JSPが遅い理由をJava屋さんはまるでわかってないらしい - kなんとかの日記

JSPServletに変換されて実行されるくらい、ワシでも知っとる。このコメントだけでは、ワシのどの文章が『JSPを勘違いしている』と思ったのかわからない。


2つ目のコメントから、雲行きが怪しくなる。

はなこ 2010/06/04 19:00
書いてる通りですけど、JSP や Velocity は実行前にはすでにサーブレットに変換され、コンパイルされており、インタプリタ言語のように JSP の構文を解析しながら実行されるものではありません。
コンパイルするのでこの時点で構文エラーもチェックされており、
ここで仰っている動的、静的で言うならば、静的だと思います。

JSPが遅い理由をJava屋さんはまるでわかってないらしい - kなんとかの日記

まず、『インタプリタ言語のように JSP の構文を解析しながら実行されるものではありません』という文章から、インタプリタ構文解析をしながら実行していると勘違いしている。しかしシェルスクリプトならともかく、今ドキのインタプリタ (Ruby/Python/Perl/PHP/...) は構文解析してから実行するので、『構文解析しながら実行』というのは間違い。

また『コンパイルするのでこの時点で構文エラーもチェックされており、ここで仰っている動的、静的で言うならば、静的だと思います。』という文章から、明らかに動的言語・静的言語を誤解していることがわかる。コンパイルする・しないことと、言語が動的・静的ということは、別に関係はない。Pythonは動的な言語だが事前にコンパイル可能だし、Rubyならコンパイルできるかどうかは処理系に依存する (RubiniusはできるがMatz Rubyではできない)。

さらに、『Velocity は実行前にはすでにサーブレットに変換され』ると書いてあるが、そんな事実はない。確かにVelocityではVelocityServletというクラスが用意されているが、それはVelocityをサーブレットに変換するわけでもないし、変換されたものでもない。まあさすがにこの間違いは自分で気づいたようで、次のコメントで訂正している。

はなこ 2010/06/04 19:16
Velocity はコンパイルされないようですね。

JSPが遅い理由をJava屋さんはまるでわかってないらしい - kなんとかの日記

つうかそのくらい知っとこうぜ、Java屋さんなら。Velocityは歴史ある有名ライブラリなんだから。


次のコメントもボロがでてる。

はなこ 2010/06/04 20:29
先に
> Velocity はコンパイルされないようですね。
と書いてます。Velocity のしくみはおっしゃる通りのようですが、
Velocity と JSP はしくみが違います。

Velocity - You make the decision - Generation?
http://www.jajakarta.org/velocity/velocity-1.2/docs-ja/ymtd/ymtd-generation.html

> Perl/PHP/Python/Ruby/JavaScriptといった現代のインタプリタはどれも、実行に先立って構文解析を済ませてから実行されます。

承知しています。

言いたかったのは、
"JSP は実行前にサーブレットに変換されスタンバイしてます。" っていうことです。
JSP が遅いということは、サーブレットが遅いと言っていることと同じに思えたのでコメントしました。

JSPが遅い理由をJava屋さんはまるでわかってないらしい - kなんとかの日記

『承知しています』なんて書いてあるけど、理解してたら『インタプリタ言語のように JSP の構文を解析しながら実行されるものではありません。』などど書くわけがない。明らかに理解しているふりをしてるだけ。

また『JSP が遅いということは、サーブレットが遅いと言っていることと同じに思えた』と書いてあるのだが、この人は元記事を読んだのだろうか。JSPが遅いのは動的な言語を導入したからだと元記事に書いてあるのに、なぜServletのことを持ち出すのだろうか。関係ないだろうに。


ここまできてようやく気づいたのだが、どうもこのJava屋さんは「動的な言語」というのを勘違いしているようだ。恐らく、事前にコンパイルするのが「静的な言語」で、そうでないのを「動的な言語」と思い込んでいるらしい。だから「JSPは動的な言語を導入したから遅い」という主張に対し、「JSPは事前にコンパイルするから静的である。JSPを勘違いしてない?」というアサッテな反論をしているわけだ。

いやいやいや、勘違いしているのはそちらですから。

はなこ 2010/06/06 01:00
jsp は遅延評価使わなければ静的では。jsp 使う時 el 必須とは思ってない。el 限定の話ならタイトル変えて欲しい。最初から読まないから。

JSPが遅い理由をJava屋さんはまるでわかってないらしい - kなんとかの日記

『遅延評価』などと言っているが、これも明らかに勘違いだわな。遅延評価をバリバリ使っているHaskellが静的なのに『遅延評価使わなければ静的』とかアホか。聞きかじった用語でコメントされても困る。いや違うな、聞きかじった用語で*エラそうに*コメントされても困る。

他のJava屋さんは誰も見てないのかなあ。こんなコメントするやつを放置するなんて、身内には甘いよね。

しかしなあ、『el 限定の話ならタイトル変えて欲しい。最初から読まないから。』と言い出すとか、逆切れだろ。Java屋さんのほうが勝手に勘違いしてただけのくせに、そこを謝ることは一切しない。自分の間違いや勘違いは正そうとせず、相手のせいにするんだから、まあマスゴミモンスターペアレントの類いと一緒だわな。


やっぱこんな国は一度バルスされちゃったほうがいいよ。まじで。


#「はなこ」なんて名乗ってるけどこれで男だったら笑える

Rubyでライブラリのrequireにかかる時間を計測するスクリプト

## usage: ruby -s invoke.rb [-n=1000] [command]

require 'benchmark'

n = ($n || 1000).to_i
puts "*** n=#{n}"
commands = ARGV.collect {|lib| "ruby -e nil -r #{lib}" }
commands.unshift("ruby -e nil")

Benchmark.bm(30) do |job|
  commands.each do |command|
    title = command
    job.report(title) do
      (n/10).times do
        system command; system command
        system command; system command
        system command; system command
        system command; system command
        system command; system command
      end
    end
  end
end

実行例 (MacOS X 10.6)。見れば分かるけど、ミリ秒単位はうまく計測できないようだ。

$ ruby -v
ruby 1.8.7 (2010-01-10 patchlevel 249) [i686-darwin10.2.0]
$ ruby -s invoke2.rb -n=1000 erb cgi openssl yaml rexml/document
*** n=1000
                                    user     system      total        real
ruby -e nil                     0.040000   0.380000   5.080000 (  5.388027)
ruby -e nil -r erb              0.050000   0.390000   7.930000 (  8.334806)
ruby -e nil -r cgi              0.040000   0.440000  15.960000 ( 16.603249)
ruby -e nil -r openssl          0.050000   0.400000  18.750000 ( 19.486394)
ruby -e nil -r yaml             0.060000   0.460000  30.750000 ( 31.581488)
ruby -e nil -r rexml/document   0.050000   0.460000  38.750000 ( 39.970713)

opensslがかなり重いと思ってたけど、yamlやrexmlはそれよりも重いのか。yamlはextentionのくせになぜ重いんだろう?

Windows では Ruby の require が遅いらしい

Ruby の require が Windows では遅いという話。

このような現象が起こるのは一体なぜなのでしょうか?理由は、read_class_onefile_binmodeを見るとすぐに分かります。バイナリモードでは、Windowsの1.8.7と1.9.1は違いがないのです。バイナリモードとテキストモードの違いは、文字列のエンコード関係の処理を行うか否かですので、これにより、Windowsではファイル読み込み時のエンコード処理に時間がかかることがわかります。

require高速化:基準となるベンチマーク - ashelの日記

(強調はワシによる)

これって、File.open('file.rb', 'r') と File.open('file.rb', 'rb') の違いを言っているんだよね。だったらエンコードじゃなくて改行文字の変換 ("\r\n" → "\n") をするかどうかじゃないのかな。

あと requrie のベンチマークは、1 回の require だけで計測してるけど、これだと時間が短すぎて計測誤差が大きいように思うけど、そんなことない?複数回 require して計測できればそのほうがいい。

形式的な仕様記述がすごいらしい

 早速,メルコ・パワー・システムズの開発部門が原因究明に乗り出す。しかし,いくらデバッグしてみても不具合の原因が分からない。それどころか,現象が非常にまれにしか起きないため,不具合の再現すらおぼつかない状況だった。

 開発作業に暗雲が立ち始めたちょうどその時,社内でモデル検査技術の試験適用を進めていた早水公二氏が現れる。新技術の調査・導入を担当する技術統括部 ビジネスチーム2に所属するモデル検査の専門家だ。既に何件かのプロジェクトでモデル検査の効果に手応えを感じ始めていた同氏は,この不具合の原因究明にもモデル検査技術が使えないかと思い立ち,作業に取り掛かる。そして,モデルの作成やツールによる探索作業などを経た13時間後,見事,不具合の原因を見つけ出したのだった(図2,図3,図4)。

第5回:国内でも成功事例が出現,形式的手法の壁は高くない(2ページ目) | 日経 xTECH(クロステック)

形式的手法を用いることで、再現性が非常に低いバグの原因をわずか13時間で見つけたとな。すごすぎる。

 今回の不具合の要因は結局,境界値に起因する条件判断文の単純な実装ミスだった(図3)。バグというのは,大概が思いも寄らない部分に潜むからこそ,発見できずに不具合となる。レビューやテストの観点を増やせば検出できるという正論だけでは限界がある。「コンピュータのルーチンワークとしてプログラム中を探索するモデル検査は,人力に頼るテストの限界を別の観点から補足してくれるもの」(星野氏)といえる。

第5回:国内でも成功事例が出現,形式的手法の壁は高くない(4ページ目) | 日経 xTECH(クロステック)

形式的な仕様記述は、プログラミング言語とは独立して利用できるのかな。だとしたら、動的な言語においてテストを補完するものになるのだろうか。

 自然言語の仕様であれば,人により解釈は千差万別。一度きりの片方向のやりとりであれば,目立たない仕様解釈の誤差も,仕様精査や実装のサイクルが回る中で蓄積され増幅されてしまうのだった。サイクルを経ても,解釈の誤差が入り込まないような方法論,それが形式的手法だったのだ。「10万行もの仕様をVDMで書いたと言うと,『ほとんど実装に近いですね』などと言われるが,仕様策定者が実装を書いて何が悪いのか。そのくらいの詳細度が本当に必要な立場であれば,実装並みに厳密な仕様でも,ためらいなく書くべきだ」(同社の栗田氏)。

 栗田氏によると,形式的手法による仕様記述を経験すると,仕様を厳密に書く発想が根付くため「今は日本語に戻っても十分厳密に仕様を書く自信がある。形式的仕様記述言語がソフトウエア技術者の共通言語になれば望ましいのでは」とする。

第5回:国内でも成功事例が出現,形式的手法の壁は高くない(5ページ目) | 日経 xTECH(クロステック)

かっこえー!
英語の勉強やめて、仕様記述言語を勉強しようかな。

@ITの炎上していたブログ主がクビになってた

昨日紹介した、SQLオブジェクト指向の話題で炎上していた『ベンチャー社長で技術者で』というブログの主が、なんとクビになってた。

【お知らせ】

 @IT自分戦略研究所 編集部です。

 2010年5月28日、『ベンチャー社長で技術者で』を執筆する生島勘富氏を、エンジニアライフ コラムニストより除名いたしました。

 今回の件について、多くの読者から問い合わせをいただきました。今回の処置について、生島氏には了承いただきましたが、「これまで行ってきた議論のまとめはしっかり行いたい」と、最終原稿掲載の依頼を受けました。

 編集部で協議した結果、掲載すべきであると判断し、下記に生島氏より受領した最終原稿を掲載します。なお、この内容は@IT自分戦略研究所の見解・意向を示すものではありません。

生島勘富氏の最終コラム:エンジニアライフ運営日誌:エンジニアライフ

アンチのせいでクビか。アンチ大喜びだろうな。

思うに、きっとSQLわかってない連中かつ分かろうとしない連中がアンチになってんだよな。分かってたら、あのブログには当たり前のことしか書いてないことがわかるから、いちいち粘着することなんかないだろうに。

   :
   :
   :

ところでワシが昨日のエントリを書いた直後に、たぶんそのブログ主だと思われる人が、はてなダイアリー始めてた。まさかワシのエントリがきっかけで始めたとは思わんが、偶然にしてはちょっとできすぎ。

 さて、私は @IT でコラムを書いていてクビになったのですが、理由は私の書いたことが誹謗中傷に当たるとのこと。

 私は特定の技術を選ぶのに、職業的な使命に沿って選ぶべきで、技術者の好き嫌いで選んではいけないと主張してきた。私がSQLが好きだから「みんなにやるように!」と言ってるのではなく、職業SEにとって工数とパフォーマンスを上げるというのは、最も優先されるべき使命で、その使命に忠実であるならば、SQLのスキルを上げることが一番であると言うことを書いてきた。

 社内SEは技術を追求するよりも、今ある技術で業務をつつがなくこなすことが重要と書いてきた。

 それを踏まえて、社内SEをオブジェクト指向言語ができないことを理由に攻める(他人の人格とかほっとけ)一方で「SQLなんて変な言語イヤだ」「複雑なSQLはできる人が少ないから使うべきでない」と、できない技術者の理屈を持ち出すのはダブルスタンダードで、職業的な使命から考えるとおかしい。そんな理屈の通らないことを言うのは技術者とは呼べない。カスだ!クズだ!と書いたわけです。

 これって特定の個人を誹謗中傷しているのではなく、「人殺しをするような人間はクズだ!」ってのと変わらない。でも、誹謗中傷だと感じた人が多かったのだろう。つまり、オブジェクト指向言語が分からない社内SEは攻めるけれど、自分はSQLはできないし、できるようになる気もないという人が多いということの証明だろう。

 私の書き方が下手糞なんだろうけれど、私は誰を誹謗中傷したのか、それともただの言葉狩りだったのか、判然としなかった。私に対しては明らかな誹謗中傷はありましたけれどね。

表現の自由と規制 - SQLer 生島勘富 のブログ

(強調は筆者)

わかるわー、これ。
ワシは、自称オブジェクト指向屋さんが、COBLやPHPをさんざんバカにしてきたのをたくさん見聞きしている (COBOLPHPをろくに知らないのに批判してるんだよ、バカじゃない?)。だから、「SQLを知らない技術者はクズだ」と言われてオブジェクト指向屋さんが怒るのはおかしいと思う。おまえどの口で反論してるの?あれだけ他の技術を古くさいだのバカにしてたくせに、自分が同じ立場になったら「誹謗中傷だ!」とわめきたてるのは、頭おかしいんじゃね?
#といったら、きっと「自分は組み込みの現場にいて、SQLは使っておりません。だからSQLができない技術者はカスだ!というのは誹謗中傷です」とか、「自分のところはNoSQLを使っているためSQLは一切必要ありません。だからSQLができない技術者はカスだ!というのは誹謗中傷です」とか言い出すやつがいるんだろうなあ。だれもそんな話はしてねーっつーの。


なんか、この話を思い出した。

この言い方と雰囲気は誤解を与えるよね。間接的に情報を入手した私はもっと誤解してると思う。こういうのはLTの場面では言うべきじゃない気がする。もしほんとにRubyが現代の言語でJavaを近代の言語という話をしたいなら、きちんとした資料を用意して時間をかけて言葉を選びつつ慎重に話すべきだと思う。悪気のないネタにしてもあまり心地のいいネタじゃない。

Rubyの素晴らしさは、他の言語との比較によって明らかにする物じゃなくて、もっと純粋にRubyだけで語れば良いんじゃないかな。言語ごとのテストカバレッジの比較なんかはなるほどすばらしいなと思う。きちんとした数字で話ができるから。けど、近代とか現代とかいきなり話されても、何が根拠でどういう展開でその結論になったのかは理解できない。「えっ!?えっ!?」て思って周りをきょろきょろしちゃう。で、笑いに包まれた会場をみてショボーンってなっちゃうと思う。

http://d.hatena.ne.jp/tonocchi/20080624/1214337928

Javaのスバラシサを説くために、自分たちがさんざんC++COBOLと比較 (つーか罵倒?) してたくせに、自分たちが比較される側になると『他の言語との比較によって明らかにする物じゃな』いと言い出すんだから、バカかと思う。そんなら自分たちが最初から『純粋にJavaだけで』語っとけっつーの。