lib/hyper_resource/modules/http.rb in hyperresource-0.2.4 vs lib/hyper_resource/modules/http.rb in hyperresource-0.9.0
- old
+ new
@@ -2,126 +2,235 @@
require 'uri'
require 'json'
require 'digest/md5'
class HyperResource
+
+ ## Returns this resource's fully qualified URL. Returns nil when
+ ## `root` or `href` are malformed.
+ def url
+ begin
+ URI.join(self.root, (self.href || '')).to_s
+ rescue StandardError
+ nil
+ end
+ end
+
+
+ ## Performs a GET request to this resource's URL, and returns a
+ ## new resource representing the response.
+ def get
+ to_link.get
+ end
+
+ ## Performs a POST request to this resource's URL, sending all of
+ ## `attributes` as a request body unless an `attrs` Hash is given.
+ ## Returns a new resource representing the response.
+ def post(attrs=nil)
+ to_link.post(attrs)
+ end
+
+ ## Performs a PUT request to this resource's URL, sending all of
+ ## `attributes` as a request body unless an `attrs` Hash is given.
+ ## Returns a new resource representing the response.
+ def put(*args)
+ to_link.put(*args)
+ end
+
+ ## Performs a PATCH request to this resource's URL, sending
+ ## `attributes.changed_attributes` as a request body
+ ## unless an `attrs` Hash is given. Returns a new resource
+ ## representing the response.
+ def patch(*args)
+ self.to_link.patch(*args)
+ end
+
+ ## Performs a DELETE request to this resource's URL. Returns a new
+ ## resource representing the response.
+ def delete(*args)
+ to_link.delete(*args)
+ end
+
+ ## Creates a Link representing this resource. Used for HTTP delegation.
+ # @private
+ def to_link(args={})
+ self.class::Link.new(self,
+ :href => args[:href] || self.href,
+ :params => args[:params] || self.attributes)
+ end
+
+
+
+ # @private
+ def create(attrs)
+ _hr_deprecate('HyperResource#create is deprecated. Please use '+
+ '#post instead.')
+ to_link.post(attrs)
+ end
+
+ # @private
+ def update(*args)
+ _hr_deprecate('HyperResource#update is deprecated. Please use '+
+ '#put or #patch instead.')
+ to_link.put(*args)
+ end
+
module Modules
+
+ ## HyperResource::Modules::HTTP is included by HyperResource::Link.
+ ## It provides support for GET, POST, PUT, PATCH, and DELETE.
+ ## Each method returns a new object which is a kind_of HyperResource.
module HTTP
## Loads and returns the resource pointed to by +href+. The returned
## resource will be blessed into its "proper" class, if
## +self.class.namespace != nil+.
def get
- self.response = faraday_connection.get(self.href || '')
- finish_up
+ ## Adding default_attributes to URL query params is not automatic
+ url = FuzzyURL.new(self.url || '')
+ query_str = url[:query] || ''
+ query_attrs = Hash[ query_str.split('&').map{|p| p.split('=')} ]
+ attrs = (self.resource.default_attributes || {}).merge(query_attrs)
+ attrs_str = attrs.inject([]){|pairs,(k,v)| pairs<<"#{k}=#{v}"}.join('&')
+ if attrs_str != ''
+ url = FuzzyURL.new(url.to_hash.merge(:query => attrs_str))
+ end
+ response = faraday_connection.get(url.to_s)
+ new_resource_from_response(response)
end
## By default, calls +post+ with the given arguments. Override to
## change this behavior.
def create(*args)
+ _hr_deprecate('HyperResource::Link#create is deprecated. Please use '+
+ '#post instead.')
post(*args)
end
## POSTs the given attributes to this resource's href, and returns
## the response resource.
def post(attrs=nil)
- attrs || self.attributes
- self.response = faraday_connection.post do |req|
- req.body = adapter.serialize(attrs)
+ attrs ||= self.resource.attributes
+ attrs = (self.resource.default_attributes || {}).merge(attrs)
+ response = faraday_connection.post do |req|
+ req.body = self.resource.adapter.serialize(attrs)
end
- finish_up
+ new_resource_from_response(response)
end
- ## By default, calls +put+ with the given arguments. Override to
+ ## By default, calls +puwt+ with the given arguments. Override to
## change this behavior.
def update(*args)
+ _hr_deprecate('HyperResource::Link#update is deprecated. Please use '+
+ '#put or #patch instead.')
put(*args)
end
## PUTs this resource's attributes to this resource's href, and returns
## the response resource. If attributes are given, +put+ uses those
## instead.
def put(attrs=nil)
- attrs ||= self.attributes
- self.response = faraday_connection.put do |req|
- req.body = adapter.serialize(attrs)
+ attrs ||= self.resource.attributes
+ attrs = (self.resource.default_attributes || {}).merge(attrs)
+ response = faraday_connection.put do |req|
+ req.body = self.resource.adapter.serialize(attrs)
end
- finish_up
+ new_resource_from_response(response)
end
## PATCHes this resource's changed attributes to this resource's href,
## and returns the response resource. If attributes are given, +patch+
## uses those instead.
def patch(attrs=nil)
- attrs ||= self.attributes.changed_attributes
- self.response = faraday_connection.patch do |req|
- req.body = adapter.serialize(attrs)
+ attrs ||= self.resource.attributes.changed_attributes
+ attrs = (self.resource.default_attributes || {}).merge(attrs)
+ response = faraday_connection.patch do |req|
+ req.body = self.resource.adapter.serialize(attrs)
end
- finish_up
+ new_resource_from_response(response)
end
## DELETEs this resource's href, and returns the response resource.
def delete
- self.response = faraday_connection.delete
- finish_up
+ response = faraday_connection.delete
+ new_resource_from_response(response)
end
+ private
+
## Returns a raw Faraday connection to this resource's URL, with proper
## headers (including auth). Threadsafe.
def faraday_connection(url=nil)
- url ||= URI.join(self.root, self.href)
- key = Digest::MD5.hexdigest({
+ rsrc = self.resource
+ url ||= self.url
+ headers = rsrc.headers_for_url(url) || {}
+ auth = rsrc.auth_for_url(url) || {}
+
+ key = ::Digest::MD5.hexdigest({
'faraday_connection' => {
'url' => url,
- 'headers' => self.headers,
- 'ba' => self.auth[:basic]
+ 'headers' => headers,
+ 'ba' => auth[:basic]
}
}.to_json)
return Thread.current[key] if Thread.current[key]
- fc = Faraday.new(self.faraday_options.merge(:url => url))
- fc.headers.merge!('User-Agent' => "HyperResource #{HyperResource::VERSION}")
- fc.headers.merge!(self.headers || {})
- if ba=self.auth[:basic]
+ fo = rsrc.faraday_options_for_url(url) || {}
+ fc = Faraday.new(fo.merge(:url => url))
+ fc.headers.merge!('User-Agent' => rsrc.user_agent)
+ fc.headers.merge!(headers)
+ if ba=auth[:basic]
fc.basic_auth(*ba)
end
Thread.current[key] = fc
end
- private
- def finish_up
+ ## Given a Faraday::Response object, create a new resource
+ ## object to represent it. The new resource will be in its
+ ## proper class according to its configured `namespace` and
+ ## the response's detected data type.
+ def new_resource_from_response(response)
+ status = response.status
+ is_success = (status / 100 == 2)
+ adapter = self.resource.adapter || HyperResource::Adapter::HAL_JSON
+
+ body = nil
begin
- self.body = self.adapter.deserialize(self.response.body) unless self.response.body.nil?
+ if response.body
+ body = adapter.deserialize(response.body)
+ end
rescue StandardError => e
- raise HyperResource::ResponseError.new(
- "Error when deserializing response body",
- :response => self.response,
- :cause => e
- )
+ if is_success
+ raise HyperResource::ResponseError.new(
+ "Error when deserializing response body",
+ :response => response,
+ :cause => e
+ )
+ end
end
- self.adapter.apply(self.body, self)
- self.loaded = true
+ new_rsrc = resource.new_from(:link => self,
+ :body => body,
+ :response => response)
- status = self.response.status
if status / 100 == 2
- return to_response_class
+ return new_rsrc
elsif status / 100 == 3
- ## TODO redirect logic?
+ raise NotImplementedError,
+ "HyperResource has not implemented redirection."
elsif status / 100 == 4
raise HyperResource::ClientError.new(status.to_s,
- :response => self.response,
- :body => self.body)
+ :response => response,
+ :body => body)
elsif status / 100 == 5
raise HyperResource::ServerError.new(status.to_s,
- :response => self.response,
- :body => self.body)
-
+ :response => response,
+ :body => body)
else ## 1xx? really?
- raise HyperResource::ResponseError.new("Got status #{status}, wtf?",
- :response => self.response,
- :body => self.body)
+ raise HyperResource::ResponseError.new("Unknown status #{status}",
+ :response => response,
+ :body => body)
end
end
end