Class: Heroics::Link

Inherits:
Object
  • Object
show all
Defined in:
lib/heroics/link.rb

Overview

A link invokes requests with an HTTP server.

Defined Under Namespace

Classes: CachedResponse

Instance Method Summary collapse

Constructor Details

#initialize(url, link_schema, options = {}) ⇒ Link

Instantiate a link.



16
17
18
19
20
21
# File 'lib/heroics/link.rb', line 16

def initialize(url, link_schema, options={})
  @root_url, @path_prefix = unpack_url(url)
  @link_schema = link_schema
  @default_headers = options[:default_headers] || {}
  @cache = options[:cache] || {}
end

Instance Method Details

#run(*parameters) ⇒ String, ...

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.

Raises:

  • (ArgumentError)

    Raised if either too many or too few parameters were provided.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/heroics/link.rb', line 46

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