def run(*parameters)
      path, body = @link_schema.format_path(parameters)
      path = "#{@path_prefix}#{path}" unless @path_prefix == '/'
      headers = @default_headers
      if body
        case @link_schema.method
        when :put, :post, :patch
          headers = headers.merge({'Content-Type' => @link_schema.content_type})
          body = @link_schema.encode(body)
        when :get, :delete
          if body.is_a?(Hash)
            query = body
          else
            query = MultiJson.load(body)
          end
          body = nil
        end
      end

      connection = Excon.new(@root_url, thread_safe_sockets: true)
      response = request_with_cache(connection,
                                    method: @link_schema.method, path: path,
                                    headers: headers, body: body, query: query,
                                    expects: [200, 201, 202, 204, 206])
      content_type = response.headers['Content-Type']
      if content_type && content_type =~ /application\/.*json/
        body = MultiJson.load(response.body)
        if response.status == 206
          next_range = response.headers['Next-Range']
          Enumerator.new do |yielder|
            while true do
              # Yield the results we got in the body.
              body.each do |item|
                yielder << item
              end

              # Only make a request to get the next page if we have a valid
              # next range.
              break unless next_range
              headers = headers.merge({'Range' => next_range})
              response = request_with_cache(connection,
                                            method: @link_schema.method,
                                            path: path, headers: headers,
                                            expects: [200, 201, 206])
              body = MultiJson.load(response.body)
              next_range = response.headers['Next-Range']
            end
          end
        else
          body
        end
      elsif !response.body.empty?
        response.body
      end
    end
def run(*parameters)
      path, body = @link_schema.format_path(parameters)
      path = "#{@path_prefix}#{path}" unless @path_prefix == '/'
      headers = @default_headers
      if body
        case @link_schema.method
        when :put, :post, :patch
          headers = headers.merge({'Content-Type' => @link_schema.content_type})
          body = @link_schema.encode(body)
        when :get, :delete
          if body.is_a?(Hash)
            query = body
          else
            query = MultiJson.load(body)
          end
          body = nil
        end
      end

      connection = Excon.new(@root_url, thread_safe_sockets: true)
      response = request_with_cache(connection,
                                    method: @link_schema.method, path: path,
                                    headers: headers, body: body, query: query,
                                    expects: [200, 201, 202, 204, 206])
      content_type = response.headers['Content-Type']
      if content_type && content_type =~ /application\/.*json/
        body = MultiJson.load(response.body)
        if response.status == 206
          next_range = response.headers['Next-Range']
          Enumerator.new do |yielder|
            while true do
              # Yield the results we got in the body.
              body.each do |item|
                yielder << item
              end

              # Only make a request to get the next page if we have a valid
              # next range.
              break unless next_range
              headers = headers.merge({'Range' => next_range})
              response = request_with_cache(connection,
                                            method: @link_schema.method,
                                            path: path, headers: headers,
                                            expects: [200, 201, 206])
              body = MultiJson.load(response.body)
              next_range = response.headers['Next-Range']
            end
          end
        else
          body
        end
      elsif !response.body.empty?
        response.body
      end
    end
Instantiate a link.

 @param url [String] The URL to use when making requests.  Include the
   username and password to use with HTTP basic auth.
 @param link_schema [LinkSchema] The schema for this link.
 @param options [Hash] Configuration for the link.  Possible keys
   include:
   - default_headers: Optionally, a set of headers to include in every
     request made by the client.  Default is no custom headers.
   - cache: Optionally, a Moneta-compatible cache to store ETags.
     Default is no caching.
 Make a request to the server.

 JSON content received with an ETag is cached.  When the server returns a
 *304 Not Modified* status code content is loaded and returned from the
 cache.  The cache considers headers, in addition to the URL path, when
 creating keys so that requests to the same path, such as for paginated
 results, don't cause cache collisions.

 When the server returns a *206 Partial Content* status code the result
 is assumed to be an array and an enumerator is returned.  The enumerator
 yields results from the response until they've been consumed at which
 point, if additional content is available from the server, it blocks and
 makes a request to fetch the subsequent page of data.  This behaviour
 continues until the client stops iterating the enumerator or the dataset
 from the server has been entirely consumed.

 @param parameters [Array] The list of parameters to inject into the
   path.  A request body can be passed as the final parameter and will
   always be converted to JSON before being transmitted.
 @raise [ArgumentError] Raised if either too many or too few parameters
   were provided.
 @return [String,Object,Enumerator] A string for text responses, an
   object for JSON responses, or an enumerator for list responses.