QA@IT

rails の named route で Symbol を使うことの利点と欠点

2259 PV

about_path や about_url の使い分けが面倒なので出来るだけ :about のような書き方をしているのですが、 _path や _url を使うのと比べてどういう利点や欠点があるでしょうか?

追記:
シンボルの使い方としては以下のように link_to で使ったり redirect_to で使ったりしています。

  • link_to t(:back), :posts, ...
  • link_to t(:edit), [:edit, @post], method: :delete, confirm: ...
  • link_to t(:new), :new_post, ...
  • redirect_to :posts

回答

最終的に言ってまえば「好み」ではありますが、私はシンボルやActiveModel(以下AMo)オブジェクトを渡してURL生成させるのが好きなので、メリットを説明してみますね。

シンボル渡しについて

まずシンボル渡しの動きとしては、それをto_sして_urlをつけたnamed routeを呼び出します。つまり、url_for(:about)about_urlを内部で呼び出します(前にソース読んだときはそうなっていました...)。

また、複数のシンボルを渡せます。すると_でつないだ上で上記処理をしたはずです。つまり[:admin, :signin]ではadmin_signin_urlを呼びます。これはkennさんも言っているようにネストしたのを扱う場合には便利かと思います。

という内部の動きがイメージできていると、使うのに抵抗はなくなると思われます。その上で、記述量がコンパクトになるので私は好んで使っています。とくにデメリット(パフォーマンスやメンテナンス性や有意に落ちる、など)は感じていません。

これがすごい

このシンボル渡しも良いのですが、AMoオブジェクトを渡したルーティングと組み合わせるととってもcoolです。AMoオブジェクトを使う場合と、そのクラス名とIDからいい感じにnamed routeの呼び出しが作れます。

例えば

url_for(user)

user_url(user.id)

と同じように動きます。このAMo渡しもネストできるので、

url_for([User.find(1), Post.find(42)])

では

user_post_url(User.find(1), Post.find(42))

相当になります。

という前置きで、AMo渡しはシンボル渡しとも組み合わせられるため

url_for([:admin, User.find(1)])

という呼び出し方で

admin_user_url(User.find(1))

相当のnamed route呼び出しになります。これは実際に使ってみるとかなり便利で、新しくアプリを使う場面のほとんどではAMo渡しをベースにURLを作っています。必然、単純にAMoオブジェクトを渡すだけで解決できない場合はシンボル渡しをよく使います。

ちなみにこの配列渡しの書き方はform_forの引数にも使えます。その場合、配列で渡した場合の最後のAMoオブジェクトがフォームのオブジェクトになります。

まとめ

ということで、まとめると次のような感じになります。

  • シンボル渡しとAMo渡しを組み合わせるととても便利
  • ↑との一貫した書き方をするためにシンボルもよく使います
  • という話を拙著の http://www.sbcr.jp/products/4797363821.html レシピ20に書いておりますので、よろしければご覧ください。
編集 履歴 (0)

そんなことできましたっけ?url_forのソースを見ても、 :back 以外のsymbolはないみたいですけど。。。

https://github.com/rails/rails/blob/3-2-stable/actionpack/lib/action_view/helpers/url_helper.rb#L100

質問に対する直接の答えにはならないですが、about_pathとabout_urlの使い分けの指針は、原則として全部、より省エネな _path の相対パスで、メールのテンプレートなどwebの外(現在表示しているページというものが存在しない)で使う場合にだけ例外的に _url の絶対パスを使う、という感じでやってます。default_url_optionsでenvごとにグローバル設定しておけばhostもいちいち設定する必要ないですし。

追記を受けての追記

それはI18nのtメソッド(translateのエイリアス)ですよ。ActiveSupport::Inflectorで表現しきれないようなマッピング関係を表現するために使っているのだと思いますが、こうなってくると要件の前提が違ってくるので一般論では話せないと思います。

I18nをroutesに使うライブラリはいくつかあるようですが、

https://github.com/francesc/rails-translate-routes
https://github.com/raul/translate_routes
https://github.com/kwi/i18n_routing

それぞれのrationaleをみて、自身の要件に合うかどうかを評価するしかないと思います。

ただ、私見でいうと、いずれにせよlocalesに大量の定義が発生してDRYでないうえに、localesはファイル上での距離も遠いので、メンテにより多くのエネルギーが必要で、developers happinesは落ちるような気がします。

勘違いしていたのでさらに追記

あぁ、よくみるとtはリンクテキストのほうでした。すみません。urlのほうにsymbolを使うのは、たしかに form_for [ :admin, @item ] みたいな感じでroutesにnamespace使ってる場合なんかに使ったりしますねそういえば。redirect_toとかでも使えるんですね、共通のところで処理されてるので考えてみれば当然なんでしょうけど。。。

よくわかってなかったのですが、そいつはどうやら ActionDispatch::Routing::PolymorphicRoutes#polymorphic_url が処理してるようです。polymorphicなんで、モデルによらず対応するレコードを汎用的に探しだしてrouteに変換してくれる機構だと。

コントローラを階層化してたりするときには、たしかにちょくちょく使う場面が出てきますね。上の例だと、対応するのは admin_item_path(@item) ってことになりますか。Symbol-based routeというよりは、インスタンス変数をformのターゲットに使う概念の延長上にある、という捉え方のほうがわかりやすいと思います。

でもやっぱり個人的には可能な限りnamed routesを選ぶと思います。アンダースコアでつないで一個のトークンになってくれてると、リファクタリングのやりやすさが全然違いますから。

それに、namespace / scopeが深いroutesでも、よくつかうrouteは、例えば dashboard_item_subitems_path みたいな長いやつでも、asをつかって subitem_path みたいな短い名前にエイリアスできるので、より可読性の改善に対する柔軟性が高いとおもいます。同じことをpolymorphic_urlでやると [ :dashboard, :item, :subitems ] ってやるしかないですよねたぶん。

編集 履歴 (2)
ウォッチ

この質問への回答やコメントをメールでお知らせします。