pt 0.8.6 → 0.9.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.
@@ -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