todorb 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Rahul Kumar
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.markdown ADDED
@@ -0,0 +1,20 @@
1
+ # todo.rb
2
+
3
+ ## Command-line todo manager
4
+
5
+ This is a port of my [todoapp written in shell](http://github.com/rkumar/todoapp). That todo app is similar to the famous one by Gina Trapani, except that it added various features including sub-tasks, and notes.
6
+
7
+ I may not make this application as feature-rich as the [shell version](http://github.com/rkumar/todoapp), however I needed to do this so I stop writing command-line programs using shell (portability issues across BSD and GNU versions of all commands). The shell version used `sed` extensively.
8
+
9
+ The fun things about this app, is that I am having to write ruby code that will give me sed's functionality (subset of course). Some of the stuff is present in sed.rb and is heavily being rewritten and improved as I go along.
10
+
11
+ The TODO file output is TODO2.txt and is a plain text file. A TAB separates the task number from the Task. I use task numbers, since I may refer to tasks elsewhere. Gina's app never saved a task Id but kept showing them.
12
+
13
+ The shell version, todoapp, allowed for any levels of sub-tasks. I have not yet added that, I may.
14
+
15
+ I also may fork this and make a YAML file, so that the format is standard, especially when having sub-tasks.
16
+ After this, I will port over my bug tracker, [bugzy.txt](http://github.com/rkumar/bugzy.txt), which uses a TAB delimited file. It's a cool app to use for bug tracking - you should try it out. I will possibly use sqlite instead of screwing around with a delimited file.
17
+
18
+ ## Copyright
19
+
20
+ Copyright (c) 2010 Rahul Kumar. See LICENSE for details.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/bin/todorb ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ #require 'todorb/todo.rb'
4
+ require 'todorb'
5
+ Todo.main(ARGV)
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Color constants so we can print onto console using print or puts
4
+ # @example
5
+ # string = "hello ruby"
6
+ # puts " #{RED}#{BOLD}#{UNDERLINE}#{string}#{CLEAR}"
7
+ #
8
+ # http://wiki.bash-hackers.org/scripting/terminalcodes
9
+ # ripped off highline gem
10
+ module ColorConstants
11
+
12
+ # Embed in a String to clear all previous ANSI sequences. This *MUST* be
13
+ # done before the program exits!
14
+ #
15
+ CLEAR = "\e[0m"
16
+ # An alias for CLEAR.
17
+ RESET = CLEAR
18
+ NORMAL = CLEAR
19
+ # Erase the current line of terminal output.
20
+ ERASE_LINE = "\e[K"
21
+ # Erase the character under the cursor.
22
+ ERASE_CHAR = "\e[P"
23
+ # The start of an ANSI bold sequence.
24
+ BOLD = "\e[1m"
25
+ # The start of an ANSI dark sequence. (Terminal support uncommon.)
26
+ DARK = "\e[2m"
27
+ DIM = DARK
28
+ # The start of an ANSI underline sequence.
29
+ UNDERLINE = "\e[4m"
30
+ # An alias for UNDERLINE.
31
+ UNDERSCORE = UNDERLINE
32
+ # The start of an ANSI blink sequence. (Terminal support uncommon.)
33
+ BLINK = "\e[5m"
34
+ # The start of an ANSI reverse sequence.
35
+ REVERSE = "\e[7m"
36
+ # The start of an ANSI concealed sequence. (Terminal support uncommon.)
37
+ CONCEALED = "\e[8m"
38
+
39
+ # added from http://understudy.net/custom.html
40
+ BOLD_OFF = "\e[22m"
41
+ UNDERILNE_OFF = "\e[24m"
42
+ BLINK_OFF = "\e[25m"
43
+ REVERSE_OFF = "\e[27m"
44
+
45
+ # Set the terminal's foreground ANSI color to black.
46
+ BLACK = "\e[30m"
47
+ # Set the terminal's foreground ANSI color to red.
48
+ RED = "\e[31m"
49
+ # Set the terminal's foreground ANSI color to green.
50
+ GREEN = "\e[32m"
51
+ # Set the terminal's foreground ANSI color to yellow.
52
+ YELLOW = "\e[33m"
53
+ # Set the terminal's foreground ANSI color to blue.
54
+ BLUE = "\e[34m"
55
+ # Set the terminal's foreground ANSI color to magenta.
56
+ MAGENTA = "\e[35m"
57
+ # Set the terminal's foreground ANSI color to cyan.
58
+ CYAN = "\e[36m"
59
+ # Set the terminal's foreground ANSI color to white.
60
+ WHITE = "\e[37m"
61
+
62
+ # Set the terminal's background ANSI color to black.
63
+ ON_BLACK = "\e[40m"
64
+ # Set the terminal's background ANSI color to red.
65
+ ON_RED = "\e[41m"
66
+ # Set the terminal's background ANSI color to green.
67
+ ON_GREEN = "\e[42m"
68
+ # Set the terminal's background ANSI color to yellow.
69
+ ON_YELLOW = "\e[43m"
70
+ # Set the terminal's background ANSI color to blue.
71
+ ON_BLUE = "\e[44m"
72
+ # Set the terminal's background ANSI color to magenta.
73
+ ON_MAGENTA = "\e[45m"
74
+ # Set the terminal's background ANSI color to cyan.
75
+ ON_CYAN = "\e[46m"
76
+ # Set the terminal's background ANSI color to white.
77
+ ON_WHITE = "\e[47m"
78
+
79
+ end
data/lib/common/sed.rb ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby -w
2
+ #*************************************************************
3
+ # Sed like operations - just change row and delete row
4
+ # While converting some shell scripts in which i have used
5
+ # sed extensively, I need this kind of functionality
6
+ #
7
+ #*************************************************************
8
+ ##
9
+ # changes one or more rows based on pattern and replacement
10
+ # Also if replacement is not given, expects a block and yields
11
+ # matching lines to it
12
+
13
+ module Sed
14
+ def change_row filename, pattern, replacement = nil
15
+ d = _read filename
16
+ d.each { |row|
17
+ if row =~ pattern
18
+ if replacement
19
+ row.gsub!( pattern, replacement)
20
+ else
21
+ yield row
22
+ end
23
+ end
24
+ }
25
+ _write filename, d
26
+ end
27
+ def change_file filename
28
+ d = _read filename
29
+ d.each { |row|
30
+ yield row
31
+ }
32
+ _write filename, d
33
+ end
34
+ ##
35
+ # deletes on more rows based on a pattern
36
+ # Also takes a block and yields each row to it
37
+ def delete_row filename, pattern = nil
38
+ d = _read filename
39
+ if pattern
40
+ d.delete_if { |row| row =~ pattern }
41
+ else
42
+ d.delete_if { |row| yield row }
43
+ end
44
+ _write filename, d
45
+ end
46
+ ##
47
+ # read the given filename into an array
48
+ def _read filename
49
+ d = []
50
+ File.open(filename).each { |line|
51
+ d << line
52
+ }
53
+ return d
54
+ end
55
+ ##
56
+ # write the given array to the filename
57
+ def _write filename, array
58
+ File.open(filename, "w") do |file|
59
+ array.each { |row| file.puts row }
60
+ end
61
+ end
62
+ end # module
63
+
64
+ if __FILE__ == $0
65
+ include Sed
66
+ filename = "tmp.txt"
67
+ change_row filename, /_10_/ do |row|
68
+ row.sub!(/_10_/,"10")
69
+ end
70
+ change_row filename, /\+13\+/, "13"
71
+ delete_row(filename,/XXX/)
72
+ delete_row(filename) do |line|
73
+ line =~ /junk/
74
+ end
75
+ end
data/lib/todorb.rb ADDED
@@ -0,0 +1,698 @@
1
+ #!/usr/bin/env ruby -w
2
+ =begin
3
+ * Name: todorb.rb
4
+ * Description: a command line todo list manager
5
+ * Author: rkumar
6
+ * Date: 2010-06-10 20:10
7
+ * License: GPL
8
+
9
+ =end
10
+ require 'rubygems'
11
+ #require 'csv'
12
+ require 'common/colorconstants'
13
+ include ColorConstants
14
+ require 'common/sed'
15
+ include Sed
16
+
17
+ PRI_A = YELLOW + BOLD
18
+ PRI_B = WHITE + BOLD
19
+ PRI_C = GREEN + BOLD
20
+ PRI_D = CYAN + BOLD
21
+ VERSION = "1.0"
22
+ DATE = "2010-06-10"
23
+ APPNAME = $0
24
+ AUTHOR = "rkumar"
25
+
26
+ class Todo
27
+ def initialize options, argv
28
+
29
+ @options = options
30
+ @argv = argv
31
+ @file = options[:file]
32
+ ## data is a 2 dim array: rows and fields. It contains each row of the file
33
+ # as an array of strings. The item number is space padded.
34
+ @data = []
35
+ init_vars
36
+ end
37
+ def init_vars
38
+ @todo_default_action = "list"
39
+ @todo_file_path = @options[:file] || "TODO2.txt"
40
+ @archive_path = "archive.txt" # should take path of todo and put there TODO:
41
+ @todo_delim = "\t"
42
+ @appname = File.basename( Dir.getwd ) #+ ".#{$0}"
43
+ t = Time.now
44
+ @now = t.strftime("%Y-%m-%d %H:%M:%S")
45
+ @today = t.strftime("%Y-%m-%d")
46
+ @verbose = @options[:verbose]
47
+ #@actions = %w[ list add pri priority depri tag del delete status redo note archive help]
48
+ @actions = {}
49
+ @actions["list"] = "List all tasks.\n\t --hide-numbering --renumber"
50
+ @actions["add"] = "Add a task. \n\t #{$0} add <TEXT>\n\t --component C --project P --priority X add <TEXT>"
51
+ @actions["pri"] = "Add priority to task. \n\t #{$0} pri <ITEM> [A-Z]"
52
+ @actions["priority"] = "Same as pri"
53
+ @actions["depri"] = "Remove priority of task. \n\t #{$0} depri <ITEM>"
54
+ @actions["delete"] = "Delete a task. \n\t #{$0} delete <ITEM>"
55
+ @actions["del"] = "Same as delete"
56
+ @actions["status"] = "Change the status of a task. \n\t #{$0} status <STAT> <ITEM>\n\t<STAT> are closed started pending unstarted hold next"
57
+ @actions["redo"] = "Renumbers the todo file starting 1"
58
+ @actions["note"] = "Add a note to an item. \n\t #{$0} note <ITEM> <TEXT>"
59
+ @actions["archive"] = "archive closed tasks to archive.txt"
60
+ @actions["help"] = "Display help"
61
+
62
+
63
+ # TODO config
64
+ # we need to read up from config file and update
65
+ end
66
+ # menu MENU
67
+ def run
68
+ @action = @argv[0] || @todo_default_action
69
+ @action = @action.downcase
70
+ @action.sub!('priority', 'pri')
71
+ @action.sub!(/^del$/, 'delete')
72
+
73
+
74
+ @argv.shift
75
+ if @actions.include? @action
76
+ send(@action, @argv)
77
+ else
78
+ help @argv
79
+ end
80
+ end
81
+ def help args
82
+ #puts "Actions are #{@actions.join(", ")} "
83
+ @actions.each_pair { |name, val| puts "#{name}\t#{val}" }
84
+ end
85
+ def add args
86
+ if args.empty?
87
+ print "Enter todo: "
88
+ STDOUT.flush
89
+ text = gets.chomp
90
+ if text.empty?
91
+ exit 1
92
+ end
93
+ Kernel.print("You gave me '#{text}'")
94
+ else
95
+ text = args.join " "
96
+ Kernel.print("I got '#{text}'")
97
+ end
98
+ # convert actual newline to C-a. slash n's are escapes so echo -e does not muck up.
99
+ text.tr! "\n", ''
100
+ Kernel.print("Got '#{text}'\n")
101
+ item = _get_serial_number
102
+ paditem = _paditem(item)
103
+ print "item no is:#{paditem}:\n"
104
+ priority = @options[:priority] ? " (#{@options[:priority]})" : ""
105
+ project = @options[:project] ? " +#{@options[:project]}" : ""
106
+ component = @options[:component] ? " @#{@options[:component]}" : ""
107
+ newtext="#{paditem}#{@todo_delim}[ ]#{priority}#{project}#{component} #{text} (#{@today})"
108
+ puts "Adding"
109
+ puts newtext
110
+ File.open(@todo_file_path, "a") { | file| file.puts newtext }
111
+
112
+ end
113
+ ##
114
+ # reads serial_number file, returns serialno for this app
115
+ # and increments the serial number and writes back.
116
+ def _get_serial_number
117
+ require 'fileutils'
118
+ appname = @appname
119
+ filename = "/Users/rahul/serial_numbers"
120
+ h = {}
121
+ File.open(filename).each { |line|
122
+ #sn = $1 if line.match regex
123
+ x = line.split ":"
124
+ h[x[0]] = x[1].chomp
125
+ }
126
+ sn = h[appname] || 1
127
+ # update the sn in file
128
+ nsn = sn.to_i + 1
129
+ # this will create if not exists in addition to storing if it does
130
+ h[appname] = nsn
131
+ # write back to file
132
+ File.open(filename, "w") do |f|
133
+ h.each_pair {|k,v| f.print "#{k}:#{v}\n"}
134
+ end
135
+ return sn
136
+ end
137
+ ##
138
+ # After doing a redo of the numbering, we need to reset the numbers for that app
139
+ def _set_serial_number number
140
+ appname = @appname
141
+ pattern = Regexp.new "^#{appname}:.*$"
142
+ filename = "/Users/rahul/serial_numbers"
143
+ _backup filename
144
+ change_row filename, pattern, "#{appname}:#{number}"
145
+ end
146
+ def _backup filename=@todo_file_path
147
+ require 'fileutils'
148
+ FileUtils.cp filename, "#{filename}.org"
149
+ end
150
+ ##
151
+ # for historical reasons, I pad item to 3 spaces in text file.
152
+ # It used to help me in printing straight off without any formatting in unix shell
153
+ def _paditem item
154
+ return sprintf("%3s", item)
155
+ end
156
+ def populate
157
+ @ctr = 0
158
+ @total = 0
159
+ #CSV.foreach(@file,:col_sep => "\t") do |row| # 1.9 2009-10-05 11:12
160
+ File.open(@file).each do |line|
161
+ row = line.chomp.split "\t"
162
+ @total += 1
163
+ if @options[:show_all]
164
+ @data << row
165
+ @ctr += 1
166
+ else
167
+ unless row[1] =~ /^\[x\]/
168
+ @data << row
169
+ @ctr += 1
170
+ end
171
+ end
172
+ end
173
+ end
174
+ ##
175
+ # filters output based on project and or component and or priority
176
+ def filter
177
+ project = @options[:project]
178
+ component = @options[:component]
179
+ priority = @options[:priority]
180
+ if project
181
+ r = Regexp.new "\\+#{project}"
182
+ @data = @data.select { |row| row[1] =~ r }
183
+ end
184
+ if component
185
+ r = Regexp.new "@#{component}"
186
+ @data = @data.select { |row| row[1] =~ r }
187
+ end
188
+ if priority
189
+ r = Regexp.new "\\(#{priority}\\)"
190
+ @data = @data.select { |row| row[1] =~ r }
191
+ end
192
+ end
193
+ def list args
194
+ populate
195
+ grep if @options[:grep]
196
+ filter if @options[:filter]
197
+ sort if @options[:sort]
198
+ renumber if @options[:renumber]
199
+ colorize # << currently this is where I print !! Since i colorize the whole line
200
+ puts
201
+ puts " #{@data.length} of #{@total} rows displayed from #{@todo_file_path} "
202
+
203
+ end
204
+ def print_todo
205
+ @ctr = 0
206
+ @data.each { |row|
207
+ unless row[1] =~ /^\[x\]/
208
+ puts " #{row[0]} | #{row[1]} " #unless row[1] =~ /^\[x\]/
209
+ @ctr += 1
210
+ end
211
+ }
212
+ end
213
+ def each
214
+ @data.each { |row|
215
+ yield row
216
+ }
217
+ end
218
+ def active_tasks
219
+ @ctr = 0
220
+ @data.each { |row|
221
+ unless row[1] =~ /^\[x\]/
222
+ yield row
223
+ @ctr += 1
224
+ end
225
+ }
226
+ end
227
+ ##
228
+ # colorize each line, if required.
229
+ # However, we should put the colors in some Map, so it can be changed at configuration level.
230
+ #
231
+ def colorize
232
+ colorme = @options[:colorize]
233
+ @data.each do |r|
234
+ if @options[:hide_numbering]
235
+ string = "#{r[1]} "
236
+ else
237
+ string = " #{r[0]} #{r[1]} "
238
+ end
239
+ if colorme
240
+ m=string.match(/\(([A-Z])\)/)
241
+ if m
242
+ case m[1]
243
+ when "A", "B", "C", "D"
244
+ pri = self.class.const_get("PRI_#{m[1]}")
245
+ #string = "#{YELLOW}#{BOLD}#{string}#{CLEAR}"
246
+ string = "#{pri}#{string}#{CLEAR}"
247
+ else
248
+ string = "#{NORMAL}#{GREEN}#{string}#{CLEAR}"
249
+ #string = "#{BLUE}\e[6m#{string}#{CLEAR}"
250
+ #string = "#{BLUE}#{string}#{CLEAR}"
251
+ end
252
+ else
253
+ string = "#{NORMAL}#{string}#{CLEAR}"
254
+ end
255
+ end # colorme
256
+ ## since we've added notes, we convert C-a to newline with spaces
257
+ # so it prints in next line with some neat indentation.
258
+ string.gsub!('', "\n ")
259
+ #string.tr! '', "\n"
260
+ puts string
261
+ end
262
+ end
263
+ def sort
264
+ @data.sort! { |a,b| b[1] <=> a[1] }
265
+ end
266
+ def grep
267
+ r = Regexp.new @options[:grep]
268
+ #@data = @data.grep r
269
+ @data = @data.find_all {|i| i[1] =~ r }
270
+ end
271
+ ##
272
+ # Adds or changes priority for a task
273
+ #
274
+ # @param [Array] priority, single char A-Z, item or items
275
+ # @return
276
+ # @ example:
277
+ # pri A 5 6 7
278
+ # pri 5 6 7 A
279
+ # pri A 5 6 7 B 1 2 3
280
+ # pri 5 6 7 A 1 2 3 B
281
+
282
+ def pri args
283
+ populate
284
+ changeon = nil
285
+ items = []
286
+ prior = nil
287
+ item = nil
288
+ ## if the first arg is priority then following items all have that priority
289
+ ## if the first arg is item/s then wait for priority and use that
290
+ if args[0] =~ /^[A-Z]$/
291
+ changeon = :ITEM
292
+ elsif args[0] =~ /^[0-9]+$/
293
+ changeon = :PRI
294
+ else
295
+ puts "ERROR! "
296
+ exit 1
297
+ end
298
+ puts "args 0 is #{args[0]} "
299
+ args.each do |arg|
300
+ if arg =~ /^[A-Z]$/
301
+ prior = arg #$1
302
+ if changeon == :PRI
303
+ puts " changing previous items #{items} to #{prior} "
304
+ items.each { |i| _pri(i, prior) }
305
+ items = []
306
+ end
307
+ elsif arg =~ /^[0-9]+$/
308
+ item = arg #$1
309
+ if changeon == :ITEM
310
+ puts " changing #{item} to #{prior} "
311
+ _pri(item, prior)
312
+ else
313
+ items << item
314
+ end
315
+ else
316
+ puts "ERROR in arg :#{arg}:"
317
+ end
318
+ end
319
+ save_array
320
+ end
321
+ ##
322
+ # Reove the priority of a task
323
+ #
324
+ # @param [Array] items to deprioritize
325
+ # @return
326
+ public
327
+ def depri(args)
328
+ populate
329
+ puts "depri got #{args} "
330
+ each do |row|
331
+ item = row[0].sub(/^[ -]*/,'')
332
+ if args.include? item
333
+ if row[1] =~ /\] (\([A-Z]\) )/
334
+ puts row[1]
335
+ row[1].sub!(/\([A-Z]\) /,"")
336
+ puts "#{RED}#{row[1]}#{CLEAR}"
337
+ end
338
+ end
339
+ end
340
+ end
341
+ ##
342
+ # saves the task array to disk
343
+ def save_array
344
+ File.open(@todo_file_path, "w") do |file|
345
+ @data.each { |row| file.puts "#{row[0]}\t#{row[1]}" }
346
+ end
347
+ end
348
+ ##
349
+ # change priority of given item to priority in array
350
+ private
351
+ def _pri item, pri
352
+ paditem = _paditem(item)
353
+ @data.each { |row|
354
+ if row[0] == paditem
355
+ puts " #{row[0]} : #{row[1]} "
356
+ if row[1] =~ /\] (\([A-Z]\) )/
357
+ row[1].sub!(/\([A-Z]\) /,"")
358
+ end
359
+ row[1].sub!(/\] /,"] (#{pri}) ")
360
+ puts " #{RED}#{row[0]} : #{row[1]} #{CLEAR}"
361
+ return true
362
+ end
363
+ }
364
+ puts " #{RED} no such item #{item} #{CLEAR} "
365
+ return false
366
+
367
+ end
368
+ ##
369
+ # Appends a tag to task
370
+ #
371
+ # @param [Array] items and tag, or tag and items
372
+ # @return
373
+ public
374
+ def tag(args)
375
+ puts "tags args #{args} "
376
+ items_first = items_first? args
377
+ items = []
378
+ tag = nil
379
+ args.each do |arg|
380
+ if arg =~ /^[a-zA-Z]/
381
+ tag = arg
382
+ elsif arg =~ /^[0-9]+$/
383
+ items << arg
384
+ end
385
+ end
386
+ #items.each { |i| change_row }
387
+ change_file @todo_file_path do |line|
388
+ item = line.match(/^ *([0-9]+)/)
389
+ if items.include? item[1]
390
+ puts "#{line}" if @verbose
391
+ line.sub!(/$/, " @#{tag}")
392
+ puts "#{RED}#{line}#{CLEAR} " if @verbose
393
+ end
394
+ end
395
+ end
396
+ ##
397
+ # deletes one or more items
398
+ #
399
+ # @param [Array, #include?] items to delete
400
+ # @return
401
+ public
402
+ def delete(args)
403
+ puts "delete with #{args} "
404
+ delete_row @todo_file_path do |line|
405
+ #puts "line #{line} "
406
+ item = line.match(/^ *([0-9]+)/)
407
+ #puts "item #{item} "
408
+ if args.include? item[1]
409
+ if @options[:force]
410
+ true
411
+ else
412
+ puts line
413
+ print "Do you wish to delete (Y/N): "
414
+ STDOUT.flush
415
+ ans = STDIN.gets.chomp
416
+ ans =~ /[Yy]/
417
+ end
418
+ end
419
+ end
420
+ end
421
+ ##
422
+ # Change status of given items
423
+ #
424
+ # @param [Array, #include?] items to delete
425
+ # @return [true, false] success or fail
426
+ public
427
+ def status(args)
428
+ stat, items = _separate args
429
+ status, newstatus = _resolve_status stat
430
+ if status.nil?
431
+ print_red "Status #{stat} is invalid!"
432
+ exit 1
433
+ end
434
+ change_items(items, /(\[.\])/, "[#{newstatus}]")
435
+ #change_items items do |item, line|
436
+ #puts line if @verbose
437
+ #line.sub!(/(\[.\])/, "[#{newstatus}]")
438
+ #puts "#{RED}#{line}#{CLEAR}" if @verbose
439
+ #end
440
+ end
441
+ ##
442
+ # separates args into tag or subcommand and items
443
+ # This allows user to pass e.g. a priority first and then item list
444
+ # or item list first and then priority.
445
+ # This can only be used if the tag or pri or status is non-numeric and the item is numeric.
446
+ def _separate args
447
+ tag = nil
448
+ items = []
449
+ args.each do |arg|
450
+ if arg =~ /^[a-zA-Z]/
451
+ tag = arg
452
+ elsif arg =~ /^[0-9]+$/
453
+ items << arg
454
+ end
455
+ end
456
+ return tag, items
457
+ end
458
+ ##
459
+ # Renumber while displaying
460
+ #
461
+ # @return [true, false] success or fail
462
+ private
463
+ def renumber
464
+ @data.each_with_index { |row, i|
465
+ paditem = _paditem(i+1)
466
+ row[0] = paditem
467
+ }
468
+ end
469
+ ##
470
+ # For given items, add a note
471
+ #
472
+ # @param [Array, #include?] items to add note to, note
473
+ # @return [true, false] success or fail
474
+ public
475
+ def note(args)
476
+ _backup
477
+ text = args.pop
478
+ change_items args do |item, line|
479
+ m = line.match(/^ */)
480
+ indent = m[0]
481
+ puts line if @verbose
482
+ # we place the text before the date, adding a C-a and indent
483
+ # At printing the C-a is replaced with a newline and some spaces
484
+ ret = line.sub!(/ (\([0-9]{4})/," #{indent}* #{text} "+'\1')
485
+ print_red line if @verbose
486
+ end
487
+ end
488
+ ##
489
+ # Archive all items
490
+ #
491
+ # @param none (ignored)
492
+ # @return [true, false] success or fail
493
+ public
494
+ def archive(args=nil)
495
+ filename = @archive_path
496
+ file = File.open(filename, "a")
497
+ ctr = 0
498
+ delete_row @todo_file_path do |line|
499
+ if line =~ /\[x\]/
500
+ file.puts line
501
+ ctr += 1
502
+ puts line if @verbose
503
+ true
504
+ end
505
+ end
506
+ file.close
507
+ puts "Archived #{ctr} tasks."
508
+ end
509
+ ##
510
+ # For given items, ...
511
+ #
512
+ # @param [Array, #include?] items to delete
513
+ # @return [true, false] success or fail
514
+ public
515
+ def CHANGEME(args)
516
+ end
517
+ ##
518
+ # yields lines from file that match the given item
519
+ # We do not need to now parse and match the item in each method
520
+ def change_items args, pattern=nil, replacement=nil
521
+ change_file @todo_file_path do |line|
522
+ item = line.match(/^ *([0-9]+)/)
523
+ if args.include? item[1]
524
+ if pattern
525
+ puts line if @verbose
526
+ line.sub!(pattern, replacement)
527
+ print_red line if @verbose
528
+ else
529
+ yield item[1], line
530
+ end
531
+ end
532
+ end
533
+ end
534
+ ##
535
+ # Redoes the numbering in the file.
536
+ # Useful if the numbers have gone high and you want to start over.
537
+ def redo args
538
+ ctr = 1
539
+ require 'fileutils'
540
+ FileUtils.cp @todo_file_path, "#{@todo_file_path}.org"
541
+ puts "Saved #{@todo_file_path} as #{@todo_file_path}.org"
542
+ change_file @todo_file_path do |line|
543
+ paditem = _paditem ctr
544
+ line.sub!(/^ *[0-9]+/, paditem)
545
+ ctr += 1
546
+ end
547
+ _set_serial_number ctr
548
+ puts "Redone numbering"
549
+ end
550
+ ##
551
+ # does this command start with items or something else
552
+ private
553
+ def items_first?(args)
554
+ return true if args[0] =~ /^[0-9]+$/
555
+ return false
556
+ end
557
+ def print_red line
558
+ puts "#{RED}#{line}#{CLEAR}"
559
+ end
560
+ private
561
+ def _resolve_status stat
562
+ status = nil
563
+ #puts " got #{stat} "
564
+ case stat
565
+ when "@","sta","star","start","started"
566
+ status="start"
567
+ newstatus = "@"
568
+ when "P","pen","pend","pending"
569
+ status="pend"
570
+ newstatus = "P"
571
+ when "x","clo","clos","close","closed"
572
+ status="close"
573
+ newstatus = "x"
574
+ when "1","next"
575
+ status="next"
576
+ newstatus = "1"
577
+ when "H","hold"
578
+ status="hold"
579
+ newstatus = "H"
580
+ when "u","uns","unst","unstart","unstarted"
581
+ status="unstarted"
582
+ newstatus = " "
583
+ end
584
+ #puts " after #{status} "
585
+ #newstatus=$( echo $status | sed 's/^start/@/;s/^pend/P/;s/^close/x/;s/hold/H/;s/next/1/;s/^unstarted/ /' )
586
+ return status, newstatus
587
+ end
588
+
589
+ def self.main args
590
+ begin
591
+ # http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
592
+ require 'optparse'
593
+ options = {}
594
+ options[:verbose] = false
595
+ options[:colorize] = true
596
+
597
+ OptionParser.new do |opts|
598
+ opts.banner = "Usage: #{$0} [options] action"
599
+
600
+ opts.separator ""
601
+ opts.separator "Specific options:"
602
+
603
+
604
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
605
+ options[:verbose] = v
606
+ end
607
+ opts.on("-f", "--file FILENAME", "CSV filename") do |v|
608
+ options[:file] = v
609
+ end
610
+ opts.on("-P", "--project PROJECTNAME", "name of project for add or list") { |v|
611
+ options[:project] = v
612
+ options[:filter] = true
613
+ }
614
+ opts.on("-p", "--priority A-Z", "priority code for add or list") { |v|
615
+ options[:priority] = v
616
+ options[:filter] = true
617
+ }
618
+ opts.on("-C", "--component COMPONENT", "component name for add or list") { |v|
619
+ options[:component] = v
620
+ options[:filter] = true
621
+ }
622
+ opts.on("--force", "force delete or add without prompting") do |v|
623
+ options[:force] = v
624
+ end
625
+ opts.separator ""
626
+ opts.separator "List options:"
627
+
628
+ opts.on("--[no-]color", "--[no-]colors", "colorize listing") do |v|
629
+ options[:colorize] = v
630
+ options[:color_scheme] = 1
631
+ end
632
+ opts.on("-s", "--sort", "sort list on priority") do |v|
633
+ options[:sort] = v
634
+ end
635
+ opts.on("-g", "--grep REGEXP", "filter list on pattern") do |v|
636
+ options[:grep] = v
637
+ end
638
+ opts.on("--renumber", "renumber while listing") do |v|
639
+ options[:renumber] = v
640
+ end
641
+ opts.on("--hide-numbering", "hide-numbering while listing ") do |v|
642
+ options[:hide_numbering] = v
643
+ end
644
+ opts.on("--show-all", "show all tasks (incl closed)") do |v|
645
+ options[:show_all] = v
646
+ end
647
+
648
+ opts.separator ""
649
+ opts.separator "Common options:"
650
+
651
+ opts.on_tail("-d DIR", "--dir DIR", "Use TODO file in this directory") do |v|
652
+ require 'FileUtils'
653
+ dir = File.expand_path v
654
+ if File.directory? dir
655
+ options[:dir] = dir
656
+ FileUtils.cd dir
657
+ else
658
+ puts "#{RED}#{v}: no such directory #{CLEAR}"
659
+ exit 1
660
+ end
661
+ end
662
+ # No argument, shows at tail. This will print an options summary.
663
+ # Try it and see!
664
+ opts.on_tail("-h", "--help", "Show this message") do
665
+ puts opts
666
+ exit
667
+ end
668
+
669
+ opts.on_tail("--show-actions", "show actions ") do |v|
670
+ todo = Todo.new(options, ARGV)
671
+ todo.help nil
672
+ exit 0
673
+ end
674
+
675
+ opts.on_tail("--version", "Show version") do
676
+ puts "#{APPNAME} version #{VERSION}, #{DATE}"
677
+ puts "by #{AUTHOR}. This software is under the GPL License."
678
+ exit 0
679
+ end
680
+ end.parse!(args)
681
+
682
+ #options[:file] ||= "rbcurse/TODO2.txt"
683
+ options[:file] ||= "TODO2.txt"
684
+ if options[:verbose]
685
+ p options
686
+ print "ARGV: "
687
+ p args #ARGV
688
+ end
689
+ #raise "-f FILENAME is mandatory" unless options[:file]
690
+
691
+ todo = Todo.new(options, args)
692
+ todo.run
693
+ ensure
694
+ end
695
+ end # main
696
+ end # class Todo
697
+
698
+ Todo.main(ARGV) if __FILE__ == $0
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: todorb
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Rahul Kumar
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-06-13 00:00:00 +05:30
18
+ default_executable: todorb
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: thoughtbot-shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :development
31
+ version_requirements: *id001
32
+ description: "command-line program that manages a todo list text file "
33
+ email: sentinel1879@gmail.com
34
+ executables:
35
+ - todorb
36
+ extensions: []
37
+
38
+ extra_rdoc_files:
39
+ - LICENSE
40
+ - README.markdown
41
+ files:
42
+ - README.markdown
43
+ - VERSION
44
+ - lib/common/colorconstants.rb
45
+ - lib/common/sed.rb
46
+ - lib/todorb.rb
47
+ - LICENSE
48
+ has_rdoc: true
49
+ homepage: http://github.com/rkumar/todorb
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options:
54
+ - --charset=UTF-8
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.3.6
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: command-line todo list manager
78
+ test_files: []
79
+