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 CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rallycat (0.3.0)
4
+ rallycat (0.3.1)
5
5
  builder
6
6
  nokogiri
7
7
  rally_rest_api
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
@@ -32,7 +32,6 @@ module Rallycat
32
32
 
33
33
  Plan Estimate: #{story.plan_estimate}
34
34
  State: #{story.schedule_state}
35
- Task Actual: #{story.task_actual_total}
36
35
  Task Estimate: #{story.task_estimate_total}
37
36
  Task Remaining: #{story.task_remaining_total}
38
37
  Owner: #{story.owner}
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 run
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 |user|
15
- options[:user] = user
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
- global.order! @argv
59
-
60
- command = @argv.shift
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
- case command
73
+ def run
74
+ case @command
64
75
  when 'cat'
65
- api = Rallycat::Connection.new(options[:user], options[:password]).api
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[:user], options[:password]).api
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
@@ -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 = []
@@ -1,3 +1,3 @@
1
1
  module Rallycat
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -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{ cat US4567 -u foo.bar@rallycat.com -p password }, sout
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{ update -p TA6666 -u foo.bar@rallycat.com -p password }, sout
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{ update -d TA6666 -u foo.bar@rallycat.com -p password }, sout
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{ update -c TA6666 -u foo.bar@rallycat.com -p password }, sout
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{ update -b TA6666 -u foo.bar@rallycat.com -p password }, sout
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{ update -o Freddy\ Fender TA6666 -u foo.bar@rallycat.com -p password }, sout
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{ update -o Norman\ Notreal TA6666 -u foo.bar@rallycat.com -p password }, sout
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{ update -i TA9999 -u foo.bar@rallycat.com -p password }, sout
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
@@ -0,0 +1,15 @@
1
+ class GenericResponder
2
+ attr_reader :requests
3
+
4
+ def initialize
5
+ @requests = []
6
+ end
7
+
8
+ def last_request
9
+ @requests.last
10
+ end
11
+
12
+ def call(env)
13
+ [200, {}, ['']]
14
+ end
15
+ end
@@ -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.0
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-17 00:00:00.000000000 Z
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: &70303674111620 !ruby/object:Gem::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: *70303674111620
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: &70303674111020 !ruby/object:Gem::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: *70303674111020
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: &70303674110340 !ruby/object:Gem::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: *70303674110340
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: 4474919884476792167
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: 4474919884476792167
125
+ hash: 1844620157454279745
110
126
  requirements: []
111
127
  rubyforge_project:
112
- rubygems_version: 1.8.7
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