todo-txt 0.1
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 +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: []
|