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

create[RW]

Operations to be performed on a document

delete[RW]

Operations to be performed on a document

fetcher[RW]

The fetcher this patch belongs to. We mainly use this to work out how to fetch content for the patch proper

file[RW]

The file the patch loads from

from_ids[RW]

These record the transformation in terms of patch ID values.

time[RW]

The time the file was submitted

to_id[RW]

These record the transformation in terms of patch ID values.

update[RW]

Operations to be performed on a document

version[RW]

What version of patch file is this? Determined from XML file

Public Class Methods

from_string(fetcher, str) click to toggle source

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
new(fetcher=nil, file=nil) click to toggle source

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

<=>(o) click to toggle source
# 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_to(document) click to toggle source

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_to!(document) click to toggle source

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_patch?(document) click to toggle source

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
load_data(str=nil) click to toggle source

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
to_s() click to toggle source

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