pt 0.8.6 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,771 +0,0 @@
1
- require 'yaml'
2
- require 'colored'
3
- require 'highline'
4
- require 'tempfile'
5
- require 'uri'
6
- module PT
7
- class UI
8
-
9
- GLOBAL_CONFIG_PATH = ENV['HOME'] + "/.pt"
10
- LOCAL_CONFIG_PATH = Dir.pwd + '/.pt'
11
-
12
- def initialize(args)
13
- require 'pt/debugger' if ARGV.delete('--debug')
14
- @io = HighLine.new
15
- @global_config = load_global_config
16
- @client = Client.new(@global_config[:api_number])
17
- @local_config = load_local_config
18
- @project = @client.get_project(@local_config[:project_id])
19
- command = args[0].to_sym rescue :my_work
20
- @params = args[1..-1]
21
- commands.include?(command.to_sym) ? send(command.to_sym) : help
22
- end
23
-
24
- def my_work
25
- title("My Work for #{user_s} in #{project_to_s}")
26
- stories = @client.get_my_work(@project, @local_config[:user_name])
27
- TasksTable.new(stories).print @global_config
28
- end
29
-
30
- def todo
31
- title("My Work for #{user_s} in #{project_to_s}")
32
- stories = @client.get_my_work(@project, @local_config[:user_name])
33
- stories = stories.select { |story| story.current_state == "unscheduled" }
34
- TasksTable.new(stories).print @global_config
35
- end
36
-
37
- %w[unscheduled started finished delivered accepted rejected].each do |state|
38
- define_method(state.to_sym) do
39
- if @params[0]
40
- stories = @project.stories(filter: "owner:#{@params[0]} state:#{state}")
41
- TasksTable.new(stories).print @global_config
42
- else
43
- # otherwise show them all
44
- title("Stories #{state} for #{project_to_s}")
45
- stories = @project.stories(filter:"state:#{state}")
46
- TasksTable.new(stories).print @global_config
47
- end
48
- end
49
- end
50
-
51
- def list
52
- if @params[0]
53
- if @params[0] == "all"
54
- stories = @client.get_work(@project)
55
- TasksTable.new(stories).print @global_config
56
- else
57
- stories = @client.get_my_work(@project, @params[0])
58
- TasksTable.new(stories).print @global_config
59
- end
60
- else
61
- members = @client.get_members(@project)
62
- table = MembersTable.new(members)
63
- user = select("Please select a member to see his tasks.", table).name
64
- title("Work for #{user} in #{project_to_s}")
65
- stories = @client.get_my_work(@project, user)
66
- TasksTable.new(stories).print @global_config
67
- end
68
- end
69
-
70
- def recent
71
- title("Your recent stories from #{project_to_s}")
72
- stories = @project.stories( ids: @local_config[:recent_tasks].join(',') )
73
- MultiUserTasksTable.new(stories).print @global_config
74
- end
75
-
76
- def label
77
-
78
- task = get_task_from_params "Please select a story to show"
79
- unless task
80
- message("No matches found for '#{@params[0]}', please use a valid pivotal story Id")
81
- return
82
- end
83
-
84
- if @params[1]
85
- label = @params[1]
86
- else
87
- label = ask("Which label?")
88
- end
89
-
90
- @client.add_label( @project, task, label );
91
- show_task(task_by_id_or_pt_id(task.id))
92
- end
93
-
94
- def create
95
- if @params[0]
96
- name = @params[0]
97
- owner = @params[1] || @local_config[:user_name]
98
- requester = @local_config[:user_name]
99
- task_type = task_type_or_nil(@params[1]) || task_type_or_nil(@params[2]) || 'feature'
100
- else
101
- title("Let's create a new task:")
102
- name = ask("Name for the new task:")
103
- end
104
-
105
- owner = @client.find_member(@project, owner).person.id if owner.kind_of?(String)
106
-
107
- unless owner
108
- if ask('Do you want to assign it now? (y/n)').downcase == 'y'
109
- members = @client.get_members(@project)
110
- table = PersonsTable.new(members.map(&:person))
111
- owner = select("Please select a member to assign him the task.", table).id
112
- else
113
- owner = nil
114
- end
115
- requester = @local_config[:user_name]
116
- task_type = ask('Type? (c)hore, (b)ug, anything else for feature)')
117
- end
118
-
119
- task_type = case task_type
120
- when 'c', 'chore'
121
- 'chore'
122
- when 'b', 'bug'
123
- 'bug'
124
- else
125
- 'feature'
126
- end
127
- result = nil
128
-
129
- owner_ids = [owner]
130
- # did you do a -m so you can add a description?
131
- if ARGV.include? "-m" or ARGV.include? "--m"
132
- editor = ENV.fetch('EDITOR') { 'vi' }
133
- temp_path = "/tmp/editor-#{ Process.pid }.txt"
134
- system "#{ editor } #{ temp_path }"
135
-
136
- description = File.read(temp_path)
137
- story = @client.create_task_with_description(@project, name, owner_ids, task_type, description)
138
- else
139
- story = @client.create_task(@project, name, owner_ids, task_type)
140
- end
141
- # TODO need result
142
- congrats("#{task_type} for #{owner} open #{story.url}")
143
- end
144
-
145
- def open
146
- if @params[0]
147
- if task = @client.get_task_by_id(@project, @params[0])
148
- congrats("Opening #{task.name}")
149
- `open #{task.url}`
150
- else
151
- message("Story ##{@params[0]} not found")
152
- end
153
- else
154
- tasks = @client.get_my_open_tasks(@project, @local_config[:user_name])
155
- table = TasksTable.new(tasks)
156
- title("Tasks for #{user_s} in #{project_to_s}")
157
- task = select("Please select a story to open it in the browser", table)
158
- end
159
- end
160
-
161
- def comment
162
- if @params[0]
163
- task = task_by_id_or_pt_id @params[0].to_i
164
- comment = @params[1]
165
- title("Adding a comment to #{task.name}")
166
- else
167
- tasks = @client.get_my_work(@project, @local_config[:user_name])
168
- table = TasksTable.new(tasks)
169
- title("Tasks for #{user_s} in #{project_to_s}")
170
- task = select("Please select a story to comment it", table)
171
- comment = ask("Write your comment")
172
- end
173
- if @client.comment_task(@project, task, comment)
174
- congrats("Comment sent, thanks!")
175
- save_recent_task( task.id )
176
- else
177
- error("Ummm, something went wrong.")
178
- end
179
- end
180
-
181
- def assign
182
- if @params[0]
183
- task = task_by_id_or_pt_id @params[0].to_i
184
- owner = find_owner @params[1]
185
- else
186
- title("Tasks for #{user_s} in #{project_to_s}")
187
- tasks = @client.get_tasks_to_assign(@project)
188
- table = TasksTable.new(tasks)
189
- task = select("Please select a task to assign it an owner", table)
190
- end
191
-
192
- unless owner
193
- members = @client.get_members(@project)
194
- table = PersonsTable.new(members.map(&:person))
195
- owner = select("Please select a member to assign him the task", table)
196
- end
197
- @client.assign_task(@project, task, owner)
198
-
199
- congrats("Task assigned to #{owner.initials}, thanks!")
200
- end
201
-
202
- def estimate
203
- if @params[0]
204
- task = task_by_id_or_pt_id @params[0].to_i
205
- title("Estimating '#{task.name}'")
206
-
207
- if [0,1,2,3].include? @params[1].to_i
208
- estimation = @params[1]
209
- end
210
- else
211
- tasks = @client.get_my_tasks_to_estimate(@project, @local_config[:user_name])
212
- table = TasksTable.new(tasks)
213
- title("Tasks for #{user_s} in #{project_to_s}")
214
- task = select("Please select a story to estimate it", table)
215
- end
216
-
217
- estimation ||= ask("How many points you estimate for it? (#{@project.point_scale})")
218
- @client.estimate_task(@project, task, estimation)
219
- congrats("Task estimated, thanks!")
220
- end
221
-
222
- def start
223
- if @params[0]
224
- task = task_by_id_or_pt_id @params[0].to_i
225
- title("Starting '#{task.name}'")
226
- else
227
- tasks = @client.get_my_tasks_to_start(@project, @local_config[:user_name])
228
- table = TasksTable.new(tasks)
229
- title("Tasks for #{user_s} in #{project_to_s}")
230
- task = select("Please select a story to mark it as started", table)
231
- end
232
- start_task task
233
- end
234
-
235
- def finish
236
- if @params[0]
237
- task = task_by_id_or_pt_id @params[0].to_i
238
- title("Finishing '#{task.name}'")
239
- else
240
- tasks = @client.get_my_tasks_to_finish(@project, @local_config[:user_name])
241
- table = TasksTable.new(tasks)
242
- title("Tasks for #{user_s} in #{project_to_s}")
243
- task = select("Please select a story to mark it as finished", table)
244
- end
245
- finish_task task
246
- end
247
-
248
- def deliver
249
- if @params[0]
250
- task = task_by_id_or_pt_id @params[0].to_i
251
- title("Delivering '#{task.name}'")
252
- else
253
- tasks = @client.get_my_tasks_to_deliver(@project, @local_config[:user_name])
254
- table = TasksTable.new(tasks)
255
- title("Tasks for #{user_s} in #{project_to_s}")
256
- task = select("Please select a story to mark it as delivered", table)
257
- end
258
-
259
- deliver_task task
260
- end
261
-
262
- def accept
263
- if @params[0]
264
- task = task_by_id_or_pt_id @params[0].to_i
265
- title("Accepting '#{task.name}'")
266
- else
267
- tasks = @client.get_my_tasks_to_accept(@project, @local_config[:user_name])
268
- table = TasksTable.new(tasks)
269
- title("Tasks for #{user_s} in #{project_to_s}")
270
- task = select("Please select a story to mark it as accepted", table)
271
- end
272
- @client.mark_task_as(@project, task, 'accepted')
273
- congrats("Task accepted, hooray!")
274
- end
275
-
276
- def show
277
- title("Tasks for #{user_s} in #{project_to_s}")
278
- task = get_task_from_params "Please select a story to show"
279
- unless task
280
- message("No matches found for '#{@params[0]}', please use a valid pivotal story Id")
281
- return
282
- end
283
- show_task(task)
284
- end
285
-
286
- def tasks
287
- title("Open story tasks for #{user_s} in #{project_to_s}")
288
-
289
- unless story = get_task_from_params( "Please select a story to show pending tasks" )
290
- message("No matches found for '#{@params[0]}', please use a valid pivotal story Id")
291
- return
292
- end
293
-
294
- story_task = get_open_story_task_from_params(story)
295
-
296
- if story_task.position == -1
297
- description = ask('Title for new task')
298
- story.create_task(:description => description)
299
- congrats("New todo task added to \"#{story.name}\"")
300
- else
301
- edit_story_task story_task
302
- end
303
- end
304
-
305
- # takes a comma separated list of ids and prints the collection of tasks
306
- def show_condensed
307
- title("Tasks for #{user_s} in #{project_to_s}")
308
- tasks = []
309
- if @params[0]
310
- @params[0].each_line(',') do |line|
311
- tasks << @client.get_task_by_id(@project, line.to_i)
312
- end
313
- table = TasksTable.new(tasks)
314
- table.print
315
- end
316
- end
317
-
318
- # TODO implement story notes and comment
319
- def reject
320
- title("Tasks for #{user_s} in #{project_to_s}")
321
- if @params[0]
322
- task = @client.get_story(@project, @params[0])
323
- title("Rejecting '#{task.name}'")
324
- else
325
- tasks = @client.get_my_tasks_to_reject(@project, @local_config[:user_name])
326
- table = TasksTable.new(tasks)
327
- title("Tasks for #{user_s} in #{project_to_s}")
328
- task = select("Please select a story to mark it as rejected", table)
329
- end
330
-
331
- if @params[1]
332
- comment = @params[1]
333
- else
334
- comment = ask("Please explain why are you rejecting the task.")
335
- end
336
-
337
- if @client.comment_task(@project, task, comment)
338
- result = @client.mark_task_as(@project, task, 'rejected')
339
- congrats("Task rejected, thanks!")
340
- else
341
- error("Ummm, something went wrong.")
342
- end
343
- end
344
-
345
- def done
346
- if @params[0]
347
- task = task_by_id_or_pt_id @params[0].to_i
348
-
349
- #we need this for finding again later
350
- task_id = task.id
351
-
352
- if !@params[1] && task.estimate == -1
353
- error("You need to give an estimate for this task")
354
- return
355
- end
356
-
357
- if @params[1] && task.estimate == -1
358
- if [0,1,2,3].include? @params[1].to_i
359
- estimate_task(task, @params[1].to_i)
360
- end
361
- if @params[2]
362
- task = task_by_id_or_pt_id task_id
363
- @client.comment_task(@project, task, @params[2])
364
- end
365
- else
366
- @client.comment_task(@project, task, @params[1]) if @params[1]
367
- end
368
-
369
- task = task_by_id_or_pt_id task_id
370
- start_task task
371
-
372
- task = task_by_id_or_pt_id task_id
373
- finish_task task
374
-
375
- task = task_by_id_or_pt_id task_id
376
- deliver_task task
377
- end
378
- end
379
-
380
- def estimate_task task, difficulty
381
- result = @client.estimate_task(@project, task, difficulty)
382
- if result.errors.any?
383
- error(result.errors.errors)
384
- else
385
- congrats("Task estimated, thanks!")
386
- end
387
- end
388
-
389
- def start_task task
390
- @client.mark_task_as(@project, task, 'started')
391
- congrats("Task started, go for it!")
392
- end
393
-
394
- def finish_task task
395
- if task.story_type == 'chore'
396
- @client.mark_task_as(@project, task, 'accepted')
397
- else
398
- @client.mark_task_as(@project, task, 'finished')
399
- end
400
- congrats("Another task bites the dust, yeah!")
401
- end
402
-
403
- def deliver_task task
404
- return if task.story_type == 'chore'
405
- @client.mark_task_as(@project, task, 'delivered')
406
- congrats("Task delivered, congrats!")
407
- end
408
-
409
- def find
410
- if (story_id = @params[0].to_i).nonzero?
411
- if task = task_by_id_or_pt_id(@params[0].to_i)
412
- return show_task(task)
413
- else
414
- message("Task not found by id (#{story_id}), falling back to text search")
415
- end
416
- end
417
-
418
- if @params[0]
419
- tasks = @client.search_for_story(@project, @params[0])
420
- tasks.each do |story_task|
421
- title("--- [#{(tasks.index story_task) + 1 }] -----------------")
422
- show_task(story_task)
423
- end
424
- message("No matches found for '#{@params[0]}'") if tasks.empty?
425
- else
426
- message("You need to provide a substring for a tasks title.")
427
- end
428
- end
429
-
430
- def updates
431
- activities = @client.get_activities(@project, @params[0])
432
- tasks = @client.get_my_work(@project, @local_config[:user_name])
433
- title("Recent Activity on #{project_to_s}")
434
- activities.each do |activity|
435
- show_activity(activity, tasks)
436
- end
437
- end
438
-
439
-
440
- def help
441
- if ARGV[0] && ARGV[0] != 'help'
442
- message("Command #{ARGV[0]} not recognized. Showing help.")
443
- end
444
-
445
- title("Command line usage for pt #{VERSION}")
446
- help = <<-HELP
447
- pt # show all available stories
448
-
449
- pt todo <owner> # show all unscheduled stories
450
-
451
- pt (unscheduled,started,finished,delivered, accepted, rejected) <owner> # show all (unscheduled,started,finished,delivered, accepted, rejected) stories
452
-
453
- pt create [title] <owner> <type> -m # create a new story (and include description ala git commit)
454
-
455
- pt show [id] # shows detailed info about a story
456
-
457
- pt tasks [id] # manage tasks of story
458
-
459
- pt open [id] # open a story in the browser
460
-
461
- pt assign [id] <owner> # assign owner
462
-
463
- pt comment [id] [comment] # add a comment
464
-
465
- pt label [id] [label] # add a label
466
-
467
- pt estimate [id] [0-3] # estimate a story in points scale
468
-
469
- pt (start,finish,deliver,accept) [id] # mark a story as started
470
-
471
- pt reject [id] [reason] # mark a story as rejected, explaining why
472
-
473
- pt done [id] <0-3> <comment> # lazy mans finish story, opens, assigns to you, estimates, finish & delivers
474
-
475
- pt find [query] # looks in your stories by title and presents it
476
-
477
- pt list [owner] # list all stories for another pt user
478
-
479
- pt list all # list all stories for all users
480
-
481
- pt updates # shows number recent activity from your current project
482
-
483
- pt recent # shows stories you've recently shown or commented on with pt
484
-
485
- All commands can be run entirely without arguments for a wizard based UI. Otherwise [required] <optional>.
486
- Anything that takes an id will also take the num (index) from the pt command.
487
- HELP
488
- puts(help)
489
- end
490
-
491
- protected
492
-
493
- def commands
494
- (public_methods - Object.public_methods).map{ |c| c.to_sym}
495
- end
496
-
497
- # Config
498
-
499
- def load_global_config
500
-
501
- # skip global config if env vars are set
502
- if ENV['PIVOTAL_EMAIL'] and ENV['PIVOTAL_API_KEY']
503
- config = {
504
- :email => ENV['PIVOTAL_EMAIL'],
505
- :api_number => ENV['PIVOTAL_API_KEY']
506
- }
507
- return config
508
- end
509
-
510
- config = YAML.load(File.read(GLOBAL_CONFIG_PATH)) rescue {}
511
- if config.empty?
512
- message "I can't find info about your Pivotal Tracker account in #{GLOBAL_CONFIG_PATH}."
513
- while !config[:api_number] do
514
- config[:api_number] = ask "What is your token?"
515
- end
516
- congrats "Thanks!",
517
- "Your API id is " + config[:api_number],
518
- "I'm saving it in #{GLOBAL_CONFIG_PATH} so you don't have to log in again."
519
- save_config(config, GLOBAL_CONFIG_PATH)
520
- end
521
- config
522
- end
523
-
524
- def get_local_config_path
525
- # If the local config path does not exist, check to see if we're in a git repo
526
- # And if so, try the top level of the checkout
527
- if (!File.exist?(LOCAL_CONFIG_PATH) && system('git rev-parse 2> /dev/null'))
528
- return `git rev-parse --show-toplevel`.chomp() + '/.pt'
529
- else
530
- return LOCAL_CONFIG_PATH
531
- end
532
- end
533
-
534
- def load_local_config
535
- check_local_config_path
536
- config = YAML.load(File.read(get_local_config_path())) rescue {}
537
-
538
- if ENV['PIVOTAL_PROJECT_ID']
539
-
540
- config[:project_id] = ENV['PIVOTAL_PROJECT_ID']
541
-
542
- project = @client.get_project(config[:project_id])
543
- config[:project_name] = project.name
544
-
545
- membership = @client.get_my_info
546
- config[:user_name], config[:user_id], config[:user_initials] = membership.name, membership.id, membership.initials
547
- save_config(config, get_local_config_path())
548
-
549
- end
550
-
551
- if config.empty?
552
- message "I can't find info about this project in #{get_local_config_path()}"
553
- projects = ProjectTable.new(@client.get_projects)
554
- project = select("Please select the project for the current directory", projects)
555
- config[:project_id], config[:project_name] = project.id, project.name
556
- project = @client.get_project(project.id)
557
- membership = @client.get_my_info
558
- config[:user_name], config[:user_id], config[:user_initials] = membership.name, membership.id, membership.initials
559
- congrats "Thanks! I'm saving this project's info",
560
- "in #{get_local_config_path()}: remember to .gitignore it!"
561
- save_config(config, get_local_config_path())
562
- end
563
- config
564
- end
565
-
566
- def check_local_config_path
567
- if GLOBAL_CONFIG_PATH == get_local_config_path()
568
- error("Please execute .pt inside your project directory and not in your home.")
569
- exit
570
- end
571
- end
572
-
573
- def save_config(config, path)
574
- File.new(path, 'w') unless File.exists?(path)
575
- File.open(path, 'w') {|f| f.write(config.to_yaml) }
576
- end
577
-
578
- # I/O
579
-
580
- def split_lines(text)
581
- text.respond_to?(:join) ? text.join("\n") : text
582
- end
583
-
584
- def title(*msg)
585
- puts "\n#{split_lines(msg)}".bold
586
- end
587
-
588
- def congrats(*msg)
589
- puts "\n#{split_lines(msg).green.bold}"
590
- end
591
-
592
- def message(*msg)
593
- puts "\n#{split_lines(msg)}"
594
- end
595
-
596
- def compact_message(*msg)
597
- puts "#{split_lines(msg)}"
598
- end
599
-
600
- def error(*msg)
601
- puts "\n#{split_lines(msg).red.bold}"
602
- end
603
-
604
- def select(msg, table)
605
- if table.length > 0
606
- begin
607
- table.print @global_config
608
- row = ask "#{msg} (1-#{table.length}, 'q' to exit)"
609
- quit if row == 'q'
610
- selected = table[row]
611
- error "Invalid selection, try again:" unless selected
612
- end until selected
613
- selected
614
- else
615
- table.print @global_config
616
- message "Sorry, there are no options to select."
617
- quit
618
- end
619
- end
620
-
621
- def quit
622
- message "bye!"
623
- exit
624
- end
625
-
626
- def ask(msg)
627
- @io.ask("#{msg.bold}")
628
- end
629
-
630
- def ask_secret(msg)
631
- @io.ask("#{msg.bold}"){ |q| q.echo = '*' }
632
- end
633
-
634
- def user_s
635
- "#{@local_config[:user_name]} (#{@local_config[:user_initials]})"
636
- end
637
-
638
- def project_to_s
639
- "Project #{@local_config[:project_name].upcase}"
640
- end
641
-
642
- def task_type_or_nil query
643
- if (["feature", "bug", "chore"].index query)
644
- return query
645
- end
646
- nil
647
- end
648
-
649
- def task_by_id_or_pt_id id
650
- if id < 1000
651
- tasks = @client.get_my_work(@project, @local_config[:user_name])
652
- table = TasksTable.new(tasks)
653
- table[id]
654
- else
655
- @client.get_task_by_id @project, id
656
- end
657
- end
658
-
659
- def find_task query
660
- members = @client.get_members(@project)
661
- members.each do | member |
662
- if member.name.downcase.index query
663
- return member.name
664
- end
665
- end
666
- nil
667
- end
668
-
669
- def find_owner query
670
- if query
671
- member = @client.get_member(@project, query)
672
- return member ? member.person : nil
673
- end
674
- nil
675
- end
676
-
677
- def show_task(task)
678
- title task.name.green
679
- estimation = [-1, nil].include?(task.estimate) ? "Unestimated" : "#{task.estimate} points"
680
- message "#{task.current_state.capitalize} #{task.story_type} | #{estimation} | Req: #{task.requested_by.initials} |
681
- Owners: #{task.owners.map(&:initials).join(',')} | Id: #{task.id}"
682
-
683
- if (task.labels)
684
- message "Labels: " + task.labels.map(&:name).join(', ')
685
- end
686
- message task.description unless task.description.nil? || task.description.empty?
687
- message "View on pivotal: #{task.url}"
688
-
689
- if task.tasks
690
- title('tasks'.red)
691
- task.tasks.each{ |t| compact_message "- #{t.complete ? "(v) " : "( )"} #{t.description}" }
692
- end
693
-
694
-
695
- task.comments.each do |n|
696
- title('========================================='.red)
697
- text = ">> #{n.person.initials}: #{n.text}"
698
- text << "[#{n.file_attachment_ids.size}F]" if n.file_attachment_ids
699
- message text
700
- end
701
- save_recent_task( task.id )
702
- end
703
-
704
-
705
- def show_activity(activity, tasks)
706
- message("#{activity.message}")
707
- end
708
-
709
- def get_open_story_task_from_params(task)
710
- title "Pending tasks for '#{task.name}'"
711
- task_struct = Struct.new(:description, :position)
712
-
713
- pending_tasks = [
714
- task_struct.new('<< Add new task >>', -1)
715
- ]
716
-
717
- task.tasks.each{ |t| pending_tasks << t unless t.complete }
718
- table = TodoTaskTable.new(pending_tasks)
719
- select("Pick task to edit, 1 to add new task", table)
720
- end
721
-
722
- def get_task_from_params(prompt)
723
- if @params[0]
724
- task = task_by_id_or_pt_id(@params[0].to_i)
725
- else
726
- tasks = @client.get_all_stories(@project, @local_config[:user_name])
727
- table = TasksTable.new(tasks)
728
- task = select(prompt, table)
729
- end
730
- end
731
-
732
- def edit_story_task(story_task)
733
- action_class = Struct.new(:action, :key)
734
-
735
- table = ActionTable.new([
736
- action_class.new('Complete', :complete),
737
- action_class.new('Delete', :delete),
738
- action_class.new('Edit', :edit)
739
- # Move?
740
- ])
741
- action_to_execute = select('What to do with todo?', table)
742
-
743
- case action_to_execute.key
744
- when :complete then
745
- story_task.update(:complete => true)
746
- congrats('Todo task completed!')
747
- when :delete then
748
- story_task.delete
749
- congrats('Todo task removed')
750
- when :edit then
751
- new_description = ask('New task description')
752
- story_task.update(:description => new_description)
753
- congrats("Todo task changed to: \"#{story_task.description}\"")
754
- end
755
- end
756
-
757
- def save_recent_task( task_id )
758
- # save list of recently accessed tasks
759
- unless (@local_config[:recent_tasks])
760
- @local_config[:recent_tasks] = Array.new();
761
- end
762
- @local_config[:recent_tasks].unshift( task_id )
763
- @local_config[:recent_tasks] = @local_config[:recent_tasks].uniq()
764
- if @local_config[:recent_tasks].length > 10
765
- @local_config[:recent_tasks].pop()
766
- end
767
- save_config( @local_config, get_local_config_path() )
768
- end
769
-
770
- end
771
- end