kなんとかの日記 このページをアンテナに追加

2010-06-12

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

| 09:15 |  Rails の select() 系ヘルパーはイケてないと声を大にして言いたい - kなんとかの日記 を含むブックマーク

背景

いつも思うが、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(....) 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 つのヘルパーメソッドだけで済む
    • ブロック万歳!

2010-04-17

Rails の routes.rb がわかりにくい

| 07:44 |  Rails の routes.rb がわかりにくい - kなんとかの日記 を含むブックマーク

Railsroutes.rb と格闘中なんだけど、なんで Rails の routing はこんなにわかりにくいのだろうか。

## named route は省略
map.with_options(:controller=>'books') do |x|
  x.connect '/books',            :action=>'index',  :conditions=>{:method=>:get}
  x.connect '/books',            :action=>'create', :conditions=>{:method=>:post}
  x.connect '/books/new',        :action=>'new',    :conditions=>{:method=>:get}
  x.connect '/books/:title',     :action=>'show',   :conditions=>{:method=>:get}
  x.connect '/books/:title',     :action=>'update', :conditions=>{:method=>:put}
  x.connect '/books/:title',     :action=>'delete', :conditions=>{:method=>:delete}
  x.connect '/books/:title/edit', :action=>'edit',  :conditions=>{:method=>:get}
end

なんか、もう、こう、ね、もっとわかりやすく書きたいよね。

こうじゃだめなんだろうか。

map.with_options(:controller=>BooksController) do |x|
  x.connect '/books',             :GET=>'index', :POST=>'create'
  x.connect '/books/new',         :GET=>'new'
  x.connect '/books/:title',      :GET=>'show', :PUT=>'update', :DELETE=>'delete'
  x.connect '/books/:title/edit', :GET=>'edit'
end

こっちのほうが簡潔でわかりやすいや。「リクエストパスとリクエストメソッドからアクションが決定される」という仕組みも一目瞭然だし。Rails の書き方だとそのへんが見えにくい。

yuguiyugui 2010/04/20 16:34 それ、map.resourcesの出番なのでは?

kwatchkwatch 2010/04/20 20:11 > それ、map.resourcesの出番なのでは?

RESTful な例を使ったので勘違いさせてしまって申し訳ないのですが、短く書きたいとかそういう話ではなく、Railsの routing
に対する考え方への疑問です。
Rails のやり方だと、request url と request method から action
が決まるということが表現されていないので、もっと素直な書き方がしたいという主張です。
記述方法こそ違いますが Sinatra だと url + method → action
というのがわかりやすく表現されているので、Sinatra 方式は個人的に望ましい方法のひとつです (最善かどうかはわかりません)。

#「Rails の routes.rb は resource を定義する場所であって url mapping
を定義する場所ではない」という主張は、ありだと思います。

2008-12-24

速報: Merb と Rails が統合

| 11:01 |  速報: Merb と Rails が統合 - kなんとかの日記 を含むブックマーク

悪い冗談としか思えないんだけど、Merb と Rails が統合されるらしい。


実は 1 週間前に、DHH が Merb の信者にうんざりさせられているという話があったんだけど、あれはみんなを騙すための fake だったのか。やられたぜ。


詳細は追って連絡する。


追記: だいぶ事情が分かってきた。簡単に言えば、Rails 3 は Merb を吸収するということ。

  • Rails 3 == Merb 2。Rails 3 では、Merb の機能や特徴を大幅に取り入れる。
    • Rails core team に Yehuda Katz (Merb lead developer) が参加。
    • Rails 3 は Rack ベースになるだろう。Rack の重要性がますます高まる。
    • Rails 2 との互換性は一部失われることになるみたい。
  • component stack を自分で選択できるようになる。
    • ORM を、ActiveRecord だけでなく DataMapper や Sequel を選択可能に。
    • JavaScript ライブラリも、Prototype.js ではなく jQuery を選択可能に。
    • Test Suite も、Test::Unit ではなく RSpec を選択可能に。
    • ただし default は ActiveRecord, Prototype.js, Test::Unit のまま (のはず)。
  • 高速になる。
    • Merb チームの高速化のノウハウを Rails にフィードバックする。
    • ベンチマーク用アプリも用意する。
    • Katz は、他の「速い言語」の framework に負けないようにすると言っているので、「Ruby だから遅い」「Rails だから遅い」ということは Rails 3 ではなくなるはず。
  • API が定義される。
    • 今までの Rails だと、API が固定されてないため、Rails をバージョンアップすると plug-in が動作しなくなることがあった。Merb は使用可能な API が定義されているため、このような問題がなくなる。
    • まあ Merb と Rails とで同じ plug-in が使えるということだな。
  • リリース予定
    • Rails 2.3 は来年 1 月。そのあと DHH は Rails 3 に本格的に取りかかる。
    • Merb はしばらく bug fix release を続け、Rails 3 が見えてきたら徐々に移行できるようにする。
    • Rails 3 == Merb 2 の beta release は、RailsConf 2009 (2009 年 5 月 4〜7 日) が目標。
  • その他
    • Merb は MRI 以外の Ruby 実装で動かすことにも力を入れているので、Rails 3 は JRuby や Rubinius でも動くようになるだろう。
    • Rails と Merb では view helper とかが違うんだけど、これも merb-helpers と rails-helpers を選択して使えるようにするんじゃないかな。'<% end =%>' の運命やいかに?
    • ActiveRecord を DataObjects ベースにしてくんないかな。
    • 最大の懸念は ActiveSupport vs. Extlib なんだけど、どうなるんだろう。

個人的には、なんか Python の歴史をなぞっているとしか思えん。

  • Python の WSGI → Ruby が Rack としてパクる
  • 「何でも取り替え可能」な framework である Pylons 登場 → Ruby でも Merb 登場
  • TurboGears と Pylons が統合 → Rails と Merb が統合

こうなったら、あとは framework 全体で Session の共通化をしてほしいな。Rails の session 情報が Ramaze とか Sinatra でも読めるようになってほしい。

→ Session 部分を Rack middleware に切り出すことも検討中らしい。


追記2: ここにも詳しい情報があります。

MerbはRails3にマージされる事になった (Hello, world! - s21g)


追記3: merb-book ML で、誰もが一部の人にはたいへん気になる質問が出てしまいました。

But the point here is: should we continue to write/translate that book?

Google グループ

ほんと、どうすんだろね。


追記4: 冒頭のリンクに Katz と Ezra を追加、また人物の説明書きを追加。記事もちょっとだけ追加。


追記5: Merb 界隈は混乱中。技術的な問題もさることながら、Rails に merge されると肥大化するのは目に見えているので、small & compact な framework を求めていた人からは否定的な声も (←全力で同意)。また Mack framework の作者のように寡占化を心配する意見も出ている。ただ、全体的には歓迎ムードのよう。

でも、DHH はよく決断したよなー。こんなの、なかなかできることじゃないよ。


追記6: DHH のブログエントリがこちらで日本語に翻訳されています。GJ です。Katz のブログも翻訳してくださるそうなので、期待して待ちましょう。

ところでこれを機に DHH は Rails から黒魔術をなくしてくれるんでしょうか。「黒魔術を使わない」というのは Merb の大きな利点なので、DHH が改心してくれることを望みます。Symbol#to_proc なんかウンコ。

lchinlchin 2008/12/25 11:20 一週間前にもこのニュースの予兆があったけど、気づいた人がほとんどいない:Ezraがこうコメントした:「patch -p0 rails-3.0 < merb-core.patch; done, there no more merb vs rails problems」
http://twitter.com/ezmobius/status/1059391701

kwatchkwatch 2008/12/25 20:07 その一週間前はまさにDHHがMerb信者に困ってるという話題がでた時なんですよね。それがこれですもん。こんなの、誰も気づかないですよねー。

2008-10-06

named_scope が Rails 1.2.6 と 2.0.4 に back port されたらしい

| 01:12 |  named_scope が Rails 1.2.6 と 2.0.4 に back port されたらしい - kなんとかの日記 を含むブックマーク

RubyFlowより。

Ruby on Rails の named scope は、O/R マッパーの使い勝手を大きく改善するが、これは Rails 2.1 からの新機能であり、古い Rails では動かない。

しかし、これを古い Rails に移植したツワモノがいるそうだ。

NamedScope For Those Left Behind: This is not simple hack of named scope like you might find in WillPaginate. This is a fully backported and tested version of NamedScope that will work on rails 1.2.6 or 2.0.4. The most important part of this plugin is that it is TESTED. In fact all the test are Shoulda/FactoryGirl ports of the tests currently found in rails 2.1.1.

This plugin includes backported code for the ActiveRecord class methods that work in conjunction with NamedScope such as first, last and all. This plugin also includes ActiveRecord.attribute_condition which allows you to use named scopes as conditions in other finder methods.

残された人々のための NamedScope: これは、WillPaginate で見られるような named scope の簡単なハックではない。こいつは NamedScope の 1.2.6 と 2.0.4 への移植 (バックポート) である。移植したバージョンは、完全でかつテストもされている。このプラグインでいちばん大事なのは、十分にテストされているということである。事実、すべてのテストは Rails 2.1.1 で動かした Shoulda/FactoryGirl からのテストの移植である。

このプラグインは、first や last や all といった、NamedScope を使った結合 (conjunction) で動作する ActiveRecord クラスメソッドの移植を含んでいる。このプラグインはまた、他の検索用メソッドの中で named scope を条件として使うための、 ActiveRecord.attribute_condition も含んでいる。

NamedScope For Those Left Behind - RubyFlow

すげえ。

今さら古い Rails に移植してどれだけうれしいかはわからないけど、すごいことをやってのけたことだけは確かだ。


Named scope について知りたい方は ここ が詳しい。

ささだささだ 2008/10/06 08:46 そこだけ引用されるとあれですが,やりたい人がいれば続くんじゃないかと思います.誰かうるさい人がいればいいんですが.

2008-05-03

Rails 2.1 の新機能

| 08:03 |  Rails 2.1 の新機能 - kなんとかの日記 を含むブックマーク

RubyFlowで紹介されていた、Rail Spikes: Rails gets more matureの翻訳。誤訳の指摘はコメントで。

Posted by Luke on Friday, May 02

Rails 2.1 is right around the corner. I've been following the new features in Edge Rails and eagerly looking forward to this release. Rails 2.1 includes a number of features that will make developers' lives easier. Here's a few of my favorites.

Rails 2.1 はもうすぐそこに来ている。私はずっと Edge Rails での新機能を追っかけてて、このリリースを切望している。Rails 2.1 は、開発者の暮らしを楽にしてくれるような数多くの機能を含んでいる。ここでは私が気にいっているいくつかを紹介しよう。

Necessary directories created if they don't exist

Neither Mercurial nor Git track empty directories. This is a pain with Rails, because you have to create a file in the log directory to make sure it gets created when you check out code, otherwise Rails won't start. This is no longer needed, because Rails will create necessary directories if they don't exist.

必要なディレクトリが存在しなければ作成される

Mercurial も Git も、空のディレクトリは管理対象ではない。これが Rails にとっては都合が悪い。なぜなら、コードをチェックアウトしたときにログディレクトリが必ず作られてる状態にするために、自分でログディレクトリに〔訳注: ダミーの〕ファイルを作成しなければいけないから。これがもう必要なくなった。なぜなら、必要なディレクトリがなければ Rails が作成してくれるからである。

Time zone support

Time zones are a huge pain in any application, in any language because they are just plain confusing. But ya gotta do it. In Rails, the solution used to be using the TzTime and TzInfoTimeZone plugins. Rails 2.1 adds support for tracking Time objects with their time zone. This is going to make everyones' lives a lot easier. Check out Geoff Buesing's in-depth tutorial.

タイムゾーンのサポート

タイムゾーンはとても混同しやすいため、どのアプリケーションでも、またどの言語でも頭痛のタネである。けど大丈夫。Rails ではかつて、TzTime と TzInfoTimeZone プラグインを使った解決方法が存在した。Rails 2.1 では Time オブジェクトのタイムゾーンを追跡する機能が加わった。これはすべての人の暮らしに大きなゆとりをもたらすだろう。Geoff Buesing の詳細なチュートリアルをご覧あれ。

Partial updates and "dirty" tracking

Two features that I knew and loved in our home-brew ORM from my former life as a Java developer have made it into Rails.

With dirty objects you can know if you need to persist an object, and which attributes have changed, and what an attribute's previous value was. This will be great for user messages and validations!

In Rails 2.1, ActiveRecord can update only the attributes which have changed. This can (sometimes) put your objects into an inconsistent state, but partial updates improve performance, especially when you have big TEXT or BLOB attributes that haven't changed. Use optimistic locking to prevent users from stomping on each others' changes.

部分的な更新と "ダーティ" な追跡

私が以前 Java 開発者だったときに、自分たちの自家製 ORM でとても気に入っていた機能が 2 つ、Rails に実装された。

ダーティオブジェクトを使うことで、オブジェクトを保存する必要があるかどうか、またどの属性が変更されたのか、変更される前の値は何だったのか、といったことがわかるようになった。これはユーザメッセージとバリデーションでは大きく役立つ機能だ!

Rails 2.1 では、ActiveRecord は変更された属性だけを更新する。これは (ときどき) オブジェクトを矛盾のある状態にするかもしれない。しかし部分的な更新はパフォーマンスを改善する。大きなサイズの TEXT や BLOB があってそれらが更新されない場合は特にだ。〔訳注: 複数のオブジェクトがあったとしてそれらが〕互いの変更を潰し合うことを避けるためには、楽観的ロックを使おう。

Timestamped migrations

With all this distributed SCM going on, the classic problem of messed up migrations gets way worse. I talked about solutions to this in my talk at acts_as_conference, one of which was timestamped migrations. Timestamped migrations allow interleaved migrations. As long as those migrations don't conflict with each other, they can be applied in any order. This has been added to Rails. Nice!

タイムスタンプマイグレーション

分散 SCM を使うようになってから、マイグレーションがおかしくなるという古くからある問題が、より一層悪くなった。私は acts_as_conference でのトークで、この問題の解説策について話した。そのうちのひとつがタイムスタンプマイグレーションである。タイムスタンプマイグレーションを使うと、交互的なマイグレーションが可能になる。これらのマイグレーションが互いに衝突しない限り、どんな順序でも適用できる。この機能が Rails に追加された。ステキ!

Better gem dependency and unpacking

I am a big fan of the vendor everything approach to gems because I got burned way too many times by missing gems.

But it doesn't always work (for example, gems which must be natively compiled are a problem), and you have to install one of the various vendor everything plugins -- and everyone seems to use a different one. In Rails 2.1, gem unpacking is built in with rake gems:unpack GEM=gemname. (more info)

And for those gems that don't work, you can list them as a dependency. Your app will fail to start if the gem is not installed. Fail early, fail often!

よりよい Gem 依存性とアンパッケージ

私は、gems をvendor everything アプローチ〔訳注: 必要な gems パッケージをすべて vendor にぶち込むこと〕の大ファンである。なぜなら、gems が見つからないことで多くの時間を無駄にしたからだ。

しかしそれらがいつもうまく動くとは限らないし (たとえば、ネイティブコードにコンパイルしなければならない gems がそうである)、さまざまな vendor everything プラグインからひとつをインストールする必要がある -- そしてみんな違うものを使っているようだ。Rails 2.1 では、gem unpacking が組み込みになり、rake gems:unpack GEM=名前 で使えるようになった (詳細はこちら)。〔訳注: rake gems:unpack は gems パッケージを vendor ディレクトリに解凍する機能。〕

また動作しない gems は、依存先として一覧できる。gem がインストールされていない場合は自分のアプリが起動しないだろう。