ipt 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Changelog.md +6 -0
- data/Gemfile +9 -0
- data/Guardfile +19 -0
- data/LICENSE.txt +21 -0
- data/README.md +37 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/ipt +10 -0
- data/bin/setup +8 -0
- data/ipt.gemspec +36 -0
- data/lib/pt.rb +13 -0
- data/lib/pt/action.rb +280 -0
- data/lib/pt/cli.rb +163 -0
- data/lib/pt/client.rb +133 -0
- data/lib/pt/configuration.rb +65 -0
- data/lib/pt/data_row.rb +83 -0
- data/lib/pt/data_table.rb +107 -0
- data/lib/pt/io.rb +184 -0
- data/lib/pt/version.rb +3 -0
- metadata +235 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f7c0b0acbed66cdaae452c45c9ef28f5decd47c4
|
4
|
+
data.tar.gz: 32dfa0167b2a113c58b55d0078c82d181ef503bd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c352f0b94d487587f71e6a883d6773f8a0ed8adb4b13f0ffa73f46e9ed75041ac1b6f12953a0fe21c742217ca5acaa5ac7e8d20147f41163855c41351f0743f2
|
7
|
+
data.tar.gz: 11fcb1385f1b10f83e833e11a1301233d041097df53cd49d1113dcc17e094d1e7c8b2097a3135288f3faa84a0428211ad340359dde9e06755be1698686f2c813
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at botoksgonzales@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Changelog.md
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
directories %w(lib spec)
|
6
|
+
|
7
|
+
notification :tmux,
|
8
|
+
display_message: true,
|
9
|
+
timeout: 5, # in seconds
|
10
|
+
default_message_format: '%s >> %s',
|
11
|
+
line_separator: ' > ', # since we are single line we need a separator
|
12
|
+
color_location: %w[status-left-bg pane-active-border-fg ]
|
13
|
+
|
14
|
+
guard :rspec, all_on_start: false, all_after_pass: false, cmd: 'rspec' do
|
15
|
+
watch(%r{^spec/.+_spec\.rb$})
|
16
|
+
watch(%r{^lib/pt/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
17
|
+
watch('spec/spec_helper.rb') { "spec" }
|
18
|
+
end
|
19
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2011 Raul Murciano raul@murciano.net
|
2
|
+
Copyright (c) 2017 Slamet Kristanto cakmet14@gmail.com
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/pt.svg)](https://badge.fury.io/rb/pt)
|
2
|
+
[![Build Status](https://travis-ci.org/drselump14/ipt.svg?branch=master)](https://travis-ci.org/drselump14/ipt)
|
3
|
+
|
4
|
+
# ipt
|
5
|
+
|
6
|
+
Minimal client to use Pivotal Tracker API v5 from the command line.
|
7
|
+
Interactive version of [pt](https://github.com/raul/pt)
|
8
|
+
|
9
|
+
# Demo
|
10
|
+
![gif](https://www.dropbox.com/s/whob1i5nocyk7hq/ipt.gif?raw=1)
|
11
|
+
|
12
|
+
## Setup
|
13
|
+
|
14
|
+
gem install ipt
|
15
|
+
|
16
|
+
The first time you run it, `ipt` will ask you some data about your Pivotal Tracker account and your current project.
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
Run `ipt` from the root folder of your project to show all your work.
|
21
|
+
|
22
|
+
## Contribute
|
23
|
+
- clone this repository
|
24
|
+
- `bundle install`
|
25
|
+
- change the code
|
26
|
+
- run test `rspec`
|
27
|
+
- `rake build`
|
28
|
+
- `rake install`
|
29
|
+
- commit and create pull request
|
30
|
+
|
31
|
+
## Thanks to...
|
32
|
+
- the contributors of pt
|
33
|
+
- the [Pivotal Tracker](https://www.pivotaltracker.com) guys for making a planning tool that doesn't suck and has an API
|
34
|
+
- [dashofcode](http://github.com/dashofcode) for `tracker_api` gem
|
35
|
+
|
36
|
+
## License
|
37
|
+
See the LICENSE file included in the distribution.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "pt"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "pry"
|
14
|
+
Pry.start
|
data/bin/ipt
ADDED
data/bin/setup
ADDED
data/ipt.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'pt/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ipt"
|
8
|
+
spec.version = PT::VERSION
|
9
|
+
spec.authors = ["Slamet Kristanto"]
|
10
|
+
spec.email = ["cakmet14@gmail.com"]
|
11
|
+
spec.licenses = ['MIT']
|
12
|
+
spec.homepage = "http://www.github.com/drselump14/ipt"
|
13
|
+
spec.summary = "Interactive Pivotal Tracker CLI (API v5)"
|
14
|
+
spec.description = "Interactive Pivotal Tracker Command Line Interface"
|
15
|
+
spec.executables = ["ipt"]
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "pry"
|
26
|
+
spec.add_development_dependency 'rspec'
|
27
|
+
spec.add_development_dependency 'aruba'
|
28
|
+
spec.add_development_dependency 'vcr'
|
29
|
+
|
30
|
+
spec.add_dependency 'highline', '>= 2.0.0.pre.develop.6'
|
31
|
+
spec.add_dependency 'tracker_api', '~> 1.6.0'
|
32
|
+
spec.add_dependency 'thor'
|
33
|
+
spec.add_dependency 'terminal-table'
|
34
|
+
spec.add_dependency 'paint'
|
35
|
+
spec.add_dependency 'config'
|
36
|
+
end
|
data/lib/pt.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'tracker_api'
|
2
|
+
require "pt/version"
|
3
|
+
require 'pt/client'
|
4
|
+
require 'pt/data_row'
|
5
|
+
require 'pt/data_table'
|
6
|
+
require 'pt/action'
|
7
|
+
require 'pt/io'
|
8
|
+
require 'pt/configuration'
|
9
|
+
require 'pt/cli'
|
10
|
+
|
11
|
+
module PT
|
12
|
+
# Your code goes here...
|
13
|
+
end
|
data/lib/pt/action.rb
ADDED
@@ -0,0 +1,280 @@
|
|
1
|
+
module PT
|
2
|
+
module Action
|
3
|
+
def show_story(story)
|
4
|
+
clear
|
5
|
+
title('========================================='.red)
|
6
|
+
title story.name.red
|
7
|
+
title('========================================='.red)
|
8
|
+
estimation = [-1, nil].include?(story.estimate) ? "Unestimated" : "#{story.estimate} points"
|
9
|
+
requester = story.requested_by ? story.requested_by.initials : Settings[:user_name]
|
10
|
+
if story.instance_variable_get(:@owners).present?
|
11
|
+
owners = story.owners.map(&:initials).join(',')
|
12
|
+
end
|
13
|
+
message "#{story.current_state.capitalize} #{story.story_type} | #{estimation} | Req: #{requester} | Owners: #{owners} | ID: #{story.id}"
|
14
|
+
|
15
|
+
if story.instance_variable_get(:@labels).present?
|
16
|
+
message "Labels: " + story.labels.map(&:name).join(', ')
|
17
|
+
end
|
18
|
+
|
19
|
+
message story.description.green unless story.description.nil? || story.description.empty?
|
20
|
+
message "View on pivotal: #{story.url}"
|
21
|
+
|
22
|
+
if story.instance_variable_get(:@tasks).present?
|
23
|
+
title('tasks'.yellow)
|
24
|
+
story.tasks.each{ |t| compact_message "- #{t.complete ? "[done]" : ""} #{t.description}" }
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
if story.instance_variable_get(:@comments).present?
|
29
|
+
story.comments.each do |n|
|
30
|
+
title('......................................'.blue)
|
31
|
+
text = ">> #{n.person.initials}: #{n.text}"
|
32
|
+
text << "[#{n.file_attachment_ids.size}F]" if n.file_attachment_ids
|
33
|
+
message text
|
34
|
+
end
|
35
|
+
end
|
36
|
+
save_recent_task( story.id )
|
37
|
+
say ""
|
38
|
+
title '================================================='.red
|
39
|
+
choice = ask "Please choose action ([b]:back to table | [m]:show menu | [q] quit)"
|
40
|
+
case choice
|
41
|
+
when 'q'
|
42
|
+
quit
|
43
|
+
when 'm'
|
44
|
+
choose_action(story)
|
45
|
+
when 'b'
|
46
|
+
say('back to table ....')
|
47
|
+
return :no_request
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def tasks_story(story)
|
52
|
+
story_task = get_open_story_task_from_params(story)
|
53
|
+
if story_task.position == -1
|
54
|
+
description = ask('Title for new task')
|
55
|
+
story.create_task(:description => description)
|
56
|
+
congrats("New todo task added to \"#{story.name}\"")
|
57
|
+
else
|
58
|
+
edit_story_task story_task
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def open_story story
|
63
|
+
`open #{story.url}`
|
64
|
+
return :no_request
|
65
|
+
end
|
66
|
+
|
67
|
+
def assign_story story
|
68
|
+
owner = choose_person
|
69
|
+
@client.assign_story(story, owner)
|
70
|
+
congrats("story assigned to #{owner.initials}, thanks!")
|
71
|
+
end
|
72
|
+
|
73
|
+
def comment_story(story)
|
74
|
+
say("Please write your comment")
|
75
|
+
comment = edit_using_editor
|
76
|
+
if @client.comment_task(story, comment)
|
77
|
+
congrats("Comment sent, thanks!")
|
78
|
+
save_recent_task( story.id )
|
79
|
+
else
|
80
|
+
error("Ummm, something went wrong.")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def label_story(story)
|
85
|
+
label = ask("Which label?")
|
86
|
+
@client.add_label(story, label );
|
87
|
+
congrats("#{label} added, thanks!")
|
88
|
+
end
|
89
|
+
|
90
|
+
def estimate_story(story)
|
91
|
+
if story.story_type == 'feature'
|
92
|
+
estimation ||= ask("How many points you estimate for it? (#{project.point_scale})")
|
93
|
+
@client.estimate_story(story, estimation)
|
94
|
+
congrats("Task estimated, thanks!")
|
95
|
+
else
|
96
|
+
error('Only feature can be estimated!')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def start_story story
|
101
|
+
if story.story_type == 'feature' && !story.estimate
|
102
|
+
estimate_story(story)
|
103
|
+
end
|
104
|
+
@client.mark_task_as(story, 'started')
|
105
|
+
congrats("story started, go for it!")
|
106
|
+
end
|
107
|
+
|
108
|
+
def unstart_story story
|
109
|
+
@client.mark_task_as(story, 'unstarted')
|
110
|
+
congrats("story unstarted")
|
111
|
+
end
|
112
|
+
|
113
|
+
def start_story story
|
114
|
+
if story.story_type == 'feature' && !story.estimate
|
115
|
+
estimate_story(story)
|
116
|
+
end
|
117
|
+
@client.mark_task_as(story, 'started')
|
118
|
+
congrats("story started, go for it!")
|
119
|
+
end
|
120
|
+
|
121
|
+
def finish_story story
|
122
|
+
if story.story_type == 'chore'
|
123
|
+
@client.mark_task_as(story, 'accepted')
|
124
|
+
else
|
125
|
+
@client.mark_task_as(story, 'finished')
|
126
|
+
end
|
127
|
+
congrats("Another story bites the dust, yeah!")
|
128
|
+
end
|
129
|
+
|
130
|
+
def deliver_story story
|
131
|
+
return if story.story_type == 'chore'
|
132
|
+
@client.mark_task_as(story, 'delivered')
|
133
|
+
congrats("story delivered, congrats!")
|
134
|
+
end
|
135
|
+
|
136
|
+
def accept_story story
|
137
|
+
@client.mark_task_as(story, 'accepted')
|
138
|
+
congrats("Accepted")
|
139
|
+
end
|
140
|
+
|
141
|
+
def reject_story(story)
|
142
|
+
comment = ask("Please explain why are you rejecting the story.")
|
143
|
+
if @client.comment_task(story, comment)
|
144
|
+
@client.mark_task_as(story, 'rejected')
|
145
|
+
congrats("story rejected, thanks!")
|
146
|
+
else
|
147
|
+
error("Ummm, something went wrong.")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def done_story(story)
|
152
|
+
start_story story
|
153
|
+
|
154
|
+
finish_story story
|
155
|
+
|
156
|
+
deliver_story story
|
157
|
+
end
|
158
|
+
|
159
|
+
def copy_story_id(story)
|
160
|
+
`echo #{story.id} | pbcopy`
|
161
|
+
congrats("Story ID copied")
|
162
|
+
end
|
163
|
+
|
164
|
+
def copy_story_url(story)
|
165
|
+
`echo #{story.url} | pbcopy`
|
166
|
+
congrats("Story URL copied")
|
167
|
+
end
|
168
|
+
|
169
|
+
def create_interactive_story
|
170
|
+
# set title
|
171
|
+
title = ask("Name for the new task:")
|
172
|
+
|
173
|
+
# set owner
|
174
|
+
if ask('Do you want to assign it now? (y/n)').downcase == 'y'
|
175
|
+
members = @client.get_members
|
176
|
+
table = PersonsTable.new(members.map(&:person))
|
177
|
+
owner = select("Please select a member to assign him the task.", table)
|
178
|
+
owner_id = owner.id
|
179
|
+
end
|
180
|
+
|
181
|
+
# set story type
|
182
|
+
type = case ask('Type? (c)hore, (b)ug, anything else for feature)')
|
183
|
+
when 'c', 'chore'
|
184
|
+
'chore'
|
185
|
+
when 'b', 'bug'
|
186
|
+
'bug'
|
187
|
+
else
|
188
|
+
'feature'
|
189
|
+
end
|
190
|
+
|
191
|
+
description = edit_using_editor if ask('Do you want to write description now?(y/n)') == 'y'
|
192
|
+
story = @client.create_story(
|
193
|
+
name: title,
|
194
|
+
owner_ids: [owner_id],
|
195
|
+
requested_by_id: Settings[:user_id],
|
196
|
+
story_type: type,
|
197
|
+
description: description
|
198
|
+
)
|
199
|
+
congrats("#{type} #{('for' + owner.name ) if owner} has been created \n #{story.url}")
|
200
|
+
story
|
201
|
+
end
|
202
|
+
|
203
|
+
def edit_story(story)
|
204
|
+
# set title
|
205
|
+
if ask("Edit title?(y/n) [#{story.name}]") { |yn| yn.limit = 1, yn.validate = /[yn]/i } == 'y'
|
206
|
+
say('Edit your story name')
|
207
|
+
story.name = edit_using_editor(story.name)
|
208
|
+
end
|
209
|
+
|
210
|
+
# set story type
|
211
|
+
story.story_type = case ask('Edit Type? (f)eature (c)hore, (b)ug, enter to skip)')
|
212
|
+
when 'c', 'chore'
|
213
|
+
'chore'
|
214
|
+
when 'b', 'bug'
|
215
|
+
'bug'
|
216
|
+
when 'f', 'feature'
|
217
|
+
'feature'
|
218
|
+
end
|
219
|
+
|
220
|
+
story.description = edit_using_editor(story.description) if ask('Do you want to edit description now?(y/n)') == 'y'
|
221
|
+
story = story.save
|
222
|
+
congrats("'#{story.name}' has been edited \n #{story.url}")
|
223
|
+
story
|
224
|
+
end
|
225
|
+
|
226
|
+
def edit_story_task(task)
|
227
|
+
action_class = Struct.new(:action, :key)
|
228
|
+
|
229
|
+
table = ActionTable.new([
|
230
|
+
action_class.new('Complete', :complete),
|
231
|
+
action_class.new('Edit', :edit)
|
232
|
+
])
|
233
|
+
action_to_execute = select('What to do with todo?', table)
|
234
|
+
|
235
|
+
task.project_id = project.id
|
236
|
+
task.client = project.client
|
237
|
+
case action_to_execute.key
|
238
|
+
when :complete then
|
239
|
+
task.complete = true
|
240
|
+
congrats('Todo task completed!')
|
241
|
+
when :edit then
|
242
|
+
new_description = ask('New task description')
|
243
|
+
task.description = new_description
|
244
|
+
congrats("Todo task changed to: \"#{task.description}\"")
|
245
|
+
end
|
246
|
+
task.save
|
247
|
+
end
|
248
|
+
|
249
|
+
def edit_using_editor(content=nil)
|
250
|
+
editor = ENV.fetch('EDITOR') { 'vi' }
|
251
|
+
temp_path = "/tmp/editor-#{ Process.pid }.txt"
|
252
|
+
File.write(temp_path, content) if content
|
253
|
+
system "#{ editor } #{ temp_path }"
|
254
|
+
content = File.read(temp_path)
|
255
|
+
File.delete(temp_path)
|
256
|
+
content
|
257
|
+
end
|
258
|
+
|
259
|
+
def choose_person
|
260
|
+
members = @client.get_members
|
261
|
+
table = PersonsTable.new(members.map(&:person))
|
262
|
+
select("Please select a member to see his tasks.", table)
|
263
|
+
end
|
264
|
+
|
265
|
+
def save_recent_task( task_id )
|
266
|
+
# save list of recently accessed tasks
|
267
|
+
unless (Settings[:recent_tasks])
|
268
|
+
Settings[:recent_tasks] = Array.new();
|
269
|
+
end
|
270
|
+
Settings[:recent_tasks].unshift( task_id )
|
271
|
+
Settings[:recent_tasks] = Settings[:recent_tasks].uniq()
|
272
|
+
if Settings[:recent_tasks].length > 10
|
273
|
+
Settings[:recent_tasks].pop()
|
274
|
+
end
|
275
|
+
@config.save_config( Settings, @config.get_local_config_path )
|
276
|
+
end
|
277
|
+
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|