pivo_flow 0.3.8 → 0.3.9
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.
- data/README.md +17 -5
- data/lib/pivo_flow/cli.rb +38 -19
- data/lib/pivo_flow/pivotal.rb +27 -4
- data/lib/pivo_flow/version.rb +1 -1
- data/spec/pivo_flow/cli_spec.rb +58 -1
- data/spec/pivo_flow/pivotal_spec.rb +88 -3
- metadata +2 -2
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
|
-
|
|
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
|
|
60
|
-
*
|
|
71
|
+
* `pf deliver` ability to deliver finished stories [#6]
|
|
72
|
+
* options via `OptionParser`
|
|
61
73
|
|
|
62
74
|
### 0.2
|
|
63
75
|
|
|
64
|
-
* git
|
|
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
|
-
|
|
40
|
-
|
|
39
|
+
unless current_story_id
|
|
40
|
+
puts no_story_found_message
|
|
41
|
+
return 1
|
|
41
42
|
end
|
|
42
|
-
pivotal_object.finish_story(
|
|
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
|
-
|
|
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
|
-
|
|
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 "
|
|
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 "
|
|
117
|
-
|
|
118
|
-
when "
|
|
119
|
-
|
|
120
|
-
when "
|
|
121
|
-
|
|
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
|
|
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
|
data/lib/pivo_flow/pivotal.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
data/lib/pivo_flow/version.rb
CHANGED
data/spec/pivo_flow/cli_spec.rb
CHANGED
|
@@ -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
|
-
@
|
|
116
|
-
@
|
|
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.
|
|
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-
|
|
12
|
+
date: 2012-08-15 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: pivotal-tracker
|