pivotal-slacker 1.8.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/bin/pivotal-slacker +82 -38
  2. data/lib/formatter.rb +5 -1
  3. metadata +2 -2
data/bin/pivotal-slacker CHANGED
@@ -8,6 +8,9 @@ require "action_view"
8
8
  require "launchy"
9
9
  require "readline"
10
10
  require "shellwords"
11
+ require "nokogiri"
12
+ require "net/http"
13
+ require "uri"
11
14
  require "pp"
12
15
  require "app_config"
13
16
  require "formatter"
@@ -28,28 +31,44 @@ end
28
31
  # Init
29
32
 
30
33
  PivotalTracker::Client.token = $config.api_key
31
- $project = PivotalTracker::Project.find $config.project
34
+ $project = PivotalTracker::Project.find($config.project)
32
35
 
33
36
  # For `shell` mode, to retain menu number history.
34
37
  $menu_story_mapping = {}
35
38
 
36
- # ----------------------------------------------------------
37
- # Main app code
38
39
 
39
40
  require "commander/import"
40
41
  require "pivotal-tracker"
41
42
 
42
- program :version, "1.7.1"
43
+ program :version, "1.8.1"
43
44
  program :description, "Pivotal Tracker command line client."
44
45
 
45
46
  # ----------------------------------------------------------
46
47
  # Support functionality
47
48
 
49
+ require 'rubygems'
50
+ require 'active_resource'
51
+
52
+ # Defining this method because retrieving stories from PivotalTracker::Iteration doesn't work right.
53
+ def get_first_story opts={:project => nil, :group => "icebox"}
54
+ resource_uri = URI.parse(
55
+ "http://www.pivotaltracker.com/services/v3/projects/#{opts[:project].id}/iterations/#{opts[:group]}"
56
+ )
57
+ response = Net::HTTP.start(resource_uri.host, resource_uri.port) do |http|
58
+ http.get(resource_uri.path, {"X-TrackerToken" => $config.api_key})
59
+ end
60
+
61
+ xml = Nokogiri.XML(response.body)
62
+ target_story_id = xml.xpath("//story").xpath("./id").first.content.strip.to_i
63
+
64
+ opts[:project].stories.find target_story_id
65
+ end
66
+
48
67
  def init_new_shell_loop
49
68
  # Commander doesn't act right when you try to run the same command more than once,
50
69
  # so we have to redefine them after we use them.
51
70
  define_commands
52
-
71
+
53
72
  # Don't let the Pivotal Tracker API client cache projects or stories.
54
73
  $project = PivotalTracker::Project.find $config.project
55
74
  end
@@ -58,7 +77,7 @@ end
58
77
  # to `id` as passed in.
59
78
  def normalize_story_id id
60
79
  mapped = $menu_story_mapping[id]
61
-
80
+
62
81
  if mapped != nil
63
82
  mapped
64
83
  else
@@ -81,12 +100,12 @@ def output_story_list opts={:project => nil, :states => [], :owned_by => nil, :m
81
100
  requested_by = Formatter.requested_by(story.requested_by)
82
101
  created_at = Formatter.time_ago(story.created_at)
83
102
  story_type = Formatter.story_type(story.story_type)
84
-
103
+
85
104
  menu_output = nil
86
105
  if opts[:menu]
87
106
  $menu_story_mapping[menu_number] = story.id
88
107
  menu_output = "#{menu_number}. "
89
-
108
+
90
109
  menu_number += 1
91
110
  end
92
111
 
@@ -94,6 +113,9 @@ def output_story_list opts={:project => nil, :states => [], :owned_by => nil, :m
94
113
  end
95
114
  end
96
115
 
116
+ # ----------------------------------------------------------
117
+ # Main app code
118
+
97
119
  # ----------------------------------------------------------
98
120
  # Command-line interaction functionality
99
121
 
@@ -106,7 +128,7 @@ def define_commands
106
128
  c.action do |args, options|
107
129
  states = %w{unstarted started finished rejected}
108
130
  owned_by = $config.user
109
-
131
+
110
132
  output_story_list :project => $project, :states => states, :owned_by => owned_by, :menu => options.menu
111
133
  end
112
134
  end
@@ -118,7 +140,7 @@ def define_commands
118
140
  c.action do |args, options|
119
141
  states = %w{accepted}
120
142
  owned_by = $config.user
121
-
143
+
122
144
  output_story_list :project => $project, :states => states, :owned_by => owned_by
123
145
  end
124
146
  end
@@ -130,7 +152,7 @@ def define_commands
130
152
  c.action do |args, options|
131
153
  states = %w{started}
132
154
  owned_by = $config.user
133
-
155
+
134
156
  output_story_list :project => $project, :states => states, :owned_by => owned_by
135
157
  end
136
158
  end
@@ -142,7 +164,7 @@ def define_commands
142
164
  c.action do |args, options|
143
165
  states = %w{unstarted}
144
166
  owned_by = $config.user
145
-
167
+
146
168
  output_story_list :project => $project, :states => states, :owned_by => owned_by
147
169
  end
148
170
  end
@@ -159,7 +181,7 @@ def define_commands
159
181
  end
160
182
 
161
183
  command :show do |c|
162
- c.syntax = "pivotal-slacker show story_id"
184
+ c.syntax = "pivotal-slacker show story_id"
163
185
  c.description = "Show the details of a given Pivotal Tracker story."
164
186
  c.example "Show a story with ID 123", "pivotal-slacker show 123"
165
187
  c.action do |args, options|
@@ -178,7 +200,7 @@ def define_commands
178
200
  puts ""
179
201
  puts Formatter.description(story.description)
180
202
  puts ""
181
-
203
+
182
204
  notes = story.notes.all.sort { |a, b| a.noted_at <=> b.noted_at }
183
205
  notes.each do |note|
184
206
  author = Formatter.note_author(note.author)
@@ -186,12 +208,12 @@ def define_commands
186
208
  note_text = Formatter.note_text(note.text)
187
209
  puts "#{author} (#{noted_at}): #{note_text}"
188
210
  end
189
-
211
+
190
212
  # Extra padding line after notes.
191
213
  if notes != nil and notes.size > 0
192
214
  puts ""
193
215
  end
194
-
216
+
195
217
  end
196
218
  end
197
219
 
@@ -201,7 +223,7 @@ def define_commands
201
223
  c.example "Start a story with ID 123", "pivotal-slacker start 123"
202
224
  c.action do |args, options|
203
225
  story_id = normalize_story_id(args[0].to_i)
204
-
226
+
205
227
  story = $project.stories.find(story_id)
206
228
  result = story.update :current_state => "started", :owned_by => $config.user
207
229
 
@@ -215,7 +237,7 @@ def define_commands
215
237
  end
216
238
  end
217
239
  end
218
-
240
+
219
241
  command :estimate do |c|
220
242
  c.syntax = "pivotal-slacker estimate story_id"
221
243
  c.description = "Assign an estimate to a Pivotal Tracker story."
@@ -223,11 +245,11 @@ def define_commands
223
245
  c.option "--points", "The estimate in points to assign to the story"
224
246
  c.action do |args, options|
225
247
  options.default :points => nil
226
-
248
+
227
249
  raise "--points is required" if options.points == nil
228
-
250
+
229
251
  story_id = normalize_story_id(args[0].to_i)
230
-
252
+
231
253
  story = $project.stories.find(story_id)
232
254
  result = story.update :estimate => options.points
233
255
 
@@ -242,10 +264,10 @@ def define_commands
242
264
  c.action do |args, options|
243
265
  story_id = normalize_story_id(args[0].to_i)
244
266
  comment = args[1]
245
-
267
+
246
268
  story = $project.stories.find(story_id)
247
269
  story.notes.create :text => comment
248
-
270
+
249
271
  comment = Formatter.note_text(comment)
250
272
  puts "#{Formatter.story_action "Commented on", story.id, story.name}:"
251
273
  puts "#{Formatter.note_author($config.user)}: #{comment}"
@@ -258,10 +280,10 @@ def define_commands
258
280
  c.example "Finish a story with ID 123", "pivotal-slacker finish 123"
259
281
  c.action do |args, options|
260
282
  story_id = normalize_story_id(args[0].to_i)
261
-
283
+
262
284
  story = $project.stories.find(story_id)
263
285
  story.update :current_state => "finished"
264
-
286
+
265
287
  puts Formatter.story_action(Formatter.state("finished"), story.id, story.name) + "."
266
288
  end
267
289
  end
@@ -273,29 +295,44 @@ def define_commands
273
295
  c.option "--feature", "Designate the story as a feature"
274
296
  c.option "--bug", "Designate the story as a bug"
275
297
  c.option "--chore", "Designate the story as a chore"
298
+ c.option "--backlog", "Put this story at the top of the topmost 'Backlog' iteration"
276
299
  c.option "--release", "Designate the story as a release"
277
300
  c.option "--owner STRING", String, "Assigns the story to a user"
278
301
  c.option "--name STRING", String, "Name of story"
279
302
  c.option "--description STRING", String, "Description of story"
280
303
  c.action do |args, options|
281
- options.default :chore => false, :owner => $config.user, :name => nil, :description => nil
282
-
304
+ options.default(
305
+ :chore => false, :owner => $config.user, :name => nil, :description => nil
306
+ )
307
+
283
308
  raise "--name is required" if options.name == nil
284
309
  raise "--description is required" if options.description == nil
285
310
 
311
+ if not options.feature and not options.bug and not options.chore and not options.release
312
+ raise "Story type is required (e.g. --chore)"
313
+ end
314
+
286
315
  story_type = "feature" if options.feature
287
316
  story_type = "bug" if options.bug
288
317
  story_type = "chore" if options.chore
289
318
  story_type = "release" if options.release
290
319
 
320
+ iteration = nil
321
+ if options.backlog
322
+ iteration = "backlog"
323
+ end
324
+
291
325
  puts ""
292
326
  puts Formatter.story_name(options.name, :heading => true)
293
327
  puts "#{Formatter.attr_descriptor('type is', Formatter.story_type(story_type))}"
294
328
  puts "#{Formatter.attr_descriptor('requested by', Formatter.owner($config.user))}"
295
329
  puts "#{Formatter.attr_descriptor('owned by', Formatter.owner(options.owner))}"
296
330
  puts "#{Formatter.attr_descriptor('description is', Formatter.description(options.description))}"
331
+ if iteration
332
+ puts "#{Formatter.attr_descriptor('iteration is', Formatter.iteration(iteration))}"
333
+ end
297
334
  puts ""
298
-
335
+
299
336
  if agree "Really create? (y/n)"
300
337
  story = $project.stories.create(
301
338
  :name => options.name,
@@ -304,6 +341,13 @@ def define_commands
304
341
  :requested_by => $config.user,
305
342
  :owned_by => options.owner
306
343
  )
344
+
345
+ # If asked to, put the story at the top of the backlog.
346
+ if options.backlog
347
+ first_backlog_story = get_first_story :project => $project, :group => "backlog"
348
+ story.move(:before, first_backlog_story)
349
+ end
350
+
307
351
  puts Formatter.story_action("Created", story.id, story.name)
308
352
  else
309
353
  puts "Didn't create story."
@@ -318,7 +362,7 @@ command :shell do |c|
318
362
  c.syntax = "pivotal-slacker shell"
319
363
  c.description = %q{
320
364
  The pivotal-slacker shell mode allows entry of commands with less effort.
321
-
365
+
322
366
  In shell mode you can run commands as normal. You can also enter "menu numbers"
323
367
  (listed to the left of stories in story listings) instead of story IDs when running
324
368
  commands (e.g. `show 3` instead of `show 456123`).
@@ -330,32 +374,32 @@ command :shell do |c|
330
374
  c.action do |args, options|
331
375
  loop do
332
376
  input = Readline::readline("pivotal-slacker> ")
333
-
377
+
334
378
  break if input.nil? or input == "q" or input == "quit"
335
379
  next if input.strip == ""
336
-
380
+
337
381
  # Add input to Readinput history.
338
382
  Readline::HISTORY.push(input)
339
383
 
340
384
  # Turn output into an ARGV array.
341
- argv = Shellwords.shellwords input
342
-
385
+ argv = Shellwords.shellwords input
386
+
343
387
  # Shift the command name off the ARGV array; Commander doesn't want it in there.
344
388
  command_name = argv.shift
345
389
  command = command(command_name.to_sym)
346
-
390
+
347
391
  if command.nil?
348
392
  puts Formatter.err "No such command, \"#{command_name}\""
349
393
  next
350
394
  end
351
-
395
+
352
396
  # Tell all commands to be in "menu" mode (print list numbers, save list number history, etc.)
353
397
  # Only do this if the command supports --menu.
354
398
  command.options.each do |option|
355
399
  args = option[:args]
356
400
  if (not args.nil?) and args.include? "--menu"
357
401
  argv.push "--menu"
358
-
402
+
359
403
  # Reset the menu mapping, since the command we're about to call will update them,
360
404
  # and we want this mapping to not have any stale data in it.
361
405
  menu_story_mapping = {}
@@ -367,8 +411,8 @@ command :shell do |c|
367
411
  rescue Exception => e
368
412
  puts e
369
413
  end
370
-
414
+
371
415
  init_new_shell_loop
372
- end
416
+ end
373
417
  end
374
418
  end
data/lib/formatter.rb CHANGED
@@ -52,7 +52,7 @@ class Formatter
52
52
  end
53
53
 
54
54
  def self.story_type type
55
- type.color '#cc6600'
55
+ "#{type}".color '#cc6600'
56
56
  end
57
57
 
58
58
  def self.attr_descriptor descriptor, attr
@@ -63,6 +63,10 @@ class Formatter
63
63
  description
64
64
  end
65
65
 
66
+ def self.iteration iteration
67
+ "#{iteration}".color('#cccc00')
68
+ end
69
+
66
70
  # Format error messages.
67
71
  def self.err errmsg
68
72
  errmsg.background('#990000').color(:white)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pivotal-slacker
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.8.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-06 00:00:00.000000000 Z
12
+ date: 2013-02-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pivotal-tracker