pivo_flow 0.3.8 → 0.3.9

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -17,7 +17,15 @@ All the required information is gathered on demand, but it's a good idea to prep
17
17
  * project's Pivotal Tracker ID
18
18
  * your Pivotal Tracker API token
19
19
 
20
- Get list of current stories
20
+ Show help
21
+
22
+ pf help
23
+
24
+ or
25
+
26
+ pf --help
27
+
28
+ Get list of current stories with an interactive choice menu
21
29
 
22
30
  pf stories
23
31
 
@@ -33,6 +41,10 @@ Clear current story without notifying Pivotal
33
41
 
34
42
  pf clear
35
43
 
44
+ Show finished stories and select the one you would like to deliver
45
+
46
+ pf deliver
47
+
36
48
  Display current gem version
37
49
 
38
50
  pf version
@@ -50,18 +62,18 @@ This gem installs a `pepare-commit-msg` hook by adding a reference to `pf-prepar
50
62
 
51
63
  ### 0.3 Current release
52
64
 
53
- * single-story view
65
+ * single-story view with comments and tasks
54
66
  * flow:
55
67
  * select story
56
68
  * read the story description
57
69
  * accept or back to story selection
58
70
  * `pf info` displaying info about current task
59
- * `pf --help` and other options via OptionParser
60
- * comments and tasks in a single-story view
71
+ * `pf deliver` ability to deliver finished stories [#6]
72
+ * options via `OptionParser`
61
73
 
62
74
  ### 0.2
63
75
 
64
- * git hoook
76
+ * git hook
65
77
  * formatted output
66
78
  * bugfixes
67
79
 
data/lib/pivo_flow/cli.rb CHANGED
@@ -36,17 +36,29 @@ module PivoFlow
36
36
  end
37
37
 
38
38
  def finish story_id=nil
39
- if File.exists? @file_story_path
40
- story_id = File.open(@file_story_path).read.strip
39
+ unless current_story_id
40
+ puts no_story_found_message
41
+ return 1
41
42
  end
42
- pivotal_object.finish_story(story_id)
43
+ pivotal_object.finish_story(current_story_id)
44
+ end
45
+
46
+ def deliver
47
+ pivotal_object.deliver
43
48
  end
44
49
 
45
50
  def clear
46
- if File.exists? @file_story_path
51
+ unless current_story_id.nil?
47
52
  FileUtils.remove_file(@file_story_path)
53
+ puts "Current pivotal story id cleared."
54
+ else
55
+ puts no_story_found_message
56
+ return 1
48
57
  end
49
- puts "Current pivotal story id cleared."
58
+ end
59
+
60
+ def current
61
+ puts current_story_id || no_story_found_message
50
62
  end
51
63
 
52
64
  def reconfig
@@ -67,6 +79,10 @@ module PivoFlow
67
79
  @pivotal_object ||= PivoFlow::Pivotal.new(@options)
68
80
  end
69
81
 
82
+ def no_story_found_message
83
+ "No story found in #{@file_story_path}"
84
+ end
85
+
70
86
  def no_method_error
71
87
  puts "You forgot a method name"
72
88
  end
@@ -75,6 +91,11 @@ module PivoFlow
75
91
  puts "Ups, no such method..."
76
92
  end
77
93
 
94
+ def current_story_id
95
+ return nil unless File.exists?(@file_story_path)
96
+ File.open(@file_story_path).read.strip
97
+ end
98
+
78
99
  def parse_argv(args)
79
100
  @options = {}
80
101
 
@@ -82,11 +103,14 @@ module PivoFlow
82
103
  opts.banner = "Usage: pf <COMMAND> [OPTIONS]\n"
83
104
  opts.separator "Commands"
84
105
  opts.separator " clear: clears STORY_ID from temp file"
85
- opts.separator " info: shows info about current story"
106
+ opts.separator " deliver: shows finished stories and mark selected as delivered in Pivotal Tracker"
107
+ opts.separator " help shows this message"
86
108
  opts.separator " finish [STORY_ID]: finish story on Pivotal"
109
+ opts.separator " info: shows info about current story"
87
110
  opts.separator " reconfig: clears API_TOKEN and PROJECT_ID from git config"
88
111
  opts.separator " start <STORY_ID>: start a story of given ID"
89
112
  opts.separator " stories: list stories for current project"
113
+ opts.separator " version: show gem version"
90
114
  opts.separator ""
91
115
  opts.separator "Options"
92
116
 
@@ -113,21 +137,15 @@ module PivoFlow
113
137
  opt_parser.parse!(args)
114
138
 
115
139
  case args[0]
116
- when "clear"
117
- clear
118
- when "reconfig"
119
- reconfig
120
- when "start"
121
- start args[1]
122
- when "stories"
123
- stories
124
- when "finish"
125
- finish args[1]
126
- when "info"
127
- info
140
+ when "start", "finish"
141
+ self.send(args[0].to_sym, args[1])
142
+ when "help"
143
+ puts opt_parser
144
+ when "clear", "current", "deliver", "info", "reconfig", "stories"
145
+ self.send(args[0].to_sym)
128
146
  when nil
129
147
  no_method_error
130
- puts opt_parser.to_s
148
+ puts opt_parser
131
149
  return 1
132
150
  else
133
151
  invalid_method_error
@@ -138,4 +156,5 @@ module PivoFlow
138
156
  end
139
157
 
140
158
  end
159
+
141
160
  end
@@ -35,6 +35,10 @@ module PivoFlow
35
35
  project_stories.select{ |story| story.owned_by == nil }
36
36
  end
37
37
 
38
+ def finished_stories
39
+ fetch_stories(10, "finished")
40
+ end
41
+
38
42
  def current_story force = false
39
43
  if (@options[:current_story] && !force)
40
44
  @options[:current_story]
@@ -44,6 +48,11 @@ module PivoFlow
44
48
  end
45
49
 
46
50
  def list_stories_to_output stories
51
+ if (stories.nil? || stories.empty?)
52
+ puts "No stories to show"
53
+ return 1
54
+ end
55
+
47
56
  HighLine.new.choose do |menu|
48
57
  menu.header = "\n--- STORIES FROM PIVOTAL TRACKER ---\nWhich one would you like to start? "
49
58
  menu.prompt = "story no.? "
@@ -54,13 +63,18 @@ module PivoFlow
54
63
  end
55
64
  end
56
65
 
66
+ def deliver
67
+ list_stories_to_output finished_stories
68
+ end
69
+
57
70
  def show_story story_id
58
71
  story = find_story(story_id)
59
72
  show_info story
60
- proceed = ask_question "Do you want to start this story?"
73
+ ask_for = story.current_state == "finished" ? "deliver" : "start"
74
+ proceed = ask_question "Do you want to #{ask_for} this story?"
61
75
  accepted_answers = %w[yes y sure ofc jup yep yup ja tak]
62
76
  if accepted_answers.include?(proceed.downcase)
63
- pick_up_story(story_id)
77
+ story.current_state == "finished" ? deliver_story(story_id) : pick_up_story(story_id)
64
78
  else
65
79
  show_stories
66
80
  end
@@ -94,7 +108,7 @@ module PivoFlow
94
108
  owner: story_owner(story),
95
109
  description: story.description,
96
110
  labels: story_labels(story),
97
- started: story.current_state == "started" ? "S" : "N"
111
+ started: story_state_sign(story)
98
112
  }
99
113
  if long
100
114
  "STORY %{started} %{story_type} [#%{story_id}]
@@ -148,6 +162,11 @@ module PivoFlow
148
162
  story.labels.nil? ? "" : story.labels.split(",").map{ |l| "##{l}" }.join(", ")
149
163
  end
150
164
 
165
+ def story_state_sign story
166
+ return "*" if story.current_state == "unstarted"
167
+ story.current_state[0].capitalize
168
+ end
169
+
151
170
  def initials name
152
171
  name.split.map{ |n| n[0] }.join
153
172
  end
@@ -189,6 +208,10 @@ module PivoFlow
189
208
  remove_story_id_file if story_id.nil? or update_story(story_id, :finished)
190
209
  end
191
210
 
211
+ def deliver_story story_id
212
+ update_story(story_id, :delivered)
213
+ end
214
+
192
215
  def remove_story_id_file
193
216
  FileUtils.remove_file(@story_id_file_path)
194
217
  end
@@ -203,7 +226,7 @@ module PivoFlow
203
226
  list_stories_to_output stories.first(9)
204
227
  end
205
228
 
206
- def fetch_stories(count = 100, state = "unstarted,started,unscheduled")
229
+ def fetch_stories(count = 100, state = "unstarted,started,unscheduled,rejected")
207
230
  conditions = { current_state: state, limit: count }
208
231
  @options[:stories] = @options[:project].stories.all(conditions)
209
232
  end
@@ -1,3 +1,3 @@
1
1
  module PivoFlow
2
- VERSION = "0.3.8"
2
+ VERSION = "0.3.9"
3
3
  end
@@ -60,8 +60,65 @@ describe PivoFlow::Cli do
60
60
  end
61
61
 
62
62
  end
63
+
64
+ describe "reads story id from file" do
65
+
66
+ it "and returns nil if there is no such file" do
67
+ File.stub(:exists?).and_return(false)
68
+ PivoFlow::Cli.new.send(:current_story_id).should be_nil
69
+ end
70
+
71
+ it "and returns story id if file exists" do
72
+ File.stub(:exists?).and_return(true)
73
+ f = mock('File', :read => "123")
74
+ File.stub(:open).and_return(f)
75
+ PivoFlow::Cli.new.send(:current_story_id).should eq "123"
76
+ end
77
+ end
78
+
79
+ describe "finish method" do
80
+
81
+ it "calls finish_story on pivotal object on finish method" do
82
+ pivo = mock("PivoFlow::Pivotal")
83
+ PivoFlow::Cli.any_instance.should_receive(:pivotal_object).and_return(pivo)
84
+ pivo.should_receive(:finish_story).with("123")
85
+ PivoFlow::Cli.any_instance.should_receive(:current_story_id).twice.and_return("123")
86
+ PivoFlow::Cli.new("finish").go!
87
+ end
88
+
89
+ it "returns 1 if there is no current_story_id" do
90
+ PivoFlow::Cli.any_instance.should_receive(:current_story_id).and_return(nil)
91
+ PivoFlow::Cli.new.send(:finish).should eq 1
92
+ end
93
+ end
94
+
95
+ describe "start method" do
96
+
97
+ it "returns 1 if no story given" do
98
+ PivoFlow::Cli.new.send(:start).should eq 1
99
+ end
100
+
101
+ end
102
+
103
+ describe "clear method" do
104
+
105
+ it "returns 1 if current story is nil" do
106
+ PivoFlow::Cli.any_instance.should_receive(:current_story_id).and_return(nil)
107
+ PivoFlow::Cli.new.send(:clear).should eq 1
108
+ end
109
+
110
+
111
+ it "removes file if current story is present" do
112
+ PivoFlow::Cli.any_instance.should_receive(:current_story_id).and_return(1)
113
+ FileUtils.should_receive(:remove_file).and_return(true)
114
+ PivoFlow::Cli.new.send(:clear)
115
+ end
116
+
117
+ end
118
+
119
+
63
120
  describe "should allow to run command named" do
64
- methods = [:stories, :start, :info, :finish, :clear, :reconfig]
121
+ methods = [:stories, :start, :info, :finish, :clear, :help, :reconfig, :current, :deliver]
65
122
  methods.each do |method|
66
123
  it "#{method.to_s}" do
67
124
  PivoFlow::Cli.any_instance.stub(method)
@@ -44,9 +44,91 @@ describe PivoFlow::Pivotal do
44
44
  pivotal.unasigned_stories.should eq [@story_unassigned]
45
45
  end
46
46
 
47
+ it "show_stories should display stories on output" do
48
+ pivotal.should_receive(:list_stories_to_output)
49
+ pivotal.show_stories
50
+ end
51
+
52
+ describe "story_string" do
53
+
54
+ it "includes story id" do
55
+ pivotal.story_string(@story_feature).should match(/[##{@story_feature.id}]/) end
56
+
57
+ it "includes story name" do
58
+ pivotal.story_string(@story_feature).should match(/#{@story_feature.name}/)
59
+ end
60
+
61
+ end
62
+
63
+ describe "deliver" do
64
+
65
+ it "list only the stories with 'finished' status" do
66
+ @project.stub_chain(:stories, :all).and_return([@story_finished])
67
+ pivotal.should_receive(:list_stories_to_output).with([@story_finished])
68
+ pivotal.deliver
69
+ end
70
+
71
+ end
72
+
73
+ describe "list_stories_to_output" do
74
+
75
+ it "returns 1 if stories are nil" do
76
+ pivotal.list_stories_to_output(nil).should eq 1
77
+ end
78
+
79
+ it "returns 1 if stories are []" do
80
+ pivotal.list_stories_to_output([]).should eq 1
81
+ end
82
+
83
+ end
84
+
85
+ describe "show_story" do
86
+
87
+ after(:each) do
88
+ pivotal.stub(:show_stories)
89
+ # pivotal.stub(:update_story)
90
+ pivotal.stub(:show_info)
91
+
92
+ pivotal.show_story 1
93
+ end
94
+
95
+ describe "if story's state is 'finished'" do
96
+
97
+ before(:each) do
98
+ @story_feature.stub(:current_state).and_return("finished")
99
+ pivotal.stub(:ask_question).and_return("y")
100
+ end
101
+
102
+ it "ask for deliver the story" do
103
+ pivotal.should_receive(:ask_question).with(/deliver/)
104
+ end
105
+
106
+ it "updates story on pivotal with 'delivered' status" do
107
+ @story_feature.should_receive(:update).with({ owned_by: @story_feature.owned_by, current_state: :delivered })
108
+ end
109
+
110
+ end
111
+
112
+
113
+ it "shows info about the story" do
114
+ pivotal.stub(:ask_question).and_return("no")
115
+ pivotal.should_receive(:show_info)
116
+ end
117
+
118
+ it "updates the story if user response is 'yes'" do
119
+ pivotal.stub(:ask_question).and_return("yes")
120
+ pivotal.should_receive(:update_story)
121
+ end
122
+
123
+ it "show stories if user response is 'no'" do
124
+ pivotal.stub(:ask_question).and_return("no")
125
+ pivotal.should_receive(:show_stories)
126
+ end
127
+ end
128
+
47
129
  describe "start_story" do
130
+
48
131
  before(:each) do
49
- @story_feature.stub_chain(:update, :errors, :count).and_return(0)
50
132
  @story_feature.should_receive(:update).with({ current_state: :started, owned_by: pivotal.user_name })
51
133
  end
52
134
 
@@ -112,7 +194,10 @@ def stub_pivotal_project
112
194
  requested_by: "Paul Newman",
113
195
  owned_by: "Adam Newman",
114
196
  labels: "first,second")
115
- @story_unassigned = PivotalTracker::Story.new(owned_by: nil)
116
- @project.stub_chain(:stories, :all).and_return([@story_feature, @story_unassigned])
197
+ @story_feature.stub_chain(:update).and_return(mock(errors: []))
198
+ @story_unassigned = PivotalTracker::Story.new(owned_by: nil, name: "test", current_state: "started")
199
+ @story_rejected = PivotalTracker::Story.new(current_state: "rejected", owned_by: "Mark Marco", name: "test_rejected")
200
+ @story_finished = PivotalTracker::Story.new(current_state: "finished", owned_by: "Mark Marco", name: "finished")
201
+ @project.stub_chain(:stories, :all).and_return([@story_feature, @story_unassigned, @story_rejected, @story_finished])
117
202
  PivotalTracker::Project.stub(:find).and_return(@project)
118
203
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pivo_flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.3.9
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: 2012-08-13 00:00:00.000000000 Z
12
+ date: 2012-08-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pivotal-tracker