在检查值之前检查变量是否存在

问题描述:

此代码适用于我,但我怀疑有更多的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)为零。有没有办法在一行中做到这一点?

谢谢!

你在找什么叫做“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

现在,这超越了!我会尝试将您的示例适用于我的应用程序。 –

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

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

我们可以在2.3使用安全导航操作&.

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

此建议已于7小时前发布。 –