lib/rest_baby.rb
# rest_baby.rb
# @author Dave McNulla
# Originated: 2011
# This library was written to simplify communications with rest services.
require 'net/http'
require 'net/https'
require 'json'
require 'nokogiri'
require 'rest_baby/version'
# RestBaby is a small rest client. It encapsulates some of the details for
# creating and using the rest service.
# @private
module RestBaby
# Sends and receives data to a restful web service
class Client
PARAM_STARTER = '?'.freeze
PARAM_SEPARATOR = '&'.freeze
# The WebService Response from the last call
attr_reader :wsresponse
# The user (for authentication)
attr_reader :user
# The password for the user (for authentication)
attr_reader :password
# Response Code
attr_reader :code
# Creates a new rest client
#
# @param url [String] url of the rest service
# eg. http://myrestservice.com:80/time
# @param headers [Hash] default headers to use.
# eg. '{ \"Content-Type\" => \"application/json\"}'
# Can be multiple headers separated by commas inside the brackets.
def initialize(url, headers = {}, user = nil, password = nil)
@url = url
@headers = headers
@user = user
@password = password
@verbose = (ENV['DEBUG_HTTP'] != 'false')
end
# Adds user/password to the rest client
#
# @param user [String] user that has authorization
# @param password [String] authorized user's password
def set_auth(user, password)
@user = user
@password = password
end
# Modifies the headers by merging new headers with current headers.
#
# @param headers [Hash] new headers to merge with current headers
def add_headers(headers)
@headers = @headers.merge(headers)
end
# Basic web services Get command
#
# @param headers [Hash] header parameters including authorization
# and Content-Type
# @param path [String] Path of service to send the command to
# @param parameters [Hash] query string that added as part of the URL
# @return The response from the rest server is returned
def get(headers = {}, path = '', parameters = {})
full_path = [path, URI.encode_www_form(parameters)].join(PARAM_STARTER)
uri = URI.parse("#{@url}#{full_path}")
request = Net::HTTP::Get.new(uri.request_uri)
request.body = nil
send_request(uri, request, headers)
end
# Basic web services Post command
#
# @param path [String] Path of service to send the command to
# @param body [Any type of string] Data to put in the
# body such as json or xml
# @param headers [Hash] header parameters including authorization
# and Content-Type
# @return The response from the rest server is returned
def post(body = nil, headers = {}, path = '')
uri = URI.parse("#{@url}#{path}")
request = Net::HTTP::Post.new(uri.request_uri)
request.body = body
send_request(uri, request, headers)
end
# Basic web services Delete command
#
# @param path [String] Path of service to send the command to
# @param headers [Hash] header parameters including
# authorization and Content-Type
# @return The response from the rest server is returned
def delete(headers = {}, path = '', parameters = {})
full_path = [path, URI.encode_www_form(parameters)].join(PARAM_STARTER)
uri = URI.parse("#{@url}#{full_path}")
request = Net::HTTP::Delete.new(uri.request_uri)
request.body = nil
send_request(uri, request, headers)
end
# Basic web services Put command
#
# @param path [String] Path of service to send the command to
# @param body [String] data to put in the body
# @param headers [Hash] header parameters including authorization
# and Content-Type
# @return Response from the rest server is returned
def put(body = nil, headers = {}, path = '')
uri = URI.parse("#{@url}#{path}")
request = Net::HTTP::Put.new(uri.request_uri)
request.body = body
send_request(uri, request, headers)
end
private
def send_request(uri, request, headers)
@headers.merge(headers).each { |key, value| request[key] = value }
request.basic_auth(@user, @password)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if uri.scheme == 'https'
print_request(request, uri)
@wsresponse = http.request(request)
print_response(@wsresponse)
@wsresponse
end
def print_request(request, uri)
return nil unless @verbose
query = uri.query == '' ? '' : "?#{uri.query}"
puts ">> REQUEST\n"\
"> URL: #{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}#{query}\n"\
"> Headers:\n"\
"#{header_text(request, '> ')}\n"\
"> BODY =\n"\
"#{body_text(request['Content-Type'], request.body)}"
end
def print_response(response)
return nil unless @verbose
puts "<< RESPONSE\n"\
"< CODE = #{response.code}\n"\
"< MESSAGE = #{response.message}\n"\
"#{header_text(response, ' <')}\n"\
"< BODY =\n"\
"#{body_text(response['Content-Type'], response.body)}"
end
def header_text(message, pointer)
headers = ''
message.each { |key, value| headers << "#{pointer} #{key} = #{value}\n" }
headers
end
def body_text(type, response_body)
response_body ||= '[Empty]'
case type
when 'application/json'
pretty_json(response_body)
when 'application/xml'
pretty_xml(response_body)
else
"#{response_body}\n<"
end
end
def pretty_json(json)
json = JSON(json) unless json.class == Hash
JSON.pretty_generate(json)
end
def pretty_xml(xml)
doc = Nokogiri.XML(xml) { |config| config.default_xml.noblanks }
doc.to_xml(indent: 2)
end
end
end