class Rubyfocus::Patch
The patch class represents a text-file patch, storing update, delete, and creation operations. It should also be able to apply itself to an existing document.
Attributes
Operations to be performed on a document
Operations to be performed on a document
The fetcher this patch belongs to. We mainly use this to work out how to fetch content for the patch proper
The file the patch loads from
These record the transformation in terms of patch ID values.
The time the file was submitted
These record the transformation in terms of patch ID values.
Operations to be performed on a document
What version of patch file is this? Determined from XML file
Public Class Methods
Load from a string.
# File lib/rubyfocus/patch.rb, line 51 def self.from_string(fetcher, str) n = new(fetcher) n.load_data(str) n end
By default we initialize patches from a file. To initialize from a string, use the .from_string method. This class will lazily load data from the file proper
# File lib/rubyfocus/patch.rb, line 25 def initialize(fetcher=nil, file=nil) @fetcher = fetcher @file = file @update = [] @create = [] @delete = [] if file if File.basename(file) =~ /^(\d+)=(.*)\./ time_string = $1 self.time = if (time_string == "00000000000000") Time.at(0) else Time.parse(time_string) end ids = $2.split("+") self.to_id = ids.pop self.from_ids = ids else raise ArgumentError, "Constructed patch from a malformed patch file: #{file}." end end end
Public Instance Methods
# File lib/rubyfocus/patch.rb, line 153 def <=> o if self.time.nil? if o.time.nil? 0 else -1 end else if o.time.nil? 1 else self.time <=> o.time end end end
Apply this patch to a document. Check to make sure ids match
# File lib/rubyfocus/patch.rb, line 111 def apply_to(document) if can_patch?(document) apply_to!(document) else raise RuntimeError, "Patch ID mismatch (patch from_ids: [#{self.from_ids.join(", ")}], document.patch_id: #{document.patch_id}" end end
Apply this patch to a document.
# File lib/rubyfocus/patch.rb, line 120 def apply_to!(document) load_data # Updates depend on version! if version == 1 #V1 updates overwrite elements self.update.each{ |node| document.overwrite_element(node) } elsif version == 2 #V2 updates actually update elements self.update.each{ |node| document.update_element(node) } else raise RuntimeError, "Cannot run updates using Version #{version.inspect} OF patches!" end # Deletes remove elements self.delete.each{ |node| document.remove_element(node["id"]) } # Creates make new elements self.create.each{ |node| document.update_element(node) } # Modify current patch_id to show new value document.patch_id = self.to_id end
Can we apply this patch to a given document?
# File lib/rubyfocus/patch.rb, line 106 def can_patch?(document) self.from_ids.include?(document.patch_id) end
Loads data from the file. Optional argument str
if you want to supply your own data, otherwise will load file data
# File lib/rubyfocus/patch.rb, line 59 def load_data(str=nil) return if @data_loaded @data_loaded = true str ||= fetcher.patch(self.file) doc = Nokogiri::XML(str) # Root should be an <omnifocus> and have an XMLNS # XMLNS should be one of: # * http://www.omnigroup.com/namespace/OmniFocus/v1 # * http://www.omnigroup.com/namespace/OmniFocus/v2 omnifocus = doc.root if omnifocus.name == "omnifocus" xmlns = omnifocus.namespace && omnifocus.namespace.href case xmlns when "http://www.omnigroup.com/namespace/OmniFocus/v1" self.version = 1 when "http://www.omnigroup.com/namespace/OmniFocus/v2" self.version = 2 else raise ArgumentError, "Unrecognised namespace #{xmlns.inspect} for Omnifocus patch file." end else raise ArgumentError, "Root element should be <omnifocus>, instead was <#{omnifocus.name}>." end doc.root.children.select{ |n| !n.text?}.each do |child| case child["op"] when "update" @update << child when "delete" @delete << child when "reference" # Ignore! when nil @create << child else raise RuntimeError, "Rubyfocus::Patch encountered unknown operation type #{child["op"]}." end end end
String representation
# File lib/rubyfocus/patch.rb, line 145 def to_s if from_ids.size == 1 "(#{from_ids.first} -> #{to_id})" else "([#{from_ids.join(", ")}] -> #{to_id})" end end