Edited at

[Rails] partialが自身からの相対パスで他partailを参照する場合の注意

先日、仕事中に少し嵌ったpartialの振る舞いについてまとめます。


環境

Rails 5.1.4


内容

例えば、以下のようなディレクトリ構成のViewがあるとします。

もちろん、Controllerとcofig/routes.rbは適切に設定されています。

view/

├── layouts/
└── test/
├── index.html.haml
├── _child.html.haml
└── _grandson.html.haml


app/view/test/index.html.haml

= render partial: 'child'



app/view/test/_child.html.haml

= render partial: 'grandson'



app/view/test/_grandson.html.haml

%p hoge from test


/testにアクセスすれば、当然、hoge from testと表示されます。この振る舞いについては自然に思われます。

次にTest2Controller.rbを作成し、以下のようにindex.html.hamlを追加します。

view/

├── layouts/
├── test/
│   ├── index.html.haml
│   ├── _child.html.haml
│   └── _grandson.html.haml
└── test2/
└── index.html.haml


app/view/test2/index.html.haml

= render partial: 'test/child'


この様な場合も、hoge from testと表示されるものと私は期待したのですが、実際には以下のようなエラーになりました。

Missing partial test2/_grandson, application/_grandson with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:raw, :erb, :html, :builder, :ruby, :ts, :haml, :jbuilder]}. Searched in:

* "/#{アプリケーションのディレクトリ}/app/views"

これをみるとtest/_grandsonではなく、test2/_grandsonを見にいってしまっていることがわかります。

そこで、以下のファイルを追加すると、


app/view/test2/_grandson.html.haml

%p hoge from test2


hoge from test2と表示されます。

つまり、相対パスの基準はpartialのディレクトリではなく、呼出元のhamlがあるディレクトリに依存するというわけです。

test2でも test/_grandsonをみるようにするためには、view/からの相対パスで書けばよいです。


app/view/test/_child.html.haml

= render partial: 'test/grandson'



結論

view/からの相対パスで書くのが安全かもしれません。

(view/からの相対パスで書かないのはバッドプラクティスといえるほど経験を積んでいないので、可能性の示唆に留めて置きます。そういう言明があれば教えていただきたいです。)