todo-jsonl 0.1.14 → 0.1.19

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 -308
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c84036e07afa19297ed5bf2e0967d7e715dba649ba5a8bd84bacfe2936ea1d1f
4
- data.tar.gz: '0291fca44038ce3674086f8078348c1a44d40a2755dbc36479eeabf223abe2db'
3
+ metadata.gz: 2fd0447c1a7546c396ad8c792f7b7da0d2a72dcf9bf0d20aefd554cffb4c40cb
4
+ data.tar.gz: 49428f5380ed3c56b4c548e80661c77a1a83e6129a69dae7292457f423532390
5
5
  SHA512:
6
- metadata.gz: 398dbcea18e7730823ffc9dc65192187b5f0eb223aff1402c6d3089b3a479cdbeebf01f6969970fa4dfc046aad26921367ec67bd032c09fedadc680f01e4aca0
7
- data.tar.gz: 3987df17cc1cbeb03c8cad2a3dc15f3ff867ff1213b80352a0745d48b0e7b506e32f6b820e25dd2019f7870efb196b207fa1cbede3367faa426ebbbe5d03a715
6
+ metadata.gz: 0cdc1df93f9b9c194fbe48486dc0b6d92b8f2613af3d2b9d398ac2bd2b0478025ce8a495e98c1860888cbe7335d33997ce23ef5e7b9d1c2be53bbaf400a6e967
7
+ data.tar.gz: 8d04c7605fdc2e96ec5b8adcd88b115e805a5441cc32ec50b3c7839beb350e1fb215f07874f34a14301ffd8b36cfe6f1f735731e65e17b8d98235a56e6c61052
data/bin/todo.rb CHANGED
@@ -27,352 +27,365 @@
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> toggle high priority flag
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
- title = task[:title]
155
- match_data = title.match(DUE_DATE_TAG_PATTERN)
156
- if match_data
157
- task[:title] = title.gsub(DUE_DATE_TAG_PATTERN, '')
158
- 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
159
202
  end
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)
213
- tasks = load_tasks(item)
214
- tasks[item][:priority] = !tasks[item][:priority]
215
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
216
- write_tasks(tasks)
217
- list(tasks)
218
- end
252
+ def delete(item)
253
+ tasks = load_tasks(item)
254
+ tasks.delete(item)
255
+ write_tasks(tasks)
256
+ list
257
+ end
219
258
 
220
- def due_date(item, date = '', task = nil)
221
- tasks = load_tasks(item)
222
- tasks[item][:due] = convert_due_date(date)
223
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
224
- write_tasks(tasks)
225
- list(tasks)
226
- 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
227
270
 
228
- def list(tasks = nil, patterns = nil)
229
- items = {}
230
- tasks = tasks || load_tasks
231
- task_indent = [tasks.keys.max.to_s.size, 4].max
232
- patterns = patterns.nil? || patterns.empty? ? [QUERIES[':active']] : patterns
233
- tasks.each do |num, task|
234
- normalized_task = "state=#{task[:state]} due=#{task[:due]} #{task[:title]}"
235
- match = true
236
- patterns.each do |pattern|
237
- 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)
238
279
  end
239
- items[num] = task if match
280
+ write_tasks(tasks)
281
+ list(tasks)
240
282
  end
241
- items = items.sort_by do |num, task|
242
- [task[:priority] && task[:state] != 'done' ? 0 : 1, ORDER[task[:state] || 'default'], task[:due] || 'n/a', num]
283
+
284
+ def due_date(item, date = '')
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)
243
291
  end
244
- items.each do |num, task|
245
- state = task[:state] || 'default'
246
- color = COLORS[state]
247
- display_state = colorize(STATES[state], color)
248
- title = task[:title].gsub(CONTEXT_TAG_PATTERN) do |tag|
249
- (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
250
305
  end
251
- priority_flag = task[:priority] ? colorize(PRIORITY_FLAG, :red) : ' '
252
- due_date = ''
253
- if task[:due] && state != 'done'
254
- date_diff = (Date.parse(task[:due]) - TODAY.to_date).to_i
255
- if date_diff < 0
256
- due_date = colorize("(#{date_diff.abs}d overdue)", :red)
257
- elsif date_diff == 0 || date_diff == 1
258
- due_date = colorize("(#{DUE_DATE_DAYS_SIMPLE[date_diff]})", :yellow)
259
- else
260
- 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
261
328
  end
262
- due_date = ' ' + due_date
329
+ puts "#{num.to_s.rjust(task_indent, ' ')}:#{priority_flag}#{display_state} #{title}#{due_date}"
263
330
  end
264
- puts "#{num.to_s.rjust(task_indent, ' ')}:#{priority_flag}#{display_state} #{title}#{due_date}"
331
+ puts 'No todos found' if items.empty?
265
332
  end
266
- puts 'No todos found' if items.empty?
267
- end
268
-
269
- def add_note(item, text)
270
- tasks = load_tasks(item)
271
- tasks[item][:note] ||= []
272
- tasks[item][:note].push(text)
273
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
274
- write_tasks(tasks)
275
- show(item)
276
- end
277
333
 
278
- def delete_note(item)
279
- tasks = load_tasks(item)
280
- tasks[item][:note] = []
281
- tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
282
- write_tasks(tasks)
283
- show(item)
284
- 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
285
342
 
286
- def show(item)
287
- tasks = load_tasks(item)
288
- tasks[item].each do |key, value|
289
- val = value.kind_of?(Array) ? "\n" + value.join("\n") : value
290
- 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)
291
349
  end
292
- end
293
350
 
294
- def start_repl
295
- command = ''
296
- while !['exit', 'quit'].include?(command)
297
- if ['clear', 'cls'].include?(command)
298
- print "\e[H\e[2J"
299
- else
300
- 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}"
301
356
  end
302
- print "\ntodo> "
303
- command = STDIN.gets.chomp
304
357
  end
305
- end
306
358
 
307
- def colorize(text, color)
308
- "\e[#{COLOR_CODES[color]}m#{text}\e[0m"
309
- 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
310
371
 
311
- def convert_due_date(date = '')
312
- due = nil
313
- day_index = DUE_DATE_DAYS.index(date.to_s.downcase) ||
314
- DUE_DATE_DAYS_SIMPLE.index(date.to_s.downcase) ||
315
- DUE_DATE_DAYS.map do |day| day[0..2] end.index(date.to_s.downcase)
316
- if day_index
317
- due = (TODAY.to_date + day_index).strftime(DATE_FORMAT)
318
- else
319
- 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"
320
374
  end
321
- return due
322
- end
323
375
 
324
- def read(arguments)
325
- begin
326
- action = arguments.first
327
- args = arguments[1..-1] || []
328
- case action
329
- when 'add'
330
- raise action + ' command requires at least one parameter' if args.nil? || args.empty?
331
- add(args.join(' '))
332
- when 'start'
333
- args.length > 0 ? change_state(args.first.to_i, 'started', args[1..-1].join(' ')) : list(nil, [':started'])
334
- when 'done'
335
- args.length > 0 ? change_state(args.first.to_i, 'done', args[1..-1].join(' ')) : list(nil, [':done'])
336
- when 'block'
337
- args.length > 0 ? change_state(args.first.to_i, 'blocked', args[1..-1].join(' ')) : list(nil, [':blocked'])
338
- when 'reset'
339
- args.length > 0 ? change_state(args.first.to_i, 'new', args[1..-1].join(' ')) : list(nil, [':new'])
340
- when 'prio'
341
- raise action + ' command requires exactly one parameter' if args.length != 1
342
- set_priority(args.first.to_i)
343
- when 'due'
344
- raise action + ' command requires at least one parameter' if args.length < 1
345
- due_date(args.first.to_i, (args[1..-1] || []).join(' '))
346
- when 'append'
347
- raise action + ' command requires at least two parameters' if args.length < 2
348
- append(args.first.to_i, args[1..-1].join(' '))
349
- when 'rename'
350
- raise action + ' command requires at least two parameters' if args.length < 2
351
- rename(args.first.to_i, args[1..-1].join(' '))
352
- when 'del'
353
- raise action + ' command requires exactly one parameter' if args.length != 1
354
- delete(args.first.to_i)
355
- when 'note'
356
- raise action + ' command requires at least two parameters' if args.length < 2
357
- add_note(args.first.to_i, args[1..-1].join(' '))
358
- when 'delnote'
359
- raise action + ' command requires exactly one parameter' if args.length != 1
360
- delete_note(args.first.to_i)
361
- when 'list'
362
- list(nil, args)
363
- when 'show'
364
- raise action + ' command requires exactly one parameter' if args.length != 1
365
- show(args.first.to_i)
366
- when 'help'
367
- puts usage
368
- when 'repl'
369
- 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)
370
383
  else
371
- list(nil, arguments)
384
+ due = date.nil? || date.empty? ? nil : Date.parse(date).strftime(DATE_FORMAT)
372
385
  end
373
- rescue => error
374
- puts "#{colorize('ERROR:', :red)} #{error}"
386
+ return due
375
387
  end
388
+
376
389
  end
377
390
 
378
- read(ARGV)
391
+ Todo.new.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.14
4
+ version: 0.1.19
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-25 00:00:00.000000000 Z
11
+ date: 2021-03-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: