todo-txt 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +95 -0
- data/lib/todo-txt.rb +5 -0
- data/lib/todo-txt/list.rb +87 -0
- data/lib/todo-txt/task.rb +114 -0
- data/spec/data/todo.txt +5 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/todo-txt/list_spec.rb +71 -0
- data/spec/todo-txt/task_spec.rb +67 -0
- data/todo-txt.gemspec +16 -0
- metadata +55 -0
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Todo.txt
|
2
|
+
|
3
|
+
This is a Ruby client library for Gina Trapani's [todo.txt](https://github.com/ginatrapani/todo.txt-cli/). It allows for easy parsing of task lists and tasks in the todo.txt format.
|
4
|
+
|
5
|
+
# Installation
|
6
|
+
|
7
|
+
Installation is very simple. The project is packaged as a Ruby gem and can be installed by running:
|
8
|
+
|
9
|
+
gem install todo-txt
|
10
|
+
|
11
|
+
# Usage
|
12
|
+
|
13
|
+
## Todo::List
|
14
|
+
|
15
|
+
A `Todo::List` object encapsulates your todo.txt file. You initialise it by passing the path to your todo.txt to the constructor:
|
16
|
+
|
17
|
+
``` ruby
|
18
|
+
require 'todo-txt'
|
19
|
+
|
20
|
+
list = Todo::List.new "path/to/todo.txt"
|
21
|
+
```
|
22
|
+
|
23
|
+
`Todo::List` subclasses `Array` so it has all of the standard methods that are available on an array. It is, basically, an array of `Todo::Task` items.
|
24
|
+
|
25
|
+
### Filtering
|
26
|
+
|
27
|
+
You can filter your todo list by priority, project, context or a combination of all three with ease.
|
28
|
+
|
29
|
+
``` ruby
|
30
|
+
require 'todo-txt
|
31
|
+
|
32
|
+
list = Todo::List.new "path/to/todo.txt"
|
33
|
+
|
34
|
+
list.by_priority "A"
|
35
|
+
# => Contains a Todo::List object with only priority A tasks.
|
36
|
+
|
37
|
+
list.by_context "@code"
|
38
|
+
# => Returns a new Todo::List with only tasks that have a @code context.
|
39
|
+
|
40
|
+
list.by_project "+manhatten"
|
41
|
+
# => Returns a new Todo::List with only tasks that are part of the
|
42
|
+
+manhatten project (see what I did there?)
|
43
|
+
|
44
|
+
# And you can combine these, like so
|
45
|
+
list.by_project("+manhatten").by_priority("B")
|
46
|
+
```
|
47
|
+
|
48
|
+
## Todo::Task
|
49
|
+
|
50
|
+
A `Todo::Task` object can be created from a standard task string if you don't want to use the `Todo::List` approach (though using `Todo::List` is recommended).
|
51
|
+
|
52
|
+
``` ruby
|
53
|
+
require 'todo-txt'
|
54
|
+
|
55
|
+
task = Todo::Task.new "(A) This task is top priority! +project @context"
|
56
|
+
|
57
|
+
task.priority
|
58
|
+
# => "A"
|
59
|
+
|
60
|
+
task.contexts
|
61
|
+
# => ["@context"]
|
62
|
+
|
63
|
+
task.projects
|
64
|
+
# => ["+project"]
|
65
|
+
|
66
|
+
task.text
|
67
|
+
# => "This task is top priority!"
|
68
|
+
|
69
|
+
task.orig
|
70
|
+
# => "(A) This task is top priority! +project @context"
|
71
|
+
```
|
72
|
+
|
73
|
+
### Comparable
|
74
|
+
|
75
|
+
The `Todo::Task` object includes the `Comparable` mixin. It compares with other tasks and sorts by priority in descending order.
|
76
|
+
|
77
|
+
``` ruby
|
78
|
+
task1 = Todo::Task.new "(A) Priority A."
|
79
|
+
task2 = Todo::Task.new "(B) Priority B."
|
80
|
+
|
81
|
+
task1 > task2
|
82
|
+
# => true
|
83
|
+
|
84
|
+
task1 == task2
|
85
|
+
# => false
|
86
|
+
|
87
|
+
task2 > task1
|
88
|
+
# => false
|
89
|
+
```
|
90
|
+
|
91
|
+
Tasks without a priority will always be less than a task with a priority.
|
92
|
+
|
93
|
+
# Requirements
|
94
|
+
|
95
|
+
The todo-txt gem requires Ruby 1.9.2 or higher. It doesn't currently run on 1.8.7.
|
data/lib/todo-txt.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
module Todo
|
2
|
+
class List < Array
|
3
|
+
# Initializes a Todo List object with a path to the corresponding todo.txt
|
4
|
+
# file. For example, if your todo.txt file is located at:
|
5
|
+
#
|
6
|
+
# /home/sam/Dropbox/todo/todo.txt
|
7
|
+
#
|
8
|
+
# You would initialize this object like do:
|
9
|
+
#
|
10
|
+
# list = Todo::List.new "/home/sam/Dropbox/todo/todo-txt"
|
11
|
+
#
|
12
|
+
# Alternately, you can initialize this object with an array of strings or
|
13
|
+
# tasks. If the array is of strings, the strings will be converted into
|
14
|
+
# tasks. You can supply a mixed list of string and tasks if you wish.
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
#
|
18
|
+
# array = Array.new
|
19
|
+
# array.push "(A) A string task!"
|
20
|
+
# array.push Todo::Task.new("(A) An actual task!")
|
21
|
+
#
|
22
|
+
# list = Todo::List.new array
|
23
|
+
def initialize list
|
24
|
+
if list.is_a? Array
|
25
|
+
# No file path was given.
|
26
|
+
@path = nil
|
27
|
+
|
28
|
+
# If path is an array, loop over it, adding to self.
|
29
|
+
list.each do |task|
|
30
|
+
# If it's a string, make a new task out of it.
|
31
|
+
if task.is_a? String
|
32
|
+
self.push Todo::Task.new task
|
33
|
+
# If it's a task, just add it.
|
34
|
+
elsif task.is_a? Todo::Task
|
35
|
+
self.push task
|
36
|
+
end
|
37
|
+
end
|
38
|
+
elsif list.is_a? String
|
39
|
+
@path = list
|
40
|
+
|
41
|
+
# Read in lines from file, create Todo::Tasks out of them and push them
|
42
|
+
# onto self.
|
43
|
+
File.open(list) do |file|
|
44
|
+
file.each_line { |line| self.push Todo::Task.new line }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# The path to the todo.txt file that you supplied when you created the
|
50
|
+
# Todo::List object.
|
51
|
+
def path
|
52
|
+
@path
|
53
|
+
end
|
54
|
+
|
55
|
+
# Filters the list by priority and returns a new list.
|
56
|
+
#
|
57
|
+
# Example:
|
58
|
+
#
|
59
|
+
# list = Todo::List.new "/path/to/list"
|
60
|
+
# list.by_priority "A" #=> Will be a new list with only priority A tasks
|
61
|
+
def by_priority priority
|
62
|
+
Todo::List.new self.select { |task| task.priority == priority }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Filters the list by context and returns a new list.
|
66
|
+
#
|
67
|
+
# Example:
|
68
|
+
#
|
69
|
+
# list = Todo::List.new "/path/to/list"
|
70
|
+
# list.by_context "@context" #=> Will be a new list with only tasks
|
71
|
+
# containing "@context"
|
72
|
+
def by_context context
|
73
|
+
Todo::List.new self.select { |task| task.contexts.include? context }
|
74
|
+
end
|
75
|
+
|
76
|
+
# Filters the list by project and returns a new list.
|
77
|
+
#
|
78
|
+
# Example:
|
79
|
+
#
|
80
|
+
# list = Todo::List.new "/path/to/list"
|
81
|
+
# list.by_project "+project" #=> Will be a new list with only tasks
|
82
|
+
# containing "+project"
|
83
|
+
def by_project project
|
84
|
+
Todo::List.new self.select { |task| task.projects.include? project }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Todo
|
2
|
+
class Task
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
# The regular expression used to match contexts.
|
6
|
+
def self.contexts_regex
|
7
|
+
/(?:\s+|^)@\w+/
|
8
|
+
end
|
9
|
+
|
10
|
+
# The regex used to match projects.
|
11
|
+
def self.projects_regex
|
12
|
+
/(?:\s+|^)\+\w+/
|
13
|
+
end
|
14
|
+
|
15
|
+
# The regex used to match priorities.
|
16
|
+
def self.priotity_regex
|
17
|
+
/^\([A-Za-z]\)\s+/
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates a new task. The argument that you pass in must be a string.
|
21
|
+
def initialize task
|
22
|
+
@orig = task
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the original content of the task.
|
26
|
+
#
|
27
|
+
# Example:
|
28
|
+
#
|
29
|
+
# task = Todo::Task.new "(A) @context +project Hello!"
|
30
|
+
# task.orig #=> "(A) @context +project Hello!"
|
31
|
+
def orig
|
32
|
+
@orig
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the priority, if any.
|
36
|
+
#
|
37
|
+
# Example:
|
38
|
+
#
|
39
|
+
# task = Todo::Task.new "(A) Some task."
|
40
|
+
# task.priority #=> "A"
|
41
|
+
#
|
42
|
+
# task = Todo::Task.new "Some task."
|
43
|
+
# task.priority #=> nil
|
44
|
+
def priority
|
45
|
+
@priority ||= if orig =~ self.class.priotity_regex
|
46
|
+
orig[1]
|
47
|
+
else
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Retrieves an array of all the @context annotations.
|
53
|
+
#
|
54
|
+
# Example:
|
55
|
+
#
|
56
|
+
# task = Todo:Task.new "(A) @context Testing!"
|
57
|
+
# task.context #=> ["@context"]
|
58
|
+
def contexts
|
59
|
+
@contexts ||= orig.scan(self.class.contexts_regex).map { |item| item.strip }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Retrieves an array of all the +project annotations.
|
63
|
+
#
|
64
|
+
# Example:
|
65
|
+
#
|
66
|
+
# task = Todo:Task.new "(A) +test Testing!"
|
67
|
+
# task.projects #=> ["+test"]
|
68
|
+
def projects
|
69
|
+
@projects ||= orig.scan(self.class.projects_regex).map { |item| item.strip }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Gets just the text content of the todo, without the priority, contexts
|
73
|
+
# and projects annotations.
|
74
|
+
#
|
75
|
+
# Example:
|
76
|
+
#
|
77
|
+
# task = Todo::Task.new "(A) @test Testing!"
|
78
|
+
# task.text #=> "Testing!"
|
79
|
+
def text
|
80
|
+
@text ||= orig.
|
81
|
+
gsub(self.class.priotity_regex, '').
|
82
|
+
gsub(self.class.contexts_regex, '').
|
83
|
+
gsub(self.class.projects_regex, '').
|
84
|
+
strip
|
85
|
+
end
|
86
|
+
|
87
|
+
# Compares the priorities of two tasks.
|
88
|
+
#
|
89
|
+
# Example:
|
90
|
+
#
|
91
|
+
# task1 = Todo::Task.new "(A) Priority A."
|
92
|
+
# task2 = Todo::Task.new "(B) Priority B."
|
93
|
+
#
|
94
|
+
# task1 > task2
|
95
|
+
# # => true
|
96
|
+
#
|
97
|
+
# task1 == task2
|
98
|
+
# # => false
|
99
|
+
#
|
100
|
+
# task2 > task1
|
101
|
+
# # => false
|
102
|
+
def <=> other_task
|
103
|
+
if self.priority.nil? and other_task.priority.nil?
|
104
|
+
0
|
105
|
+
elsif other_task.priority.nil?
|
106
|
+
1
|
107
|
+
elsif self.priority.nil?
|
108
|
+
-1
|
109
|
+
else
|
110
|
+
other_task.priority <=> self.priority
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/spec/data/todo.txt
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative '../lib/todo-txt'
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Todo::List do
|
4
|
+
# A helper method to grab the test data list.
|
5
|
+
def list
|
6
|
+
Todo::List.new(File.dirname(__FILE__) + '/../data/todo.txt')
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should grab a list of Todo::Tasks' do
|
10
|
+
list.each do |task|
|
11
|
+
task.class.should == Todo::Task
|
12
|
+
end
|
13
|
+
|
14
|
+
# This is a little bit fragile but it helps me sleep at night.
|
15
|
+
list[0].priority.should == "A"
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should be able to filter by priority' do
|
19
|
+
list.by_priority("A").each do |task|
|
20
|
+
task.priority.should == "A"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Make sure some data was actually checked
|
24
|
+
list.by_priority("A").length.should be > 0
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should be able to filter by context' do
|
28
|
+
list.by_context("@context").each do |task|
|
29
|
+
task.contexts.should include "@context"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Make sure some data was actually checked
|
33
|
+
list.by_context("@context").length.should be > 0
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should be able to filter by project' do
|
37
|
+
list.by_project("+project").each do |task|
|
38
|
+
task.projects.should include "+project"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Make sure some data was actually checked
|
42
|
+
list.by_project("+project").length.should be > 0
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should be able to filter by project, context and priority' do
|
46
|
+
filtered = list.by_project("+project").
|
47
|
+
by_context("@context").
|
48
|
+
by_priority("C")
|
49
|
+
|
50
|
+
filtered.each do |task|
|
51
|
+
task.projects.should include "+project"
|
52
|
+
task.contexts.should include "@context"
|
53
|
+
task.priority.should == "C"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Make sure some data was actually checked
|
57
|
+
filtered.length.should be > 0
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should be sortable' do
|
61
|
+
list.sort.each_cons(2) do |task_a, task_b|
|
62
|
+
task_a.should be <= task_b
|
63
|
+
end
|
64
|
+
|
65
|
+
# Make sure some data was actually checked
|
66
|
+
list.sort.length.should be > 0
|
67
|
+
|
68
|
+
# Class should still be Todo::List
|
69
|
+
list.sort.class.should == Todo::List
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe Todo::Task do
|
4
|
+
it 'should recognise priorities' do
|
5
|
+
task = Todo::Task.new "(A) Hello world!"
|
6
|
+
task.priority.should == "A"
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should only recognise priorities at the start of a task' do
|
10
|
+
task = Todo::Task.new "Hello, world! (A)"
|
11
|
+
task.priority.should == nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should recognise contexts' do
|
15
|
+
task = Todo::Task.new "Hello, world! @test"
|
16
|
+
task.contexts.should == ["@test"]
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should recognise multiple contexts' do
|
20
|
+
task = Todo::Task.new "Hello, world! @test @test2"
|
21
|
+
task.contexts.should == ["@test", "@test2"]
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should recognise projects' do
|
25
|
+
task = Todo::Task.new "Hello, world! +test"
|
26
|
+
task.projects.should == ["+test"]
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should recognise multiple projects' do
|
30
|
+
task = Todo::Task.new "Hello, world! +test +test2"
|
31
|
+
task.projects.should == ["+test", "+test2"]
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should retain the original task' do
|
35
|
+
task = Todo::Task.new "(A) This is an awesome task, yo. +winning"
|
36
|
+
task.orig.should == "(A) This is an awesome task, yo. +winning"
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should be able to get just the text, no contexts etc.' do
|
40
|
+
task = Todo::Task.new "(B) This is a sweet task. @context +project"
|
41
|
+
task.text.should == "This is a sweet task."
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should be comparable' do
|
45
|
+
task1 = Todo::Task.new "(A) Top priority, y'all!"
|
46
|
+
task2 = Todo::Task.new "(B) Not quite so high."
|
47
|
+
|
48
|
+
assertion = task1 > task2
|
49
|
+
assertion.should == true
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should be comparable to task without priority' do
|
53
|
+
task1 = Todo::Task.new "Top priority, y'all!"
|
54
|
+
task2 = Todo::Task.new "(B) Not quite so high."
|
55
|
+
|
56
|
+
assertion = task1 < task2
|
57
|
+
assertion.should == true
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should be able to compare two tasks without priority' do
|
61
|
+
task1 = Todo::Task.new "Top priority, y'all!"
|
62
|
+
task2 = Todo::Task.new "Not quite so high."
|
63
|
+
|
64
|
+
assertion = task1 == task2
|
65
|
+
assertion.should == true
|
66
|
+
end
|
67
|
+
end
|
data/todo-txt.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = %q{todo-txt}
|
3
|
+
s.version = "0.1"
|
4
|
+
s.date = %q{2011-08-19}
|
5
|
+
s.authors = ["Sam Rose"]
|
6
|
+
s.email = %q{samwho@lbak.co.uk}
|
7
|
+
s.summary = %q{A client library for parsing todo.txt files.}
|
8
|
+
s.homepage = %q{http://lbak.co.uk}
|
9
|
+
s.description = %q{Allows for simple parsing of todo.txt files, as per Gina Trapani's todo.txt project.}
|
10
|
+
s.required_ruby_version = '>= 1.9.2'
|
11
|
+
s.license = 'GPL-2'
|
12
|
+
|
13
|
+
# Add all files to the files parameter.
|
14
|
+
s.files = []
|
15
|
+
Dir["**/*.*"].each { |path| s.files.push path }
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: todo-txt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sam Rose
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-08-19 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: Allows for simple parsing of todo.txt files, as per Gina Trapani's todo.txt
|
15
|
+
project.
|
16
|
+
email: samwho@lbak.co.uk
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- README.md
|
22
|
+
- spec/todo-txt/list_spec.rb
|
23
|
+
- spec/todo-txt/task_spec.rb
|
24
|
+
- spec/data/todo.txt
|
25
|
+
- spec/spec_helper.rb
|
26
|
+
- todo-txt.gemspec
|
27
|
+
- lib/todo-txt/list.rb
|
28
|
+
- lib/todo-txt/task.rb
|
29
|
+
- lib/todo-txt.rb
|
30
|
+
homepage: http://lbak.co.uk
|
31
|
+
licenses:
|
32
|
+
- GPL-2
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.9.2
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 1.8.8
|
52
|
+
signing_key:
|
53
|
+
specification_version: 3
|
54
|
+
summary: A client library for parsing todo.txt files.
|
55
|
+
test_files: []
|