NoMethodError: undefined method `+' for nil:NilClass for variable in private method

NoMethodError: undefined method `+' for nil:NilClass for variable in private method

您好,我有一项服务 class 从 Hubspot 中提取信息。

module HubspotApi
  class CompanyImporter < ApplicationService
    MAX_RETRIES = 3

    def initialize(company_vid)
      @company_vid = company_vid
      @attempt = 0
    end

    def service_body
      imported_profile
    end

    private
 
    attr_reader :company_vid, :attempt

    def imported_profile
      ## load hubspot record over here and take other actions
    end

    def hubspot_record
      @hubspot_record ||= Hubspot::Company.find_by_id(company_vid.to_i)
    rescue Hubspot::RequestError
      if (attempt += 1) <= MAX_RETRIES
        sleep 2**attempt
        retry
      else
        @messages << 'Raise some hubspot error'
      end
    end
  end
end

我尝试使用不正确的 company_vid 调用它以确保重试正常进行,但我不断收到错误消息:

NoMethodError: undefined method `+' for nil:NilClass from `rescue in hubspot_record'
Caused by Hubspot::RequestError: Response body: {"status":"error","message":"resource not found"}

我不确定我是不是脑子放屁了,但我就是想不出这里的错误,因为应该定义变量

在 Ruby 中,局部变量从对它们的赋值被 解析 (未执行)的那一刻开始定义。

由于您要分配给 attempt,Ruby 将使 attempt 成为 hubspot_record 的局部变量。但是,由于它没有被初始化,它的计算结果将是 nil.

attempt += 1

等同于

attempt = attempt + 1

并且由于 attempt 未初始化并求值为 nil,这实际上是在求 nil + 1.

如果您想使用属性 reader 方法,您必须向 Ruby 明确表示您打算发送消息而不是访问局部变量。有两种方法可以做到这一点:局部变量不能有接收者,局部变量不能有参数列表。因此,如果您添加其中任何一个,Ruby 将知道这是一条消息发送而不是局部变量:

attempt()
self.attempt

其中任何一个都会向 Ruby 表明您打算调用 HubspotApi::CompanyImporter#attempt 方法。

但是,这仍然行不通,因为您正在尝试分配给并且您实际上没有属性编写器,所以您的下一条错误消息将类似于

NoMethodError: undefined method `attempt=' for HubspotApi::CompanyImporter

Ruby 2.7 之前解决此问题的方法是:

attr_reader :attempt更改为attr_accessor :attempt

def hubspot_record
  @hubspot_record ||= Hubspot::Company.find_by_id(company_vid.to_i)
rescue Hubspot::RequestError
  if (self.attempt = attempt + 1) <= MAX_RETRIES ## This was fixed in Ruby 2.7 but for earlier versions you need to read using self and write directly.
    sleep 2**attempt
    retry
  else
    @messages << 'Raise some hubspot error'
  end
end

Link Ruby 2.7 更新:https://blog.saeloun.com/2019/12/24/ruby-2-7-allows-calling-a-private-method-with-self.html