todo-txt 0.1

Sign up to get free protection for your applications and to get access to all the features.
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,5 @@
1
+ # Require all files in the main lib directory
2
+ Dir[File.dirname(__FILE__) + '/todo-txt/*.rb'].each do |file|
3
+ require file
4
+ end
5
+
@@ -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
@@ -0,0 +1,5 @@
1
+ (A) Crack the Da Vinci Code.
2
+ (B) +winning Win.
3
+ @context Give it some context.
4
+ Just a POD: Plain old task.
5
+ (C) +project @context This one has it all!
@@ -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: []