The following ruby method is expected to return either an array or nil, depending on whether or not the api call was successful.
# @return [Array or nil] - but should not throw error if anything fails
def fetch_coords_from_api
api_url = "some_api_url"
params = { api_key: ENV["API_KEY"] }
response = ApiClient.new(api_url, params).get
return unless response
json = JSON.parse(response.body)
return unless json.is_a?(Hash) && json["result"]
addresses = json["result"]
return unless addresses.is_a?(Array) && !addresses.empty?
address = addresses.find { |add| add["zip"] == zip }
return unless address&.key?("latitude") && address&.key?("longitude")
[ address["latitude"], address["longitude"] ]
end
Some context: the method is part of a class that attempts several methods of retrieving coordinates (if one method fails we move on to the next).
This works as intended but also seems like it violates several best practices and conventions (regarding the multiple checks for an invalid state). Can anyone suggest a more robust/efficient/conventional/pretty way of handling a scenario like this?
While I am still unclear on the implementation here is how I would go about refactoring the code you have now to avoid all the early returns
def fetch_coords_from_api
api_url = "some_api_url"
params = { api_key: ENV["API_KEY"] }
if response = ApiClient.new(api_url, params).get
json = JSON.parse(response.body, {symbolize_names: true})
#please note the local variable `zip` does not exist in the current context
case json
in result: [*,{latitude: , longitude: , zip: ^zip, **},*]
[latitude,longitude]
else
nil
end
end
end
This uses pattern matching to find the desired entry by asserting the specified structure and pinning the desired zip code. If it exists this will return the latitude and longitude as an Array
otherwise it will return nil
.