taskwarrior 0.0.3 → 0.0.4
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.
- data/README.md +17 -2
- data/lib/taskwarrior/annotation.rb +14 -1
- data/lib/taskwarrior/project.rb +11 -0
- data/lib/taskwarrior/tag.rb +8 -1
- data/lib/taskwarrior/task.rb +27 -0
- data/lib/taskwarrior/version.rb +1 -1
- data/test/test_helper.rb +18 -0
- data/test/unit/test_annotation.rb +33 -0
- data/test/unit/test_project.rb +27 -0
- data/test/unit/test_repository.rb +12 -2
- data/test/unit/test_tag.rb +27 -5
- data/test/unit/test_task.rb +41 -1
- metadata +2 -2
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# taskwarrior
|
2
2
|
|
3
|
-
Ruby bindings for [TaskWarrior](http://taskwarrior.org)
|
3
|
+
Ruby bindings for [TaskWarrior](http://taskwarrior.org). Right now this gem provides read-only access to tasks, projects, tags etc.
|
4
4
|
|
5
5
|
[](http://travis-ci.org/nerab/taskwarrior)
|
6
6
|
|
@@ -20,7 +20,22 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
|
23
|
+
`TaskWarrior::Repository` is the main entry point. It expects an array of JSON objects, typically produced by `task export`. Technically, anything that can be consumed by `JSON.parse` is fine as long as it follows the format TaskWarrior uses.
|
24
|
+
|
25
|
+
# Assuming that a TaskWarrior export was written to a file
|
26
|
+
r = TaskWarrior::Repository.new(File.read('/tmp/task_export.json'))
|
27
|
+
|
28
|
+
Once instantiated, the repository provides access to tasks, projects and tags. Each task will also carry its attributes (description, uuid, etc) as well as its project and tags.
|
29
|
+
|
30
|
+
puts r.tasks.size
|
31
|
+
puts r.projects.size
|
32
|
+
puts r.tags.size
|
33
|
+
|
34
|
+
puts r.tasks.first.description
|
35
|
+
puts r.tasks.first.project.name
|
36
|
+
puts r.tasks.first.tags.join(' ')
|
37
|
+
|
38
|
+
Please see the [examples](/nerab/taskwarrior/tree/master/examples) for further use, or have a look at the [twdeps](/nerab/twdeps) tool which creates a graph that visualizes the dependencies between tasks.
|
24
39
|
|
25
40
|
## Contributing
|
26
41
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_model'
|
2
|
+
require 'date'
|
2
3
|
|
3
4
|
module TaskWarrior
|
4
5
|
class Annotation
|
@@ -11,12 +12,24 @@ module TaskWarrior
|
|
11
12
|
include TaskWarrior::Validations
|
12
13
|
validate :entry_cannot_be_in_the_future
|
13
14
|
|
14
|
-
def initialize(description = nil)
|
15
|
+
def initialize(description = nil, entry = nil)
|
15
16
|
@description = description
|
17
|
+
@entry = entry
|
16
18
|
end
|
17
19
|
|
18
20
|
def to_s
|
19
21
|
"Annotation (#{entry}): #{description}"
|
20
22
|
end
|
23
|
+
|
24
|
+
def hash
|
25
|
+
entry.hash + description.hash
|
26
|
+
end
|
27
|
+
|
28
|
+
# Annotations are value objects. They have no identity.
|
29
|
+
# If entry date and description are the same, the annotations are identical.
|
30
|
+
def ==(other)
|
31
|
+
return false unless other.is_a?(Annotation)
|
32
|
+
entry.eql?(other.entry) && description.eql?(other.description)
|
33
|
+
end
|
21
34
|
end
|
22
35
|
end
|
data/lib/taskwarrior/project.rb
CHANGED
@@ -23,6 +23,17 @@ module TaskWarrior
|
|
23
23
|
"Project #{name} (#{@tasks.size} tasks)"
|
24
24
|
end
|
25
25
|
|
26
|
+
def hash
|
27
|
+
name.hash + tasks.hash
|
28
|
+
end
|
29
|
+
|
30
|
+
# Projects are value objects. They have no identity.
|
31
|
+
# If name and tasks are the same, the projects are identical.
|
32
|
+
def ==(other)
|
33
|
+
return false unless other.is_a?(Project)
|
34
|
+
name.eql?(other.name) && tasks.eql?(other.tasks)
|
35
|
+
end
|
36
|
+
|
26
37
|
private
|
27
38
|
def name_may_not_contain_spaces
|
28
39
|
if !name.blank? and name[/\s/]
|
data/lib/taskwarrior/tag.rb
CHANGED
@@ -34,8 +34,15 @@ module TaskWarrior
|
|
34
34
|
"Tag: #{name} (#{@tasks.size} tasks)"
|
35
35
|
end
|
36
36
|
|
37
|
+
def hash
|
38
|
+
name.hash + tasks.hash
|
39
|
+
end
|
40
|
+
|
41
|
+
# Tags are value objects. They have no identity.
|
42
|
+
# If name and tasks are the same, the tags are identical.
|
37
43
|
def ==(other)
|
38
|
-
|
44
|
+
return false unless other.is_a?(Tag)
|
45
|
+
name.eql?(other.name) && tasks.eql?(other.tasks)
|
39
46
|
end
|
40
47
|
|
41
48
|
private
|
data/lib/taskwarrior/task.rb
CHANGED
@@ -35,10 +35,37 @@ module TaskWarrior
|
|
35
35
|
@children = []
|
36
36
|
@tags = []
|
37
37
|
@annotations = []
|
38
|
+
|
39
|
+
# http://www.ruby-forum.com/topic/164078#722181
|
40
|
+
@uuid = %x[uuidgen].strip
|
38
41
|
end
|
39
42
|
|
40
43
|
def to_s
|
41
44
|
"Task '#{description}'".tap{|result| result << " <#{uuid}>" if uuid}
|
42
45
|
end
|
46
|
+
|
47
|
+
# other may have the same uuid, but if its attributes differ, it will not be equal
|
48
|
+
def eql?(other)
|
49
|
+
# TODO Find a way to call attributes instead of listing them here again
|
50
|
+
# Maybe Virtus?
|
51
|
+
# http://solnic.eu/2012/01/10/ruby-datamapper-status.html
|
52
|
+
[:description, :id, :entry, :status, :uuid,
|
53
|
+
:project, :dependencies, :parent, :children,
|
54
|
+
:priority, :tags, :annotations, :start_at, :wait_at,
|
55
|
+
:end_at, :due_at].each do |attr|
|
56
|
+
return false unless send(attr).eql?(other.send(attr))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def hash
|
61
|
+
uuid.hash
|
62
|
+
end
|
63
|
+
|
64
|
+
# Tasks are entity objects. They have their identity defined by the uuid.
|
65
|
+
# If the uuids are the same, the tasks are identical.
|
66
|
+
def ==(other)
|
67
|
+
return false unless other.is_a?(Task)
|
68
|
+
uuid == other.uuid
|
69
|
+
end
|
43
70
|
end
|
44
71
|
end
|
data/lib/taskwarrior/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -18,6 +18,24 @@ module TaskWarrior
|
|
18
18
|
assert(task.invalid?, 'Expect validation to fail')
|
19
19
|
end
|
20
20
|
|
21
|
+
def assert_equality(a1, a2)
|
22
|
+
assert_not_equal(a1.object_id, a2.object_id)
|
23
|
+
assert_equal(a1, a2)
|
24
|
+
assert(a1 == a2)
|
25
|
+
assert(a1.hash == a2.hash)
|
26
|
+
assert(a1.eql?(a2))
|
27
|
+
assert_equal(1, [a1, a2].uniq.size)
|
28
|
+
end
|
29
|
+
|
30
|
+
def assert_inequality(a1, a2)
|
31
|
+
assert_not_equal(a1.object_id, a2.object_id)
|
32
|
+
assert_not_equal(a1, a2)
|
33
|
+
assert(!(a1 == a2))
|
34
|
+
assert(!(a1.hash == a2.hash))
|
35
|
+
assert(!a1.eql?(a2))
|
36
|
+
assert_equal(2, [a1, a2].uniq.size)
|
37
|
+
end
|
38
|
+
|
21
39
|
def error_message(errors)
|
22
40
|
errors.each_with_object([]){|e, result|
|
23
41
|
result << e.join(' ')
|
@@ -50,4 +50,37 @@ class TestAnnotation < Test::Unit::TestCase
|
|
50
50
|
def test_valid
|
51
51
|
assert_valid(@annotation)
|
52
52
|
end
|
53
|
+
|
54
|
+
def test_equality
|
55
|
+
a1 = TaskWarrior::Annotation.new('foo')
|
56
|
+
a2 = TaskWarrior::Annotation.new('foo')
|
57
|
+
|
58
|
+
assert_equal(a1, a2)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_equality_different_description
|
62
|
+
a1 = TaskWarrior::Annotation.new('foo')
|
63
|
+
a2 = TaskWarrior::Annotation.new('bar')
|
64
|
+
assert_inequality(a1, a2)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_equality_different_entry
|
68
|
+
a1 = TaskWarrior::Annotation.new('foo')
|
69
|
+
a1.entry = DateTime.now
|
70
|
+
|
71
|
+
a2 = TaskWarrior::Annotation.new('foo')
|
72
|
+
a2.entry = DateTime.now.advance(:days => -1)
|
73
|
+
|
74
|
+
assert_inequality(a1, a2)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_equality_different_description_and_entry
|
78
|
+
a1 = TaskWarrior::Annotation.new('foo')
|
79
|
+
a1.entry = DateTime.now
|
80
|
+
|
81
|
+
a2 = TaskWarrior::Annotation.new('bar')
|
82
|
+
a2.entry = DateTime.now.advance(:days => -1)
|
83
|
+
|
84
|
+
assert_inequality(a1, a2)
|
85
|
+
end
|
53
86
|
end
|
data/test/unit/test_project.rb
CHANGED
@@ -52,4 +52,31 @@ class TestProject < Test::Unit::TestCase
|
|
52
52
|
assert_equal(project, t1.project)
|
53
53
|
assert_equal(project, t2.project)
|
54
54
|
end
|
55
|
+
|
56
|
+
def test_equality
|
57
|
+
a1 = TaskWarrior::Project.new('foo')
|
58
|
+
a2 = TaskWarrior::Project.new('foo')
|
59
|
+
|
60
|
+
assert_equal(a1, a2)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_equality_different_name
|
64
|
+
a1 = TaskWarrior::Project.new('foo')
|
65
|
+
a2 = TaskWarrior::Project.new('bar')
|
66
|
+
assert_inequality(a1, a2)
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_equality_different_tasks
|
70
|
+
a1 = TaskWarrior::Project.new('foo')
|
71
|
+
a2 = TaskWarrior::Project.new('foo', [TaskWarrior::Task.new('foobar')])
|
72
|
+
|
73
|
+
assert_inequality(a1, a2)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_equality_different_name_and_tasks
|
77
|
+
a1 = TaskWarrior::Project.new('foo')
|
78
|
+
a2 = TaskWarrior::Project.new('bar', [TaskWarrior::Task.new('baz')])
|
79
|
+
|
80
|
+
assert_inequality(a1, a2)
|
81
|
+
end
|
55
82
|
end
|
@@ -46,8 +46,8 @@ class TestRepository < Test::Unit::TestCase
|
|
46
46
|
tags = @repo.tags
|
47
47
|
assert_not_nil(tags)
|
48
48
|
assert_equal(2, tags.size)
|
49
|
-
assert(tags.include?(
|
50
|
-
assert(tags.include?(
|
49
|
+
assert(tags.include?(@repo.tag('finance')))
|
50
|
+
assert(tags.include?(@repo.tag('mall')))
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_tasks_of_tag_finance
|
@@ -61,4 +61,14 @@ class TestRepository < Test::Unit::TestCase
|
|
61
61
|
assert_not_nil(mall)
|
62
62
|
assert_equal(3, mall.tasks.size)
|
63
63
|
end
|
64
|
+
|
65
|
+
def test_equality
|
66
|
+
t1 = @repo['b587f364-c68e-4438-b4d6-f2af6ad62518']
|
67
|
+
t2 = t1.dup
|
68
|
+
assert_not_equal(t1.object_id, t2.object_id)
|
69
|
+
|
70
|
+
t1.description = 'changed'
|
71
|
+
assert_equal(t1, t2)
|
72
|
+
assert(!(t1.eql?(t2)))
|
73
|
+
end
|
64
74
|
end
|
data/test/unit/test_tag.rb
CHANGED
@@ -48,10 +48,32 @@ class TestTag < Test::Unit::TestCase
|
|
48
48
|
assert_equal(Tag.new(foo), foo)
|
49
49
|
end
|
50
50
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
assert_equal(
|
51
|
+
def test_equality
|
52
|
+
a1 = Tag.new('foo')
|
53
|
+
a2 = Tag.new('foo')
|
54
|
+
|
55
|
+
assert_equal(a1, a2)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_equality_different_name
|
59
|
+
a1 = Tag.new('foo')
|
60
|
+
a2 = Tag.new('bar')
|
61
|
+
assert_inequality(a1, a2)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_equality_different_tasks
|
65
|
+
a1 = Tag.new('foo')
|
66
|
+
a2 = Tag.new('foo')
|
67
|
+
a2 << TaskWarrior::Task.new('baz')
|
68
|
+
|
69
|
+
assert_inequality(a1, a2)
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_equality_different_name_and_tasks
|
73
|
+
a1 = Tag.new('foo')
|
74
|
+
a2 = Tag.new('bar')
|
75
|
+
a2 << TaskWarrior::Task.new('baz')
|
76
|
+
|
77
|
+
assert_inequality(a1, a2)
|
56
78
|
end
|
57
79
|
end
|
data/test/unit/test_task.rb
CHANGED
@@ -5,10 +5,11 @@ require 'active_support/core_ext'
|
|
5
5
|
# TODO Add tests for dependencies
|
6
6
|
|
7
7
|
class TestTask < Test::Unit::TestCase
|
8
|
+
include TaskWarrior
|
8
9
|
include TaskWarrior::Test::Validations
|
9
10
|
|
10
11
|
def setup
|
11
|
-
@task =
|
12
|
+
@task = Task.new('foobar')
|
12
13
|
@task.id = 1
|
13
14
|
@task.uuid = '66465716-b08d-41ea-8567-91b988a2bcbf'
|
14
15
|
@task.entry = DateTime.now
|
@@ -182,4 +183,43 @@ class TestTask < Test::Unit::TestCase
|
|
182
183
|
@task.send("#{sym}=", DateTime.now.advance(:days => -1))
|
183
184
|
assert_valid(@task)
|
184
185
|
end
|
186
|
+
|
187
|
+
def test_equality
|
188
|
+
a1 = Task.new('foo')
|
189
|
+
a2 = Task.new('foo')
|
190
|
+
|
191
|
+
# Tasks are entities, so even with the same attributes, two different objects
|
192
|
+
# must not be treated equal
|
193
|
+
assert_inequality(a1, a2)
|
194
|
+
|
195
|
+
# But comparing the same thing to itself is fine
|
196
|
+
assert_equal(a1, a1)
|
197
|
+
assert_equal(a2, a2)
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_equality_different_description
|
201
|
+
a1 = Task.new('foo')
|
202
|
+
a2 = Task.new('bar')
|
203
|
+
assert_inequality(a1, a2)
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_equality_different_entry
|
207
|
+
a1 = Task.new('foo')
|
208
|
+
a1.entry = DateTime.now
|
209
|
+
|
210
|
+
a2 = Task.new('foo')
|
211
|
+
a2.entry = DateTime.now.advance(:days => -1)
|
212
|
+
|
213
|
+
assert_inequality(a1, a2)
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_equality_different_description_and_entry
|
217
|
+
a1 = Task.new('foo')
|
218
|
+
a1.entry = DateTime.now
|
219
|
+
|
220
|
+
a2 = Task.new('bar')
|
221
|
+
a2.entry = DateTime.now.advance(:days => -1)
|
222
|
+
|
223
|
+
assert_inequality(a1, a2)
|
224
|
+
end
|
185
225
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: taskwarrior
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activemodel
|