1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 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 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
require 'faraday'
require 'addressable/template'
module Sawyer
class Agent
NO_BODY = Set.new([:get, :head])
attr_accessor :links_parser
attr_accessor :allow_undefined_methods
class << self
attr_writer :serializer
end
def self.serializer
@serializer ||= Serializer.any_json
end
def self.encode(data)
serializer.encode(data)
end
def self.decode(data)
serializer.decode(data)
end
# Agents handle making the requests, and passing responses to
# Sawyer::Response.
#
# endpoint - String URI of the API entry point.
# options - Hash of options.
# :allow_undefined_methods - Allow relations to call all the HTTP verbs,
# not just the ones defined.
# :faraday - Optional Faraday::Connection to use.
# :links_parser - Optional parser to parse link relations
# Defaults: Sawyer::LinkParsers::Hal.new
# :serializer - Optional serializer Class. Defaults to
# self.serializer_class.
#
# Yields the Faraday::Connection if a block is given.
def initialize(endpoint, options = nil)
@endpoint = endpoint
@conn = (options && options[:faraday]) || Faraday.new
@serializer = (options && options[:serializer]) || self.class.serializer
@links_parser = (options && options[:links_parser]) || Sawyer::LinkParsers::Hal.new
@allow_undefined_methods = (options && options[:allow_undefined_methods])
@conn.url_prefix = @endpoint
yield @conn if block_given?
end
# Public: Close the underlying connection.
def close
@conn.close if @conn.respond_to?(:close)
end
# Public: Retains a reference to the root relations of the API.
#
# Returns a Sawyer::Relation::Map.
def rels
@rels ||= root.data._rels
end
# Public: Retains a reference to the root response of the API.
#
# Returns a Sawyer::Response.
def root
@root ||= start
end
# Public: Hits the root of the API to get the initial actions.
#
# Returns a Sawyer::Response.
def start
call :get, @endpoint
end
# Makes a request through Faraday.
#
# method - The Symbol name of an HTTP method.
# url - The String URL to access. This can be relative to the Agent's
# endpoint.
# data - The Optional Hash or Resource body to be sent. :get or :head
# requests can have no body, so this can be the options Hash
# instead.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
#
# Returns a Sawyer::Response.
def call(method, url, data = nil, options = nil)
if NO_BODY.include?(method)
options ||= data
data = nil
end
options ||= {}
url = expand_url(url, options[:uri])
started = nil
res = @conn.send method, url do |req|
if data
req.body = data.is_a?(String) ? data : encode_body(data)
end
if params = options[:query]
req.params.update params
end
if headers = options[:headers]
req.headers.update headers
end
started = Time.now
end
Response.new self, res, :sawyer_started => started, :sawyer_ended => Time.now
end
# Encodes an object to a string for the API request.
#
# data - The Hash or Resource that is being sent.
#
# Returns a String.
def encode_body(data)
@serializer.encode(data)
end
# Decodes a String response body to a resource.
#
# str - The String body from the response.
#
# Returns an Object resource (Hash by default).
def decode_body(str)
@serializer.decode(str)
end
def parse_links(data)
@links_parser.parse(data)
end
def expand_url(url, options = nil)
tpl = url.respond_to?(:expand) ? url : Addressable::Template.new(url.to_s)
tpl.expand(options || {}).to_s
end
def allow_undefined_methods?
!!@allow_undefined_methods
end
def inspect
%(<#{self.class} #{@endpoint}>)
end
# private
def to_yaml_properties
[:@endpoint]
end
def marshal_dump
[@endpoint]
end
def marshal_load(dumped)
@endpoint = *dumped.shift(1)
end
end
end
|