class Grac::Client
Attributes
uri[R]
Public Class Methods
new(uri, options = {})
click to toggle source
# File lib/grac/client.rb, line 13 def initialize(uri, options = {}) URI.parse(uri) @uri = uri @options = { :connecttimeout => options[:connecttimeout] || 0.1, :timeout => options[:timeout] || 15, :params => options[:params] || {}, :headers => { "User-Agent" => "Grac v#{Grac::VERSION}", "Content-Type" => "application/json;charset=utf-8" }.merge(options[:headers] || {}), :postprocessing => {}, :middleware => options[:middleware] || [] } if options[:postprocessing] options[:postprocessing] .each_with_object(postprocessing = {}) do |(pattern, transformation), obj| if pattern.kind_of?(Regexp) obj[pattern] = transformation else obj[Regexp.new(pattern)] = transformation end end @options[:postprocessing] = postprocessing end @options.freeze [:params, :headers, :postprocessing, :middleware].each do |k| @options[k].freeze end @uri.freeze end
Public Instance Methods
call(opts, request_uri, method, params, body)
click to toggle source
# File lib/grac/client.rb, line 81 def call(opts, request_uri, method, params, body) request_hash = { :method => method, :params => params, # Query params are escaped by Typhoeus :body => body, :connecttimeout => opts[:connecttimeout], :timeout => opts[:timeout], :headers => opts[:headers] } request = ::Typhoeus::Request.new(request_uri, request_hash) response = request.run # Retry GET and HEAD requests - modifying requests might not be idempotent response = request.run if response.timed_out? && ['get', 'head'].include?(method) # A request can time out while receiving data. In this case response.code might indicate # success although data hasn't been fully transferred. Thus rely on Typhoeus for # detecting a timeout. if response.timed_out? raise Exception::ServiceTimeout.new(method, request.url, response.return_message) end # List of possible codes # https://github.com/typhoeus/ethon/blob/ab052b6a317309b6ae8be7b538738b138b11437a/lib/ethon/curls/codes.rb#L10 case response.return_code # A request may have received a response but may have aborted while receiving the data. # Typhoeus does not necessarily consider this a timeout but might try to parse a response. # If the response content length does not match the content length header, # the return code will be :partial_file. # In that case we do not want to pass it on as it would result in JSON Parser errors # due to invalid JSON from the incomplete response. when :partial_file raise Exception::PartialResponse.new(method, request.url, response.return_message) end return Response.new(response) end
path(path, variables = {})
click to toggle source
# File lib/grac/client.rb, line 60 def path(path, variables = {}) variables.each do |key, value| path = path.gsub("{#{key}}", escape_url_param(value)) end self.class.new("#{@uri}#{path}", @options) end
set(options = {})
click to toggle source
# File lib/grac/client.rb, line 51 def set(options = {}) options = options.merge({ headers: @options[:headers].merge(options[:headers] || {}), middleware: @options[:middleware] + (options[:middleware] || []) }) self.class.new(@uri, @options.merge(options)) end
Private Instance Methods
build_and_run(method, options = {})
click to toggle source
# File lib/grac/client.rb, line 122 def build_and_run(method, options = {}) body = prepare_body_by_content_type(options[:body]) params = @options[:params].merge(options[:params] || {}) return middleware_chain.call(@options, uri, method, params, body) end
check_response(method, response)
click to toggle source
# File lib/grac/client.rb, line 165 def check_response(method, response) case response.code when 200..203, 206..299 # unknown status codes must be treated as the x00 of their class, so 200 if response.json_content? return postprocessing(response.parsed_json) end return response.body when 204, 205 return true when 0 raise Exception::RequestFailed.new(method, response.effective_url, response.return_message) else begin # The Response class doesn't have enough information to create a proper exception, so # catch its exception and raise a proper one. parsed_body = response.parsed_json rescue Exception::InvalidContent raise Exception::ErrorWithInvalidContent.new( method, response.effective_url, response.code, response.body, 'json' ) end case response.code when 400 raise Exception::BadRequest.new(method, response.effective_url, parsed_body) when 403 raise Exception::Forbidden.new(method, response.effective_url, parsed_body) when 404 raise Exception::NotFound.new(method, response.effective_url, parsed_body) when 409 raise Exception::Conflict.new(method, response.effective_url, parsed_body) else raise Exception::ServiceError.new(method, response.effective_url, parsed_body) end end end
escape_url_param(value)
click to toggle source
# File lib/grac/client.rb, line 231 def escape_url_param(value) # We don't want spaces to be encoded as plus sign - a plus sign can be ambiguous in a URL and # either represent a plus sign or a space. # CGI::escape replaces all plus signs with their percent-encoding representation, so all # remaining plus signs are spaces. Replacing these with a space's percent encoding makes the # encoding unambiguous. CGI::escape(value).gsub('+', '%20') end
headers()
click to toggle source
# File lib/grac/client.rb, line 128 def headers @options[:headers] || {} end
middleware_chain()
click to toggle source
# File lib/grac/client.rb, line 148 def middleware_chain callee = self @options[:middleware].reverse.each do |mw| if mw.kind_of?(Array) middleware_class = mw[0] params = mw[1..-1] callee = middleware_class.new(callee, *params) else callee = mw.new(callee) end end return callee end
postprocessing(data, processing = nil)
click to toggle source
# File lib/grac/client.rb, line 207 def postprocessing(data, processing = nil) return data if @options[:postprocessing].nil? || @options[:postprocessing].empty? if data.kind_of?(Hash) data.each do |key, value| processing = nil regexp = @options[:postprocessing].keys.detect { |pattern| pattern.match?(key) } if !regexp.nil? processing = @options[:postprocessing][regexp] end data[key] = postprocessing(value, processing) end elsif data.kind_of?(Array) data.each_with_index do |value, index| data[index] = postprocessing(value, processing) end else data = processing.nil? ? data : processing.call(data) end return data end
prepare_body_by_content_type(body)
click to toggle source
# File lib/grac/client.rb, line 132 def prepare_body_by_content_type(body) return nil if body.nil? || body.empty? case headers['Content-Type'] when /\Aapplication\/json/ return body.to_json when /\Aapplication\/x-www-form-urlencoded/ # Typhoeus will take care of the encoding when receiving a hash return body else # Do not encode other unknown Content-Types either. # The default is JSON through the Content-Type header which is set by default. return body end end