lib/leadlight/request.rb
require 'ostruct'
require 'monitor'
require 'fattr'
require 'forwardable'
require 'hookr'
require 'leadlight/errors'
require 'leadlight/blank'
require 'leadlight/hyperlinkable'
require 'leadlight/representation'
require 'leadlight/type_map'
require 'leadlight/header_helpers'
require 'leadlight/null_link'
module Leadlight
class Request
include HookR::Hooks
include MonitorMixin
extend Forwardable
include HeaderHelpers
fattr(:http_method)
fattr(:url)
fattr(:connection)
fattr(:body)
fattr(:service)
fattr(:codec)
fattr(:type_map) { service.type_map || TypeMap.new }
attr_reader :response, :options, :link
define_hook :on_prepare_request, :request
define_hook :on_complete, :response
def_delegator :service, :service_options
def initialize(service, connection, url, method, body=nil, options={})
@options = options
self.connection = connection
self.url = url
self.http_method = method
self.body = body
self.service = service
@completed = new_cond
@state = :initialized
@env = nil
@response = nil
@link = options.fetch(:link) { NullLink.new(url) }
super()
end
def completed?
:completed == @state
end
def submit
entity = type_map.to_entity_body(body)
entity_body = entity.body
content_type = entity.content_type
connection.run_request(http_method, url.to_s, entity_body, {}) do
|request|
request.headers['Content-Type'] = content_type if content_type
request.options[:leadlight_request] = self
execute_hook(:on_prepare_request, request)
end.on_complete do |env|
synchronize do
@response = env.fetch(:response)
execute_hook :on_complete, @response
@env = env
@state = :completed
@completed.broadcast
end
end
end
def wait
synchronize do
@completed.wait_until{completed?}
end
yield(@env.fetch(:leadlight_representation)) if block_given?
self
end
def submit_and_wait(&block)
submit
wait(&block)
end
alias_method :then, :submit_and_wait
def on_error
on_or_after_complete do |response|
unless response.success?
yield(response.env.fetch(:leadlight_representation))
end
end
self
end
def raise_on_error
on_error do |representation|
raise representation
end
end
def on_or_after_complete(&block)
synchronize do
if completed?
block.call(response)
else
on_complete(&block)
end
end
end
def represent(env)
content_type = env[:response_headers]['Content-Type']
content_type = clean_content_type(content_type)
representation = type_map.to_native(content_type, env[:body])
representation.
extend(Representation).
initialize_representation(env[:leadlight_service], location(env), env[:response], self).
extend(Hyperlinkable).
apply_all_tints
end
def params
link_params.merge(request_params)
end
def location(env=@env)
env ||= {}
url = env.fetch(:response_headers){{}}.fetch('location'){ env.fetch(:url){ self.url } }
Addressable::URI.parse(url)
end
private
fattr(:faraday_request) {
env.fetch(:request) { OpenStruct.new(params: {}) }
}
def env
if completed?
@env
else
{}
end
end
def representation
raise "No representation until complete" unless completed?
@env.fetch(:leadlight_representation)
end
def request_params
faraday_request.params
end
def link_params
link.params
end
end
end