lib/rest_adapter.rb in dm-rest-adapter-0.9.2 vs lib/rest_adapter.rb in dm-rest-adapter-0.9.3
- old
+ new
@@ -1,50 +1,41 @@
require 'rubygems'
-gem 'dm-core', '=0.9.2'
+require 'pathname'
+require Pathname(__FILE__).dirname + 'rest_adapter/version'
+gem 'dm-core', DataMapper::More::RestAdapter::VERSION
require 'dm-core'
require 'extlib'
require 'dm-serializer'
-require 'pathname'
require 'net/http'
require 'rexml/document'
# TODO: Abstract XML support out from the protocol
# TODO: Build JSON support
module DataMapper
module Adapters
class RestAdapter < AbstractAdapter
include Extlib
-
- # def read_one(query)
- # raise NotImplementedError
- # end
- #
- # def update(attributes, query)
- # raise NotImplementedError
- # end
- #
- # def delete(query)
- # raise NotImplementedError
- # end
-
+
# Creates a new resource in the specified repository.
def create(resources)
- success = true
+ count = 0
resources.each do |resource|
- resource_name = Inflection.underscore(resource.class.name.downcase)
+ resource_name = Inflection.underscore(resource.class.name)
result = http_post("/#{resource_name.pluralize}.xml", resource.to_xml)
# TODO: Raise error if cannot reach server
- success = success && result.instance_of?(Net::HTTPCreated)
+ success = result.instance_of?(Net::HTTPCreated)
if success
- updated_resource = parse_resource(result.body, resource.class)
- resource.id = updated_resource.id
+ count += 1
+ # TODO: Fix commented out code below to work through the identity_map of the repository
+ # values = parse_resource(result.body, resource.class)
+ # resource.id = updated_resource.id
end
# TODO: We're not using the response to update the DataMapper::Resource with the newly acquired ID!!!
end
- success
+ count
end
-
+
# read_set
#
# Examples of query string:
# A. []
# GET /books/
@@ -53,138 +44,187 @@
# GET /books/4200
#
# IN PROGRESS
# TODO: Need to account for query.conditions (i.e., [[:eql, #<Property:Book:id>, 1]] for books/1)
def read_many(query)
- resource_name = Inflection.underscore(query.model.name.downcase)
- case query.conditions
- when []
- read_set_all(repository, query, resource_name)
- else
- read_set_for_condition(repository, query, resource)
+ resource_name = Inflection.underscore(query.model.name)
+ Collection.new(query) do |collection|
+ case query.conditions
+ when []
+ resources_meta = read_set_all(repository, query, resource_name)
+ else
+ resources_meta = read_set_for_condition(repository, query, resource_name)
+ end
+ resources_meta.each do |resource_meta|
+ if resource_meta.has_key?(:associations)
+ load_nested_resources_from resource_meta[:associations], query
+ end
+ collection.load(resource_meta[:values])
+ end
end
end
-
+
def read_one(query)
- # puts "---------------- QUERY: #{query} #{query.inspect}"
- id = query.conditions.first[2]
- # KLUGE: Again, we're assuming below that we're dealing with a pluralized resource mapping
+ resource = nil
resource_name = resource_name_from_query(query)
- response = http_get("/#{resource_name.pluralize}/#{id}.xml")
-
- # KLUGE: Rails returns HTML if it can't find a resource. A properly RESTful app would return a 404, right?
- return nil if response.is_a? Net::HTTPNotFound || response.content_type == "text/html"
-
- data = response.body
- res = parse_resource(data, query.model)
- res
+ resources_meta = nil
+ if query.conditions.empty? && query.limit == 1
+ results = read_set_all(repository, query, resource_name)
+ resource_meta = results.first unless results.empty?
+ else
+ id = query.conditions.first[2]
+ # KLUGE: Again, we're assuming below that we're dealing with a pluralized resource mapping
+
+ response = http_get("/#{resource_name.pluralize}/#{id}.xml")
+
+ # KLUGE: Rails returns HTML if it can't find a resource. A properly RESTful app would return a 404, right?
+ return nil if response.is_a? Net::HTTPNotFound || response.content_type == "text/html"
+
+ data = response.body
+ resource_meta = parse_resource(data, query.model, query)
+ end
+ if resource_meta
+ if resource_meta.has_key?(:associations)
+ load_nested_resources_from resource_meta[:associations], query
+ end
+ resource = query.model.load(resource_meta[:values], query)
+ end
+ resource
end
-
+
def update(attributes, query)
- # TODO update for v0.9.2
+ # TODO What if we have a compound key?
raise NotImplementedError.new unless is_single_resource_query? query
id = query.conditions.first[2]
- resource = query.model.new
+ resource = nil
+ query.repository.scope do
+ resource = query.model.get(id)
+ end
attributes.each do |attr, val|
resource.send("#{attr.name}=", val)
end
# KLUGE: Again, we're assuming below that we're dealing with a pluralized resource mapping
- http_put("/#{resource_name_from_query(query).pluralize}/#{id}.xml", resource.to_xml)
+ res = http_put("/#{resource_name_from_query(query).pluralize}/#{id}.xml", resource.to_xml)
# TODO: Raise error if cannot reach server
+ res.kind_of?(Net::HTTPSuccess) ? 1 : 0
end
-
+
def delete(query)
- #puts ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>> QUERY: #{query} #{query.inspect}"
- # TODO update for v0.9.2
raise NotImplementedError.new unless is_single_resource_query? query
id = query.conditions.first[2]
- http_delete("/#{resource_name_from_query(query).pluralize}/#{id}.xml")
+ res = http_delete("/#{resource_name_from_query(query).pluralize}/#{id}.xml")
+ res.kind_of?(Net::HTTPSuccess) ? 1 : 0
end
-
- protected
+
+ protected
+ def load_nested_resources_from(nested_resources, query)
+ nested_resources.each do |resource_meta|
+ # TODO: Houston, we have a problem. Model#load expects a Query. When we're nested, we don't have a query yet...
+ #resource_meta[:model].load(resource_meta[:values])
+ #if resource_meta.has_key? :associations
+ # load_nested_resources_from resource_meta, query
+ #end
+ end
+ end
+
def read_set_all(repository, query, resource_name)
# TODO: how do we know whether the resource we're talking to is singular or plural?
res = http_get("/#{resource_name.pluralize}.xml")
data = res.body
- parse_resources(data, query.model)
+ parse_resources(data, query.model, query)
# TODO: Raise error if cannot reach server
end
-
+
# GET /books/4200
def read_set_for_condition(repository, query, resource_name)
# More complex conditions
raise NotImplementedError.new
- end
-
+ end
+
# query.conditions like [[:eql, #<Property:Book:id>, 4200]]
def is_single_resource_query?(query)
query.conditions.length == 1 && query.conditions.first.first == :eql && query.conditions.first[1].name == :id
end
-
+
def http_put(uri, data = nil)
- request { |http| http.put(uri, data) }
+ request { |http| http.put(uri, data, {"Content-Type", "application/xml"}) }
end
def http_post(uri, data)
request { |http| http.post(uri, data, {"Content-Type", "application/xml"}) }
end
def http_get(uri)
- request { |http| http.get(uri) }
+ request { |http| http.get(uri, {"Content-Type", "application/xml"}) }
end
def http_delete(uri)
- request { |http| http.delete(uri) }
+ request { |http| http.delete(uri, {"Content-Type", "application/xml"}) }
end
def request(&block)
res = nil
Net::HTTP.start(@uri[:host], @uri[:port].to_i) do |http|
res = yield(http)
end
res
- end
+ end
- def resource_from_rexml(entity_element, dm_model_class)
- resource = dm_model_class.new
+ def values_from_rexml(entity_element, dm_model_class)
+ resource = {}
+ resource[:values] = []
entity_element.elements.each do |field_element|
- attribute = resource.attributes.find do |name, val|
+ attribute = dm_model_class.properties(repository.name).find do |property|
# *MUST* use Inflection.underscore on the XML as Rails converts '_' to '-' in the XML
- name.to_s == Inflection.underscore(field_element.name.to_s)
+ property.name.to_s == Inflection.underscore(field_element.name.to_s)
end
- resource.send("#{Inflection.underscore(attribute[0])}=", field_element.text) if attribute
+ if attribute
+ resource[:values] << field_element.text
+ next
+ end
+ association = dm_model_class.relationships.find do |name, dm_relationship|
+ field_element.name.to_s == Inflection.pluralize(Inflection.underscore(dm_relationship.child_model.to_s))
+ end
+ if association
+ field_element.each_element do |associated_element|
+ model = association[1].child_model
+ (resource[:associations] ||= []) << {
+ :model => model,
+ :value => values_from_rexml(associated_element, association[1].child_model)
+ }
+ end
+ end
end
- resource.instance_eval { @new_record= false }
resource
end
- def parse_resource(xml, dm_model_class)
+ def parse_resource(xml, dm_model_class, query = nil)
doc = REXML::Document::new(xml)
# TODO: handle singular resource case as well....
entity_element = REXML::XPath.first(doc, "/#{resource_name_from_model(dm_model_class)}")
return nil unless entity_element
- resource_from_rexml(entity_element, dm_model_class)
+ values_from_rexml(entity_element, dm_model_class)
end
-
- def parse_resources(xml, dm_model_class)
+
+ def parse_resources(xml, dm_model_class, query = nil)
doc = REXML::Document::new(xml)
# # TODO: handle singular resource case as well....
# array = XPath(doc, "/*[@type='array']")
# if array
# parse_resources()
# else
resource_name = resource_name_from_model dm_model_class
doc.elements.collect("#{resource_name.pluralize}/#{resource_name}") do |entity_element|
- resource_from_rexml(entity_element, dm_model_class)
+ values_from_rexml(entity_element, dm_model_class)
end
- end
-
+ end
+
def resource_name_from_model(model)
- Inflection.underscore(model.name.downcase)
+ Inflection.underscore(model.name)
end
-
+
def resource_name_from_query(query)
resource_name_from_model(query.model)
end
end
end
-end
+end
\ No newline at end of file