class ActiveRestClient::Request

Attributes

body[RW]
forced_url[RW]
get_params[RW]
headers[RW]
method[RW]
object[RW]
original_url[RW]
path[RW]
post_params[RW]
url[RW]

Public Class Methods

new(method, object, params = {}) click to toggle source
# File lib/active_rest_client/request.rb, line 11
def initialize(method, object, params = {})
  @method                     = method
  @method[:options]           ||= {}
  @method[:options][:lazy]    ||= []
  @method[:options][:has_one] ||= {}
  @overridden_name             = @method[:options][:overridden_name]
  @object                     = object
  @response_delegate          = ActiveRestClient::RequestDelegator.new(nil)
  @params                     = params
  @headers                    = HeadersList.new
end

Public Instance Methods

api_auth_access_id() click to toggle source
# File lib/active_rest_client/request.rb, line 59
def api_auth_access_id
  if object_is_class?
    @object.api_auth_access_id
  else
    @object.class.api_auth_access_id
  end
end
api_auth_secret_key() click to toggle source
# File lib/active_rest_client/request.rb, line 67
def api_auth_secret_key
  if object_is_class?
    @object.api_auth_secret_key
  else
    @object.class.api_auth_secret_key
  end
end
append_get_parameters() click to toggle source
# File lib/active_rest_client/request.rb, line 246
def append_get_parameters
  if @get_params.any?
    @url += "?" + @get_params.to_query
  end
end
base_url() click to toggle source
# File lib/active_rest_client/request.rb, line 43
def base_url
  if object_is_class?
    @object.base_url
  else
    @object.class.base_url
  end
end
call(explicit_parameters=nil) click to toggle source
# File lib/active_rest_client/request.rb, line 131
def call(explicit_parameters=nil)
  @instrumentation_name = "#{class_name}##{@method[:name]}"
  result = nil
  cached = nil
  ActiveSupport::Notifications.instrument("request_call.active_rest_client", :name => @instrumentation_name) do
    @explicit_parameters = explicit_parameters
    @body = nil
    prepare_params
    prepare_url
    if fake = @method[:options][:fake]
      if fake.respond_to?(:call)
        fake = fake.call(self)
      end
      ActiveRestClient::Logger.debug "  \033[1;4;32m#{ActiveRestClient::NAME}\033[0m #{@instrumentation_name} - Faked response found"
      content_type = @method[:options][:fake_content_type] || "application/json"
      return handle_response(OpenStruct.new(status:200, body:fake, response_headers:{"X-ARC-Faked-Response" => "true", "Content-Type" => content_type}))
    end
    if object_is_class?
      @object.send(:_filter_request, :before, @method[:name], self)
    else
      @object.class.send(:_filter_request, :before, @method[:name], self)
    end
    append_get_parameters
    prepare_request_body
    self.original_url = self.url
    cached = original_object_class.read_cached_response(self)
    if cached
      if cached.expires && cached.expires > Time.now
        ActiveRestClient::Logger.debug "  \033[1;4;32m#{ActiveRestClient::NAME}\033[0m #{@instrumentation_name} - Absolutely cached copy found"
        return handle_cached_response(cached)
      elsif cached.etag.to_s != "" #present? isn't working for some reason
        ActiveRestClient::Logger.debug "  \033[1;4;32m#{ActiveRestClient::NAME}\033[0m #{@instrumentation_name} - Etag cached copy found with etag #{cached.etag}"
        etag = cached.etag
      end
    end

    response = (
      if proxy
        proxy.handle(self) do |request|
          request.do_request(etag)
        end
      else
        do_request(etag)
      end
    )

    # This block is called immediately when this request is not inside a parallel request block.
    # Otherwise this callback is called after the parallel request block ends.
    response.on_complete do |response_env|
      if verbose?
        ActiveRestClient::Logger.debug "  Response"
        ActiveRestClient::Logger.debug "  << Status : #{response_env.status}"
        response_env.response_headers.each do |k,v|
          ActiveRestClient::Logger.debug "  << #{k} : #{v}"
        end
        ActiveRestClient::Logger.debug "  << Body:\n#{response_env.body}"
      end

      if object_is_class? && @object.record_response?
        @object.record_response(self.url, response_env)
      end
      if object_is_class?
        @object.send(:_filter_request, :after, @method[:name], response_env)
      else
        @object.class.send(:_filter_request, :after, @method[:name], response_env)
      end

      result = handle_response(response_env, cached)
      @response_delegate.__setobj__(result)
      original_object_class.write_cached_response(self, response_env, result)
    end

    # If this was not a parallel request just return the original result
    return result if response.finished?
    # Otherwise return the delegate which will get set later once the call back is completed
    return @response_delegate
  end
end
class_name() click to toggle source
# File lib/active_rest_client/request.rb, line 27
def class_name
  if object_is_class?
    @object.name
  else
    @object.class.name
  end
end
do_request(etag) click to toggle source
# File lib/active_rest_client/request.rb, line 262
def do_request(etag)
  http_headers = {}
  http_headers["If-None-Match"] = etag if etag
  http_headers["Accept"] = "application/hal+json, application/json;q=0.5"
  headers.each do |key,value|
    value = value.join(",") if value.is_a?(Array)
    http_headers[key] = value
  end
  if @method[:options][:url] || @forced_url
    @url = @method[:options][:url] || @method[:url]
    @url = @forced_url if @forced_url
    if connection = ActiveRestClient::ConnectionManager.find_connection_for_url(@url)
      @url = @url.slice(connection.base_url.length, 255)
    else
      parts = @url.match(%r{^(https?://[a-z\d\.:-]+?)(/.*)}).to_a
      if (parts.empty?) # Not a full URL, so use hostname/protocol from existing base_url
        uri = URI.parse(base_url)
        @base_url = "#{uri.scheme}://#{uri.host}#{":#{uri.port}" if uri.port != 80 && uri.port != 443}"
        @url = "#{base_url}#{@url}".gsub(@base_url, "")
      else
        _, @base_url, @url = parts
      end
      base_url.gsub!(%r{//(.)}, "//#{username}:#{password}@\\1") if username && !base_url[%r{//[^/]*:[^/]*@}]
      connection = ActiveRestClient::ConnectionManager.get_connection(@base_url)
    end
  else
    parts = @url.match(%r{^(https?://[a-z\d\.:-]+?)(/.*)}).to_a
    if (parts.empty?) # Not a full URL, so use hostname/protocol from existing base_url
      uri = URI.parse(base_url)
      @base_url = "#{uri.scheme}://#{uri.host}#{":#{uri.port}" if uri.port != 80 && uri.port != 443}"
      @url = "#{base_url}#{@url}".gsub(@base_url, "")
      base_url = @base_url
    end
    base_url.gsub!(%r{//(.)}, "//#{username}:#{password}@\\1") if username && !base_url[%r{//[^/]*:[^/]*@}]
    connection = ActiveRestClient::ConnectionManager.get_connection(base_url)
  end
  ActiveRestClient::Logger.info "  \033[1;4;32m#{ActiveRestClient::NAME}\033[0m #{@instrumentation_name} - Requesting #{connection.base_url}#{@url}"

  if verbose?
    ActiveRestClient::Logger.debug "ActiveRestClient Verbose Log:"
    ActiveRestClient::Logger.debug "  Request"
    ActiveRestClient::Logger.debug "  >> #{http_method.upcase} #{@url} HTTP/1.1"
    http_headers.each do |k,v|
      ActiveRestClient::Logger.debug "  >> #{k} : #{v}"
    end
    ActiveRestClient::Logger.debug "  >> Body:\n#{@body}"
  end

  request_options = {:headers => http_headers}
  if using_api_auth?
    request_options[:api_auth] = {
      :api_auth_access_id => api_auth_access_id,
      :api_auth_secret_key => api_auth_secret_key
    }
  end

  case http_method
  when :get
    response = connection.get(@url, request_options)
  when :put
    response = connection.put(@url, @body, request_options)
  when :post
    response = connection.post(@url, @body, request_options)
  when :delete
    response = connection.delete(@url, request_options)
  else
    raise InvalidRequestException.new("Invalid method #{http_method}")
  end

  response
end
hal_response?() click to toggle source
# File lib/active_rest_client/request.rb, line 442
def hal_response?
  _, content_type = @response.response_headers.detect{|k,v| k.downcase == "content-type"}
  faked_response = @response.response_headers.detect{|k,v| k.downcase == "x-arc-faked-response"}
  if content_type && content_type.respond_to?(:each)
    content_type.each do |ct|
      return true if ct[%r{application\/hal\+json}i]
      return true if ct[%r{application\/json}i]
    end
    faked_response
  elsif content_type && (content_type[%r{application\/hal\+json}i] || content_type[%r{application\/json}i]) || faked_response
    true
  else
    false
  end
end
handle_cached_response(cached) click to toggle source
# File lib/active_rest_client/request.rb, line 334
def handle_cached_response(cached)
  if cached.result.is_a? ActiveRestClient::ResultIterator
    cached.result
  else
    if object_is_class?
      cached.result
    else
      @object._copy_from(cached.result)
      @object
    end
  end
end
handle_response(response, cached = nil) click to toggle source
# File lib/active_rest_client/request.rb, line 347
def handle_response(response, cached = nil)
  @response = response
  status = @response.status || 200

  if cached && response.status == 304
    ActiveRestClient::Logger.debug "  \033[1;4;32m#{ActiveRestClient::NAME}\033[0m #{@instrumentation_name}" +
      ' - Etag copy is the same as the server'
    return handle_cached_response(cached)
  end

  if (200..399).include?(status)
    if @method[:options][:plain]
      return @response = response.body
    elsif is_json_response? || is_xml_response?
      if @response.respond_to?(:proxied) && @response.proxied
        ActiveRestClient::Logger.debug "  \033[1;4;32m#{ActiveRestClient::NAME}\033[0m #{@instrumentation_name} - Response was proxied, unable to determine size"
      else
        ActiveRestClient::Logger.debug "  \033[1;4;32m#{ActiveRestClient::NAME}\033[0m #{@instrumentation_name} - Response received #{@response.body.size} bytes"
      end
      result = generate_new_object(ignore_xml_root: @method[:options][:ignore_xml_root])
    else
      raise ResponseParseException.new(status:status, body:@response.body)
    end
  else
    if is_json_response? || is_xml_response?
      error_response = generate_new_object(mutable: false, ignore_xml_root: @method[:options][:ignore_xml_root])
    else
      error_response = @response.body
    end
    if status == 400
      raise HTTPBadRequestClientException.new(status:status, result:error_response, url:@url)
    elsif status == 401
      raise HTTPUnauthorisedClientException.new(status:status, result:error_response, url:@url)
    elsif status == 403
      raise HTTPForbiddenClientException.new(status:status, result:error_response, url:@url)
    elsif status == 404
      raise HTTPNotFoundClientException.new(status:status, result:error_response, url:@url)
    elsif (400..499).include? status
      raise HTTPClientException.new(status:status, result:error_response, url:@url)
    elsif (500..599).include? status
      raise HTTPServerException.new(status:status, result:error_response, url:@url)
    elsif status == 0
      raise TimeoutException.new("Timed out getting #{response.url}")
    end
  end

  result
end
http_method() click to toggle source
# File lib/active_rest_client/request.rb, line 127
def http_method
  @method[:method]
end
new_object(attributes, name = nil) click to toggle source
# File lib/active_rest_client/request.rb, line 396
def new_object(attributes, name = nil)
  @method[:options][:has_many] ||= {}
  name = name.to_sym rescue nil
  if @method[:options][:has_many][name]
    overridden_name = name
    object = @method[:options][:has_many][name].new
  elsif @method[:options][:has_one][name]
    overridden_name = name
    object = @method[:options][:has_one][name].new
  else
    object = create_object_instance
  end

  if hal_response? && name.nil?
    attributes = handle_hal_links_embedded(object, attributes)
  end

  attributes.each do |k,v|
    k = k.to_sym
    overridden_name = select_name(k, overridden_name)
    if @method[:options][:lazy].include?(k)
      object._attributes[k] = ActiveRestClient::LazyAssociationLoader.new(overridden_name, v, self, overridden_name:(overridden_name))
    elsif v.is_a? Hash
      object._attributes[k] = new_object(v, overridden_name )
    elsif v.is_a? Array
      object._attributes[k] = ActiveRestClient::ResultIterator.new
      v.each do |item|
        if item.is_a? Hash
          object._attributes[k] << new_object(item, overridden_name)
        else
          object._attributes[k] << item
        end
      end
    else
      if v.to_s[/\d{4}\-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})/]
        object._attributes[k] = DateTime.parse(v)
      else
        object._attributes[k] = v
      end
    end
  end
  object.clean! unless object_is_class?

  object
end
object_is_class?() click to toggle source
# File lib/active_rest_client/request.rb, line 23
def object_is_class?
  !@object.respond_to?(:dirty?)
end
original_object_class() click to toggle source
# File lib/active_rest_client/request.rb, line 35
def original_object_class
  if object_is_class?
    @object
  else
    @object.class
  end
end
password() click to toggle source
# File lib/active_rest_client/request.rb, line 83
def password
  if object_is_class?
    @object.password
  else
    @object.class.password
  end
end
prepare_params() click to toggle source
# File lib/active_rest_client/request.rb, line 210
def prepare_params
  params = @params || @object._attributes rescue {}
  if params.is_a?(String) || params.is_a?(Fixnum)
    params = {id:params}
  end

  default_params = @method[:options][:defaults] || {}

  if @explicit_parameters
    params = @explicit_parameters
  end
  if http_method == :get
    @get_params = default_params.merge(params || {})
    @post_params = nil
  else
    @post_params = default_params.merge(params || {})
    @get_params = {}
  end
end
prepare_request_body(params = nil) click to toggle source
# File lib/active_rest_client/request.rb, line 252
def prepare_request_body(params = nil)
  if request_body_type == :form_encoded
    @body ||= (params || @post_params || {}).to_query
    headers["Content-Type"] ||= "application/x-www-form-urlencoded"
  elsif request_body_type == :json
    @body ||= (params || @post_params || {}).to_json
    headers["Content-Type"] ||= "application/json; charset=utf-8"
  end
end
prepare_url() click to toggle source
# File lib/active_rest_client/request.rb, line 230
def prepare_url
  if @forced_url && @forced_url.present?
    @url = @forced_url
  else
    @url = @method[:url].dup
    matches = @url.scan(/(:[a-z_-]+)/)
    @get_params ||= {}
    @post_params ||= {}
    matches.each do |token|
      token = token.first[1,999]
      target = @get_params.delete(token.to_sym) || @post_params.delete(token.to_sym) || @get_params.delete(token.to_s) || @post_params.delete(token.to_s) || ""
      @url.gsub!(":#{token}", target.to_s)
    end
  end
end
proxy() click to toggle source
# File lib/active_rest_client/request.rb, line 117
def proxy
  if object_is_class?
    @object.proxy
  else
    @object.class.proxy
  end
rescue
  nil
end
request_body_type() click to toggle source
# File lib/active_rest_client/request.rb, line 91
def request_body_type
  if @method[:options][:request_body_type]
    @method[:options][:request_body_type]
  elsif object_is_class?
    @object.request_body_type
  else
    @object.class.request_body_type
  end
end
translator() click to toggle source
# File lib/active_rest_client/request.rb, line 109
def translator
  if object_is_class?
    @object.translator
  else
    @object.class.translator
  end
end
username() click to toggle source
# File lib/active_rest_client/request.rb, line 75
def username
  if object_is_class?
    @object.username
  else
    @object.class.username
  end
end
using_api_auth?() click to toggle source
# File lib/active_rest_client/request.rb, line 51
def using_api_auth?
  if object_is_class?
    @object.using_api_auth?
  else
    @object.class.using_api_auth?
  end
end
verbose?() click to toggle source
# File lib/active_rest_client/request.rb, line 101
def verbose?
  if object_is_class?
    @object.verbose
  else
    @object.class.verbose
  end
end

Private Instance Methods

create_object_instance() click to toggle source
# File lib/active_rest_client/request.rb, line 491
def create_object_instance
  return object_is_class? ? @object.new : @object.class.new
end
generate_new_object(options={}) click to toggle source
# File lib/active_rest_client/request.rb, line 511
def generate_new_object(options={})
  if @response.body.is_a?(Array) || @response.body.is_a?(Hash)
    body = @response.body
  elsif is_json_response?
    body = @response.body.blank? ? {} : MultiJson.load(@response.body)
  elsif is_xml_response?
    body = @response.body.blank? ? {} : Crack::XML.parse(@response.body)
    if options[:ignore_xml_root]
      body = body[options[:ignore_xml_root].to_s]
    end
  end
  body = begin
    @method[:name].nil? ? body : translator.send(@method[:name], body)
  rescue NoMethodError
    body
  end
  if body.is_a? Array
    result = ActiveRestClient::ResultIterator.new(@response)
    body.each do |json_object|
      result << new_object(json_object, @overridden_name)
    end
  else
    result = new_object(body, @overridden_name)
    result._status = @response.status
    result._headers = @response.response_headers
    result._etag = @response.response_headers['ETag']
    if !object_is_class? && options[:mutable] != false
      @object._copy_from(result)
      @object._clean!
      result = @object
    end
  end
  result
end
is_json_response?() click to toggle source
# File lib/active_rest_client/request.rb, line 503
def is_json_response?
  @response.response_headers['Content-Type'].nil? || @response.response_headers['Content-Type'].include?('json')
end
is_xml_response?() click to toggle source
# File lib/active_rest_client/request.rb, line 507
def is_xml_response?
  @response.response_headers['Content-Type'].include?('xml')
end
select_name(name, parent_name) click to toggle source
# File lib/active_rest_client/request.rb, line 495
def select_name(name, parent_name)
  if @method[:options][:has_many][name] || @method[:options][:has_one][name]
    return name
  end

  parent_name || name
end