story_branch 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/lib/story_branch.rb +81 -41
  4. metadata +11 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 51fb5eb1e204d970116356bb1f2f273ac0de0804
4
- data.tar.gz: 6ae1d58a21ae39e5def105f4b58d327147fd83a9
3
+ metadata.gz: d9cc200c930d34b90699a03c634f1de29e2c599b
4
+ data.tar.gz: 7abb7d7e9c1001d9e4d1c2236925995de29ff39b
5
5
  SHA512:
6
- metadata.gz: 010f313b23b8b1604af7dd9fd679d60f9c67b8109fc4083c39932480be23f1ed544fcbe8ad5a3e488df9b3a68af67581e7e8c3a7edec2f82f7553d3650913fea
7
- data.tar.gz: 2d227dfd93c2a3655f78a512f1ee3d7adf5d57c2c41c6fb96264bd88dc738bd94185dc5c611057d1ea52d3a997704b87d14f0f24bf249e9db418714a45ad94e0
6
+ metadata.gz: dd86ebf0d42dcbc66a70ab1e4e2d9403996ddce94a422b8ae2ccd46921cd5ddb1eec096f127678f7f506604f219083be4717c2760e21b2f9390b8363c637236b
7
+ data.tar.gz: 4e7e215be23201bd48cc94fec811f76dcb87eaae3bd3214994bae5c1e3b4d25761b4638d7c36b899dd1e3bad64f378dc35e43f59adc53fdadf6da2b7ed9d850e
data/README.md CHANGED
@@ -17,8 +17,8 @@ accept. The branch will be created (the story_id will automatically
17
17
  be used as a suffix in the branch name)
18
18
 
19
19
  **git start**: Start a story in Pivotal Tracker from the terminal.
20
- List all unstarted stories in your current project. Entering a
21
- partial string will fuzzy match against the list.
20
+ Lists all unstarted stories in your current project. Select a story
21
+ from the displayed list, and it will tell Pivotal Tracker to start it.
22
22
 
23
23
  **git finish**: Create a finishing commit + message, for example:
24
24
  "[Finishes #1234567] My Story Title" - optionally Finishes the story
data/lib/story_branch.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # Dominic Wong <dominic.wong.617@gmail.com>
6
6
  # Gabe Hollombe <gabe@neo.com>
7
7
  #
8
- # Version: 0.2.0
8
+ # Version: 0.2.1
9
9
  #
10
10
  # ## Description
11
11
  #
@@ -77,10 +77,17 @@ require 'git'
77
77
  require 'active_support/core_ext/string/inflections'
78
78
  require 'levenshtein'
79
79
 
80
+ trap('INT') {exit}
81
+
80
82
  module StoryBranch
81
83
 
82
84
  class Main
83
85
 
86
+ ERRORS = {
87
+ "Stories in the started state must be estimated." =>
88
+ "Error: Pivotal won't allow you to start an unestimated story"
89
+ }
90
+
84
91
  PIVOTAL_CONFIG_FILES = ['.story_branch',"#{ENV['HOME']}/.story_branch"]
85
92
 
86
93
  attr_accessor :p
@@ -97,13 +104,14 @@ module StoryBranch
97
104
 
98
105
  def create_story_branch
99
106
  begin
107
+ puts "Connecting with Pivotal Tracker"
100
108
  @p.get_project
101
- stories = @p.display_stories :started
109
+ puts "Getting stories..."
110
+ stories = @p.display_stories :started, false
102
111
  if stories.length < 1
103
- puts "No stories started... exiting"
112
+ puts "No stories started, exiting"
104
113
  exit
105
114
  end
106
- puts "[0] Exit"
107
115
  story = @p.select_story stories
108
116
  if story
109
117
  @p.create_feature_branch story
@@ -114,16 +122,20 @@ module StoryBranch
114
122
  end
115
123
  end
116
124
 
117
- def story_start
125
+ def pick_and_update filter, hash, msg, is_estimated
118
126
  begin
127
+ puts "Connecting with Pivotal Tracker"
119
128
  @p.get_project
120
- # TODO: Use a predicate for Estimated and Backlog'ed stories
121
- stories = @p.display_stories :unstarted
122
- puts "[0] Exit"
129
+ puts "Getting stories..."
130
+ stories = @p.filtered_stories_list filter, is_estimated
123
131
  story = @p.select_story stories
124
132
  if story
125
- story.update :current_state => "started"
126
- puts "#{story.id} started"
133
+ result = story.update hash
134
+ if result.errors.count > 0
135
+ puts result.errors.to_a.uniq.map{|e| ERRORS[e] }
136
+ return nil
137
+ end
138
+ puts "#{story.id} #{msg}"
127
139
  end
128
140
  rescue RestClient::Unauthorized
129
141
  puts "Pivotal API key or Project ID invalid"
@@ -131,16 +143,24 @@ module StoryBranch
131
143
  end
132
144
  end
133
145
 
146
+ def story_start
147
+ pick_and_update(:unstarted, {:current_state => "started"}, "started", true)
148
+ end
149
+
134
150
  def story_unstart
135
- # TODO: unstart a started story.
151
+ pick_and_update(:started, {:current_state => "unstarted"}, "unstarted", false)
152
+ end
153
+
154
+ def story_estimate
155
+ # TODO: estimate a story
136
156
  end
137
157
 
138
158
  def story_finish
139
159
  begin
160
+ puts "Connecting with Pivotal Tracker"
140
161
  @p.get_project
141
162
  unless @p.is_current_branch_a_story?
142
- puts "Your current branch: #{GitUtils.current_branch}"
143
- puts "is not linked to a started story."
163
+ puts "Your current branch: '#{GitUtils.current_branch}' is not linked to a Pivotal Tracker story."
144
164
  return
145
165
  end
146
166
 
@@ -158,7 +178,7 @@ module StoryBranch
158
178
  end
159
179
 
160
180
  puts "Use standard finishing commit message: [y/N]?"
161
- commit_message = "[Finishes ##{GitUtils.current_branch_story_parts[:id]}] #{GitUtils.current_branch_story_parts[:description]}"
181
+ commit_message = "[Finishes ##{GitUtils.current_branch_story_parts[:id]}] #{GitUtils.current_branch_story_parts[:description].StringUtils.undashed}"
162
182
  puts commit_message
163
183
 
164
184
  if gets.chomp!.downcase == "y"
@@ -202,6 +222,10 @@ module StoryBranch
202
222
  s.tr '\'"%!@#$(){}[]*\\?', ''
203
223
  end
204
224
 
225
+ def self.undashed s
226
+ s.underscore.humanize
227
+ end
228
+
205
229
  end
206
230
 
207
231
  class GitUtils
@@ -297,8 +321,9 @@ module StoryBranch
297
321
  end
298
322
 
299
323
  def is_current_branch_a_story?
324
+ GitUtils.current_story and
300
325
  GitUtils.current_story.length == 3 and
301
- filtered_stories_list(:started).map(&:id).include? GitUtils.current_story[2].to_i
326
+ filtered_stories_list(:started, true).map(&:id).include? GitUtils.current_story[2].to_i
302
327
  end
303
328
 
304
329
  def story_from_current_branch
@@ -310,32 +335,46 @@ module StoryBranch
310
335
  # Filtering on tags/labels
311
336
  # Filtering on estimation (estimated?, 0 point, 1 point etc.)
312
337
 
313
- def filtered_stories_list state
338
+ def filtered_stories_list state, estimated
314
339
  project = get_project
315
- project.stories.all({current_state: state})
340
+ stories = project.stories.all({current_state: state})
341
+ if estimated
342
+ stories.select{|s| s.estimate and s.estimate > 1 }
343
+ else
344
+ stories
345
+ end
316
346
  end
317
347
 
318
- def display_stories state
319
- filtered_stories_list(state).each_with_index {|s,i| puts one_line_story s, i}
348
+ def display_stories state, estimated
349
+ filtered_stories_list(state, estimated).each {|s| puts one_line_story s }
320
350
  end
321
351
 
322
- def one_line_story s, i
323
- "[#{i+1}] ##{s.id} : #{s.name}"
352
+ def one_line_story s
353
+ "#{s.id} - #{s.name}"
324
354
  end
325
355
 
326
356
  def select_story stories
327
- story_selection = nil
328
- while story_selection == nil or story_selection.to_i > stories.length + 1
329
- puts "invalid selection" if story_selection != nil
330
- story_texts = Range.new(1,stories.length).to_a.map(&:to_s)
331
- story_selection = readline("Select a story: ", story_texts)
357
+ story_texts = stories.map{|s| one_line_story s }
358
+ puts "Leave blank to exit, use <up>/<down> to scroll through stories, TAB to list all and auto-complete"
359
+ story_selection = readline("Select a story: ", story_texts)
360
+ if story_selection == "" or story_selection.nil?
361
+ return nil
332
362
  end
333
- if story_selection.to_i == 0
363
+ story = stories.select{|s| story_matcher s, story_selection }.first
364
+ if story.nil?
365
+ puts "Not found: #{story_selection}"
334
366
  return nil
367
+ else
368
+ puts "Selected : #{one_line_story story}"
369
+ return story
335
370
  end
336
- story = stories[story_selection.to_i - 1]
337
- puts "Selected : ##{story.id} : #{story.name}"
338
- return story
371
+ end
372
+
373
+ def story_matcher story, selection
374
+ m = selection.match(/^(\d*) /)
375
+ return false unless m
376
+ id = m.captures.first
377
+ return story.id.to_s == id
339
378
  end
340
379
 
341
380
  def create_feature_branch story
@@ -343,7 +382,7 @@ module StoryBranch
343
382
  feature_branch_name = nil
344
383
  puts "You are checked out at: #{GitUtils.current_branch}"
345
384
  while feature_branch_name == nil or feature_branch_name == ""
346
- puts "Provide a new branch name... (C-p or <up> for suggested name)" if [nil, ""].include? feature_branch_name
385
+ puts "Provide a new branch name... (TAB for suggested name)" if [nil, ""].include? feature_branch_name
347
386
  feature_branch_name = readline("Name of feature branch: ", [dashed_story_name])
348
387
  end
349
388
  feature_branch_name.chomp!
@@ -361,14 +400,14 @@ module StoryBranch
361
400
  end
362
401
  existing_name_score = GitUtils.is_existing_branch?(name)
363
402
  unless existing_name_score == -1
364
- puts <<-END.strip_heredoc
403
+ puts <<-EOD.strip_heredoc
365
404
  Name Collision Error:
366
405
 
367
406
  #{name}
368
407
 
369
408
  This is too similar to the name of an existing
370
409
  branch, a more unique name is required
371
- END
410
+ EOD
372
411
  end
373
412
  end
374
413
 
@@ -379,15 +418,16 @@ module StoryBranch
379
418
  name.match valid
380
419
  end
381
420
 
382
- def readline prompt, history=[]
383
- if history.length > 0
384
- history.each {|i| Readline::HISTORY.push i}
385
- end
386
- begin
387
- Readline.readline(prompt, false)
388
- rescue Interrupt
421
+ def readline prompt, completions=[]
422
+ # Store the state of the terminal
423
+ Readline::HISTORY.clear
424
+ if completions.length > 0
425
+ completions.each {|i| Readline::HISTORY.push i}
426
+ Readline.special_prefixes = " .{}()[]!?\"'_-#@$%^&*"
427
+ Readline.completion_proc = proc {|s| completions.grep(/#{Regexp.escape(s)}/) }
428
+ Readline.completion_append_character = ""
389
429
  end
430
+ Readline.readline(prompt, false)
390
431
  end
391
-
392
432
  end
393
433
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: story_branch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Milkins
@@ -17,56 +17,56 @@ dependencies:
17
17
  name: pivotal-tracker
18
18
  requirement: !ruby/object:Gem::Requirement
19
19
  requirements:
20
- - - "~>"
20
+ - - ~>
21
21
  - !ruby/object:Gem::Version
22
22
  version: '0.5'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
27
+ - - ~>
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0.5'
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: git
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  requirements:
34
- - - "~>"
34
+ - - ~>
35
35
  - !ruby/object:Gem::Version
36
36
  version: '1.2'
37
37
  type: :runtime
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
- - - "~>"
41
+ - - ~>
42
42
  - !ruby/object:Gem::Version
43
43
  version: '1.2'
44
44
  - !ruby/object:Gem::Dependency
45
45
  name: levenshtein-ffi
46
46
  requirement: !ruby/object:Gem::Requirement
47
47
  requirements:
48
- - - "~>"
48
+ - - ~>
49
49
  - !ruby/object:Gem::Version
50
50
  version: '1.0'
51
51
  type: :runtime
52
52
  prerelease: false
53
53
  version_requirements: !ruby/object:Gem::Requirement
54
54
  requirements:
55
- - - "~>"
55
+ - - ~>
56
56
  - !ruby/object:Gem::Version
57
57
  version: '1.0'
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rspec
60
60
  requirement: !ruby/object:Gem::Requirement
61
61
  requirements:
62
- - - "~>"
62
+ - - ~>
63
63
  - !ruby/object:Gem::Version
64
64
  version: '3.0'
65
65
  type: :development
66
66
  prerelease: false
67
67
  version_requirements: !ruby/object:Gem::Requirement
68
68
  requirements:
69
- - - "~>"
69
+ - - ~>
70
70
  - !ruby/object:Gem::Version
71
71
  version: '3.0'
72
72
  description: Simple gem that fetches the available stories in your pivotaltracker
@@ -120,12 +120,12 @@ require_paths:
120
120
  - lib
121
121
  required_ruby_version: !ruby/object:Gem::Requirement
122
122
  requirements:
123
- - - ">="
123
+ - - '>='
124
124
  - !ruby/object:Gem::Version
125
125
  version: 1.9.3
126
126
  required_rubygems_version: !ruby/object:Gem::Requirement
127
127
  requirements:
128
- - - ">="
128
+ - - '>='
129
129
  - !ruby/object:Gem::Version
130
130
  version: '0'
131
131
  requirements: []