module Restfulie::Common::Converter end module Restfulie::Common::Converter::Xml end class Hash def links(*args) links = fetch("link", []) Restfulie::Common::Converter::Xml::Links.new(links) end def method_missing(sym, *args) self[sym.to_s].nil? ? super(sym, args) : self[sym.to_s] end def respond_to?(sym) include?(sym.to_s) || super(sym) end end class Restfulie::Common::Converter::Xml::Links def initialize(links) links = [links] unless links.kind_of? Array links = [] unless links @links = links.map do |l| link = Restfulie::Common::Converter::Xml::Link.new(l) link.instance_eval { self.class.send :include, ::Restfulie::Client::HTTP::LinkRequestBuilder } link end end def method_missing(sym, *args) @links.find do |link| link.rel == sym.to_s end end end class Restfulie::Common::Converter::Xml::Link def initialize(options = {}) @options = options end def href @options["href"] end def rel @options["rel"] end def content_type @options["type"] end def type content_type end end # Implements the interface for marshal Xml media type requests (application/xml) module Restfulie::Common::Converter::Xml mattr_reader :media_type_name @@media_type_name = 'application/xml' mattr_reader :headers @@headers = { :post => { 'Content-Type' => media_type_name } } def self.marshal(entity, options = {}) to_xml(entity, options) end def self.unmarshal(string) Hash.from_xml string end mattr_reader :recipes @@recipes = {} def self.describe_recipe(recipe_name, options={}, &block) raise 'Undefined recipe' unless block_given? raise 'Undefined recipe_name' unless recipe_name @@recipes[recipe_name] = block end def self.to_xml(obj, options = {}, &block) return obj if obj.kind_of?(String) if block_given? recipe = block elsif options[:recipe] recipe = @@recipes[options[:recipe]] else return obj.values.first.to_xml(:root => obj.keys.first) if obj.kind_of?(Hash) && obj.size==1 return obj.to_xml end # Create representation and proxy builder = Builder.new(obj) # Check recipe arity size before calling it recipe.call(*[builder, obj, options][0,recipe.arity]) builder.doc.to_xml end def self.helper Restfulie::Common::Converter::Xml::Helpers end end class Restfulie::Common::Converter::Xml::Builder attr_reader :doc def initialize(obj, options = {}) @doc = Nokogiri::XML::Document.new @obj = obj if options[:root] root = options[:root] else root = root_element_for(obj) end @parent = @doc.create_element(root) @parent.parent = @doc end def values(options = nil, &block) options.each do |key,value| attr = key.to_s if attr =~ /^xmlns(:\w+)?$/ ns = attr.split(":", 2)[1] @parent.add_namespace_definition(ns, value) end end if options yield Restfulie::Common::Converter::Components::Values.new(self) end def insert_value(name, prefix, *args, &block) node = create_element(name.to_s, prefix, *args) node.parent = @parent if block_given? @parent = node block.call @parent = node.parent end end def link(relationship, uri, options = {}) options["rel"] = relationship.to_s options["href"] = uri options["type"] ||= "application/xml" insert_value("link", nil, options) end def members(a_collection = nil, &block) collection = a_collection || @obj raise Restfulie::Common::Error::BuilderError("Members method require a collection to execute") unless collection.respond_to?(:each) collection.each do |member| entry = @doc.create_element(root_element_for(member)) entry.parent = @parent @parent = entry block.call(self, member) @parent = entry.parent end end private def root_element_for(obj) if obj.kind_of?(Hash) && obj.size==1 obj.keys.first.to_s elsif obj.kind_of?(Array) && !obj.empty? root_element_for(obj.first).underscore.pluralize else obj.class.to_s.underscore end end def create_element(node, prefix, *args) node = @doc.create_element(node) do |n| if prefix if namespace = prefix_valid?(prefix) # Adding namespace prefix n.namespace = namespace namespace = nil end end args.each do |arg| case arg # Adding XML attributes when Hash arg.each { |k,v| key = k.to_s if key =~ /^xmlns(:\w+)?$/ ns_name = key.split(":", 2)[1] n.add_namespace_definition(ns_name, v) next end n[k.to_s] = v.to_s } # Adding XML node content else arg.kind_of?(Time) || arg.kind_of?(DateTime) ? content = arg.xmlschema : content = arg n.content = content end end end end def prefix_valid?(prefix) ns = @parent.namespace_definitions.find { |x| x.prefix == prefix.to_s } unless ns @parent.ancestors.each do |a| next if a == @doc ns = a.namespace_definitions.find { |x| x.prefix == prefix.to_s } break if ns end end return ns end end module Restfulie::Common::Converter::Xml::Helpers def collection(obj, *args, &block) Restfulie::Common::Converter::Xml.to_xml(obj, {}, &block) end def member(obj, *args, &block) Restfulie::Common::Converter::Xml.to_xml(obj, {}, &block) end end