todorb 0.2.1 → 1.0.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/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
|