当作为参数传递给另一个方法时,HTTParty::Response 被转换为 Hash

HTTParty::Response is cast into Hash when passed as argument to another method

我有一个发出 api 请求的服务方法,如果响应不正确,它会通知 Bugsnag。它看起来像这样:

def send_request
  @response = HTTParty.get(api_endpoint, options)
  return JSON.parse(@response.body, symbolize_names: true) if @response.ok?
  raise StandardError.new(JSON.parse(@response.body))
rescue StandardError => exception
  BugsnagService.notify(exception, @response)
end

我的 BugsnagService#notify 看起来像这样:

class BugsnagService
  def self.notify(exception, response = nil, **options)
    if response
      response_body = if valid_json?(response.body) # Error right here
                        JSON.parse(response.body)
                      else
                        response.body
                      end
      options[:response_body] = response_body
      options[:response_code] = response.code
    end

    # Raising exception in test and development environment, or else the exception will be
    # silently ignored.
    raise exception if Rails.env.test? || Rails.env.development?

    Bugsnag.notify(exception) do |report|
      report.add_tab(:debug_info, options) if options.present?
    end
  end

  def self.valid_json?(json_string)
    JSON.parse(json_string)
    true
  rescue JSON::ParserError => e
    false
  end
end

我在 notify 方法中设置了 response = nil 因为不是每个错误都是 API 错误,所以有时我会调用 BugsnagService.notify(exception).

我发现如果我只是像上面代码片段中那样调用它,它会引发一个错误,指出它无法在 Hash 上调用 .body。不知何故,当我将 @response 传递给 BugsnagService#notify 时,对象从 HTTParty::Response 变为 Hash

但是如果我为 **options 参数传递一些东西,它就会起作用。所以我可以这样称呼它:

BugsnagService.notify(exception, @response, { })

我一直在努力弄清楚这一点,但我找不到任何可以解释这一点的东西。我不确定我定义参数的方式是否有问题,或者这是 HTTParty gem 的一些错误。任何人都可以看到为什么会这样吗?谢谢!

问题是您的 @response 被作为 options 传入,因为 response 可以是 nil。 double splat 将其转换为散列。

尝试:

def testing(x, y = nil, **z)
  puts "x = #{x}"
  puts "y = #{y}"
  puts "z = #{z}"
end

testing 1, 2, z: 3
#=> x = 1
#=> y = 2
#=> z = {:z=>3}

testing 1, y: 2
#=> x = 1
#=> y = 
#=> z = {:y=>2}

testing 1, { y: 2 }, {}
#=> x = 1
#=> {:y=>2}
#=> {}

我建议最好的方法是让 response 成为关键字参数,如:

def self.notify(exception, response: nil, **options)
  ...
end

这样,您仍然可以根据需要省略或包含响应,并传入后续选项。