rallycat 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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