rallycat 0.3.0 → 0.3.1
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/Gemfile.lock +1 -1
- data/TODO +9 -0
- data/lib/rallycat/cat.rb +0 -1
- data/lib/rallycat/cli.rb +29 -18
- data/lib/rallycat/update.rb +6 -0
- data/lib/rallycat/version.rb +1 -1
- data/spec/integration_spec.rb +8 -8
- data/spec/lib/rallycat/cat_spec.rb +0 -3
- data/spec/lib/rallycat/cli_spec.rb +44 -1
- data/spec/lib/rallycat/update_spec.rb +18 -0
- data/spec/support/generic_responder.rb +15 -0
- data/spec/support/rally_defect_responder.rb +1 -11
- data/spec/support/rally_no_results_responder.rb +2 -11
- data/spec/support/rally_story_responder.rb +1 -11
- data/spec/support/rally_task_update_responder.rb +1 -11
- metadata +28 -11
data/Gemfile.lock
CHANGED
data/TODO
CHANGED
@@ -1,10 +1,19 @@
|
|
1
1
|
# Rallycat TODO
|
2
2
|
|
3
3
|
* update help (use option_parser help or custom help)
|
4
|
+
* todo time
|
5
|
+
* set todo time
|
6
|
+
* set notes on a task (-n overwrite, -N to append)
|
4
7
|
* add update behavior for stories (maybe)
|
5
8
|
* add update behavior for defects (maybe)
|
6
9
|
* Slow as balls? Make nice wait messages
|
7
10
|
* Add man page
|
11
|
+
* Ordering of arguments matters? (rallycat update TA1234 -b silently fails)
|
12
|
+
* Create .rallycatrc file on first run?
|
13
|
+
* Listing stories
|
14
|
+
* Per iteration
|
15
|
+
* Per project (project configured in rc or passed in?)
|
16
|
+
* AC-only story view
|
8
17
|
|
9
18
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
10
19
|
|
data/lib/rallycat/cat.rb
CHANGED
data/lib/rallycat/cli.rb
CHANGED
@@ -2,21 +2,26 @@ require 'optparse'
|
|
2
2
|
|
3
3
|
module Rallycat
|
4
4
|
class CLI
|
5
|
+
attr_reader :options
|
6
|
+
|
5
7
|
def initialize(argv, stdout=STDOUT)
|
6
8
|
@argv = argv
|
7
9
|
@stdout = stdout
|
10
|
+
|
11
|
+
parse_global_options!
|
12
|
+
parse_command_options!
|
8
13
|
end
|
9
14
|
|
10
|
-
def
|
11
|
-
options
|
15
|
+
def parse_global_options!
|
16
|
+
@options ||= {}
|
12
17
|
|
13
18
|
global = OptionParser.new do |opts|
|
14
|
-
opts.on('-u USERNAME', '--username') do |
|
15
|
-
options[:
|
19
|
+
opts.on('-u USERNAME', '--username') do |username|
|
20
|
+
@options[:username] = username
|
16
21
|
end
|
17
22
|
|
18
23
|
opts.on('-p PASSWORD', '--password') do |password|
|
19
|
-
options[:password] = password
|
24
|
+
@options[:password] = password
|
20
25
|
end
|
21
26
|
|
22
27
|
opts.on('-h', '--help') do
|
@@ -25,6 +30,12 @@ module Rallycat
|
|
25
30
|
end
|
26
31
|
end
|
27
32
|
|
33
|
+
global.order! @argv
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_command_options!
|
37
|
+
@options ||= {}
|
38
|
+
|
28
39
|
commands = {
|
29
40
|
'cat' => OptionParser.new,
|
30
41
|
|
@@ -32,37 +43,37 @@ module Rallycat
|
|
32
43
|
opts.banner = 'Usage: rallycat update <story number> [options]'
|
33
44
|
|
34
45
|
opts.on('-b', '--blocked') do |blocked|
|
35
|
-
options[:blocked] = true
|
46
|
+
@options[:blocked] = true
|
36
47
|
end
|
37
48
|
|
38
49
|
opts.on('-p', '--in-progress') do |in_progress|
|
39
|
-
options[:in_progress] = true
|
50
|
+
@options[:in_progress] = true
|
40
51
|
end
|
41
52
|
|
42
53
|
opts.on('-c', '--completed') do |completed|
|
43
|
-
options[:completed] = true
|
54
|
+
@options[:completed] = true
|
44
55
|
end
|
45
56
|
|
46
57
|
opts.on('-d', '--defined') do |defined|
|
47
|
-
options[:defined] = true
|
58
|
+
@options[:defined] = true
|
48
59
|
end
|
49
60
|
|
50
61
|
opts.on('-o OWNER', '--owner') do |owner|
|
51
|
-
options[:owner] = owner
|
62
|
+
@options[:owner] = owner
|
52
63
|
end
|
53
64
|
end,
|
54
65
|
|
55
66
|
'help' => OptionParser.new
|
56
67
|
}
|
57
68
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
commands[command].order! @argv if commands.has_key? command
|
69
|
+
@command = @argv.shift
|
70
|
+
commands[@command].parse! @argv if commands.has_key? @command
|
71
|
+
end
|
62
72
|
|
63
|
-
|
73
|
+
def run
|
74
|
+
case @command
|
64
75
|
when 'cat'
|
65
|
-
api = Rallycat::Connection.new(options[:
|
76
|
+
api = Rallycat::Connection.new(options[:username], options[:password]).api
|
66
77
|
|
67
78
|
story_number = @argv.shift
|
68
79
|
|
@@ -74,7 +85,7 @@ module Rallycat
|
|
74
85
|
abort e.message
|
75
86
|
end
|
76
87
|
when 'update'
|
77
|
-
api = Rallycat::Connection.new(options[:
|
88
|
+
api = Rallycat::Connection.new(options[:username], options[:password]).api
|
78
89
|
|
79
90
|
task_number = @argv.shift
|
80
91
|
|
@@ -96,7 +107,7 @@ module Rallycat
|
|
96
107
|
# `puts` calls `to_s`
|
97
108
|
@stdout.puts Rallycat::Help.new
|
98
109
|
else
|
99
|
-
@stdout.puts "'#{command}' is not a supported command. See 'rallycat help'."
|
110
|
+
@stdout.puts "'#{@command}' is not a supported command. See 'rallycat help'."
|
100
111
|
end
|
101
112
|
end
|
102
113
|
end
|
data/lib/rallycat/update.rb
CHANGED
@@ -29,6 +29,12 @@ module Rallycat
|
|
29
29
|
attributes[:owner] = login_name
|
30
30
|
end
|
31
31
|
|
32
|
+
# If the task is marked as 'Complete', we should remove the remaining
|
33
|
+
# hours.
|
34
|
+
if attributes[:state] == "Completed"
|
35
|
+
attributes[:to_do] = 0.0
|
36
|
+
end
|
37
|
+
|
32
38
|
task.update(attributes)
|
33
39
|
|
34
40
|
messages = []
|
data/lib/rallycat/version.rb
CHANGED
data/spec/integration_spec.rb
CHANGED
@@ -7,7 +7,7 @@ describe 'Rallycat' do
|
|
7
7
|
it 'fetches, parses and outputs the rally story requested by the user' do
|
8
8
|
sout = StringIO.new
|
9
9
|
|
10
|
-
cli = Rallycat::CLI.new %w{
|
10
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password cat US4567 }, sout
|
11
11
|
|
12
12
|
story_responder = RallyStoryResponder.new
|
13
13
|
|
@@ -68,7 +68,7 @@ describe 'Rallycat' do
|
|
68
68
|
it 'sets the state to in-progress' do
|
69
69
|
sout = StringIO.new
|
70
70
|
|
71
|
-
cli = Rallycat::CLI.new %w{
|
71
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password update -p TA6666 }, sout
|
72
72
|
|
73
73
|
task_responder = RallyTaskUpdateResponder.new
|
74
74
|
|
@@ -83,7 +83,7 @@ describe 'Rallycat' do
|
|
83
83
|
it 'sets the state to defined' do
|
84
84
|
sout = StringIO.new
|
85
85
|
|
86
|
-
cli = Rallycat::CLI.new %w{
|
86
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password update -d TA6666 }, sout
|
87
87
|
|
88
88
|
task_responder = RallyTaskUpdateResponder.new
|
89
89
|
|
@@ -98,7 +98,7 @@ describe 'Rallycat' do
|
|
98
98
|
it 'sets the state to completed' do
|
99
99
|
sout = StringIO.new
|
100
100
|
|
101
|
-
cli = Rallycat::CLI.new %w{
|
101
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password update -c TA6666 }, sout
|
102
102
|
|
103
103
|
task_responder = RallyTaskUpdateResponder.new
|
104
104
|
|
@@ -113,7 +113,7 @@ describe 'Rallycat' do
|
|
113
113
|
it 'blocks the task' do
|
114
114
|
sout = StringIO.new
|
115
115
|
|
116
|
-
cli = Rallycat::CLI.new %w{
|
116
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password update -b TA6666 }, sout
|
117
117
|
|
118
118
|
task_responder = RallyTaskUpdateResponder.new
|
119
119
|
|
@@ -128,7 +128,7 @@ describe 'Rallycat' do
|
|
128
128
|
it 'assigns the owner of the task' do
|
129
129
|
sout = StringIO.new
|
130
130
|
|
131
|
-
cli = Rallycat::CLI.new %w{
|
131
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password update -o Freddy\ Fender TA6666 }, sout
|
132
132
|
|
133
133
|
task_responder = RallyTaskUpdateResponder.new
|
134
134
|
|
@@ -143,7 +143,7 @@ describe 'Rallycat' do
|
|
143
143
|
it 'aborts when the owner does not exist' do
|
144
144
|
sout = StringIO.new
|
145
145
|
|
146
|
-
cli = Rallycat::CLI.new %w{
|
146
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password update -o Norman\ Notreal TA6666 }, sout
|
147
147
|
|
148
148
|
task_responder = RallyTaskUpdateResponder.new
|
149
149
|
|
@@ -171,7 +171,7 @@ describe 'Rallycat' do
|
|
171
171
|
it 'aborts when a task does not exist' do
|
172
172
|
sout = StringIO.new
|
173
173
|
|
174
|
-
cli = Rallycat::CLI.new %w{
|
174
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password update -i TA9999 }, sout
|
175
175
|
|
176
176
|
task_responder = RallyNoResultsResponder.new
|
177
177
|
|
@@ -22,7 +22,6 @@ describe Rallycat::Cat, '#story' do
|
|
22
22
|
|
23
23
|
Plan Estimate: 1.0
|
24
24
|
State: In-Progress
|
25
|
-
Task Actual: 0.0
|
26
25
|
Task Estimate: 6.5
|
27
26
|
Task Remaining: 0.5
|
28
27
|
Owner: scootin@fruity.com
|
@@ -60,7 +59,6 @@ STORY
|
|
60
59
|
|
61
60
|
Plan Estimate: 1.0
|
62
61
|
State: In-Progress
|
63
|
-
Task Actual: 0.0
|
64
62
|
Task Estimate: 6.5
|
65
63
|
Task Remaining: 0.5
|
66
64
|
Owner: scootin@fruity.com
|
@@ -108,7 +106,6 @@ XML
|
|
108
106
|
|
109
107
|
Plan Estimate:
|
110
108
|
State:
|
111
|
-
Task Actual:
|
112
109
|
Task Estimate:
|
113
110
|
Task Remaining:
|
114
111
|
Owner:
|
@@ -9,7 +9,6 @@ describe Rallycat::CLI do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
describe Rallycat::CLI, '#run' do
|
12
|
-
|
13
12
|
it 'should execute command' do
|
14
13
|
sout = StringIO.new
|
15
14
|
cli = Rallycat::CLI.new ['foo'], sout
|
@@ -20,3 +19,47 @@ describe Rallycat::CLI, '#run' do
|
|
20
19
|
sout.read.should == "'foo' is not a supported command. See 'rallycat help'.\n"
|
21
20
|
end
|
22
21
|
end
|
22
|
+
|
23
|
+
describe Rallycat::CLI, '#options' do
|
24
|
+
|
25
|
+
it 'parses global options' do
|
26
|
+
sout = StringIO.new
|
27
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password help }, sout
|
28
|
+
|
29
|
+
cli.options.should == {
|
30
|
+
username: 'foo.bar@rallycat.com',
|
31
|
+
password: 'password'
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'raises if global options do not appear before the command' do
|
36
|
+
sout = StringIO.new
|
37
|
+
|
38
|
+
lambda {
|
39
|
+
cli = Rallycat::CLI.new %w{ help -u foo.bar@rallycat.com -p password }, sout
|
40
|
+
}.should raise_error(OptionParser::InvalidOption, 'invalid option: -u')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'parses all options' do
|
44
|
+
sout = StringIO.new
|
45
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password update -p TA6666 -b }, sout
|
46
|
+
|
47
|
+
cli.options.should == {
|
48
|
+
username: 'foo.bar@rallycat.com',
|
49
|
+
password: 'password',
|
50
|
+
in_progress: true,
|
51
|
+
blocked: true
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'parses command options (no global options)' do
|
56
|
+
sout = StringIO.new
|
57
|
+
cli = Rallycat::CLI.new %w{ update TA6666 -pb }, sout
|
58
|
+
|
59
|
+
cli.options.should == {
|
60
|
+
in_progress: true,
|
61
|
+
blocked: true
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
@@ -53,6 +53,24 @@ describe Rallycat::Update do
|
|
53
53
|
body.should include('<State>In-Progress</State>')
|
54
54
|
end
|
55
55
|
|
56
|
+
it 'sets the todo hours to zero when completing the task' do
|
57
|
+
responder = RallyTaskUpdateResponder.new
|
58
|
+
|
59
|
+
Artifice.activate_with responder do
|
60
|
+
task_num = "TA6666"
|
61
|
+
update = Rallycat::Update.new(@api)
|
62
|
+
message = update.task(task_num, state: "Completed")
|
63
|
+
end
|
64
|
+
|
65
|
+
post_request = responder.requests[2] # this is the request that actually updates the task
|
66
|
+
post_request.should be_post
|
67
|
+
post_request.url.should == 'https://rally1.rallydev.com/slm/webservice/1.17/task/12345'
|
68
|
+
|
69
|
+
body = post_request.body.tap(&:rewind).read
|
70
|
+
body.should include('<State>Completed</State>')
|
71
|
+
body.should include('<ToDo>0.0</ToDo>')
|
72
|
+
end
|
73
|
+
|
56
74
|
|
57
75
|
it 'blocks the task' do
|
58
76
|
responder = RallyTaskUpdateResponder.new
|
@@ -1,14 +1,4 @@
|
|
1
|
-
class RallyDefectResponder
|
2
|
-
|
3
|
-
attr_reader :requests
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@requests = []
|
7
|
-
end
|
8
|
-
|
9
|
-
def last_request
|
10
|
-
@requests.last
|
11
|
-
end
|
1
|
+
class RallyDefectResponder < GenericResponder
|
12
2
|
|
13
3
|
def call(env)
|
14
4
|
@requests << request = Rack::Request.new(env)
|
@@ -1,13 +1,4 @@
|
|
1
|
-
class RallyNoResultsResponder
|
2
|
-
attr_reader :requests
|
3
|
-
|
4
|
-
def initialize
|
5
|
-
@requests = []
|
6
|
-
end
|
7
|
-
|
8
|
-
def last_request
|
9
|
-
@requests.last
|
10
|
-
end
|
1
|
+
class RallyNoResultsResponder < GenericResponder
|
11
2
|
|
12
3
|
def call(env)
|
13
4
|
@requests << request = Rack::Request.new(env)
|
@@ -25,4 +16,4 @@ class RallyNoResultsResponder
|
|
25
16
|
XML
|
26
17
|
]]
|
27
18
|
end
|
28
|
-
end
|
19
|
+
end
|
@@ -1,16 +1,6 @@
|
|
1
1
|
require 'cgi'
|
2
2
|
|
3
|
-
class RallyStoryResponder
|
4
|
-
|
5
|
-
attr_reader :requests
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@requests = []
|
9
|
-
end
|
10
|
-
|
11
|
-
def last_request
|
12
|
-
@requests.last
|
13
|
-
end
|
3
|
+
class RallyStoryResponder < GenericResponder
|
14
4
|
|
15
5
|
def call(env)
|
16
6
|
@requests << request = Rack::Request.new(env)
|
@@ -1,14 +1,4 @@
|
|
1
|
-
class RallyTaskUpdateResponder
|
2
|
-
|
3
|
-
attr_reader :requests
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@requests = []
|
7
|
-
end
|
8
|
-
|
9
|
-
def last_request
|
10
|
-
@requests.last
|
11
|
-
end
|
1
|
+
class RallyTaskUpdateResponder < GenericResponder
|
12
2
|
|
13
3
|
def call(env)
|
14
4
|
@requests << request = Rack::Request.new(env)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rallycat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-07-
|
13
|
+
date: 2012-07-19 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: builder
|
17
|
-
requirement:
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,15 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements:
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
26
31
|
- !ruby/object:Gem::Dependency
|
27
32
|
name: rally_rest_api
|
28
|
-
requirement:
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
29
34
|
none: false
|
30
35
|
requirements:
|
31
36
|
- - ! '>='
|
@@ -33,10 +38,15 @@ dependencies:
|
|
33
38
|
version: '0'
|
34
39
|
type: :runtime
|
35
40
|
prerelease: false
|
36
|
-
version_requirements:
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
37
47
|
- !ruby/object:Gem::Dependency
|
38
48
|
name: nokogiri
|
39
|
-
requirement:
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
40
50
|
none: false
|
41
51
|
requirements:
|
42
52
|
- - ! '>='
|
@@ -44,7 +54,12 @@ dependencies:
|
|
44
54
|
version: '0'
|
45
55
|
type: :runtime
|
46
56
|
prerelease: false
|
47
|
-
version_requirements:
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
48
63
|
description: The Rally website sucks. CLI is better.
|
49
64
|
email:
|
50
65
|
- adam@adamtanner.org
|
@@ -79,6 +94,7 @@ files:
|
|
79
94
|
- spec/lib/rallycat/html_to_text_converter_spec.rb
|
80
95
|
- spec/lib/rallycat/update_spec.rb
|
81
96
|
- spec/spec_helper.rb
|
97
|
+
- spec/support/generic_responder.rb
|
82
98
|
- spec/support/rally_defect_responder.rb
|
83
99
|
- spec/support/rally_no_results_responder.rb
|
84
100
|
- spec/support/rally_story_responder.rb
|
@@ -97,7 +113,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
113
|
version: '0'
|
98
114
|
segments:
|
99
115
|
- 0
|
100
|
-
hash:
|
116
|
+
hash: 1844620157454279745
|
101
117
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
118
|
none: false
|
103
119
|
requirements:
|
@@ -106,10 +122,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
122
|
version: '0'
|
107
123
|
segments:
|
108
124
|
- 0
|
109
|
-
hash:
|
125
|
+
hash: 1844620157454279745
|
110
126
|
requirements: []
|
111
127
|
rubyforge_project:
|
112
|
-
rubygems_version: 1.8.
|
128
|
+
rubygems_version: 1.8.23
|
113
129
|
signing_key:
|
114
130
|
specification_version: 3
|
115
131
|
summary: The Rally website sucks. CLI is better.
|
@@ -121,6 +137,7 @@ test_files:
|
|
121
137
|
- spec/lib/rallycat/html_to_text_converter_spec.rb
|
122
138
|
- spec/lib/rallycat/update_spec.rb
|
123
139
|
- spec/spec_helper.rb
|
140
|
+
- spec/support/generic_responder.rb
|
124
141
|
- spec/support/rally_defect_responder.rb
|
125
142
|
- spec/support/rally_no_results_responder.rb
|
126
143
|
- spec/support/rally_story_responder.rb
|