From 6324f17f907ceb7f4b49c403ce770dd334d008dd Mon Sep 17 00:00:00 2001 From: Brandon Robins Date: Thu, 25 Jan 2018 22:29:15 -0600 Subject: [PATCH] Use Nokogiri::XML throughout FileResource Removes Calligraphy::XML::Node and Calligraphy::XML::Namespace in favor of using Nokogiri's XML classes. --- lib/calligraphy.rb | 2 - lib/calligraphy/resource/file_resource.rb | 78 +++++++++++++++-------- lib/calligraphy/utils.rb | 2 + lib/calligraphy/web_dav_request/lock.rb | 3 +- lib/calligraphy/xml/namespace.rb | 16 ----- lib/calligraphy/xml/node.rb | 36 ----------- lib/calligraphy/xml/utils.rb | 31 ++++++++- 7 files changed, 83 insertions(+), 85 deletions(-) delete mode 100644 lib/calligraphy/xml/namespace.rb delete mode 100644 lib/calligraphy/xml/node.rb diff --git a/lib/calligraphy.rb b/lib/calligraphy.rb index 0b7e8e0..0851ccb 100644 --- a/lib/calligraphy.rb +++ b/lib/calligraphy.rb @@ -7,8 +7,6 @@ require 'calligraphy/rails/web_dav_requests_controller' require 'calligraphy/xml/web_dav_elements' require 'calligraphy/xml/builder' -require 'calligraphy/xml/namespace' -require 'calligraphy/xml/node' require 'calligraphy/xml/utils' require 'calligraphy/utils' diff --git a/lib/calligraphy/resource/file_resource.rb b/lib/calligraphy/resource/file_resource.rb index 74371d8..1990afe 100644 --- a/lib/calligraphy/resource/file_resource.rb +++ b/lib/calligraphy/resource/file_resource.rb @@ -12,6 +12,7 @@ module Calligraphy ].freeze include Calligraphy::Utils + include Calligraphy::XML::Utils #:nodoc: def initialize(resource: nil, req: nil, mount: nil, root_dir: Dir.pwd) @@ -129,6 +130,7 @@ module Calligraphy nodes.each do |node| next unless node.is_a? Nokogiri::XML::Element + properties[node.name.to_sym] = node end @@ -182,6 +184,7 @@ module Calligraphy end end + properties[:found] = properties[:found].uniq.flatten if properties[:found] properties end @@ -378,39 +381,46 @@ module Calligraphy end def create_lock_token - token = Calligraphy::XML::Node.new - token.name = 'locktoken' + href = xml_node 'href' + href.content = ['urn', 'uuid', SecureRandom.uuid].join ':' - href = Calligraphy::XML::Node.new - href.name = 'href' - href.text = ['urn', 'uuid', SecureRandom.uuid].join ':' - - token.children = [href] - token + token = xml_node 'locktoken' + token.add_child href + token.serialize end def timeout_node - Calligraphy::XML::Node.new.tap do |node| - node.name = 'timeout' - node.text = ['Second', Calligraphy.lock_timeout_period].join '-' - end + node = xml_node 'timeout' + node.content = ['Second', Calligraphy.lock_timeout_period].join '-' + node.serialize end def add_lock_properties(activelock, properties) properties.each_key do |prop| - activelock[prop] = Calligraphy::XML::Node.new properties[prop] + activelock[prop] = properties[prop].serialize end end def fetch_lock_info return nil if @store.nil? - @lock_info = @store.transaction(true) { @store[:lockdiscovery] } + @lock_info = @store.transaction(true) do + @store[:lockdiscovery]&.map do |lock_info| + lock_info.transform_values do |xml_fragment| + parse_serialized_fragment xml_fragment + end + end + end + @lock_info.nil? ? nil : map_array_of_hashes(@lock_info) end def lockscope - @lock_info[-1][:lockscope].children[0].name + @lock_info[-1][:lockscope] + .children + .select { |x| x.is_a? Nokogiri::XML::Element } + .last + .name end def can_unlock?(headers = nil) @@ -423,7 +433,8 @@ module Calligraphy def lock_tokens fetch_lock_info - @lock_info&.each { |x| x }&.map { |k| k[:locktoken].children[0].text } + + @lock_info&.each { |x| x }&.map { |x| x[:locktoken].text.strip } end def locking_ancestor?(ancestor_path, ancestors, headers = nil) @@ -508,13 +519,11 @@ module Calligraphy def get_property(prop) case prop.name - when 'lockdiscovery' - fetch_lock_info when *DAV_PROPERTY_METHODS prop.content = send prop.name prop else - get_custom_property prop.name + get_custom_property prop.name, deserialize: true end end @@ -562,8 +571,15 @@ module Calligraphy JSON.generate [exclusive_write, shared_write] end - def get_custom_property(prop) - @store_properties ||= @store.transaction(true) { @store[:properties] } + def get_custom_property(prop, deserialize: false) + @store_properties ||= @store.transaction(true) do + if deserialize + deserialize_stored_properties @store[:properties] + else + @store[:properties] + end + end + @store_properties[prop.to_sym] unless @store_properties.nil? || prop.nil? end @@ -594,10 +610,9 @@ module Calligraphy prop.children.each do |property| next unless node.is_a? Nokogiri::XML::Element - node = Calligraphy::XML::Node.new property prop_sym = property.name.to_sym - store_property_node node, prop_sym + store_property_node property.serialize, prop_sym actions[:set].push property end @@ -624,15 +639,18 @@ module Calligraphy def store_mismatch_namespace_property_node(node, prop) node_arr = @store[:properties][prop] - namespace_mismatch = node_arr.select do |x| - x.namespace.href == node.namespace.href + namespace_mismatch = node_arr.select do |stored_node| + same_namespace? stored_node, node end.length.positive? @store[:properties][prop].push node unless namespace_mismatch end def same_namespace?(node1, node2) - node1.namespace&.href == node2.namespace&.href + node1_xml = parse_serialized_fragment node1 + node2_xml = parse_serialized_fragment node2 + + node1_xml.namespace&.href == node2_xml.namespace&.href end def store_mismatch_namespace_property_nodes(node, prop) @@ -669,7 +687,11 @@ module Calligraphy ancestor_store.transaction do ancestor_store[:lockdiscovery][-1][:timeout] = timeout_node - ancestor_store[:lockdiscovery] + ancestor_store[:lockdiscovery]&.map do |lock_info| + lock_info.transform_values do |xml_fragment| + parse_serialized_fragment xml_fragment + end + end end end @@ -681,7 +703,7 @@ module Calligraphy @store.delete :lockdiscovery else @store[:lockdiscovery] = @store[:lockdiscovery].reject do |activelock| - activelock[:locktoken].children[0].text == token + activelock[:locktoken].include? token end end end diff --git a/lib/calligraphy/utils.rb b/lib/calligraphy/utils.rb index 4e3b1f1..53da729 100644 --- a/lib/calligraphy/utils.rb +++ b/lib/calligraphy/utils.rb @@ -34,6 +34,8 @@ module Calligraphy # Given an array of hashes, returns an array of hash values. def map_array_of_hashes(arr_hashes) + return if arr_hashes.nil? + [].tap do |output_array| arr_hashes.each do |hash| output_array.push hash.values diff --git a/lib/calligraphy/web_dav_request/lock.rb b/lib/calligraphy/web_dav_request/lock.rb index 087be88..4c1a942 100644 --- a/lib/calligraphy/web_dav_request/lock.rb +++ b/lib/calligraphy/web_dav_request/lock.rb @@ -71,8 +71,9 @@ module Calligraphy def extract_lock_token(properties) properties[-1] .select { |x| x.name == 'locktoken' }[0] - .children[0] + .children .text + .strip end def prepare_response_headers(lock_token) diff --git a/lib/calligraphy/xml/namespace.rb b/lib/calligraphy/xml/namespace.rb deleted file mode 100644 index 0a62ad4..0000000 --- a/lib/calligraphy/xml/namespace.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module Calligraphy - module XML - # Simple XML namespace, used to store a namespace's href and prefix values. - class Namespace - attr_accessor :href, :prefix - - #:nodoc: - def initialize(namespace) - @href = namespace.href if namespace.href - @prefix = namespace.prefix if namespace.prefix - end - end - end -end diff --git a/lib/calligraphy/xml/node.rb b/lib/calligraphy/xml/node.rb deleted file mode 100644 index 099cb78..0000000 --- a/lib/calligraphy/xml/node.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module Calligraphy - module XML - # Simple XML node, used to store resource properties in Resource methods - # and later to create XML response bodies. - class Node - attr_accessor :children, :name, :namespace, :text - - #:nodoc: - def initialize(node = nil) - return if node.nil? - - @name = node.name - @text = node.text unless node.text.empty? - - if node.namespace - @namespace = Calligraphy::XML::Namespace.new node.namespace - end - - return unless node_has_children node - - @children = [] - node.children.each { |x| @children.push Calligraphy::XML::Node.new x } - end - - private - - def node_has_children(node) - return false if node.children.nil? - - node.children.length.positive? - end - end - end -end diff --git a/lib/calligraphy/xml/utils.rb b/lib/calligraphy/xml/utils.rb index 9f60cb2..e0ca625 100644 --- a/lib/calligraphy/xml/utils.rb +++ b/lib/calligraphy/xml/utils.rb @@ -26,6 +26,24 @@ module Calligraphy end.flatten end + # Parses a serialized string or array fragment to XML. + def parse_serialized_fragment(fragment) + xml_str = fragment.is_a?(Array) ? fragment.join : fragment + + xml = Nokogiri::XML.fragment(xml_str).children + fragment.is_a?(Array) ? xml : xml[-1] + end + + # Iterates through each property in `properties` hash and deserializes + # the property's value. + def deserialize_stored_properties(properties) + return if properties.nil? + + properties.each_pair do |k, v| + properties[k] = parse_serialized_fragment v + end + end + # Iterates through top level nodes, finds node names that match and # separates matching nodes from non-matching nodes. def separate_nodes_by_name(nodes, match_name) @@ -34,14 +52,19 @@ module Calligraphy next unless node.is_a? Nokogiri::XML::Element if node.name == match_name - property[:found].push Calligraphy::XML::Node.new node + property[:found].push node else - property[:not_found].push Calligraphy::XML::Node.new node + property[:not_found].push node end end end end + # Creates a new instance of Nokogiri::XML::Node with a given name. + def xml_node(name) + Nokogiri::XML::Node.new name, dummy_doc + end + private def dav_namespace(xml) @@ -49,6 +72,10 @@ module Calligraphy return v if v == Calligraphy::DAV_NS end end + + def dummy_doc + Nokogiri::XML::Document.new + end end end end