lib/rest_adapter.rb in dm-rest-adapter-0.9.10 vs lib/rest_adapter.rb in dm-rest-adapter-0.9.11
- old
+ new
@@ -1,251 +1,15 @@
-require 'net/http'
-require 'pathname'
-require 'rexml/document'
-require 'rubygems'
+$:.push File.expand_path(File.dirname(__FILE__))
-gem 'dm-core', '~>0.9.10'
require 'dm-core'
-
-gem 'extlib', '~>0.9.10'
require 'extlib'
-
-#gem 'dm-serializer', '~>0.9.10'
+require 'pathname'
+require 'rexml/document'
+require 'rubygems'
require 'dm-serializer'
+require 'rest_adapter/version'
+require 'rest_adapter/adapter'
+require 'rest_adapter/connection'
+require 'rest_adapter/formats'
+require 'rest_adapter/exceptions'
-require Pathname(__FILE__).dirname + 'rest_adapter/version'
-
-# TODO: Abstract XML support out from the protocol
-# TODO: Build JSON support
-module DataMapper
- module Adapters
- class RestAdapter < AbstractAdapter
- include Extlib
-
- # Creates a new resource in the specified repository.
- def create(resources)
- count = 0
- resources.each do |resource|
- 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 = result.instance_of?(Net::HTTPCreated)
- if success
- 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
- count
- end
-
- # read_set
- #
- # Examples of query string:
- # A. []
- # GET /books/
- #
- # B. [[:eql, #<Property:Book:id>, 4200]]
- # 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)
- 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)
- resource = nil
- resource_name = resource_name_from_query(query)
- 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 What if we have a compound key?
- raise NotImplementedError.new unless is_single_resource_query? query
- id = query.conditions.first[2]
- 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
- 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)
- raise NotImplementedError.new unless is_single_resource_query? query
- id = query.conditions.first[2]
- res = http_delete("/#{resource_name_from_query(query).pluralize}/#{id}.xml")
- res.kind_of?(Net::HTTPSuccess) ? 1 : 0
- end
-
- 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, 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
-
- # 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 do |http|
- request = Net::HTTP::Put.new(uri, data)
- request.basic_auth(@uri[:login], @uri[:password]) unless @uri[:login].blank?
- http.request(request)
- end
- end
-
- def http_post(uri, data)
- request do |http|
- request = Net::HTTP::Post.new(uri, data, 'Content-Type' => 'application/xml')
- request.basic_auth(@uri[:login], @uri[:password]) unless @uri[:login].blank?
- http.request(request)
- end
- end
-
- def http_get(uri)
- request do |http|
- request = Net::HTTP::Get.new(uri)
- request.basic_auth(@uri[:login], @uri[:password]) unless @uri[:login].blank?
- http.request(request)
- end
- end
-
- def http_delete(uri)
- request do |http|
- request = Net::HTTP::Delete.new(uri)
- request.basic_auth(@uri[:login], @uri[:password]) unless @uri[:login].blank?
- http.request(request)
- end
- end
-
- def request(&block)
- res = nil
- Net::HTTP.start(@uri[:host], @uri[:port].to_i) do |http|
- res = yield(http)
- end
- res
- end
-
- def values_from_rexml(entity_element, dm_model_class)
- resource = {}
- resource[:values] = []
- entity_element.elements.each do |field_element|
- attribute = dm_model_class.properties(repository.name).find do |property|
- property.name.to_s == field_element.name.to_s.tr('-', '_')
- end
- 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
- end
-
- 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
- values_from_rexml(entity_element, dm_model_class)
- end
-
- 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|
- values_from_rexml(entity_element, dm_model_class)
- end
- end
-
- def resource_name_from_model(model)
- Inflection.underscore(model.name)
- end
-
- def resource_name_from_query(query)
- resource_name_from_model(query.model)
- end
- end
- end
-end
+DataMapper::Adapters::RestAdapter = DataMapperRest::Adapter