taskwarrior 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://secure.travis-ci.org/nerab/taskwarrior.png?branch=master)](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
|