new or edit path helper? We can creating easy more path!
我們可能常常會需要在 view 裡面寫到 edit or new 的 link,這是我們在常見不過的一段判斷式了
<% if @project.presnet? %>
<%= link_to 'edit', edit_project_path(@project) %>
<% else %>
<%= link_to 'new', new_project_path %>
<% end %>
然而我們認為更棒的作法,就是把這判斷式放進 helper 裡面,只為了讓 view 看起來更簡單一些,也許可以參照這篇 StackOverFlow
例如
def render_new_or_edit_project_path(project)
if project.present?
link_to 'edit', edit_project_path(project)
else
link_to 'new', new_project_path
end
end
然後我們在頁面只要載入這個 helper 就好了,對吧?
<%= render_new_or_edit_project_path(@project) %>
看起來一切完美,將 view 做了個 Refactor
but…轉折處就在這個 but
如果你未來繼續使用這樣的 Refactor 的方式,那麼 helper 裡面就會出現一堆
render_new_or_edit_xxxxxx_path
的這種鬼東西
或許你會認為可以把他用動態(dynamic)的方式生成,但得考慮到
- 如果遇到有 namespace 的 routes ?
- 用
eval
、send
,是不是有 security issue 的問題? ,也許你會說使用public_send
,但畢竟這都是 ruby 的 meta-programming,對未來維護的時候,或許不是這麼好找尋?
那麼這個 helper 看起來又不是這麼好用了,變成只專門 for 某個頁面的方法,這樣真的是 refactor 嗎?
更好的方法
面對 new or edit 這個方法,我認為有更好的選擇
- 使用 simple_form
- 將 new or edit 交給 object 決定
我們可以統一把 path 交給某個 action 處理,個人偏好使用 new
或 edit
(如果你有更好的作法可以試試看自己設置的 action )
所以我們不需要花時間在判斷上面,只要連結一律導向 new_project_path
然後在 controller 裡面寫
def new
@project = Project.find_by_id(params[:id]) || Project.new
end
如此一來,在你的 simple_form_for 裡面
<% simple_form_for @project do |f| %>
...
<% end %>
他就能在 submit 時,自動選擇「new(新建)」或是「update(更新)」行為
因為這個自動選擇,是取決於你的 Object, Simple Form 會在將 object 傳送進來時,決定你的 action 以及 method
我們在 simple form 傳進去的 object ,在這裡定義為 record
action = record.respond_to?(:persisted?) && record.persisted? ? :edit : :new
也因為在這裡就做好選擇,所以一切是這麼智能,如果你想更了解,請詳見 simple form 的 source code 是如何因 object 決定行為
補充一下,如何知道這個 object 是已經存在?或是剛 new 出來的?
Project.new.new_record? # true
Project.new.persisted? # false
# 假定數據id為1的 project 存在的情況
Project.find(1).new_record? # false
Project.find(1).persisted? # true
如此一來,就不用寫這麼多的 new_or_edit_path
的 helper 啦!