memorandums

日々のメモです。

railsのlink_toのパスの指定がやっとわかった。。。なんともすいません

今年でrailsの授業も2年目に突入しました。

Cloud9が無くなってAWS Cloud9にせざるを得なくなっていろいろと調べたり。Rails4が5になったので内容を見直したり。。。Railsの更新頻度が高いせいで、JSPサーブレットの基礎をやっていたときには¥なかった苦労がどうしても発生してしまいます。。。

それでも、何とか教えながら学んでいる感じで進めています。

先週と今週は13・14回目の演習で、いよいよ来週は定期試験なのですが。。。最後の演習は総合演習ということで、Railsで何かオリジナルアプリの開発に挑戦してもらっていました。

とある学生さんから「キーワード検索のあとに、リンクを押すとパスがおかしくなる」と問い合わせがありました。

具体的には、「/コントローラ名/:id」となるところが、「/コントローラ名/コントローラ名/:id」となるようです。

ソースコードをもらって自分のマシンで検証してみましたが、わからない。。。ここでRailsistの方ならすぐにピンとくるんでしょうね。。。

昨年の演習ではこの問題はありませんでした。その理由はあとでわかりますが。。。

このエラーの対処方法がネットで見つかりにくかった原因の1つは、授業でのパスの書き方が普通とは違うからなんですね。。。

例えば、Railsチュートリアルの最初の例では以下のようなコードがあります。

f:id:ke_takahashi:20190717184751p:plain

link_toの2番目の引数がURLになるのですが、普通はこのようにモデルのインスタンスを指定するためURLを直接表記することは少ないはずです。

授業では、Railsだけを扱っているわけではなく、とある仕様に対して、コンソールアプリ→クラス化→ファイル化→DB化→WebRickRails とアプリを少しずつ育てながら実用的なアプリに仕上げていくように教えていますので、その前のWebRickでの一般的なWebアプリのパス指定の知識をそのまま使えた方がいいな。。。と思い、パスを手動で書かせていました。

ルートであれば "/" と、bookmarksコントローラのnewアクションを呼び出すリンクであれば、 "/bookmarks/new" と書く感じです。

で、実は、昨年度の授業資料までは、このパスに "/bookmarks/new"と ルートから始める表記で書いていたのです。それは、routes.rbのパスも get "/bookmarks/new", to: "bookmarks#new" とルートから始める表記で書いていました。

しかし、Railsの関連サイトの多くではroutes.rbのパスはルート無しだったんですね。。。そこで、link_toのパスも含めて(統一させた方が理解がしやすいと思い)授業資料をルート無しにしたんです。もちろん、授業のコードではどちらでも動作することを検証した上でです。

これが間違いの元でした。

この解決のヒントは以下のサイトにありました。

Ruby - link_toでリンク先の先頭にスラッシュをいれないと、文字列が重複するのはなぜ?|teratail

初心者的な間違いをして、本当に申し訳ないです。。。

原理は簡単です。

URLの先頭にスラッシュを書かないということは相対パス指定になります。つまり、1つ上のパスが共通になり、最後のパス名だけが指定することになります。

相対URL - Wikipedia

簡単なアプリを作って検証してみました。

ルーティング(routes.rb)

Rails.application.routes.draw do
  get "aas/m1", to: "aas#m1"
  get "aas/m3", to: "aas#m3"
end

ビュー(aas/m3.html.erb)

<p>(1)<%=link_to "m1", "m1"%></p>
<p>(2)<%=link_to "./m1", "./m1"%></p>
<p>(3)<%=link_to "../m1", "../m1"%></p>  
<p>(4)<%=link_to "aas/m1", "aas/m1"%></p>  
<p>(5)<%=link_to "/aas/m1", "/aas/m1"%></p>  

実行画面
f:id:ke_takahashi:20190717191114p:plain

結果
(1) localhost:3000/aas/m1 (アクセス成功)
(2) localhost:3000/aas/m1 (アクセス成功)
(3) localhost:3000/m1 (アクセス失敗;ルートなしエラー)
(4) localhost:3000/aas/aas/m1 (アクセス失敗;ルートなしエラー★)
(5) localhost:3000/aas/m1 (アクセス成功)


今日の現象は、上記の(4)のケースでした。

同一コントローラ内の別アクションを呼び出すのであれば、アクション名だけでいいわけですが。。。そうでないのであればフルパス(スラッシュ始まり)でURLを表記した方が安全ですね。。。

こういう誤りを防ぐためにも、パスを手動で書くのではなく、モデルのインスタンス変数で書けばいい、ってのが教訓というところでしょうか。

モデルにひもつかないリンクを呼び出すときにはフルパスで。

勉強になりました。