From cab55272cab7ef56f23b343960485853928f3625 Mon Sep 17 00:00:00 2001 From: Brandon Robins Date: Sun, 22 Oct 2017 22:47:10 -0500 Subject: [PATCH] Add XML classes --- lib/calligraphy/xml/builder.rb | 147 +++++++++++++++++++++++++++++++ lib/calligraphy/xml/namespace.rb | 10 +++ lib/calligraphy/xml/node.rb | 23 +++++ 3 files changed, 180 insertions(+) create mode 100644 lib/calligraphy/xml/builder.rb create mode 100644 lib/calligraphy/xml/namespace.rb create mode 100644 lib/calligraphy/xml/node.rb diff --git a/lib/calligraphy/xml/builder.rb b/lib/calligraphy/xml/builder.rb new file mode 100644 index 0000000..59be841 --- /dev/null +++ b/lib/calligraphy/xml/builder.rb @@ -0,0 +1,147 @@ +module Calligraphy::XML + class Builder + SUPPORTED_NS_TAGS = %w( + creationdate displayname exclusive getcontentlanguage getcontentlength + getcontenttype getetag getlastmodified href lockdiscovery lockscope + locktype owner write + ) + + attr_reader :dav_ns, :default_ns, :server_protocol + + def initialize(dav_ns: 'D', server_protocol: 'HTTP/1.1') + @dav_ns = dav_ns + @default_ns = { "xmlns:#{@dav_ns}" => 'DAV:' } + @server_protocol = server_protocol + end + + def lock_res(activelock_properties) + build :prop do |xml| + xml.lockdiscovery do + activelock_properties.each do |properties| + activelock xml, properties + end + end + end + end + + def propfind_res(path, properties) + multistatus do |xml| + href xml, path + propstat xml, properties[:found], :ok + propstat xml, properties[:not_found], :not_found + end + end + + def proppatch_res(path, actions) + multistatus do |xml| + href xml, path + propstat xml, actions[:set] + propstat xml, actions[:remove] + end + end + + private + + def build(tag) + Nokogiri::XML::Builder.new do |xml| + xml[@dav_ns].send(tag, @default_ns) do + yield xml + end + end.to_xml + end + + def activelock(xml, property_set) + xml.activelock do + property_set.each do |property| + property_drilldown xml, property + end + end + end + + def href(xml, path) + xml.href path + end + + def multistatus + build :multistatus do |xml| + xml.response do + yield xml + end + end + end + + def prop(xml, property_set) + xml.prop do + property_set.each do |property| + property_drilldown xml, property + end + end + end + + def propstat(xml, property_set, status=:ok) + return unless property_set.length > 0 + + xml.propstat do + prop xml, property_set + status xml, status + end + end + + def resourcetype(xml, property) + if property.children.text == 'collection' + xml[@dav_ns].resourcetype do + xml.send 'collection' + end + else + xml[@dav_ns].resourcetype + end + end + + def status(xml, status) + xml.status status_message status + end + + # NOTE: `xml[@dav_ns].send timeout` results in Timeout being called, so + # we have this timeout method for convenience + def timeout(xml, property) + xml[@dav_ns].timeout do + xml.text property.text + end + end + + def property_drilldown(xml, property) + if property.is_a? Array + property.each do |prop| + property_drilldown xml, prop + end + elsif property.children && property.text.nil? + xml.send property.name do + property.children.each do |child| + property_drilldown xml, child + end + end + elsif property.name == 'resourcetype' + resourcetype xml, property + elsif property.name == 'timeout' + timeout xml, property + elsif SUPPORTED_NS_TAGS.include? property.name + xml[@dav_ns].send property.name do + xml.text property.text + end + elsif property.namespace && property.namespace.href + xml.send property.name, xmlns: property.namespace.href do + xml.text property.text + end + else + xml.send property.name, property.text do + xml.parent.namespace = nil + end + end + end + + def status_message(status) + status_code = Rack::Utils.status_code status + [@server_protocol, status_code, Rack::Utils::HTTP_STATUS_CODES[status_code]].join ' ' + end + end +end diff --git a/lib/calligraphy/xml/namespace.rb b/lib/calligraphy/xml/namespace.rb new file mode 100644 index 0000000..9bc81c2 --- /dev/null +++ b/lib/calligraphy/xml/namespace.rb @@ -0,0 +1,10 @@ +module Calligraphy::XML + class Namespace + attr_accessor :href, :prefix + + def initialize(namespace) + @href = namespace.href if namespace.href + @prefix = namespace.prefix if namespace.prefix + end + end +end diff --git a/lib/calligraphy/xml/node.rb b/lib/calligraphy/xml/node.rb new file mode 100644 index 0000000..0bdf162 --- /dev/null +++ b/lib/calligraphy/xml/node.rb @@ -0,0 +1,23 @@ +module Calligraphy::XML + class Node + attr_accessor :children, :name, :namespace, :text + + def initialize(node=nil) + unless node.nil? + @name = node.name + @text = node.text unless node.text.empty? + + if node.namespace + @namespace = Calligraphy::XML::Namespace.new node.namespace + end + + if node.children&.length > 0 + @children = [] + node.children.each do |child| + @children.push Calligraphy::XML::Node.new child + end + end + end + end + end +end