简体   繁体   中英

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

Hi I have a service class which is pulling information from 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

I tried calling this with an incorrect company_vid to make sure the retry works properly and I keep getting the error:

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

I am not sure if I am having a brain-fart here but I just cannot figure out the error here since the variable should be defined

In Ruby, local variables are defined from the moment that an assignment to them is parsed (NOT executed).

Since you are assigning to attempt , Ruby will make attempt a local variable of hubspot_record . However, since it is not initialized, it will evaluate to nil .

attempt += 1

is equivalent to

attempt = attempt + 1

And since attempt is un-initialized and evaluates to nil , this is essentially evaluating nil + 1 .

If you want to use the attribute reader method, you have to make it clear to Ruby that you are intending a message send and not access a local variable. There are two ways to do this: a local variable cannot have a receiver and a local variable cannot have an argument list. So, if you add either one of those, Ruby will know it is a message send and not a local variable:

attempt()
self.attempt

Either one of those will make it clear to Ruby that you intend to call the HubspotApi::CompanyImporter#attempt method.

However, that still will not work, because you are trying to assign to and you don't actually have an attribute writer, so your next error message is going to be something like

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

The way to fix this problem pre-Ruby 2.7 is to:

change attr_reader:attempt to attr_accessor:attempt

And

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 for update in Ruby 2.7: https://blog.saeloun.com/2019/12/24/ruby-2-7-allows-calling-a-private-method-with-self.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM