Search code examples
rubynet-http

How To change a URI object's path attribute?


I want to call an API using only slightly different URIs. This code snippet does what I want, but I want to know if there is a more efficient way to do it.

require 'net/http'
require 'json'

# These URIs differ only in the path
orders = URI('https://hft-api.lykke.com/api/Orders?orderType=Unknown')
asset_pairs = URI('https://hft-api.lykke.com/api/AssetPairs')

lykke_req = Net::HTTP::Get.new(orders)
lykke_req['User-Agent'] = 'curl/7.67.0'
lykke_req['api-key'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
lykke_req['Accept'] = 'application/json'

response = Net::HTTP.start(orders.hostname,
                           orders.port,
                           :use_ssl => true) {|http| http.request(lykke_req)}

puts JSON.parse(response.body)

lykke_req = Net::HTTP::Get.new(asset_pairs)
lykke_req['User-Agent'] = 'curl/7.67.0'
lykke_req['Accept'] = 'application/json'

response = Net::HTTP.start(asset_pairs.hostname,
                           asset_pairs.port,
                           :use_ssl => true) {|http| http.request(lykke_req)}

puts JSON.parse(response.body)

All I do is to reuse the same code but with a slightly different URI.

For my lykke_req objects, I can write

puts lykke_req
puts lykke_req.path

which gives me

#<Net::HTTP::Get:0x00007f947f1fdce8>
/api/Orders?orderType=Unknown

So it seems to me all i have to do is change the value of lykke_req.path. But I can't work out how to do it. I am looking for something like this

lykke_req.path = "/api/AssetPairs"

which fails with

undefined method `path=' for #<Net::HTTP::Get GET> (NoMethodError)

I found this on the official documentation page, but I can't find out what [R] means. Does it mean read only? Do I really have to go through the hassle of creating a new URI object, then creating a new Net::HTTP::Get object each time?

path [R]


Solution

  • The problem here is that you're trying to alter the net request object instead of the uri object:

    irb(main):001:0> uri = URI('https://hft-api.lykke.com/api/Orders?orderType=Unknown')
    => #<URI::HTTPS https://hft-api.lykke.com/api/Orders?orderType=Unknown>
    irb(main):002:0> uri.path = '/foo'
    => "/foo"
    irb(main):003:0> uri.to_s
    => "https://hft-api.lykke.com/foo?orderType=Unknown"
    

    But I would really just wrap this in a class so that you can encapsulate and structure your code and avoid duplication:

    class LykkeAPIClient
      BASE_URI = URI('https://hft-api.lykke.com/api')
    
      def initalize(api_key:)
        @api_key = api_key
      end
    
      def get_orders
        get '/Orders?orderType=Unknown'
      end
    
      def get_asset_pairs
        get '/AssetPairs'
      end
    
      def get(path)
        req = Net::HTTP::Get.new(BASE_URI.join(path))
        req['User-Agent'] = 'curl/7.67.0'
        req['Accept'] = 'application/json'
        req['api-key'] = @api_key
        response = Net::HTTP.start(req.hostname, req.port, use_ssl: true) do |http|
          http.request(uri)
        end
        # @todo check response status!
        JSON.parse(response.body)
      end
    end
    

    @client = LykkeAPIClient.new(api_key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
    @orders = @client.get_orders