todorb 0.2.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +12 -0
- data/Makefile +29 -0
- data/README.markdown +48 -3
- data/Rakefile +54 -0
- data/VERSION +1 -1
- data/bin/todorb +1 -1
- data/lib/common/cmdapp.rb +170 -0
- data/lib/common/sed.rb +84 -44
- data/lib/todorb.rb +508 -266
- data/tests/Makefile +2 -0
- data/tests/README +313 -0
- data/tests/aggregate-results.sh +34 -0
- data/tests/clean.sh +3 -0
- data/tests/dataset1.txt +14 -0
- data/tests/recreate.sh +33 -0
- data/tests/rtest2.sh +124 -0
- data/tests/t0001-add.sh +22 -0
- data/tests/t0002-listing.sh +58 -0
- data/tests/test-lib.sh +614 -0
- data/todorb.gemspec +62 -0
- metadata +22 -6
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
= todorb 0.2.2, 2010-06-15
|
2
|
+
* Added subtask addition. One may add subtasks under a task.
|
3
|
+
* This effects everything! Deleting a task, you may wanna delete all
|
4
|
+
children. Changing the status of a task may affect all children.
|
5
|
+
Renumberign and redoing etc affects subtasks. The impact of this change
|
6
|
+
is quite huge.
|
7
|
+
|
8
|
+
= todorb 0.2.1
|
9
|
+
* Version without subtask. This is a simpler version which i prefer.
|
10
|
+
|
11
|
+
|
12
|
+
## vim:tw=72:ai:formatoptions=tcqln:nocindent
|
data/Makefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
DISTFILES := README.markdown get_serial_number colors.sh todoapp.sh
|
2
|
+
VERSION := `cat VERSION_FILE`
|
3
|
+
|
4
|
+
all: install
|
5
|
+
|
6
|
+
install:
|
7
|
+
|
8
|
+
rake build && sudo rake install
|
9
|
+
|
10
|
+
#
|
11
|
+
# Testing
|
12
|
+
#
|
13
|
+
TESTS = $(wildcard tests/t[0-9][0-9][0-9][0-9]-*.sh)
|
14
|
+
#TEST_OPTIONS=--verbose
|
15
|
+
|
16
|
+
test-pre-clean:
|
17
|
+
rm -rf tests/test-results "tests/trash directory"*
|
18
|
+
|
19
|
+
aggregate-results: $(TESTS)
|
20
|
+
|
21
|
+
$(TESTS): test-pre-clean
|
22
|
+
-cd tests && sh $(notdir $@) $(TEST_OPTIONS)
|
23
|
+
|
24
|
+
test: aggregate-results
|
25
|
+
tests/aggregate-results.sh tests/test-results/t*-*
|
26
|
+
rm -rf tests/test-results
|
27
|
+
|
28
|
+
# Force tests to get run every time
|
29
|
+
.PHONY: test test-pre-clean aggregate-results $(TESTS)
|
data/README.markdown
CHANGED
@@ -10,11 +10,56 @@ The fun things about this app, is that I am having to write ruby code that will
|
|
10
10
|
|
11
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
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
13
|
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
14
|
|
15
|
+
## Features
|
16
|
+
|
17
|
+
1. multiple todo lists (per directory)
|
18
|
+
2. subtasks (recursive delete or status update)
|
19
|
+
3. priorities - (A) (B)
|
20
|
+
4. status - [ ], [x], [@] etc
|
21
|
+
5. tag - @WORK
|
22
|
+
6. project - +myproj
|
23
|
+
7. component - @comp1
|
24
|
+
8. notes attached to task
|
25
|
+
9. delete tasks
|
26
|
+
10. archive completed tasks
|
27
|
+
11. colored or plain output
|
28
|
+
12. show or hide completed tasks (default: hide)
|
29
|
+
13. Search, filter, sort tasks by priority (default) or task id.
|
30
|
+
14. Renumber the file
|
31
|
+
|
32
|
+
## Sample Output
|
33
|
+
|
34
|
+
3 [ ] Add a close for status close (2010-06-14)
|
35
|
+
3.1 [ ] hello there new a u 3 (2010-06-15)
|
36
|
+
3.1.1 [ ] hello there new a u 3 (2010-06-15)
|
37
|
+
3.1.2 [ ] hello there new a u 3 (2010-06-15)
|
38
|
+
3.1.3 [ ] hello there new a u 3 (2010-06-15)
|
39
|
+
3.2 [ ] hello there new a u 3.2 (2010-06-15)
|
40
|
+
3.2.1 [ ] hello there new a u 3.2.1 (2010-06-15)
|
41
|
+
3.2.1.1 [ ] hello there new a u 3.2.1.1
|
42
|
+
* a note for frank (2010-06-15)
|
43
|
+
5 [ ] list: if dir given then show full path of TODO2.txt at end (2010-06-14)
|
44
|
+
5.2 [ ] start rubyforge project for todorb (2010-06-14)
|
45
|
+
6 [@] allow for ENV VARS such as verbose, plain, force (2010-06-15)
|
46
|
+
6.1 [@] what if no serial_number file? (2010-06-14)
|
47
|
+
7 [ ] list: search terms with - + and = @RUBY (2010-06-15)
|
48
|
+
8 [ ] testing out add 1 @MYTAG @RUBY (2010-06-19)
|
49
|
+
9 [ ] testing out another one @RUBY
|
50
|
+
* a note for chris
|
51
|
+
* a note for steve (2010-06-19)
|
52
|
+
11 [x] (A) move common app code to common/cmdapp
|
53
|
+
* refactor it also, so not so reliant on variables (2010-06-20)
|
54
|
+
16 [ ] (A) add aliases to subcommand (2010-06-21)
|
55
|
+
|
56
|
+
17 of 17 rows displayed from TODO2.txt
|
57
|
+
|
58
|
+
## File format
|
59
|
+
|
60
|
+
1. The file format is almost identical to the output except that there is a TAB after the task number.
|
61
|
+
2. Notes are saved on same line as task, but displayed on another line with asterisk prefix.
|
62
|
+
|
18
63
|
## Copyright
|
19
64
|
|
20
65
|
Copyright (c) 2010 Rahul Kumar. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "todorb"
|
8
|
+
gem.summary = %Q{command-line todo list manager }
|
9
|
+
gem.description = %Q{command-line program that manages a todo list text file }
|
10
|
+
gem.email = "sentinel1879@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/rkumar/todorb"
|
12
|
+
gem.authors = ["Rahul Kumar"]
|
13
|
+
gem.rubyforge_project = "todorb"
|
14
|
+
#gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'lib' << 'test'
|
25
|
+
test.pattern = 'test/**/test_*.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
task :test => :check_dependencies
|
43
|
+
|
44
|
+
task :default => :test
|
45
|
+
|
46
|
+
require 'rake/rdoctask'
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
48
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "todorb #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/bin/todorb
CHANGED
@@ -0,0 +1,170 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
=begin
|
3
|
+
* Name : cmdapp.rb
|
4
|
+
* Description : some basic command line things
|
5
|
+
* : Moving some methods from todorb.rb here
|
6
|
+
* Author : rkumar
|
7
|
+
* Date : 2010-06-20 11:18
|
8
|
+
* License :
|
9
|
+
Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
|
10
|
+
|
11
|
+
=end
|
12
|
+
require 'common/sed'
|
13
|
+
|
14
|
+
ERRCODE = 1
|
15
|
+
|
16
|
+
module Cmdapp
|
17
|
+
|
18
|
+
##
|
19
|
+
# external dependencies:
|
20
|
+
# @app_default_action - action to run if none specified
|
21
|
+
# @app_file_path - data file we are backing up, or reading into array
|
22
|
+
# @app_serial_path - serial_number file
|
23
|
+
##
|
24
|
+
# check whether this action is mapped to some alias and *changes*
|
25
|
+
# @action and @argv if true.
|
26
|
+
# @param [String] action asked by user
|
27
|
+
# @param [Array] rest of args on command line
|
28
|
+
# @return [Boolean] whether it is mapped or not.
|
29
|
+
#
|
30
|
+
def check_aliases action, args
|
31
|
+
ret = @aliases[action]
|
32
|
+
if ret
|
33
|
+
a = ret.shift
|
34
|
+
b = [*ret, *args]
|
35
|
+
@action = a
|
36
|
+
@argv = b
|
37
|
+
#puts " #{@action} ; argv: #{@argv} "
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
##
|
43
|
+
# runs method after checking if valid or alias.
|
44
|
+
# If not found prints help.
|
45
|
+
# @return [0, ERRCODE] success 0.
|
46
|
+
def run
|
47
|
+
@action = @argv[0] || @app_default_action
|
48
|
+
@action = @action.downcase
|
49
|
+
@action.sub!('priority', 'pri')
|
50
|
+
@action.sub!(/^del$/, 'delete')
|
51
|
+
|
52
|
+
|
53
|
+
ret = 0
|
54
|
+
@argv.shift
|
55
|
+
if @actions.include? @action
|
56
|
+
ret = send(@action, @argv)
|
57
|
+
else
|
58
|
+
# check aliases
|
59
|
+
if check_aliases @action, @argv
|
60
|
+
ret = send(@action, @argv)
|
61
|
+
else
|
62
|
+
help @argv
|
63
|
+
ret = ERRCODE
|
64
|
+
end
|
65
|
+
end
|
66
|
+
ret ||= 0
|
67
|
+
ret = 0 if ret != ERRCODE
|
68
|
+
return ret
|
69
|
+
end
|
70
|
+
def help args
|
71
|
+
puts "Actions are "
|
72
|
+
@actions.each_pair { |name, val| puts "#{name}\t#{val}" }
|
73
|
+
puts " "
|
74
|
+
puts "Aliases are "
|
75
|
+
@aliases.each_pair { |name, val| puts "#{name}:\t#{val.join(' ')}" }
|
76
|
+
0
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# reads serial_number file, returns serialno for this app
|
81
|
+
# and increments the serial number and writes back.
|
82
|
+
def _get_serial_number
|
83
|
+
require 'fileutils'
|
84
|
+
appname = @appname
|
85
|
+
filename = @app_serial_path || "serial_numbers"
|
86
|
+
h = {}
|
87
|
+
# check if serial file existing in curr dir. Else create
|
88
|
+
if File.exists?(filename)
|
89
|
+
File.open(filename).each { |line|
|
90
|
+
#sn = $1 if line.match regex
|
91
|
+
x = line.split ":"
|
92
|
+
h[x[0]] = x[1].chomp
|
93
|
+
}
|
94
|
+
end
|
95
|
+
sn = h[appname] || 1
|
96
|
+
# update the sn in file
|
97
|
+
nsn = sn.to_i + 1
|
98
|
+
# this will create if not exists in addition to storing if it does
|
99
|
+
h[appname] = nsn
|
100
|
+
# write back to file
|
101
|
+
File.open(filename, "w") do |f|
|
102
|
+
h.each_pair {|k,v| f.print "#{k}:#{v}\n"}
|
103
|
+
end
|
104
|
+
return sn
|
105
|
+
end
|
106
|
+
##
|
107
|
+
# After doing a redo of the numbering, we need to reset the numbers for that app
|
108
|
+
def _set_serial_number number
|
109
|
+
appname = @appname
|
110
|
+
pattern = Regexp.new "^#{appname}:.*$"
|
111
|
+
filename = @app_serial_path || "serial_numbers"
|
112
|
+
_backup filename
|
113
|
+
change_row filename, pattern, "#{appname}:#{number}"
|
114
|
+
end
|
115
|
+
|
116
|
+
def _backup filename=@app_file_path
|
117
|
+
require 'fileutils'
|
118
|
+
FileUtils.cp filename, "#{filename}.org"
|
119
|
+
end
|
120
|
+
def die text
|
121
|
+
$stderr.puts text
|
122
|
+
exit ERRCODE
|
123
|
+
end
|
124
|
+
# prints messages to stderr
|
125
|
+
# All messages should go to stderr.
|
126
|
+
# Keep stdout only for output which can be used by other programs
|
127
|
+
def message text
|
128
|
+
$stderr.puts text
|
129
|
+
end
|
130
|
+
# print to stderr only if verbose set
|
131
|
+
def verbose text
|
132
|
+
message(text) if @options[:verbose]
|
133
|
+
end
|
134
|
+
# print to stderr only if verbose set
|
135
|
+
def warning text
|
136
|
+
print_red("WARNING: #{text}")
|
137
|
+
end
|
138
|
+
def print_red text
|
139
|
+
message "#{RED}#{text}#{CLEAR}"
|
140
|
+
end
|
141
|
+
def print_green text
|
142
|
+
message "#{GREEN}#{text}#{CLEAR}"
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# load data into array as item and task
|
147
|
+
# @see save_array to write
|
148
|
+
def load_array
|
149
|
+
#return if $valid_array
|
150
|
+
$valid_array = false
|
151
|
+
@data = []
|
152
|
+
File.open(@app_file_path).each do |line|
|
153
|
+
row = line.chomp.split "\t"
|
154
|
+
@data << row
|
155
|
+
end
|
156
|
+
$valid_array = true
|
157
|
+
end
|
158
|
+
##
|
159
|
+
# saves the task array to disk
|
160
|
+
# Please use load_array to load, and not populate
|
161
|
+
def save_array
|
162
|
+
raise "Cannot save array! Please use load_array to load" if $valid_array == false
|
163
|
+
|
164
|
+
File.open(@app_file_path, "w") do |file|
|
165
|
+
@data.each { |row| file.puts "#{row[0]}\t#{row[1]}" }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
end
|
data/lib/common/sed.rb
CHANGED
@@ -11,54 +11,94 @@
|
|
11
11
|
# matching lines to it
|
12
12
|
|
13
13
|
module Sed
|
14
|
-
def change_row filename, pattern, replacement = nil
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
22
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 }
|
23
43
|
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 }
|
44
|
+
_write filename, d
|
43
45
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
46
|
+
##
|
47
|
+
# inserts text in filename at lineno.
|
48
|
+
#
|
49
|
+
def insert_row filename, lineno, text
|
50
|
+
d = _read filename
|
51
|
+
d.insert(lineno, text)
|
52
|
+
_write filename, d
|
53
|
+
0
|
54
|
+
end
|
55
|
+
##
|
56
|
+
# read the given filename into an array
|
57
|
+
def _read filename
|
58
|
+
d = []
|
59
|
+
File.open(filename).each { |line|
|
60
|
+
d << line
|
61
|
+
}
|
62
|
+
return d
|
63
|
+
end
|
64
|
+
##
|
65
|
+
# write the given array to the filename
|
66
|
+
def _write filename, array
|
67
|
+
File.open(filename, "w") do |file|
|
68
|
+
array.each { |row| file.puts row }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
##
|
72
|
+
# searches filelist array for pattern yielding filename, linenumber and line
|
73
|
+
# @return [Array, nil] array of lines containing filename,lineno,line tab delimited
|
74
|
+
# or nil if nothing found
|
75
|
+
# Taken from http://facets.rubyforge.org/apidoc/index.html more filelist.
|
76
|
+
# egrepoptions - count only, linenumber, filename, invert etc
|
77
|
+
def egrep(filelist, pattern, egrepoptions={})
|
78
|
+
lines = []
|
79
|
+
filelist.each do |fn|
|
80
|
+
open(fn) do |inf|
|
81
|
+
count = 0
|
82
|
+
|
83
|
+
inf.each do |line|
|
84
|
+
count += 1
|
85
|
+
if pattern.match(line)
|
86
|
+
if block_given?
|
87
|
+
yield fn, count, line
|
88
|
+
else
|
89
|
+
#puts "#{fn}:#{count}:#{line}"
|
90
|
+
lines << "#{fn}#{@todo_delim}#{count}#{@todo_delim}#{line}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
unless block_given?
|
98
|
+
ret = lines.empty? nil:lines
|
99
|
+
return ret
|
100
|
+
end
|
60
101
|
end
|
61
|
-
end
|
62
102
|
end # module
|
63
103
|
|
64
104
|
if __FILE__ == $0
|