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

时间:2017-07-14 22:48:39

标签: ruby-on-rails ruby

此代码对我有用,但我怀疑这是一种更为红宝石的方法。

<% 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 =&gt; team.assignment)为零时发生的错误。是不是有办法在一行中做到这一点?

谢谢!

3 个答案:

答案 0 :(得分:3)

你正在寻找的东西被称为“无守卫”。有一些方便的模式:

# 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查询问题。

我怀疑你与学生和作业之间没有适当的关系。我会将Score重命名为StudentAssignment并在其上设置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

答案 1 :(得分:2)

您可以按照here所述使用Ruby的安全导航操作符。

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

答案 2 :(得分:0)

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

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