如何优雅地处理NilClass

时间:2012-12-23 12:56:54

标签: ruby-on-rails ruby sinatra

我试图避免因NilClass而导致的NoMethod错误。我的代码如下所示:

@branded, @nonbranded, @unknown, @affiliate, @social, @referral, @paid, @direct, @email = [], [], [], [], [], [], [], [], []

count = 0
while count < 6
    date = SDLW + count
    #TODO There has to be a better way than this.
    branded_check = Metric.first(start_date: date, end_date: date, source: 'branded')
    nonbranded_check = Metric.first(start_date: date, end_date: date, source: 'nonbranded')
    unknown_check = Metric.first(start_date: date, end_date: date, source: 'unknown')
    affiliate_check = Metric.first(start_date: date, end_date: date, source: 'affiliate')
    social_check = Metric.first(start_date: date, end_date: date, source: 'social')
    referral_check = Metric.first(start_date: date, end_date: date, source: 'referral')
    paid_check = Metric.first(start_date: date, end_date: date, source: 'paid')
    direct_check = Metric.first(start_date: date, end_date: date, source: 'direct')
    email_check = Metric.first(start_date: date, end_date: date, source: 'email')

    branded_check = branded_check.nil? ? 0 : branded_check.visits
    nonbranded_check = nonbranded_check.nil? ? 0 : nonbranded_check.visits
    unknown_check = unknown_check.nil? ? 0 : unknown_check.visits
    affiliate_check = affiliate_check.nil? ? 0 : affiliate_check.visits
    social_check = social_check.nil? ? 0 : social_check.visits
    referral_check = referral_check.nil? ? 0 : referral_check.visits
    paid_check = paid_check.nil? ? 0 : paid_check.visits
    direct_check = direct_check.nil? ? 0 : direct_check.visits
    email_check = email_check.nil? ? 0 : email_check.visits

    @branded        << branded_check
    @nonbranded << nonbranded_check
    @unknown        << unknown_check
    @affiliate  << affiliate_check
    @social         << social_check
    @referral   << referral_check
    @paid           << paid_check
    @direct         << direct_check
    @email          << email_check

    count += 1
end

我确信必须有一种更清洁,更简洁的方法来做到这一点。尽管谷歌搜索和阅读我无法弄清楚。关于如何重构的任何想法都将非常感激。

4 个答案:

答案 0 :(得分:3)

当然,创建一种避免重复的方法

def visits(tag, date)
  check = Metric.first(start_date: date, end_date: date, source: tag)    
  check.present? ? check.visits : 0   # or 'check.nil? ? 0 : check.visits' if you prefer
end

在您的方法中

count = 0
while count < 6
  date = SDLW + count

  @branded << visits('branded', date)
  @nonbranded << visits('nonbranded', date)
  ...

  count += 1
end

答案 1 :(得分:2)

让我们在一个块中调用Metric.first并使用instance_variable_set设置实例变量。

['branded', 'nonbranded', ...].each do |source|
  visits = 0.upto(5).map do |count|
    date = SDLW + count
    metric = Metric.first(start_date: date, end_date: date, source: source)
    if metric.nil? then 0 else metric.visits end
  end
  instance_variable_set "@#{source}".to_sym, visits
end

答案 2 :(得分:1)

除了其他解决方案之外,while循环可以替换为:

(SDLW...SDLW+6).each do |date|
  # date = SDLW + count #This line is superfluous
  @branded << visits('branded', date)
  #or one of the other solutions
  #etc
  # count += 1 # remove this line
end

答案 3 :(得分:0)

我只为一个来源做过例如。

@branded, @nonbranded, @unknown, @affiliate, @social, @referral, @paid, @direct, @email = [], [], [], [], [], [], [], [], []

class Metric < ActiveRecord::Base
  scope :dates, lambda { |start_date, end_date| where(:start_date => start_date, :end_date => end_date) }
  scope :branded, lambda { where(:source => "branded") }
  # ... other scopes
end

# ...

6.times do |count|
  date = SDLW + count
  by_date = Metric.dates(date, date)
  @branded << by_date.branded.try(:visits).to_i
  # ... other sources
end

<强>更新: 使用来自Jan solution和Dave Newton请求的instance_variable_set想法重构解决方案。

6.times do |count|
  date = SDLW + count
  [:branded, :nonbranded, :unknown, :affiliate, :social, :referral, :paid, :direct, :email].each do |source|
    instance_variable_set :"@#{source}", [] unless instance_variable_get :"@#{source}"
    visits = Metric.first(start_date: date, end_date: date, source: source).try(:visits).to_i
    instance_variable_set :"@#{source}", instance_variable_get(:"@#{source}").push(visits)
  end
end