Exception: CgminerApiClient::ApiError

Inherits:
Error
  • Object
show all
Defined in:
lib/cgminer_api_client/errors.rb

Overview

Raised when the miner returned a response whose STATUS field indicates an error (cgminer status code ‘E’ or ‘F’). The message contains the cgminer code and message verbatim.

Carries two structured fields for dispatch: callers ‘case e.code` instead of parsing English messages. The integer (#cgminer_code) is preserved verbatim from cgminer; the symbol (#code) is best-effort — cgminer’s MSG enum names are stable but the integers occasionally shift between firmware versions, so add a row to CGMINER_CODES when you find a wire-observed integer worth dispatching on.

Prefer #code for dispatch over #cgminer_code: paths that raise without a wire integer (the access_denied? local guard’s call to #privileged hits the wire, but the rescue inside #privileged drops the integer) leave #cgminer_code nil while still setting #code consistently.

Backward compatibility: ‘raise ApiError, “msg”` keeps working and #message is unchanged at every emission site.

Direct Known Subclasses

AccessDeniedError

Constant Summary collapse

CGMINER_CODES =
{
  14 => :invalid_command,
  45 => :access_denied
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(message = nil, cgminer_code: nil, code: nil) ⇒ ApiError

Returns a new instance of ApiError.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/cgminer_api_client/errors.rb', line 60

def initialize(message = nil, cgminer_code: nil, code: nil)
  # Fail loud at the library boundary on bad input. Without these
  # guards, cgminer_code: "45" or 45.0 silently produces code:
  # :unknown (CGMINER_CODES uses integer keys), and code: 42 raises
  # NoMethodError on .to_sym deep in the constructor — both
  # opaque failure modes. Wire-side callers that want best-effort
  # Integer coercion go through ApiError.for_status.
  unless cgminer_code.nil? || cgminer_code.is_a?(Integer)
    raise ArgumentError,
          "cgminer_code must be Integer or nil, got #{cgminer_code.class}: #{cgminer_code.inspect}"
  end
  unless code.nil? || code.is_a?(Symbol) || code.is_a?(String)
    raise ArgumentError,
          "code must be Symbol, String, or nil, got #{code.class}: #{code.inspect}"
  end

  super(message)
  @cgminer_code = cgminer_code
  @code = (code || CGMINER_CODES[cgminer_code] || :unknown).to_sym
end

Instance Attribute Details

#cgminer_codeObject (readonly)

Returns the value of attribute cgminer_code.



45
46
47
# File 'lib/cgminer_api_client/errors.rb', line 45

def cgminer_code
  @cgminer_code
end

#codeObject (readonly)

Returns the value of attribute code.



45
46
47
# File 'lib/cgminer_api_client/errors.rb', line 45

def code
  @code
end

Class Method Details

.for_status(status_code, message) ⇒ Object

Factory used at the wire-side emission point in Miner#check_status. Picks AccessDeniedError when the cgminer integer maps to :access_denied so callers can ‘rescue AccessDeniedError` for the most commonly dispatched-on case; falls back to ApiError for everything else. Wire boundary stays best-effort: a non-numeric Code coerces to nil and the symbolic tag becomes :unknown rather than raising mid-poll.



54
55
56
57
58
# File 'lib/cgminer_api_client/errors.rb', line 54

def self.for_status(status_code, message)
  cgminer_code = Integer(status_code, exception: false)
  klass = CGMINER_CODES[cgminer_code] == :access_denied ? AccessDeniedError : ApiError
  klass.new("#{status_code}: #{message}", cgminer_code: cgminer_code)
end