Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

จัดการ Empty Collections กับ .each ใน Rails

ผมชอบ each แตมีปัญหา ตอน collection ว่าง เกิดอะไรขึ้น?

ถ้าเรียก [].each ไม่เกิดอะไร และคืน [] บางครั้งอยากแบบนั้น แตบ่อยกว่า โดยเฉพาะตอนสร้าง UI อยากจัดการ empty lists แบบพิเศษ เช่น ปกติอยากโชว์ข้อความต่างตอนไม่มี data

แต่ [].each คืน [] ไใช่ nil ติดเขียนแบบนี้:

app/views/users/index.html.erb
<% if @users.empty? -%> <p>You don't have any users. <%= link_to "Add one!", new_user_path %></p> <% else -%> <% @users.each do |user| -%> <p><%= user.name %></p> <% end -%> <% end -%> 

ใช้ได้แตรู้สึก awkward ผมอยากพูดว่า "ทำอย่างนี้กับแต่ละ item ใน collection เว้นแต่ว่าว่าง ในกรณีนั้นทำอย่างอื่นทันที" อยากได้ each พร้อม "or else"

Rendering Rails Collections

ใน views Rails ช่วยได้ มันเก่ง rendering collections:

app/views/users/index.html.erb
<%= render @users %> 
app/views/users/_user.html.erb
<p><%= user.name %></p> 

ตอน render ได้ @users มัน render _user.html.erb ครั้งละ user ใน @users ข้าม each ได้เลย

โบนัส: ถ้า @users ว่าง render คืน nil เหมือนที่อยากได้! เขียนได้แบบนี้ ได้ output เดิม:

app/views/users/index.html.erb
<%= render(@users) || render('empty') %> 
app/views/users/_user.html.erb
<p><%= user.name %></p> 
app/views/users/_empty.html.erb
<p>You don't have any users. <%= link_to "Add one!", new_user_path %></p> 

ตรงไปตรงมา มากขึ้น เวลาเข้าใจ Rails conventions

นอก View ล่ะ?

ถ้าฟอลโลว์ Rails render กับ collection เร็วและ powerful สำหรับ render collections

แตบางครั้งไม่อยาก deal กับ extra partial render item หรือ render ไมรองรับ design หรือไม่อยู่ใน view

Best solution ที่เจอคือ presence

list.presence เหมือน:

if list.present? list else nil end 

คือ ได้ list กลับถ้ามีของ และ nil ถ้าว่าง

กับ presence เขียนได้:

@users.each do |user| puts user.name end.presence || puts("You don't have any users.") 

พิมพ์ชื่อทุกคนถ้ามี users ใน @users หรือ You don't have any users ถ้าไม่มี

ยังไงก็รู้สึก presence เป็น hack เพราะมันเป็น hack จริง ถ้ารองรับ อาจจะดีกว่า:

@users.each do |user| puts user.name end || puts("You don't have any users.") 

หรือ Smalltalk-ish:

@users.each(if_empty: lambda { puts "You don't have any users." }) do |user| puts user.name end 

หรือ (gasp!):

for user in @users puts user.name else puts "You don't have any users." end 

ตอนนี้ ผมมักใช้ presence หรือ basic if blank?; else each pattern