todotxt 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in todotxt.gemspec
4
+ gemspec
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Johan Sahlén, http://jsahlen.se/
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # Todotxt.rb
2
+
3
+ *todo.txt with a ruby flair*
4
+
5
+
6
+ ## About
7
+
8
+ Todotxt is a ruby CLI interface to work with a
9
+ [todo.txt](http://www.todotxt.com) file
10
+
11
+
12
+ ## Install
13
+
14
+ Clone from [jsahlen/todotxt](http://github.com/jsahlen/todotxt) and do
15
+
16
+ rake install
17
+
18
+ I might release it as a proper gem in the future…
19
+
20
+ Todotxt relies on a configuration file (`.todotxt.cfg`) in your home directory,
21
+ which points to the location of your todo.txt. You can run
22
+
23
+ todotxt generate_cfg
24
+
25
+ to generate this file, which will then point to `~/todo.txt`.
26
+
27
+
28
+ ## Usage
29
+
30
+ The gem will install a command, `todotxt` which is used to interact with your
31
+ todo.txt.
32
+
33
+ Tasks:
34
+ todotxt add | a TEXT # Add a new Todo item
35
+ todotxt append | app ITEM# STRING # Append STRING to ITEM#
36
+ todotxt del | rm ITEM#[, ITEM#, ITEM#, ...] # Remove ITEM#
37
+ todotxt do ITEM#[, ITEM#, ITEM#, ...] # Mark ITEM# as done
38
+ todotxt dp | depri ITEM#[, ITEM#, ITEM#, ...] # Remove priority for ITEM#
39
+ todotxt generate_cfg # Create a sample todo.txt
40
+ todotxt generate_config # Create a .todotxt.cfg file in your home folder, containing the path to todo.txt
41
+ todotxt help [TASK] # Describe available tasks or one specific task
42
+ todotxt list | ls [SEARCH] # List all todos, or todos matching SEARCH
43
+ todotxt listproj | lsproj # List all projects
44
+ todotxt lscon | lsc # List all contexts
45
+ todotxt lsdone | lsd # List all done items
46
+ todotxt prepend | prep ITEM# STRING # Prepend STRING to ITEM#
47
+ todotxt pri | p ITEM# PRIORITY # Set priority of ITEM# to PRIORITY
48
+ todotxt replace ITEM# TEXT # Completely replace ITEM# text with TEXT
49
+ todotxt undo | u ITEM#[, ITEM#, ITEM#, ...] # Mark ITEM# item as not done
50
+ todotxt version # Show todotxt version
51
+
52
+ Calling simply `todotxt` will automatically run the `ls` command.
53
+
54
+
55
+ ## Dependencies
56
+
57
+ * [Thor](http://github.com/wycats/thor)
58
+ * [Rainbow](http://github.com/sickill/rainbow)
59
+ * [ParseConfig](http://www.5dollarwhitebox.org/drupal/?q=node/21)
60
+
61
+
62
+ ## Bugs
63
+
64
+ Please report any bugs using the
65
+ [GitHub Issue Tracker](http://github.com/jsahlen/todotxt/issues).
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/bin/todotxt ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ libdir = File.join(File.dirname(File.dirname(__FILE__)), "lib")
4
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
5
+
6
+ args = ARGV.clone
7
+
8
+ if args.empty?
9
+ args.push "ls"
10
+ end
11
+
12
+ require "todotxt"
13
+ Todotxt::CLI.start(args)
data/conf/todo.txt ADDED
@@ -0,0 +1 @@
1
+ Learn todo.txt +myproject @home
data/conf/todotxt.cfg ADDED
@@ -0,0 +1 @@
1
+ todo_txt_path = ~/todo.txt
@@ -0,0 +1,3 @@
1
+ an item +project1 +project2 @context2
2
+ an item +project1 @context1
3
+ (A) an item
@@ -0,0 +1,4 @@
1
+ First item
2
+ Second item
3
+ Third item
4
+ x First done item
@@ -0,0 +1,289 @@
1
+ require "thor"
2
+ require "rainbow"
3
+ require "parseconfig"
4
+
5
+ module Todotxt
6
+ CFG_PATH = File.expand_path("~/.todotxt.cfg")
7
+
8
+ class CLI < Thor
9
+ include Thor::Actions
10
+ include Todotxt::CLIHelpers
11
+
12
+ def self.source_root
13
+ File.join File.dirname(__FILE__), "..", "..", "conf"
14
+ end
15
+
16
+ def initialize(*args)
17
+ super
18
+
19
+ unless ["help", "generate_config"].include? ARGV[0]
20
+ parse_config
21
+
22
+ @list = TodoList.new @txt_path
23
+ end
24
+ end
25
+
26
+ #
27
+ # Listing
28
+ #
29
+
30
+ desc "list | ls [SEARCH]", "List all todos, or todos matching SEARCH"
31
+ method_option :done, :type => :boolean, :aliases => "-d", :desc => "Include todo items that have been marked as done"
32
+ method_option :simple, :type => :boolean, :desc => "Simple output (for scripts, etc)"
33
+ def list search=""
34
+ with_done = false
35
+
36
+ with_done = true if options[:done]
37
+
38
+ @list.filter(search, :with_done => with_done)
39
+
40
+ render_list :simple => !!options[:simple]
41
+ end
42
+ map "ls" => :list
43
+
44
+ desc "lsdone | lsd", "List all done items"
45
+ def lsdone search=""
46
+ @list.filter(search, :only_done => true)
47
+
48
+ render_list
49
+ end
50
+ map "lsd" => :lsdone
51
+
52
+ desc "listproj | lsproj", "List all projects"
53
+ def listproj
54
+ @list.projects.each { |p| say p }
55
+ end
56
+ map "lsproj" => :listproj
57
+
58
+ desc "lscon | lsc", "List all contexts"
59
+ def lscon
60
+ @list.contexts.each { |c| say c }
61
+ end
62
+ map "lsc" => :lscon
63
+
64
+ #
65
+ # Todo management
66
+ #
67
+
68
+ desc "add | a TEXT", "Add a new Todo item"
69
+ def add(str, *str2)
70
+ string = "#{str} #{str2.join(' ')}"
71
+ todo = @list.add string
72
+
73
+ puts format_todo(todo)
74
+
75
+ @list.save
76
+ end
77
+ map "a" => :add
78
+
79
+ desc "do ITEM#[, ITEM#, ITEM#, ...]", "Mark ITEM# as done"
80
+ def do line1, *lines
81
+ lines.unshift(line1).each do |line|
82
+ todo = @list.find_by_line line
83
+ if todo
84
+ todo.do
85
+ puts format_todo(todo)
86
+
87
+ @list.save
88
+ else
89
+ error "No todo found at line #{line}"
90
+ end
91
+ end
92
+ end
93
+
94
+ desc "undo | u ITEM#[, ITEM#, ITEM#, ...]", "Mark ITEM# item as not done"
95
+ def undo line1, *lines
96
+ lines.unshift(line1).each do |line|
97
+ todo = @list.find_by_line line
98
+ if todo
99
+ todo.undo
100
+ puts format_todo(todo)
101
+
102
+ @list.save
103
+ else
104
+ error "No todo found at line #{line}"
105
+ end
106
+ end
107
+ end
108
+ map "u" => :undo
109
+
110
+ desc "pri | p ITEM# PRIORITY", "Set priority of ITEM# to PRIORITY"
111
+ def pri line, priority
112
+ todo = @list.find_by_line line
113
+ if todo
114
+ todo.prioritize priority
115
+ puts format_todo(todo)
116
+
117
+ @list.save
118
+ else
119
+ error "No todo found at line #{line}"
120
+ end
121
+ end
122
+ map "p" => :pri
123
+
124
+ desc "dp | depri ITEM#[, ITEM#, ITEM#, ...]", "Remove priority for ITEM#"
125
+ def dp line1, *lines
126
+ lines.unshift(line1).each do |line|
127
+ todo = @list.find_by_line line
128
+ if todo
129
+ todo.prioritize
130
+ puts format_todo(todo)
131
+
132
+ @list.save
133
+ else
134
+ error "No todo found at line #{line}"
135
+ end
136
+ end
137
+ end
138
+ map "depri" => :dp
139
+
140
+ desc "append | app ITEM# STRING", "Append STRING to ITEM#"
141
+ def append line, str, *str2
142
+ string = "#{str} #{str2.join(' ')}"
143
+ todo = @list.find_by_line line
144
+ if todo
145
+ todo.append string
146
+ puts format_todo(todo)
147
+
148
+ @list.save
149
+ else
150
+ error "No todo found at line #{line}"
151
+ end
152
+ end
153
+ map "app" => :append
154
+
155
+ desc "prepend | prep ITEM# STRING", "Prepend STRING to ITEM#"
156
+ def prepend line, str, *str2
157
+ string = "#{str} #{str2.join(' ')}"
158
+ todo = @list.find_by_line line
159
+ if todo
160
+ todo.prepend string
161
+ puts format_todo(todo)
162
+
163
+ @list.save
164
+ else
165
+ error "No todo found at line #{line}"
166
+ end
167
+ end
168
+ map "prep" => :prepend
169
+
170
+ desc "replace ITEM# TEXT", "Completely replace ITEM# text with TEXT"
171
+ def replace line, str, *str2
172
+ string = "#{str} #{str2.join(' ')}"
173
+ todo = @list.find_by_line line
174
+ if todo
175
+ todo.replace string
176
+ puts format_todo(todo)
177
+
178
+ @list.save
179
+ else
180
+ error "No todo found at line #{line}"
181
+ end
182
+ end
183
+
184
+ desc "del | rm ITEM#[, ITEM#, ITEM#, ...]", "Remove ITEM#"
185
+ method_option :force, :type => :boolean, :aliases => "-f", :desc => "Don't confirm removal"
186
+ def del line1, *lines
187
+ lines.unshift(line1).each do |line|
188
+ todo = @list.find_by_line line
189
+ if todo
190
+ say format_todo(todo)
191
+ if options[:force] || yes?("Remove this item? [y/N]")
192
+ @list.remove line
193
+ notice "Removed from list"
194
+
195
+ @list.save
196
+ end
197
+ else
198
+ error "No todo found at line #{line}"
199
+ end
200
+ end
201
+ end
202
+ map "rm" => :del
203
+
204
+ #
205
+ # File generation
206
+ #
207
+
208
+ desc "generate_config", "Create a .todotxt.cfg file in your home folder, containing the path to todo.txt"
209
+ def generate_config
210
+ copy_file "todotxt.cfg", CFG_PATH
211
+ puts ""
212
+
213
+ parse_config
214
+ end
215
+
216
+ desc "generate_cfg", "Create a sample todo.txt"
217
+ def generate_txt
218
+ copy_file "todo.txt", @txt_path
219
+ puts ""
220
+ end
221
+
222
+ #
223
+ # Extras
224
+ #
225
+
226
+ desc "version", "Show todotxt version"
227
+ def version
228
+ say "todotxt #{VERSION}"
229
+ end
230
+
231
+ private
232
+
233
+ def render_list opts={}
234
+ numsize = @list.count + 1
235
+ numsize = numsize.to_s.length + 0
236
+
237
+ @list.each do |t|
238
+ if opts[:simple]
239
+ say "#{t.line} #{t.to_s}"
240
+ else
241
+ say format_todo(t, numsize)
242
+ end
243
+ end
244
+
245
+ unless opts[:simple]
246
+ puts "TODO: #{@list.count} items".color(:black).bright
247
+ end
248
+ end
249
+
250
+ def parse_config
251
+ unless File.exist? CFG_PATH
252
+ puts "You need a .todotxt.cfg file in your home folder to continue (used to determine the path of your todo.txt.) Answer yes to have it generated for you (pointing to ~/todo.txt), or no to create it yourself.\n\n"
253
+ confirm_generate = yes? "Create ~/.todotxt.cfg? [y/N]"
254
+
255
+ if confirm_generate
256
+ generate_config
257
+ else
258
+ puts ""
259
+ exit
260
+ end
261
+ end
262
+
263
+ cfg = ParseConfig.new(CFG_PATH)
264
+
265
+ txt = cfg.get_value "todo_txt_path"
266
+
267
+ if txt
268
+ @txt_path = File.expand_path(txt)
269
+
270
+ unless File.exist? @txt_path
271
+ puts "#{txt} doesn't exist yet. Would you like to generate a sample file?"
272
+ confirm_generate = yes? "Create #{txt}? [y/N]"
273
+
274
+ if confirm_generate
275
+ generate_txt
276
+ else
277
+ puts ""
278
+ exit
279
+ end
280
+ end
281
+ else
282
+ error "Couldn't find todo_txt_path setting in ~/.todotxt.cfg."
283
+ puts "Please run the following to create a new configuration file:"
284
+ puts " todotxt generate_config"
285
+ exit
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,54 @@
1
+ module Todotxt
2
+ module CLIHelpers
3
+
4
+ def format_todo(todo, number_padding=nil)
5
+ line = todo.line.to_s
6
+
7
+ if number_padding
8
+ line = line.rjust number_padding
9
+ end
10
+
11
+ text = todo.to_s
12
+
13
+ unless todo.done
14
+ text.gsub! PRIORITY_REGEX do |p|
15
+ case p[1]
16
+ when "A"
17
+ color = :red
18
+ when "B"
19
+ color = :yellow
20
+ when "C"
21
+ color = :green
22
+ else
23
+ color = :white
24
+ end
25
+
26
+ p.to_s.color(color)
27
+ end
28
+
29
+ text.gsub! PROJECT_REGEX, '\1'.color(:green)
30
+ text.gsub! CONTEXT_REGEX, '\1'.color(:blue)
31
+ else
32
+ text = text.color(:black).bright
33
+ end
34
+
35
+ ret = ""
36
+
37
+ ret << "#{line}. ".color(:black).bright
38
+ ret << "#{text}"
39
+ end
40
+
41
+ def warn message=""
42
+ puts "WARN: #{message}".color(:yellow)
43
+ end
44
+
45
+ def notice message=""
46
+ puts "=> #{message}".color(:green)
47
+ end
48
+
49
+ def error message=""
50
+ puts "ERROR: #{message}".color(:red)
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,6 @@
1
+ module Todotxt
2
+ PRIORITY_REGEX = /^\(([A-Z])\) /
3
+ PROJECT_REGEX = /(\+\w+)/
4
+ CONTEXT_REGEX = /(@\w+)/
5
+ DONE_REGEX = /^(\([A-Z]\) )?x /
6
+ end
@@ -0,0 +1,90 @@
1
+ require "todotxt/regex"
2
+
3
+ module Todotxt
4
+ class Todo
5
+
6
+ attr_accessor :text
7
+ attr_accessor :line
8
+ attr_accessor :priority
9
+ attr_accessor :projects
10
+ attr_accessor :contexts
11
+ attr_accessor :done
12
+
13
+ def initialize text, line=nil
14
+ @line = line
15
+
16
+ create_from_text text
17
+ end
18
+
19
+ def create_from_text text
20
+ @text = text
21
+ @priority = text.scan(PRIORITY_REGEX).flatten.first || nil
22
+ @projects = text.scan(PROJECT_REGEX).flatten.uniq || []
23
+ @contexts = text.scan(CONTEXT_REGEX).flatten.uniq || []
24
+ @done = !text.scan(DONE_REGEX).empty?
25
+ end
26
+
27
+ def do
28
+ unless done
29
+ @text = "x #{text}".strip
30
+ @done = true
31
+ end
32
+ end
33
+
34
+ def undo
35
+ if done
36
+ @text = text.sub(DONE_REGEX, "").strip
37
+ @done = false
38
+ end
39
+ end
40
+
41
+ def prioritize new_priority=nil, opts={}
42
+ if new_priority && !new_priority.match(/^[A-Z]$/i)
43
+ return
44
+ end
45
+
46
+ if new_priority
47
+ new_priority = new_priority.upcase
48
+ end
49
+
50
+ priority_string = new_priority ? "(#{new_priority}) " : ""
51
+
52
+ if priority && !opts[:force]
53
+ @text.gsub! PRIORITY_REGEX, priority_string
54
+ else
55
+ @text = "#{priority_string}#{text}".strip
56
+ end
57
+
58
+ @priority = new_priority
59
+ end
60
+
61
+ def append appended_text=""
62
+ @text << " " << appended_text
63
+ end
64
+
65
+ def prepend prepended_text=""
66
+ @text = "#{prepended_text} #{text.gsub(PRIORITY_REGEX, '')}"
67
+ prioritize priority, :force => true
68
+ end
69
+
70
+ def replace text
71
+ create_from_text text
72
+ end
73
+
74
+ def to_s
75
+ text.clone
76
+ end
77
+
78
+ def <=> b
79
+ if priority.nil? && b.priority.nil?
80
+ return line <=> b.line
81
+ end
82
+
83
+ return 1 if priority.nil?
84
+ return -1 if b.priority.nil?
85
+
86
+ return priority <=> b.priority
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,83 @@
1
+ require "todotxt/todo"
2
+
3
+ module Todotxt
4
+ class TodoList
5
+ include Enumerable
6
+
7
+ attr_accessor :todos
8
+
9
+ def initialize file
10
+ @todos = []
11
+ @file = file
12
+
13
+ File.open(file).read.each_line do |l|
14
+ add l.strip unless l.empty?
15
+ end
16
+ end
17
+
18
+ def add str
19
+ todo = Todo.new str, (@todos.count + 1)
20
+ @todos.push todo
21
+ @todos.sort!
22
+
23
+ return todo
24
+ end
25
+
26
+ def remove line
27
+ @todos.reject! { |t| t.line.to_s == line.to_s }
28
+ end
29
+
30
+ def projects
31
+ map { |t| t.projects }.flatten.uniq.sort
32
+ end
33
+
34
+ def contexts
35
+ map { |t| t.contexts }.flatten.uniq.sort
36
+ end
37
+
38
+ def find_by_line line
39
+ @todos.find { |t| t.line.to_s == line.to_s }
40
+ end
41
+
42
+ def save
43
+ File.open(@file, "w") { |f| f.write to_txt }
44
+ end
45
+
46
+ def each &block
47
+ @todos.each &block
48
+ end
49
+
50
+ def filter search="", opts={}
51
+ @todos.select! do |t|
52
+ select = false
53
+
54
+ text = t.to_s.downcase
55
+
56
+ if opts[:only_done]
57
+ select = true if t.done
58
+ else
59
+ if opts[:with_done]
60
+ select = true
61
+ else
62
+ select = true unless t.done
63
+ end
64
+ end
65
+
66
+ select = false unless text.include?(search.downcase)
67
+
68
+ select
69
+ end
70
+
71
+ self
72
+ end
73
+
74
+ def to_txt
75
+ @todos.sort { |a,b| a.line <=> b.line }.map { |t| t.to_s.strip }.join("\n")
76
+ end
77
+
78
+ def to_a
79
+ map { |t| ["#{t.line}. ", t.to_s] }
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,3 @@
1
+ module Todotxt
2
+ VERSION = "0.0.1"
3
+ end
data/lib/todotxt.rb ADDED
@@ -0,0 +1,13 @@
1
+ # Setup our load paths
2
+ libdir = File.dirname(__FILE__)
3
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
4
+
5
+ module Todotxt
6
+ autoload :Todo, "todotxt/todo"
7
+ autoload :TodoList, "todotxt/todolist"
8
+ autoload :CLI, "todotxt/cli"
9
+ autoload :CLIHelpers, "todotxt/clihelpers"
10
+ end
11
+
12
+ require "todotxt/regex"
13
+ require "todotxt/version"
data/spec/todo_spec.rb ADDED
@@ -0,0 +1,129 @@
1
+ require "todotxt/todo"
2
+
3
+ describe Todotxt::Todo do
4
+
5
+ it "should create a todo item string" do
6
+ todo = Todotxt::Todo.new "an item"
7
+ todo.to_s.should eql("an item")
8
+ end
9
+
10
+ it "should parse metadata when creating a simple item" do
11
+ todo = Todotxt::Todo.new "x an item +project1 +project2 @context1 @context2"
12
+
13
+ todo.to_s.should eql "x an item +project1 +project2 @context1 @context2"
14
+ todo.priority.should eql nil
15
+ todo.projects.should eql ["+project1", "+project2"]
16
+ todo.contexts.should eql ["@context1", "@context2"]
17
+ todo.done.should eql true
18
+ end
19
+
20
+ it "should parse metadata when creating an item with priority" do
21
+ todo = Todotxt::Todo.new "(A) x an item +project1 +project2 @context1 @context2"
22
+
23
+ todo.to_s.should eql "(A) x an item +project1 +project2 @context1 @context2"
24
+ todo.priority.should eql "A"
25
+ todo.projects.should eql ["+project1", "+project2"]
26
+ todo.contexts.should eql ["@context1", "@context2"]
27
+ todo.done.should eql true
28
+ end
29
+
30
+ it "should store line number when creating an item" do
31
+ todo = Todotxt::Todo.new "an item", "2"
32
+
33
+ todo.line.should eql "2"
34
+ end
35
+
36
+ it "should set an item as done" do
37
+ todo = Todotxt::Todo.new "an item"
38
+
39
+ todo.do
40
+
41
+ todo.to_s.should eql "x an item"
42
+ todo.done.should eql true
43
+ end
44
+
45
+ it "should set an item as not done" do
46
+ todo = Todotxt::Todo.new "x an item"
47
+
48
+ todo.undo
49
+
50
+ todo.to_s.should eql "an item"
51
+ todo.done.should eql false
52
+ end
53
+
54
+ it "should add priority to an item" do
55
+ todo = Todotxt::Todo.new "an item"
56
+
57
+ todo.prioritize "a"
58
+
59
+ todo.to_s.should eql "(A) an item"
60
+ todo.priority.should eql "A"
61
+ end
62
+
63
+ it "should change priority of an item" do
64
+ todo = Todotxt::Todo.new "(A) an item"
65
+
66
+ todo.prioritize "z"
67
+
68
+ todo.to_s.should eql "(Z) an item"
69
+ todo.priority.should eql "Z"
70
+ end
71
+
72
+ it "should remove priority from an item" do
73
+ todo = Todotxt::Todo.new "(A) an item"
74
+
75
+ todo.prioritize
76
+
77
+ todo.to_s.should eql "an item"
78
+ todo.priority.should eql nil
79
+ end
80
+
81
+ it "should append text to an item" do
82
+ todo = Todotxt::Todo.new "an item"
83
+
84
+ todo.append "more text"
85
+
86
+ todo.to_s.should eql "an item more text"
87
+ end
88
+
89
+ it "should prepend text to an item" do
90
+ todo = Todotxt::Todo.new "an item"
91
+
92
+ todo.prepend "more text"
93
+
94
+ todo.to_s.should eql "more text an item"
95
+ end
96
+
97
+ it "should preserve priority when prepending text to an item" do
98
+ todo = Todotxt::Todo.new "(A) an item"
99
+
100
+ todo.prepend "more text"
101
+
102
+ todo.to_s.should eql "(A) more text an item"
103
+ todo.priority.should eql "A"
104
+ end
105
+
106
+ it "should replace an item with new text" do
107
+ todo = Todotxt::Todo.new "an item"
108
+
109
+ todo.replace "(A) a replacement item"
110
+
111
+ todo.to_s.should eql "(A) a replacement item"
112
+ todo.priority.should eql "A"
113
+ end
114
+
115
+ it "should sort based on line number" do
116
+ todo1 = Todotxt::Todo.new "an item 1", 1
117
+ todo2 = Todotxt::Todo.new "an item 2", 2
118
+
119
+ (todo1 <=> todo2).should eql -1
120
+ end
121
+
122
+ it "should value items with priority higher when sorting" do
123
+ todo1 = Todotxt::Todo.new "an item 1", 1
124
+ todo2 = Todotxt::Todo.new "(A) an item 2", 2
125
+
126
+ (todo1 <=> todo2).should eql 1
127
+ end
128
+
129
+ end
@@ -0,0 +1,99 @@
1
+ require "todotxt/todolist"
2
+
3
+ describe Todotxt::TodoList do
4
+
5
+ describe "with simple list" do
6
+ before :each do
7
+ @list = Todotxt::TodoList.new File.join(File.dirname(__FILE__), "..", "fixtures", "simple_todo.txt")
8
+ end
9
+
10
+ it "should parse a file on creation" do
11
+ @list.todos[0].to_s.should eql "First item"
12
+ @list.todos[1].to_s.should eql "Second item"
13
+ @list.todos[2].to_s.should eql "Third item"
14
+ @list.todos[3].to_s.should eql "x First done item"
15
+
16
+ @list.todos[0].line.should eql 1
17
+ @list.todos[1].line.should eql 2
18
+ @list.todos[2].line.should eql 3
19
+ @list.todos[3].line.should eql 4
20
+ end
21
+
22
+ it "should add a new item" do
23
+ @list.add "Fourth item"
24
+
25
+ @list.todos[4].to_s.should eql "Fourth item"
26
+ @list.todos[4].line.should eql 5
27
+ end
28
+
29
+ it "should remove an item" do
30
+ @list.remove 1
31
+
32
+ @list.todos[0].to_s.should eql "Second item"
33
+ end
34
+
35
+ it "should find item by line" do
36
+ todo = @list.find_by_line 3
37
+
38
+ todo.to_s.should eql "Third item"
39
+ end
40
+
41
+ it "should filter list when searching" do
42
+ @list.filter "First"
43
+
44
+ @list.todos.count.should eql 1
45
+ end
46
+
47
+ it "should filter list when searching case-sensitive" do
48
+ @list.filter "first"
49
+
50
+ @list.todos.count.should eql 1
51
+ end
52
+
53
+ it "should include done items in search when told to do so" do
54
+ @list.filter "first", :with_done => true
55
+
56
+ @list.todos.count.should eql 2
57
+ end
58
+
59
+ it "should only include done items in search when told to do so" do
60
+ @list.filter "first", :only_done => true
61
+
62
+ @list.todos.count.should eql 1
63
+ end
64
+
65
+ it "should render plain text" do
66
+ comparison_string = <<EOF
67
+ First item
68
+ Second item
69
+ Third item
70
+ x First done item
71
+ EOF
72
+ @list.to_txt.should eql comparison_string.strip
73
+ end
74
+ end
75
+
76
+ describe "with complex list" do
77
+ before :each do
78
+ @list = Todotxt::TodoList.new File.join(File.dirname(__FILE__), "..", "fixtures", "complex_todo.txt")
79
+ end
80
+
81
+ it "should sort itself automatically on parse" do
82
+ @list.todos[0].to_s.should eql "(A) an item"
83
+ @list.todos[0].line.should eql 3
84
+ end
85
+
86
+ it "should re-sort itself after adding a new item" do
87
+ @list.add "(B) A new item"
88
+
89
+ @list.todos[1].to_s.should eql "(B) A new item"
90
+ @list.todos[1].line.should eql 4
91
+ end
92
+
93
+ it "should list all projects and contexts in the list" do
94
+ @list.projects.should eql ["+project1", "+project2"]
95
+ @list.contexts.should eql ["@context1", "@context2"]
96
+ end
97
+ end
98
+
99
+ end
data/todotxt.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "todotxt/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "todotxt"
7
+ s.version = Todotxt::VERSION
8
+ s.authors = ["Johan Sahlén"]
9
+ s.email = ["johan@monospace.se"]
10
+ s.homepage = "http://github.com/jsahlen/todotxt"
11
+ s.summary = %q{todo.txt with a Ruby flair}
12
+ s.description = %q{Interact with your todo.txt using a Ruby-base command-line tool}
13
+
14
+ s.rubyforge_project = "todotxt"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "thor", ">= 0.14.6"
22
+ s.add_dependency "rainbow", ">= 1.1.1"
23
+ s.add_dependency "parseconfig", ">= 0.5.2"
24
+
25
+ s.add_development_dependency "rspec", ">= 2.6.0"
26
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: todotxt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Johan Sahlén
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-09 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: &2164860840 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.14.6
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2164860840
25
+ - !ruby/object:Gem::Dependency
26
+ name: rainbow
27
+ requirement: &2164860340 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.1
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2164860340
36
+ - !ruby/object:Gem::Dependency
37
+ name: parseconfig
38
+ requirement: &2164859880 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 0.5.2
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2164859880
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &2164859420 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 2.6.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2164859420
58
+ description: Interact with your todo.txt using a Ruby-base command-line tool
59
+ email:
60
+ - johan@monospace.se
61
+ executables:
62
+ - todotxt
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .gitignore
67
+ - Gemfile
68
+ - MIT-LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - bin/todotxt
72
+ - conf/todo.txt
73
+ - conf/todotxt.cfg
74
+ - fixtures/complex_todo.txt
75
+ - fixtures/simple_todo.txt
76
+ - lib/todotxt.rb
77
+ - lib/todotxt/cli.rb
78
+ - lib/todotxt/clihelpers.rb
79
+ - lib/todotxt/regex.rb
80
+ - lib/todotxt/todo.rb
81
+ - lib/todotxt/todolist.rb
82
+ - lib/todotxt/version.rb
83
+ - spec/todo_spec.rb
84
+ - spec/todolist_spec.rb
85
+ - todotxt.gemspec
86
+ homepage: http://github.com/jsahlen/todotxt
87
+ licenses: []
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubyforge_project: todotxt
106
+ rubygems_version: 1.8.6
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: todo.txt with a Ruby flair
110
+ test_files:
111
+ - spec/todo_spec.rb
112
+ - spec/todolist_spec.rb