rubyfocus 0.5.9 → 0.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +125 -0
- data/README.md +2 -2
- data/lib/rubyfocus/document.rb +31 -4
- data/lib/rubyfocus/fetchers/fetcher.rb +5 -0
- data/lib/rubyfocus/fetchers/local_fetcher.rb +5 -0
- data/lib/rubyfocus/fetchers/oss_fetcher.rb +14 -5
- data/lib/rubyfocus/items/project.rb +7 -4
- data/lib/rubyfocus/patch.rb +35 -2
- data/version.txt +1 -1
- metadata +3 -4
- data/HISTORY.md +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a081be1af0e05194f95e7de7e5e4452a53605971
|
4
|
+
data.tar.gz: 84f7160ffec7195233677897531a7c128eed5bb6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa6f406623b728c990eb1d79877a1485437383a427563fdf16ad657e325bed552fbcecec7eeb0b59388a4e026809470dc5831a4b4de91dc0df10ec019d28b0b6
|
7
|
+
data.tar.gz: c5ca0640fc7d3643d8b1c6946dd533c9e4f1ea2f99cb6b054ce7d5f2d8ce933bb23dd672ec21b713df1bd2e3457a283ee2b141f30cbe09b8f3e006840b1e333f
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# Change log
|
2
|
+
|
3
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
4
|
+
|
5
|
+
## Unreleased
|
6
|
+
|
7
|
+
### Added
|
8
|
+
* `Fetcher#encrypted?` will tell you if a fetcher is looking at an encrypted folder or not.
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
|
12
|
+
* `Document#update` will error if you try to update based on an encrypted fetcher.
|
13
|
+
* HISTORY.md is now CHANGELOG.md
|
14
|
+
* Reformatted Changelog to match KaC format
|
15
|
+
|
16
|
+
## [0.5.11] - 2016-09-20
|
17
|
+
|
18
|
+
### Added
|
19
|
+
* `Document#overwrite_element` now allows you to perform V1-style replacements to the database, if you really want to.
|
20
|
+
* Should now be able to deal with either V1 or V2 patch files.
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
* Major changelog reformat!
|
24
|
+
|
25
|
+
## [0.5.10] - 2016-09-07
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
* Projects will no longer be demoted to tasks if a project patch doesn't contain a "<project>" tag.
|
29
|
+
|
30
|
+
## [0.5.9] - 2016-09-04
|
31
|
+
|
32
|
+
### Fixed
|
33
|
+
* Blank entries in updates are now treated as "no change", rather than "new value is nil".
|
34
|
+
|
35
|
+
## [0.5.8] - 2016-09-02
|
36
|
+
|
37
|
+
### Fixed
|
38
|
+
* Projects inside folders were being reported as having no container, due to empty `<task/`> tags. Now fixed.
|
39
|
+
|
40
|
+
## [0.5.7] - 2016-09-02
|
41
|
+
|
42
|
+
### Fixed
|
43
|
+
* Empty `<project/>` tags in a task description will no longer cause Rubyfocus to treat said task as a project.
|
44
|
+
|
45
|
+
## [0.5.6] - 2016-09-01
|
46
|
+
|
47
|
+
### Added
|
48
|
+
* Added `Time.safely_parse`, which will not choke on empty strings or nil values
|
49
|
+
|
50
|
+
### Fixed
|
51
|
+
* `conditional_set` will no longer choke on empty string for times
|
52
|
+
|
53
|
+
## [0.5.5] - 2016-05-18
|
54
|
+
|
55
|
+
### Fixed
|
56
|
+
|
57
|
+
* If there are no patches, `Fetcher#head`s will return the ID of the base file.
|
58
|
+
* `Fetcher#head` will now return the ID of the most recent patch, not the patch itself.
|
59
|
+
* * `RankedItem#contained_within?` will now check against all objects matching a block or hash, rather than just the first one that the document can find.
|
60
|
+
|
61
|
+
### Added
|
62
|
+
* `Searchable` objects will now respond to `find_all`, which is an alias of `select`.
|
63
|
+
|
64
|
+
## [0.5.4] - 2016-02-08
|
65
|
+
|
66
|
+
### Fixed
|
67
|
+
* `LocalFetcher` will now try the default App Store location if it can't find anything at the normal location.
|
68
|
+
|
69
|
+
## [0.5.3] - 2016-02-04
|
70
|
+
|
71
|
+
### Fixed
|
72
|
+
* Re-did the code determining whether a task was blocked. Now tasks are considered blocked if their immediate container is blocked.
|
73
|
+
* Caching some task filters on the `Task` class was causing issues. Removed caching, which shouldn't hit performance much.
|
74
|
+
|
75
|
+
## [0.5.2] - 2016-02-04
|
76
|
+
|
77
|
+
### Added
|
78
|
+
* Whoops! Did I leave HTTParty out of the gem install list? My bad!
|
79
|
+
|
80
|
+
## [0.5.1] - 2016-02-03
|
81
|
+
|
82
|
+
### Fixed
|
83
|
+
* `Task#next_available_task` should no longer cause errors when a project has no tasks.
|
84
|
+
|
85
|
+
## [0.5.0] - 2016-01-20
|
86
|
+
|
87
|
+
### Added
|
88
|
+
* `Fetcher#head` returns the most recent patch.
|
89
|
+
* `Fetcher#can_reach_head_from?(id)` will inform you if you can get to the head from a given ID.
|
90
|
+
|
91
|
+
## [0.4.0] - 2016-01-01
|
92
|
+
|
93
|
+
* Happy new year!
|
94
|
+
|
95
|
+
### Added
|
96
|
+
* RankedItems can look at their ancestry much more easily, using RankedItem#ancestry and RankedItem#contained_within?
|
97
|
+
* Documents now forbid elements with duplicate IDs unless Document#allow_duplicate_ids is set to true.
|
98
|
+
* Patchers now treate CREATE nodes on elements whose IDs already exist in the database as UPDATE nodes
|
99
|
+
|
100
|
+
### Changed
|
101
|
+
* Container IDRef is now located on RankedItem, rather than having several on each RankedItem subclass.
|
102
|
+
|
103
|
+
### Fixed
|
104
|
+
* Patchers will now interpret missing parameters as "default values" e.g. project update without `status` parameter assumed to be active.
|
105
|
+
|
106
|
+
## [0.3.1] - 2015-12-31
|
107
|
+
|
108
|
+
### Changed
|
109
|
+
* IDRefs will now return `nil` if the relevant ID is not set.
|
110
|
+
|
111
|
+
## [0.3.0] - 2015-10-17
|
112
|
+
|
113
|
+
### Added
|
114
|
+
* Now supports remote syncing with the Omni Sync Server!
|
115
|
+
|
116
|
+
## [0.2.0] - 2015-10-11
|
117
|
+
|
118
|
+
### Changed
|
119
|
+
* Will now turn tasks into projects and projects into tasks if the user has done this in OmniFocus.
|
120
|
+
* Rubyfocus::Patch now does patch application, rather than delegating to the Fetcher.
|
121
|
+
|
122
|
+
## [0.1.0] - 2015-10-10
|
123
|
+
|
124
|
+
### Added
|
125
|
+
* Hello, world!
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Version: 0.5.
|
1
|
+
# Version: 0.5.11
|
2
2
|
|
3
3
|
Rubyfocus is a one-way (read-only) ruby bridge to OmniFocus. Analyse, store, inspect, or play with your projects and tasks in OmniFocus from the comfort and flexibility of ruby!
|
4
4
|
|
@@ -22,7 +22,7 @@ Now build and install it!
|
|
22
22
|
|
23
23
|
```
|
24
24
|
gem build rubyfocus.gemspec
|
25
|
-
gem install rubyfocus-0.5.
|
25
|
+
gem install rubyfocus-0.5.11.gem
|
26
26
|
```
|
27
27
|
|
28
28
|
# Usage
|
data/lib/rubyfocus/document.rb
CHANGED
@@ -70,6 +70,7 @@ class Rubyfocus::Document
|
|
70
70
|
# Use the linked fetcher to update the document
|
71
71
|
def update
|
72
72
|
if fetcher
|
73
|
+
raise RuntimeError, "Rubyfocus cannot currently read encrypted databases." if fetcher.encrypted?
|
73
74
|
fetcher.update_full(self)
|
74
75
|
else
|
75
76
|
raise RuntimeError, "Tried to update a document with no fetcher."
|
@@ -137,8 +138,8 @@ class Rubyfocus::Document
|
|
137
138
|
|
138
139
|
# Update an element in-place by applying xml. This method also takes into account:
|
139
140
|
# * new nodes (i.e. silently creates if required)
|
140
|
-
# * tasks upgraded to projects
|
141
|
-
# * projects downgraded to tasks
|
141
|
+
# * tasks upgraded to projects (if task has a non-empty <project> element)
|
142
|
+
# * projects downgraded to tasks (if project has an empty <project> element)
|
142
143
|
# Note that unlike add_element, this takes pure XML
|
143
144
|
def update_element(node)
|
144
145
|
element = self[node["id"]]
|
@@ -146,13 +147,24 @@ class Rubyfocus::Document
|
|
146
147
|
# Does element already exist?
|
147
148
|
if element
|
148
149
|
# Quick check: is it a task being upgraded to a project?
|
149
|
-
|
150
|
+
# Upgrade criteria: non-empty project tag
|
151
|
+
if(
|
152
|
+
element.class == Rubyfocus::Task &&
|
153
|
+
(node / "project *").size > 0
|
154
|
+
)
|
155
|
+
|
150
156
|
# Upgrade
|
151
157
|
new_node = element.to_project
|
152
158
|
new_node.apply_xml(node)
|
153
159
|
add_element(new_node, overwrite:true)
|
154
160
|
# or is the project being downgraded to a task?
|
155
|
-
|
161
|
+
# Downgrade criteria: presence of an empty project tag
|
162
|
+
elsif(
|
163
|
+
element.class == Rubyfocus::Project &&
|
164
|
+
(node / "project").size > 0 &&
|
165
|
+
(node / "project *").size == 0
|
166
|
+
)
|
167
|
+
|
156
168
|
# Downgrade
|
157
169
|
new_node = element.to_task
|
158
170
|
new_node.apply_xml(node)
|
@@ -167,6 +179,21 @@ class Rubyfocus::Document
|
|
167
179
|
end
|
168
180
|
end
|
169
181
|
|
182
|
+
# Update an element in-place by creating a new element, deleting the old, and adding the new.
|
183
|
+
# This method is chiefly used for patching OF documents using V1 patches. Properties not explicitly
|
184
|
+
# mentioned in the patch are reverted to their default values.
|
185
|
+
# This method also takes into account:
|
186
|
+
# * new nodes (i.e. silently creates if required)
|
187
|
+
# * tasks upgraded to projects (if task has a <project> element)
|
188
|
+
# * projects downgraded to tasks (if project has no <project> element)
|
189
|
+
# Note that unlike add_element, this takes pure XML
|
190
|
+
def overwrite_element(node)
|
191
|
+
element = self[node["id"]]
|
192
|
+
self.remove_element(element) if element
|
193
|
+
|
194
|
+
Rubyfocus::Parser.parse(self, node)
|
195
|
+
end
|
196
|
+
|
170
197
|
#-------------------------------------------------------------------------------
|
171
198
|
# Searchable stuff
|
172
199
|
def elements
|
@@ -56,6 +56,11 @@ class Rubyfocus::Fetcher
|
|
56
56
|
return false
|
57
57
|
end
|
58
58
|
|
59
|
+
# Is this fetcher encrypted?
|
60
|
+
def encrypted?
|
61
|
+
raise RuntimeError, "Method Fetcher#encrypted? called for abstract class Fetcher."
|
62
|
+
end
|
63
|
+
|
59
64
|
#---------------------------------------
|
60
65
|
# Patching methods
|
61
66
|
|
@@ -52,6 +52,11 @@ class Rubyfocus::LocalFetcher < Rubyfocus::Fetcher
|
|
52
52
|
coder.map = {"location" => @location}
|
53
53
|
end
|
54
54
|
|
55
|
+
# Is this fetcher fetching encrypted data?
|
56
|
+
def encrypted?
|
57
|
+
File.exists?(File.join(self.location, "encrypted"))
|
58
|
+
end
|
59
|
+
|
55
60
|
#---------------------------------------
|
56
61
|
# Location file setters and getters
|
57
62
|
|
@@ -49,26 +49,35 @@ class Rubyfocus::OSSFetcher < Rubyfocus::Fetcher
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
# Fetches a list of
|
53
|
-
def
|
54
|
-
@
|
52
|
+
# Fetches a list of all files contained within the database
|
53
|
+
def files
|
54
|
+
@files ||= begin
|
55
55
|
response = self.fetcher.get(url, digest_auth: auth).body
|
56
56
|
# Text is in first table, let's assume
|
57
57
|
table = response[/<table>(.*?)<\/table>/m,1]
|
58
58
|
if table
|
59
|
-
|
60
|
-
links.map{ |u| Rubyfocus::Patch.new(self,u) }
|
59
|
+
table.scan(/<a href="([^"]+)"/).flatten
|
61
60
|
else
|
62
61
|
[]
|
63
62
|
end
|
64
63
|
end
|
65
64
|
end
|
66
65
|
|
66
|
+
# Fetches a list of every patch file
|
67
|
+
def patches
|
68
|
+
@patches ||= files.select{ |f| f.end_with?(".zip") }.map{ |u| Rubyfocus::Patch.new(self,u) }
|
69
|
+
end
|
70
|
+
|
67
71
|
# Fetches the contents of a given patch file
|
68
72
|
def patch(file)
|
69
73
|
fetch_file(file)
|
70
74
|
end
|
71
75
|
|
76
|
+
# Is this encrypted?
|
77
|
+
def encrypted?
|
78
|
+
files.find{ |f| File.basename(f) == "encrypted" }
|
79
|
+
end
|
80
|
+
|
72
81
|
# Save to disk
|
73
82
|
def encode_with(coder)
|
74
83
|
coder.map = {
|
@@ -42,10 +42,13 @@ class Rubyfocus::Project < Rubyfocus::Task
|
|
42
42
|
|
43
43
|
#First, set project
|
44
44
|
p = n.at_xpath("xmlns:project")
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
# We're not always going to have a project! And this *doesn't* mean that this project is demoted
|
46
|
+
if p
|
47
|
+
conditional_set(:singleton, p.at_xpath("xmlns:singleton")) { |e| e.inner_html == "true" }
|
48
|
+
conditional_set(:review_interval, p.at_xpath("xmlns:review-interval") ) { |e| Rubyfocus::ReviewPeriod.from_string(e.inner_html) }
|
49
|
+
conditional_set(:last_review, p.at_xpath("xmlns:last-review")) { |e| Time.safely_parse e.inner_html }
|
50
|
+
conditional_set(:status, p.at_xpath("xmlns:status")) { |e| e.inner_html.to_sym }
|
51
|
+
end
|
49
52
|
end
|
50
53
|
|
51
54
|
alias_method :singleton?, :singleton
|
data/lib/rubyfocus/patch.rb
CHANGED
@@ -11,6 +11,9 @@ class Rubyfocus::Patch
|
|
11
11
|
# The file the patch loads from
|
12
12
|
attr_accessor :file
|
13
13
|
|
14
|
+
# What version of patch file is this? Determined from XML file
|
15
|
+
attr_accessor :version
|
16
|
+
|
14
17
|
# These record the transformation in terms of patch ID values.
|
15
18
|
attr_accessor :from_ids, :to_id
|
16
19
|
|
@@ -59,6 +62,26 @@ class Rubyfocus::Patch
|
|
59
62
|
|
60
63
|
str ||= fetcher.patch(self.file)
|
61
64
|
doc = Nokogiri::XML(str)
|
65
|
+
|
66
|
+
# Root should be an <omnifocus> and have an XMLNS
|
67
|
+
# XMLNS should be one of:
|
68
|
+
# * http://www.omnigroup.com/namespace/OmniFocus/v1
|
69
|
+
# * http://www.omnigroup.com/namespace/OmniFocus/v2
|
70
|
+
omnifocus = doc.root
|
71
|
+
if omnifocus.name == "omnifocus"
|
72
|
+
xmlns = omnifocus.namespace && omnifocus.namespace.href
|
73
|
+
case xmlns
|
74
|
+
when "http://www.omnigroup.com/namespace/OmniFocus/v1"
|
75
|
+
self.version = 1
|
76
|
+
when "http://www.omnigroup.com/namespace/OmniFocus/v2"
|
77
|
+
self.version = 2
|
78
|
+
else
|
79
|
+
raise ArgumentError, "Unrecognised namespace #{xmlns.inspect} for Omnifocus patch file."
|
80
|
+
end
|
81
|
+
else
|
82
|
+
raise ArgumentError, "Root element should be <omnifocus>, instead was <#{omnifocus.name}>."
|
83
|
+
end
|
84
|
+
|
62
85
|
doc.root.children.select{ |n| !n.text?}.each do |child|
|
63
86
|
case child["op"]
|
64
87
|
when "update"
|
@@ -95,8 +118,18 @@ class Rubyfocus::Patch
|
|
95
118
|
|
96
119
|
# Apply this patch to a document.
|
97
120
|
def apply_to!(document)
|
98
|
-
|
99
|
-
|
121
|
+
load_data
|
122
|
+
|
123
|
+
# Updates depend on version!
|
124
|
+
if version == 1
|
125
|
+
#V1 updates overwrite elements
|
126
|
+
self.update.each{ |node| document.overwrite_element(node) }
|
127
|
+
elsif version == 2
|
128
|
+
#V2 updates actually update elements
|
129
|
+
self.update.each{ |node| document.update_element(node) }
|
130
|
+
else
|
131
|
+
raise RuntimeError, "Cannot run updates using Version #{version.inspect} OF patches!"
|
132
|
+
end
|
100
133
|
|
101
134
|
# Deletes remove elements
|
102
135
|
self.delete.each{ |node| document.remove_element(node["id"]) }
|
data/version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.11
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyfocus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan-Yves Ruzicka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -78,7 +78,7 @@ extensions: []
|
|
78
78
|
extra_rdoc_files:
|
79
79
|
- README.md
|
80
80
|
files:
|
81
|
-
-
|
81
|
+
- CHANGELOG.md
|
82
82
|
- README.md
|
83
83
|
- lib/rubyfocus.rb
|
84
84
|
- lib/rubyfocus/core_ext.rb
|
@@ -131,4 +131,3 @@ signing_key:
|
|
131
131
|
specification_version: 4
|
132
132
|
summary: Pure ruby bridge to OmniFocus.
|
133
133
|
test_files: []
|
134
|
-
has_rdoc:
|
data/HISTORY.md
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
# History
|
2
|
-
|
3
|
-
## 0.5.9 // 2016-09-04
|
4
|
-
|
5
|
-
Further Omnifocus database format changes result in shennanigans with rubyfocus. All fixed.
|
6
|
-
|
7
|
-
* [Fixed] Blank entries in updates are now treated as "no change", rather than "new value is nil".
|
8
|
-
|
9
|
-
## 0.5.8 // 2016-09-02
|
10
|
-
|
11
|
-
Heading off edge cases, again a result of the new Omnifocus database format
|
12
|
-
|
13
|
-
* [Fixed] Projects inside folders were being reported as having no container, due to empty `<task/`> tags. Now fixed.
|
14
|
-
|
15
|
-
## 0.5.7 // 2016-09-02
|
16
|
-
|
17
|
-
A quick fix for the new OF database format
|
18
|
-
|
19
|
-
* [Fixed] Empty `<project/>` tags in a task description will no longer cause Rubyfocus to treat said task as a project.
|
20
|
-
|
21
|
-
## 0.5.6 // 2016-09-01
|
22
|
-
|
23
|
-
* [New] Added `Time.safely_parse`, which will not choke on empty strings or nil values
|
24
|
-
* [Fixed] `conditional_set` will no longer choke on empty string for times
|
25
|
-
|
26
|
-
## 0.5.5 // 2016-05-18
|
27
|
-
|
28
|
-
* [Fixed] If there are no patches, `Fetcher#head`s will return the ID of the base file.
|
29
|
-
* [Fixed] `Fetcher#head` will now return the ID of the most recent patch, not the patch itself.
|
30
|
-
* [New] `Searchable` objects will now respond to `find_all`, which is an alias of `select`.
|
31
|
-
* [Fixed] `RankedItem#contained_within?` will now check against all objects matching a block or hash, rather than just the first one that the document can find.
|
32
|
-
|
33
|
-
## 0.5.4 // 2016-02-08
|
34
|
-
|
35
|
-
* [Fixed] `LocalFetcher` will now try the default App Store location if it can't find anything at the normal location.
|
36
|
-
|
37
|
-
## 0.5.3 // 2016-02-04
|
38
|
-
|
39
|
-
* [Fixed] Re-did the code determining whether a task was blocked. Now tasks are considered blocked if their immediate container is blocked.
|
40
|
-
* [Fixed] Caching some task filters on the `Task` class was causing issues. Removed caching, which shouldn't hit performance much.
|
41
|
-
|
42
|
-
## 0.5.2 // 2016-02-04
|
43
|
-
|
44
|
-
* [Fixed] Whoops! Did I leave HTTParty out of the gem install list? My bad!
|
45
|
-
|
46
|
-
## 0.5.1 // 2016-02-03
|
47
|
-
|
48
|
-
* [Fixed] `Task#next_available_task` should no longer cause errors when a project has no tasks.
|
49
|
-
|
50
|
-
## 0.5.0 // 2016-01-20
|
51
|
-
|
52
|
-
Now work out how close to (or far from) the head of your document you are!
|
53
|
-
|
54
|
-
* [New] `Fetcher#head` returns the most recent patch.
|
55
|
-
* [New] `Fetcher#can_reach_head_from?(id)` will inform you if you can get to the head from a given ID.
|
56
|
-
|
57
|
-
## 0.4.0 // 2016-01-01
|
58
|
-
|
59
|
-
* Happy new year!
|
60
|
-
* [Modified] Container IDRef is now located on RankedItem, rather than having several on each RankedItem subclass.
|
61
|
-
* [New] RankedItems can look at their ancestry much more easily, using RankedItem#ancestry and RankedItem#contained_within?
|
62
|
-
* [New] Documents now forbid elements with duplicate IDs unless Document#allow_duplicate_ids is set to true.
|
63
|
-
* [New] Patchers now treate CREATE nodes on elements whose IDs already exist in the database as UPDATE nodes
|
64
|
-
* [Fixed] Patchers will now interpret missing parameters as "default values" e.g. project update without `status` parameter assumed to be active.
|
65
|
-
|
66
|
-
## 0.3.1 // 2015-12-31
|
67
|
-
|
68
|
-
* [Bugfix] IDRefs will now return `nil` if the relevant ID is not set.
|
69
|
-
|
70
|
-
## 0.3.0 // 2015-10-17
|
71
|
-
|
72
|
-
* [New] Now supports remote syncing with the Omni Sync Server!
|
73
|
-
|
74
|
-
## 0.2.0 // 2015-10-11
|
75
|
-
|
76
|
-
* [Bugfix] Will now turn tasks into projects and projects into tasks if the user has done this in OmniFocus.
|
77
|
-
* [Bugfix] Rubyfocus::Patch now does patch application, rather than delegating to the Fetcher.
|
78
|
-
|
79
|
-
## 0.1.0 // 2015-10-10
|
80
|
-
|
81
|
-
* Hello, world!
|