todo-jsonl 0.1.16 → 0.1.17

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/todo.rb +321 -311
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d359c12e487b44bf92a6406c044944c6aac14f4e1e45850b021c7184feb274d
4
- data.tar.gz: 84b3b6d64e208e96cb81afbad8f6b07ca2f685a13178854fbd7792c5fde9062a
3
+ metadata.gz: 66be9ecc38d960f5207cc45621ea59daf90513823b96082f2d27c7062742c270
4
+ data.tar.gz: 77fdb42c6675a963a1360e9375e03b0b0d4c7fa2fca1d0721f3337da2276685c
5
5
  SHA512:
6
- metadata.gz: 2eb6fb53e9ab1abd698a55995f7497de9e26f65787402d79afaaf62ca3c58a84cd79d363167d3001e02df0c0eb9a7e024fdd7be37c19d159833041686d6c85fc
7
- data.tar.gz: 0edda1d28b4f76aa17bbf0b6c44e11025d433022bb9fef6d1ef1671de360ba841c77dd0c47400afe9f372a0a38c3c13b1a8b85a39de813e7c68fcd146b40ce5f
6
+ metadata.gz: af9f2d586cf93b6642e3f1f99f62e2460e2bbfe1d8a5c659c4835e4c84421733674ce58961581129c75d9b334aac581ae2f92a56f7022f2cdffdfb7f55779806
7
+ data.tar.gz: 91fe599991ade9a1eccc8fae0ef841b1845d1f47b98bef6f9bc98314120552a86cae1b6c411c715f79525557d8170c5d311f140c26c87fc234990c719a00949e
data/bin/todo.rb CHANGED
@@ -27,356 +27,366 @@
27
27
  require 'json'
28
28
  require 'date'
29
29
 
30
- COLOR_CODES = {
31
- black: 30,
32
- red: 31,
33
- green: 32,
34
- yellow: 33,
35
- blue: 34,
36
- magenta: 35,
37
- cyan: 36,
38
- white: 37
39
- }
40
-
41
- STATES = {
42
- 'new' => '[ ]',
43
- 'done' => '[x]',
44
- 'started' => '[>]',
45
- 'blocked' => '[!]',
46
- 'default' => '[?]'
47
- }
48
-
49
- ORDER = {
50
- 'new' => 3,
51
- 'done' => 4,
52
- 'started' => 2,
53
- 'blocked' => 1,
54
- 'default' => 100
55
- }
56
-
57
- COLORS = {
58
- 'new' => :white,
59
- 'done' => :blue,
60
- 'started' => :green,
61
- 'blocked' => :yellow,
62
- 'default' => :magenta
63
- }
64
-
65
- TODAY = DateTime.now
66
- NEXT_7_DAYS = (0..6).map do |day| (TODAY.to_date + day) end
67
- DATE_FORMAT = '%Y-%m-%d'
68
- DUE_DATE_DAYS = NEXT_7_DAYS.map do |day| day.strftime('%A').downcase end
69
- DUE_DATES_FOR_QUERIES = NEXT_7_DAYS.map do |day| day.strftime(DATE_FORMAT) end
70
- DUE_DATE_DAYS_SIMPLE = ['today', 'tomorrow']
71
-
72
- DUE_DATE_TAG_PATTERN = /(^| )due:([a-zA-Z0-9-]+)/
73
- CONTEXT_TAG_PATTERN = /(^| )[@+][\w-]+/
74
-
75
- QUERIES = {
76
- ':active' => 'state=(new|started|blocked)',
77
- ':done' => 'state=done',
78
- ':blocked' => 'state=blocked',
79
- ':started' => 'state=started',
80
- ':new' => 'state=new',
81
- ':all' => 'state=\w+',
82
- ':today' => "due=#{DUE_DATES_FOR_QUERIES[0]}",
83
- ':tomorrow' => "due=#{DUE_DATES_FOR_QUERIES[1]}",
84
- ':next7days' => "due=(#{DUE_DATES_FOR_QUERIES.join('|')})"
85
- }
86
-
87
- PRIORITY_FLAG = '*'
88
-
89
- TODO_FILE = "#{ENV['HOME']}/todo.jsonl"
90
-
91
- def usage
92
- <<~USAGE
93
- Usage: todo <command> <arguments>
94
-
95
- Commands:
96
- * add <text> add new task
97
- * start <tasknumber> [text] mark task as started, with optional note
98
- * done <tasknumber> [text] mark task as completed, with optional note
99
- * block <tasknumber> [text] mark task as blocked, with optional note
100
- * reset <tasknumber> [text] reset task to new state, with optional note
101
- * prio <tasknumber> [text] toggle high priority flag, with optional note
102
- * due <tasknumber> [date] set/unset due date (in YYYY-MM-DD format)
103
-
104
- * append <tasknumber> <text> append text to task title
105
- * rename <tasknumber> <text> rename task
106
- * del <tasknumber> delete task
107
- * note <tasknumber> <text> add note to task
108
- * delnote <tasknumber> delete all notes from task
109
-
110
- * list <regex> [regex...] list tasks (only active tasks by default)
111
- * show <tasknumber> show all task details
112
- * repl enter read-eval-print loop mode
113
- * help this help screen
114
-
115
- With list command the following pre-defined regex patterns can be also used:
116
- #{QUERIES.keys.join(', ')}
117
-
118
- Due dates can be also added via tags in task title: "due:YYYY-MM-DD"
119
-
120
- Legend:
121
- #{STATES.select { |k, v| k != 'default' }.map { |k, v| "#{k} #{v}" }.join(', ') }, priority #{PRIORITY_FLAG}
122
-
123
- Todo file: #{TODO_FILE}
124
- USAGE
125
- end
30
+ class Todo
31
+
32
+ COLOR_CODES = {
33
+ black: 30,
34
+ red: 31,
35
+ green: 32,
36
+ yellow: 33,
37
+ blue: 34,
38
+ magenta: 35,
39
+ cyan: 36,
40
+ white: 37
41
+ }
126
42
 
127
- def load_tasks(item_to_check = nil)
128
- count = 0
129
- tasks = {}
130
- if File.exist?(TODO_FILE)
131
- File.open(TODO_FILE, 'r:UTF-8') do |file|
132
- file.each_line do |line|
133
- next if line.strip == ''
134
- count += 1
135
- tasks[count] = JSON.parse(line.chomp, :symbolize_names => true)
43
+ STATES = {
44
+ 'new' => '[ ]',
45
+ 'done' => '[x]',
46
+ 'started' => '[>]',
47
+ 'blocked' => '[!]',
48
+ 'default' => '[?]'
49
+ }
50
+
51
+ ORDER = {
52
+ 'new' => 3,
53
+ 'done' => 4,
54
+ 'started' => 2,
55
+ 'blocked' => 1,
56
+ 'default' => 100
57
+ }
58
+
59
+ COLORS = {
60
+ 'new' => :white,
61
+ 'done' => :blue,
62
+ 'started' => :green,
63
+ 'blocked' => :yellow,
64
+ 'default' => :magenta
65
+ }
66
+
67
+ DATE_FORMAT = '%Y-%m-%d'
68
+ DUE_DATE_DAYS_SIMPLE = ['today', 'tomorrow']
69
+ DUE_DATE_TAG_PATTERN = /(^| )due:([a-zA-Z0-9-]+)/
70
+ CONTEXT_TAG_PATTERN = /(^| )[@+][\w-]+/
71
+ PRIORITY_FLAG = '*'
72
+ TODO_FILE = "#{ENV['HOME']}/todo.jsonl"
73
+
74
+ def execute(arguments)
75
+ begin
76
+ setup
77
+ action = arguments.first
78
+ args = arguments[1..-1] || []
79
+ case action
80
+ when 'add'
81
+ raise action + ' command requires at least one parameter' if args.nil? || args.empty?
82
+ add(args.join(' '))
83
+ when 'start'
84
+ args.length > 0 ? change_state(args.first.to_i, 'started', (args[1..-1] || []).join(' ')) : list(nil, [':started'])
85
+ when 'done'
86
+ args.length > 0 ? change_state(args.first.to_i, 'done', (args[1..-1] || []).join(' ')) : list(nil, [':done'])
87
+ when 'block'
88
+ args.length > 0 ? change_state(args.first.to_i, 'blocked', (args[1..-1] || []).join(' ')) : list(nil, [':blocked'])
89
+ when 'reset'
90
+ args.length > 0 ? change_state(args.first.to_i, 'new', (args[1..-1] || []).join(' ')) : list(nil, [':new'])
91
+ when 'prio'
92
+ raise action + ' command requires at least one parameter' if args.length < 1
93
+ set_priority(args.first.to_i, (args[1..-1] || []).join(' '))
94
+ when 'due'
95
+ raise action + ' command requires at least one parameter' if args.length < 1
96
+ due_date(args.first.to_i, (args[1..-1] || []).join(' '))
97
+ when 'append'
98
+ raise action + ' command requires at least two parameters' if args.length < 2
99
+ append(args.first.to_i, args[1..-1].join(' '))
100
+ when 'rename'
101
+ raise action + ' command requires at least two parameters' if args.length < 2
102
+ rename(args.first.to_i, args[1..-1].join(' '))
103
+ when 'del'
104
+ raise action + ' command requires exactly one parameter' if args.length != 1
105
+ delete(args.first.to_i)
106
+ when 'note'
107
+ raise action + ' command requires at least two parameters' if args.length < 2
108
+ add_note(args.first.to_i, args[1..-1].join(' '))
109
+ when 'delnote'
110
+ raise action + ' command requires exactly one parameter' if args.length != 1
111
+ delete_note(args.first.to_i)
112
+ when 'list'
113
+ list(nil, args)
114
+ when 'show'
115
+ raise action + ' command requires exactly one parameter' if args.length != 1
116
+ show(args.first.to_i)
117
+ when 'help'
118
+ puts usage
119
+ when 'repl'
120
+ start_repl
121
+ else
122
+ list(nil, arguments)
136
123
  end
124
+ rescue => error
125
+ puts "#{colorize('ERROR:', :red)} #{error}"
137
126
  end
138
127
  end
139
- if item_to_check && !tasks.has_key?(item_to_check)
140
- raise "#{item_to_check}: No such todo"
128
+
129
+ private
130
+
131
+ def usage
132
+ <<~USAGE
133
+ Usage: todo <command> <arguments>
134
+
135
+ Commands:
136
+ * add <text> add new task
137
+ * start <tasknumber> [text] mark task as started, with optional note
138
+ * done <tasknumber> [text] mark task as completed, with optional note
139
+ * block <tasknumber> [text] mark task as blocked, with optional note
140
+ * reset <tasknumber> [text] reset task to new state, with optional note
141
+ * prio <tasknumber> [text] toggle high priority flag, with optional note
142
+ * due <tasknumber> [date] set/unset due date (in YYYY-MM-DD format)
143
+
144
+ * append <tasknumber> <text> append text to task title
145
+ * rename <tasknumber> <text> rename task
146
+ * del <tasknumber> delete task
147
+ * note <tasknumber> <text> add note to task
148
+ * delnote <tasknumber> delete all notes from task
149
+
150
+ * list <regex> [regex...] list tasks (only active tasks by default)
151
+ * show <tasknumber> show all task details
152
+ * repl enter read-eval-print loop mode
153
+ * help this help screen
154
+
155
+ With list command the following pre-defined regex patterns can be also used:
156
+ #{@queries.keys.join(', ')}
157
+
158
+ Due dates can be also added via tags in task title: "due:YYYY-MM-DD"
159
+
160
+ Legend:
161
+ #{STATES.select { |k, v| k != 'default' }.map { |k, v| "#{k} #{v}" }.join(', ') }, priority #{PRIORITY_FLAG}
162
+
163
+ Todo file: #{TODO_FILE}
164
+ USAGE
141
165
  end
142
- tasks
143
- end
144
166
 
145
- def write_tasks(tasks)
146
- File.open(TODO_FILE, 'w:UTF-8') do |file|
147
- tasks.keys.sort.each do |key|
148
- file.write(JSON.generate(tasks[key]) + "\n")
149
- end
167
+ def setup
168
+ @today = Time.now
169
+ next_7_days = (0..6).map do |day| (@today.to_date + day) end
170
+ @due_date_days = next_7_days.map do |day| day.strftime('%A').downcase end
171
+ due_dates_for_queries = next_7_days.map do |day| day.strftime(DATE_FORMAT) end
172
+
173
+ @queries = {
174
+ ':active' => 'state=(new|started|blocked)',
175
+ ':done' => 'state=done',
176
+ ':blocked' => 'state=blocked',
177
+ ':started' => 'state=started',
178
+ ':new' => 'state=new',
179
+ ':all' => 'state=\w+',
180
+ ':today' => "due=#{due_dates_for_queries[0]}",
181
+ ':tomorrow' => "due=#{due_dates_for_queries[1]}",
182
+ ':next7days' => "due=(#{due_dates_for_queries.join('|')})"
183
+ }
150
184
  end
151
- end
152
185
 
153
- def postprocess_tags(task)
154
- match_data = task[:title].match(DUE_DATE_TAG_PATTERN)
155
- if match_data
156
- task[:title] = task[:title].gsub(DUE_DATE_TAG_PATTERN, '')
157
- task[:due] = convert_due_date(match_data[2])
186
+ def load_tasks(item_to_check = nil)
187
+ count = 0
188
+ tasks = {}
189
+ if File.exist?(TODO_FILE)
190
+ File.open(TODO_FILE, 'r:UTF-8') do |file|
191
+ file.each_line do |line|
192
+ next if line.strip == ''
193
+ count += 1
194
+ tasks[count] = JSON.parse(line.chomp, :symbolize_names => true)
195
+ end
196
+ end
197
+ end
198
+ if item_to_check && !tasks.has_key?(item_to_check)
199
+ raise "#{item_to_check}: No such todo"
200
+ end
201
+ tasks
158
202
  end
159
- raise 'title must not be empty' if task[:title].empty?
160
- end
161
203
 
162
- def add(text)
163
- task = {
164
- state: 'new',
165
- title: text,
166
- modified: Time.now.strftime(DATE_FORMAT)
167
- }
168
- postprocess_tags(task)
169
- File.open(TODO_FILE, 'a:UTF-8') do |file|
170
- file.write(JSON.generate(task) + "\n")
204
+ def write_tasks(tasks)
205
+ File.open(TODO_FILE, 'w:UTF-8') do |file|
206
+ tasks.keys.sort.each do |key|
207
+ file.write(JSON.generate(tasks[key]) + "\n")
208
+ end
209
+ end
171
210
  end
172
- list
173
- end
174
211
 
175
- def append(item, text = '')
176
- tasks = load_tasks(item)
177
- tasks[item][:title] = [tasks[item][:title], text].join(' ')
178
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
179
- postprocess_tags(tasks[item])
180
- write_tasks(tasks)
181
- list(tasks)
182
- end
212
+ def postprocess_tags(task)
213
+ match_data = task[:title].match(DUE_DATE_TAG_PATTERN)
214
+ if match_data
215
+ task[:title] = task[:title].gsub(DUE_DATE_TAG_PATTERN, '')
216
+ task[:due] = convert_due_date(match_data[2])
217
+ end
218
+ raise 'title must not be empty' if task[:title].empty?
219
+ end
183
220
 
184
- def rename(item, text)
185
- tasks = load_tasks(item)
186
- tasks[item][:title] = text
187
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
188
- postprocess_tags(tasks[item])
189
- write_tasks(tasks)
190
- list(tasks)
191
- end
221
+ def add(text)
222
+ task = {
223
+ state: 'new',
224
+ title: text,
225
+ modified: @today.strftime(DATE_FORMAT)
226
+ }
227
+ postprocess_tags(task)
228
+ File.open(TODO_FILE, 'a:UTF-8') do |file|
229
+ file.write(JSON.generate(task) + "\n")
230
+ end
231
+ list
232
+ end
192
233
 
193
- def delete(item)
194
- tasks = load_tasks(item)
195
- tasks.delete(item)
196
- write_tasks(tasks)
197
- list
198
- end
234
+ def append(item, text = '')
235
+ tasks = load_tasks(item)
236
+ tasks[item][:title] = [tasks[item][:title], text].join(' ')
237
+ tasks[item][:modified] = @today.strftime(DATE_FORMAT)
238
+ postprocess_tags(tasks[item])
239
+ write_tasks(tasks)
240
+ list(tasks)
241
+ end
199
242
 
200
- def change_state(item, state, note = nil)
201
- tasks = load_tasks(item)
202
- tasks[item][:state] = state
203
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
204
- if !note.nil? && !note.empty?
205
- tasks[item][:note] ||= []
206
- tasks[item][:note].push(note)
243
+ def rename(item, text)
244
+ tasks = load_tasks(item)
245
+ tasks[item][:title] = text
246
+ tasks[item][:modified] = @today.strftime(DATE_FORMAT)
247
+ postprocess_tags(tasks[item])
248
+ write_tasks(tasks)
249
+ list(tasks)
207
250
  end
208
- write_tasks(tasks)
209
- list(tasks)
210
- end
211
251
 
212
- def set_priority(item, note = nil)
213
- tasks = load_tasks(item)
214
- tasks[item][:priority] = !tasks[item][:priority]
215
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
216
- if !note.nil? && !note.empty?
217
- tasks[item][:note] ||= []
218
- tasks[item][:note].push(note)
252
+ def delete(item)
253
+ tasks = load_tasks(item)
254
+ tasks.delete(item)
255
+ write_tasks(tasks)
256
+ list
219
257
  end
220
- write_tasks(tasks)
221
- list(tasks)
222
- end
223
258
 
224
- def due_date(item, date = '', task = nil)
225
- tasks = load_tasks(item)
226
- tasks[item][:due] = convert_due_date(date)
227
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
228
- write_tasks(tasks)
229
- list(tasks)
230
- end
259
+ def change_state(item, state, note = nil)
260
+ tasks = load_tasks(item)
261
+ tasks[item][:state] = state
262
+ tasks[item][:modified] = @today.strftime(DATE_FORMAT)
263
+ if !note.nil? && !note.empty?
264
+ tasks[item][:note] ||= []
265
+ tasks[item][:note].push(note)
266
+ end
267
+ write_tasks(tasks)
268
+ list(tasks)
269
+ end
231
270
 
232
- def list(tasks = nil, patterns = nil)
233
- items = {}
234
- tasks = tasks || load_tasks
235
- task_indent = [tasks.keys.max.to_s.size, 4].max
236
- patterns = patterns.nil? || patterns.empty? ? [QUERIES[':active']] : patterns
237
- tasks.each do |num, task|
238
- normalized_task = "state=#{task[:state]} due=#{task[:due]} #{task[:title]}"
239
- match = true
240
- patterns.each do |pattern|
241
- match = false unless /#{QUERIES[pattern] || pattern}/ix.match(normalized_task)
271
+ def set_priority(item, note = nil)
272
+ tasks = load_tasks(item)
273
+ tasks[item][:priority] = !tasks[item][:priority]
274
+ tasks[item].delete(:priority) if !tasks[item][:priority]
275
+ tasks[item][:modified] = @today.strftime(DATE_FORMAT)
276
+ if !note.nil? && !note.empty?
277
+ tasks[item][:note] ||= []
278
+ tasks[item][:note].push(note)
242
279
  end
243
- items[num] = task if match
280
+ write_tasks(tasks)
281
+ list(tasks)
244
282
  end
245
- items = items.sort_by do |num, task|
246
- [task[:priority] && task[:state] != 'done' ? 0 : 1, ORDER[task[:state] || 'default'], task[:due] || 'n/a', num]
283
+
284
+ def due_date(item, date = '', task = nil)
285
+ tasks = load_tasks(item)
286
+ tasks[item][:due] = convert_due_date(date)
287
+ tasks[item].delete(:due) if tasks[item][:due].nil?
288
+ tasks[item][:modified] = @today.strftime(DATE_FORMAT)
289
+ write_tasks(tasks)
290
+ list(tasks)
247
291
  end
248
- items.each do |num, task|
249
- state = task[:state] || 'default'
250
- color = COLORS[state]
251
- display_state = colorize(STATES[state], color)
252
- title = task[:title].gsub(CONTEXT_TAG_PATTERN) do |tag|
253
- (tag.start_with?(' ') ? ' ' : '') + colorize(tag.strip, :cyan)
292
+
293
+ def list(tasks = nil, patterns = nil)
294
+ items = {}
295
+ tasks = tasks || load_tasks
296
+ task_indent = [tasks.keys.max.to_s.size, 4].max
297
+ patterns = patterns.nil? || patterns.empty? ? [@queries[':active']] : patterns
298
+ tasks.each do |num, task|
299
+ normalized_task = "state=#{task[:state]} due=#{task[:due]} #{task[:title]}"
300
+ match = true
301
+ patterns.each do |pattern|
302
+ match = false unless /#{@queries[pattern] || pattern}/ix.match(normalized_task)
303
+ end
304
+ items[num] = task if match
254
305
  end
255
- priority_flag = task[:priority] ? colorize(PRIORITY_FLAG, :red) : ' '
256
- due_date = ''
257
- if task[:due] && state != 'done'
258
- date_diff = (Date.parse(task[:due]) - TODAY.to_date).to_i
259
- if date_diff < 0
260
- due_date = colorize("(#{date_diff.abs}d overdue)", :red)
261
- elsif date_diff == 0 || date_diff == 1
262
- due_date = colorize("(#{DUE_DATE_DAYS_SIMPLE[date_diff]})", :yellow)
263
- else
264
- due_date = colorize("(#{DUE_DATE_DAYS[date_diff] || task[:due]})", :magenta) if date_diff > 1
306
+ items = items.sort_by do |num, task|
307
+ [task[:priority] && task[:state] != 'done' ? 0 : 1, ORDER[task[:state] || 'default'], task[:due] || 'n/a', num]
308
+ end
309
+ items.each do |num, task|
310
+ state = task[:state] || 'default'
311
+ color = COLORS[state]
312
+ display_state = colorize(STATES[state], color)
313
+ title = task[:title].gsub(CONTEXT_TAG_PATTERN) do |tag|
314
+ (tag.start_with?(' ') ? ' ' : '') + colorize(tag.strip, :cyan)
315
+ end
316
+ priority_flag = task[:priority] ? colorize(PRIORITY_FLAG, :red) : ' '
317
+ due_date = ''
318
+ if task[:due] && state != 'done'
319
+ date_diff = (Date.parse(task[:due]) - @today.to_date).to_i
320
+ if date_diff < 0
321
+ due_date = colorize("(#{date_diff.abs}d overdue)", :red)
322
+ elsif date_diff == 0 || date_diff == 1
323
+ due_date = colorize("(#{DUE_DATE_DAYS_SIMPLE[date_diff]})", :yellow)
324
+ else
325
+ due_date = colorize("(#{@due_date_days[date_diff] || task[:due]})", :magenta) if date_diff > 1
326
+ end
327
+ due_date = ' ' + due_date
265
328
  end
266
- due_date = ' ' + due_date
329
+ puts "#{num.to_s.rjust(task_indent, ' ')}:#{priority_flag}#{display_state} #{title}#{due_date}"
267
330
  end
268
- puts "#{num.to_s.rjust(task_indent, ' ')}:#{priority_flag}#{display_state} #{title}#{due_date}"
331
+ puts 'No todos found' if items.empty?
269
332
  end
270
- puts 'No todos found' if items.empty?
271
- end
272
-
273
- def add_note(item, text)
274
- tasks = load_tasks(item)
275
- tasks[item][:note] ||= []
276
- tasks[item][:note].push(text)
277
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
278
- write_tasks(tasks)
279
- show(item)
280
- end
281
333
 
282
- def delete_note(item)
283
- tasks = load_tasks(item)
284
- tasks[item][:note] = []
285
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
286
- write_tasks(tasks)
287
- show(item)
288
- end
334
+ def add_note(item, text)
335
+ tasks = load_tasks(item)
336
+ tasks[item][:note] ||= []
337
+ tasks[item][:note].push(text)
338
+ tasks[item][:modified] = @today.strftime(DATE_FORMAT)
339
+ write_tasks(tasks)
340
+ show(item)
341
+ end
289
342
 
290
- def show(item)
291
- tasks = load_tasks(item)
292
- tasks[item].each do |key, value|
293
- val = value.kind_of?(Array) ? "\n" + value.join("\n") : value
294
- puts "#{colorize(key.to_s.rjust(10, ' ') + ':', :cyan)} #{val}"
343
+ def delete_note(item)
344
+ tasks = load_tasks(item)
345
+ tasks[item].delete(:note)
346
+ tasks[item][:modified] = @today.strftime(DATE_FORMAT)
347
+ write_tasks(tasks)
348
+ show(item)
295
349
  end
296
- end
297
350
 
298
- def start_repl
299
- command = ''
300
- while !['exit', 'quit'].include?(command)
301
- if ['clear', 'cls'].include?(command)
302
- print "\e[H\e[2J"
303
- else
304
- read(command == 'repl' ? [] : command.split(/\s+/))
351
+ def show(item)
352
+ tasks = load_tasks(item)
353
+ tasks[item].each do |key, value|
354
+ val = value.kind_of?(Array) ? "\n" + value.join("\n") : value
355
+ puts "#{colorize(key.to_s.rjust(10, ' ') + ':', :cyan)} #{val}"
305
356
  end
306
- print "\ntodo> "
307
- command = STDIN.gets.chomp
308
357
  end
309
- end
310
358
 
311
- def colorize(text, color)
312
- "\e[#{COLOR_CODES[color]}m#{text}\e[0m"
313
- end
359
+ def start_repl
360
+ command = ''
361
+ while !['exit', 'quit'].include?(command)
362
+ if ['clear', 'cls'].include?(command)
363
+ print "\e[H\e[2J"
364
+ else
365
+ execute(command == 'repl' ? [] : command.split(/\s+/))
366
+ end
367
+ print "\ntodo> "
368
+ command = STDIN.gets.chomp
369
+ end
370
+ end
314
371
 
315
- def convert_due_date(date = '')
316
- due = nil
317
- day_index = DUE_DATE_DAYS.index(date.to_s.downcase) ||
318
- DUE_DATE_DAYS_SIMPLE.index(date.to_s.downcase) ||
319
- DUE_DATE_DAYS.map do |day| day[0..2] end.index(date.to_s.downcase)
320
- if day_index
321
- due = (TODAY.to_date + day_index).strftime(DATE_FORMAT)
322
- else
323
- due = date.nil? || date.empty? ? nil : Date.parse(date).strftime(DATE_FORMAT)
372
+ def colorize(text, color)
373
+ "\e[#{COLOR_CODES[color]}m#{text}\e[0m"
324
374
  end
325
- return due
326
- end
327
375
 
328
- def read(arguments)
329
- begin
330
- action = arguments.first
331
- args = arguments[1..-1] || []
332
- case action
333
- when 'add'
334
- raise action + ' command requires at least one parameter' if args.nil? || args.empty?
335
- add(args.join(' '))
336
- when 'start'
337
- args.length > 0 ? change_state(args.first.to_i, 'started', (args[1..-1] || []).join(' ')) : list(nil, [':started'])
338
- when 'done'
339
- args.length > 0 ? change_state(args.first.to_i, 'done', (args[1..-1] || []).join(' ')) : list(nil, [':done'])
340
- when 'block'
341
- args.length > 0 ? change_state(args.first.to_i, 'blocked', (args[1..-1] || []).join(' ')) : list(nil, [':blocked'])
342
- when 'reset'
343
- args.length > 0 ? change_state(args.first.to_i, 'new', (args[1..-1] || []).join(' ')) : list(nil, [':new'])
344
- when 'prio'
345
- raise action + ' command requires at least one parameter' if args.length < 1
346
- set_priority(args.first.to_i, (args[1..-1] || []).join(' '))
347
- when 'due'
348
- raise action + ' command requires at least one parameter' if args.length < 1
349
- due_date(args.first.to_i, (args[1..-1] || []).join(' '))
350
- when 'append'
351
- raise action + ' command requires at least two parameters' if args.length < 2
352
- append(args.first.to_i, args[1..-1].join(' '))
353
- when 'rename'
354
- raise action + ' command requires at least two parameters' if args.length < 2
355
- rename(args.first.to_i, args[1..-1].join(' '))
356
- when 'del'
357
- raise action + ' command requires exactly one parameter' if args.length != 1
358
- delete(args.first.to_i)
359
- when 'note'
360
- raise action + ' command requires at least two parameters' if args.length < 2
361
- add_note(args.first.to_i, args[1..-1].join(' '))
362
- when 'delnote'
363
- raise action + ' command requires exactly one parameter' if args.length != 1
364
- delete_note(args.first.to_i)
365
- when 'list'
366
- list(nil, args)
367
- when 'show'
368
- raise action + ' command requires exactly one parameter' if args.length != 1
369
- show(args.first.to_i)
370
- when 'help'
371
- puts usage
372
- when 'repl'
373
- start_repl
376
+ def convert_due_date(date = '')
377
+ due = nil
378
+ day_index = @due_date_days.index(date.to_s.downcase) ||
379
+ DUE_DATE_DAYS_SIMPLE.index(date.to_s.downcase) ||
380
+ @due_date_days.map do |day| day[0..2] end.index(date.to_s.downcase)
381
+ if day_index
382
+ due = (@today.to_date + day_index).strftime(DATE_FORMAT)
374
383
  else
375
- list(nil, arguments)
384
+ due = date.nil? || date.empty? ? nil : Date.parse(date).strftime(DATE_FORMAT)
376
385
  end
377
- rescue => error
378
- puts "#{colorize('ERROR:', :red)} #{error}"
386
+ return due
379
387
  end
388
+
380
389
  end
381
390
 
382
- read(ARGV)
391
+ todo = Todo.new
392
+ todo.execute(ARGV)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: todo-jsonl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.16
4
+ version: 0.1.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabor Bata
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-26 00:00:00.000000000 Z
11
+ date: 2021-03-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: