Class: Rubyfocus::Document
- Inherits:
-
Object
- Object
- Rubyfocus::Document
- Includes:
- Searchable
- Defined in:
- lib/rubyfocus/document.rb
Overview
The Document is how rubyfocus stores an OmniFocus document, both locally and otherwise. A Document contains a number of arrays of contexts, settings, folders, projects, and tasks, and is also able to keep track of what patch it’s up to, for updating.
You can initialize a document through Document.new(doc), where doc
is either an XML string, or a Nokogiri XML document (or nil
). Alternatively, you can initialize through Document.from_file(file), which reads the file and parses it as XML
You add XML to the document by running Document::apply_xml(doc), which takes all children of the root XML node, tries to turn each child into a relevant object, and adds it to the document. This is done using the private ivar_for
method, as well as add_element(e), which you can use to add individual objects.
Instance Attribute Summary collapse
-
#contexts ⇒ Object
readonly
A number of arrays into which elements may fit.
-
#fetcher ⇒ Object
This is the fetcher object, used to fetch new data.
-
#folders ⇒ Object
readonly
A number of arrays into which elements may fit.
-
#patch_id ⇒ Object
This is the identifier of the current patch level.
-
#projects ⇒ Object
readonly
A number of arrays into which elements may fit.
-
#settings ⇒ Object
readonly
A number of arrays into which elements may fit.
-
#tasks ⇒ Object
readonly
A number of arrays into which elements may fit.
Class Method Summary collapse
-
.from_local ⇒ Object
Initialize from the local repo.
-
.from_url(url) ⇒ Object
Initialize with a URL, for remote fetching.
-
.from_xml(file) ⇒ Object
…or from file! If you provide it with an XML file, it’ll load up without a fetcher.
-
.load_from_file(file_location) ⇒ Object
Load from a a hash.
Instance Method Summary collapse
-
#[](search_id) ⇒ Object
——————————————————————————- Find elements from id.
-
#add_element(e, overwrite: false) ⇒ Object
Add an element.
-
#apply_xml(doc) ⇒ Object
——————————————————————————- Apply XML!.
-
#elements ⇒ Object
(also: #array)
——————————————————————————- Searchable stuff.
-
#has_id?(id) ⇒ Boolean
Check if the document has an element of a given ID.
-
#initialize(doc = nil) ⇒ Document
constructor
Initalise with one of: * a Nokogiri document * a string * a fetcher subclass.
-
#overwrite_element(node) ⇒ Object
Update an element in-place by creating a new element, deleting the old, and adding the new.
-
#remove_element(e) ⇒ Object
Remove an element from the document.
-
#save(file) ⇒ Object
————————————— YAML export.
-
#update ⇒ Object
————————————— Use the linked fetcher to update the document.
-
#update_element(node) ⇒ Object
Update an element in-place by applying xml.
Methods included from Searchable
Constructor Details
#initialize(doc = nil) ⇒ Document
Initalise with one of:
-
a Nokogiri document
-
a string
-
a fetcher subclass
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/rubyfocus/document.rb', line 28 def initialize(doc=nil) %w(contexts settings projects folders tasks).each{ |s| instance_variable_set("@#{s}", Rubyfocus::SearchableArray.new) } if doc if doc.is_a?(String) apply_xml(Nokogiri::XML(doc)) elsif doc.is_a?(Nokogiri::XML) apply_xml(doc) elsif doc.kind_of?(Rubyfocus::Fetcher) self.fetcher = doc base = Nokogiri::XML(doc.base) self.apply_xml(base) self.patch_id = doc.base_id end end end |
Instance Attribute Details
#contexts ⇒ Object (readonly)
A number of arrays into which elements may fit
15 16 17 |
# File 'lib/rubyfocus/document.rb', line 15 def contexts @contexts end |
#fetcher ⇒ Object
This is the fetcher object, used to fetch new data
22 23 24 |
# File 'lib/rubyfocus/document.rb', line 22 def fetcher @fetcher end |
#folders ⇒ Object (readonly)
A number of arrays into which elements may fit
15 16 17 |
# File 'lib/rubyfocus/document.rb', line 15 def folders @folders end |
#patch_id ⇒ Object
This is the identifier of the current patch level. This also determines which patches can be applied to the current document.
19 20 21 |
# File 'lib/rubyfocus/document.rb', line 19 def patch_id @patch_id end |
#projects ⇒ Object (readonly)
A number of arrays into which elements may fit
15 16 17 |
# File 'lib/rubyfocus/document.rb', line 15 def projects @projects end |
#settings ⇒ Object (readonly)
A number of arrays into which elements may fit
15 16 17 |
# File 'lib/rubyfocus/document.rb', line 15 def settings @settings end |
#tasks ⇒ Object (readonly)
A number of arrays into which elements may fit
15 16 17 |
# File 'lib/rubyfocus/document.rb', line 15 def tasks @tasks end |
Class Method Details
.from_local ⇒ Object
Initialize from the local repo
51 52 53 |
# File 'lib/rubyfocus/document.rb', line 51 def self.from_local new(Rubyfocus::LocalFetcher.new) end |
.from_url(url) ⇒ Object
Initialize with a URL, for remote fetching. Not implemented yet TODO implement
57 58 59 60 |
# File 'lib/rubyfocus/document.rb', line 57 def self.from_url(url) raise RuntimeError, "Rubyfocus::Document.from_url not yet implemented." # new(Rubyfocus::RemoteFetcher.new(url)) end |
.from_xml(file) ⇒ Object
…or from file! If you provide it with an XML file, it’ll load up without a fetcher.
46 47 48 |
# File 'lib/rubyfocus/document.rb', line 46 def self.from_xml(file) new(File.read(file)) end |
.load_from_file(file_location) ⇒ Object
Load from a a hash
63 64 65 66 67 |
# File 'lib/rubyfocus/document.rb', line 63 def self.load_from_file(file_location) d = YAML::load_file(file_location) d.fetcher.reset d end |
Instance Method Details
#[](search_id) ⇒ Object
Find elements from id
209 210 211 |
# File 'lib/rubyfocus/document.rb', line 209 def [] search_id self.elements.find{ |elem| elem.id == search_id } end |
#add_element(e, overwrite: false) ⇒ Object
Add an element. Element should be a Project, Task, Context, Folder, or Setting. If overwrite set to false and ID already occurs in the document, throw an error. If ID is nil, throw an error.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/rubyfocus/document.rb', line 103 def add_element(e, overwrite:false) # Error check raise(Rubyfocus::DocumentElementException, "Adding element to document, but it has no ID.") if e.id.nil? raise(Rubyfocus::DocumentElementException, "Adding element to document, but element with this ID already exists.") if !overwrite && has_id?(e.id) # Otherwise, full steam ahead e.document = self if (dupe_element = self[e.id]) && overwrite remove_element(dupe_element) end # Add to the correct array dest = ivar_for(e) if dest dest << e else raise ArgumentError, "You passed a #{e.class} to Document#add_element - I don't know what to do with this." end end |
#apply_xml(doc) ⇒ Object
Apply XML!
82 83 84 85 86 |
# File 'lib/rubyfocus/document.rb', line 82 def apply_xml(doc) doc.root.children.select{ |e| !e.text? }.each do |node| elem = Rubyfocus::Parser.parse(self, node) end end |
#elements ⇒ Object Also known as: array
Searchable stuff
199 200 201 |
# File 'lib/rubyfocus/document.rb', line 199 def elements @tasks + @projects + @contexts + @folders + @settings end |
#has_id?(id) ⇒ Boolean
Check if the document has an element of a given ID
214 215 216 |
# File 'lib/rubyfocus/document.rb', line 214 def has_id?(id) self.elements.any?{ |e| e.id == id } end |
#overwrite_element(node) ⇒ Object
Update an element in-place by creating a new element, deleting the old, and adding the new. This method is chiefly used for patching OF documents using V1 patches. Properties not explicitly mentioned in the patch are reverted to their default values. This method also takes into account:
-
new nodes (i.e. silently creates if required)
-
tasks upgraded to projects (if task has a <project> element)
-
projects downgraded to tasks (if project has no <project> element)
Note that unlike add_element, this takes pure XML
190 191 192 193 194 195 |
# File 'lib/rubyfocus/document.rb', line 190 def overwrite_element(node) element = self[node["id"]] self.remove_element(element) if element Rubyfocus::Parser.parse(self, node) end |
#remove_element(e) ⇒ Object
Remove an element from the document.
125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/rubyfocus/document.rb', line 125 def remove_element(e) e = self[e] if e.is_a?(String) return if e.nil? e.document = nil dest = ivar_for(e) if dest dest.delete(e) else raise ArgumentError, "You passed a #{e.class} to Document#remove_element - I don't know what to do with this." end end |
#save(file) ⇒ Object
YAML export
221 222 223 |
# File 'lib/rubyfocus/document.rb', line 221 def save(file) File.open(file, "w"){ |io| io.puts YAML::dump(self) } end |
#update ⇒ Object
Use the linked fetcher to update the document
71 72 73 74 75 76 77 78 |
# File 'lib/rubyfocus/document.rb', line 71 def update if fetcher raise RuntimeError, "Rubyfocus cannot currently read encrypted databases." if fetcher.encrypted? fetcher.update_full(self) else raise RuntimeError, "Tried to update a document with no fetcher." end end |
#update_element(node) ⇒ Object
Update an element in-place by applying xml. This method also takes into account:
-
new nodes (i.e. silently creates if required)
-
tasks upgraded to projects (if task has a non-empty <project> element)
-
projects downgraded to tasks (if project has an empty <project> element)
Note that unlike add_element, this takes pure XML
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/rubyfocus/document.rb', line 144 def update_element(node) element = self[node["id"]] # Does element already exist? if element # Quick check: is it a task being upgraded to a project? # Upgrade criteria: non-empty project tag if( element.class == Rubyfocus::Task && (node / "project *").size > 0 ) # Upgrade new_node = element.to_project new_node.apply_xml(node) add_element(new_node, overwrite:true) # or is the project being downgraded to a task? # Downgrade criteria: presence of an empty project tag elsif( element.class == Rubyfocus::Project && (node / "project").size > 0 && (node / "project *").size == 0 ) # Downgrade new_node = element.to_task new_node.apply_xml(node) add_element(new_node, overwrite:true) else # Update in-place element.apply_xml(node) end else # Create a new node and add it Rubyfocus::Parser.parse(self,node) end end |