2017-07-14 160 views
0

此代碼適用於我,但我懷疑有更多的ruby-ish方式來執行此操作。在檢查值之前檢查變量是否存在

<% this_score = this_student.scores.find_by(:assignment => team.assignment) %> 
    <% if this_score && this_score.points == 100 %> 
     <br/><small>(100%)</small> 
    <% end %> 

前兩行體現了我的問題。我這樣做是爲了避免發生錯誤,如果this_student.scores.find_by(:assignment => team.assignment)爲零。有沒有辦法在一行中做到這一點?

謝謝!

回答

3

你在找什麼叫做「nil guard」。這有幾個方便的模式:

# safe navigation operator - if `nil_thing` is nil, `points` won't be called 
nil_thing&.points 

# the double ampersand check (what you've used) 
if nil_thing && nil_thing.points == 100 

# compound one-line conditional 
do_stuff if nil_thing.points == 100 unless nil_thing.blank? 

您也可避免這種情況很多的時間:

if student.scores.where(points: 100, assignment: team.assignment).exists? 
    do_stuff 
end 

注意,我們在裝配此查詢的方式使得它很難避免N + 1個查詢問題。

我懷疑你在學生和作業之間沒有適當的關係。我會重新命名ScoreStudentAssignment,並在其上具有score屬性:

class Student 
    has_many :student_assignments 
    has_many :assignments, through: :student_assignments 
end 

class Assignment 
    has_many :student_assignments 
    has_many :students, through: :student_assignments 
end 

然後你可以使用基本的預先加載和價值的比較在Ruby中:

Assignment.includes(student_assignments: :students).each do |assignment| 
    puts "Scores for #{assignment.name}:" 
    assignment.student_assignments.each do |sa| 
    puts "#{sa.student.name} scored #{sa.score}" 
    puts "Congratulations to #{sa.student.name}" if sa.score >= 99 
    end 
end 

您可以從另一個方向做以及:循環通過一名學生,並用分數顯示他們的作業。

如果您有一個安裝程序,無法將學生連接到多對多作業,則可以設置一個像perfect_scores這樣的條件關聯,這樣可以讓您使用ActiveRecord關係熱切加載其他任意查詢導航避免N + 1:

class Student 
    has_many :scores 
    has_many :perfect_scores, -> { where(score: 100) }, class_name: 'Score', inverse_of: :student 

    def perfect_score_on_assignment?(assignment) 
    if perfect_scores.loaded? 
     # use cached data 
     perfect_scores.any? { |score| score.assignment_id == assignment.id } 
    else 
     # use sql to determine 
     perfect_scores.where(assignment: assignment).exists? 
    end 
    end 
end 

class Score 
    belongs_to: :student 
    belongs_to: :assignment 
end 

class Assignment 
    has_many :scores 
end 

# Load up all of the students and eager load perfect scores 
@students = Student.includes(perfect_scores: :assignment) 
@assignments = Assignment.all 

@assignment.each do |assignment| 
    @students.each do |student| 
    if student.perfect_score_on_assignment?(assignment) 
     puts "#{student.name} scored 100%" 
    end 
    end 
end 
+0

非常好。謝謝你的解答和你的回答。我有一個功能可以檢查每個學生的作業,因此它可以運行幾千次。你會建議哪種模式以獲得最佳性能? –

+0

@JeffZivkovic剛剛更新了兩個鬆散的假設例子,如何處理這取決於你的情況。 – coreyward

+0

現在,這超越了!我會嘗試將您的示例適用於我的應用程序。 –

2

您可以使用Ruby的安全導航運算符,如here所述。

<% this_score = this_student.scores.find_by(:assignment => team.assignment) %> 
    <% if this_score&.points == 100 %> 
     <br/><small>(100%)</small> 
    <% end %> 
0

我們可以在2.3使用安全導航操作&.

<% this_score = this_student.scores.find_by(:assignment => team.assignment) %> 
    <small>(<%= this.student&.score || 0 %>%)</small> 
<% end %> 
+0

此建議已於7小時前發佈。 –

相關問題