rallycat 0.3.1 → 0.4.0
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 +5 -12
- data/lib/rallycat.rb +10 -1
- data/lib/rallycat/cat.rb +0 -1
- data/lib/rallycat/cli.rb +73 -51
- data/lib/rallycat/config.rb +16 -0
- data/lib/rallycat/connection.rb +10 -27
- data/lib/rallycat/help.rb +19 -7
- data/lib/rallycat/list.rb +96 -0
- data/lib/rallycat/update.rb +0 -2
- data/lib/rallycat/version.rb +1 -1
- data/spec/integration_spec.rb +105 -1
- data/spec/{lib/rallycat → rallycat}/cat_spec.rb +1 -1
- data/spec/{lib/rallycat → rallycat}/cli_spec.rb +0 -0
- data/spec/{lib/rallycat → rallycat}/connection_spec.rb +2 -2
- data/spec/{lib/rallycat → rallycat}/html_to_text_converter_spec.rb +0 -0
- data/spec/rallycat/list_spec.rb +170 -0
- data/spec/{lib/rallycat → rallycat}/update_spec.rb +3 -3
- data/spec/support/generic_responder.rb +1 -0
- data/spec/support/rally_auth_responder.rb +11 -0
- data/spec/support/rally_iterations_responder.rb +76 -0
- data/spec/support/rally_stories_responder.rb +100 -0
- metadata +31 -36
data/Gemfile.lock
CHANGED
data/TODO
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# Rallycat TODO
|
2
2
|
|
3
|
-
* update help (use option_parser help or custom help)
|
4
3
|
* todo time
|
5
4
|
* set todo time
|
6
5
|
* set notes on a task (-n overwrite, -N to append)
|
@@ -8,26 +7,21 @@
|
|
8
7
|
* add update behavior for defects (maybe)
|
9
8
|
* Slow as balls? Make nice wait messages
|
10
9
|
* Add man page
|
11
|
-
* Ordering of arguments matters? (rallycat update TA1234 -b silently fails)
|
12
10
|
* Create .rallycatrc file on first run?
|
13
11
|
* Listing stories
|
14
12
|
* Per iteration
|
15
13
|
* Per project (project configured in rc or passed in?)
|
16
14
|
* AC-only story view
|
15
|
+
* consolidate spec styles
|
17
16
|
|
18
17
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
19
18
|
|
20
19
|
## Ideas
|
21
20
|
|
22
|
-
|
23
|
-
=================
|
21
|
+
rallycat list -s <search phrase> # do a contains lookup for an iteration
|
24
22
|
|
25
|
-
|
26
|
-
rallycat
|
27
|
-
|
28
|
-
rallycat update -p -o "Emilio Cavazos" TA9832
|
29
|
-
rallycat update -b TA1823 -m "Fix specs."
|
30
|
-
````
|
23
|
+
Keep a log of last (n) stories typed into `cat` so the default action for
|
24
|
+
`rallycat cat` will list the last (n) stories for the user.
|
31
25
|
|
32
26
|
### Notes
|
33
27
|
|
@@ -36,13 +30,12 @@ rallycat update -b TA1823 -m "Fix specs."
|
|
36
30
|
|
37
31
|
|
38
32
|
## Stats
|
33
|
+
|
39
34
|
* Stories in progress
|
40
35
|
* Stories accepted
|
41
36
|
* Stories not accepted
|
42
37
|
* Total To-Do Hours
|
43
38
|
|
44
|
-
|
45
|
-
|
46
39
|
rally.vim -> Making User Stories from Vim
|
47
40
|
|
48
41
|
## Rallycat Futures
|
data/lib/rallycat.rb
CHANGED
@@ -1,11 +1,20 @@
|
|
1
1
|
require 'rallycat/cat'
|
2
2
|
require 'rallycat/cli'
|
3
|
+
require 'rallycat/config'
|
3
4
|
require 'rallycat/connection'
|
4
5
|
require 'rallycat/help'
|
5
6
|
require 'rallycat/html_to_text_converter'
|
7
|
+
require 'rallycat/list'
|
6
8
|
require 'rallycat/update'
|
7
9
|
require 'rallycat/version'
|
8
10
|
|
9
11
|
module Rallycat
|
10
|
-
|
12
|
+
class RallycatError < StandardError; end
|
13
|
+
class StoryNotFound < RallycatError; end
|
14
|
+
class InvalidConfigError < RallycatError; end
|
15
|
+
class InvalidCredentialsError < RallycatError; end
|
16
|
+
class ProjectNotFound < RallycatError; end
|
17
|
+
class IterationNotFound < RallycatError; end
|
18
|
+
class UserNotFound < RallycatError; end
|
19
|
+
class TaskNotFound < RallycatError; end
|
11
20
|
end
|
data/lib/rallycat/cat.rb
CHANGED
data/lib/rallycat/cli.rb
CHANGED
@@ -12,19 +12,66 @@ module Rallycat
|
|
12
12
|
parse_command_options!
|
13
13
|
end
|
14
14
|
|
15
|
+
def run
|
16
|
+
case @command
|
17
|
+
when 'cat'
|
18
|
+
api = Rallycat::Connection.new(options[:username], options[:password]).api
|
19
|
+
|
20
|
+
story_number = @argv.shift
|
21
|
+
|
22
|
+
abort 'The "cat" command requires a story or defect number.' unless story_number
|
23
|
+
|
24
|
+
@stdout.puts Rallycat::Cat.new(api).story(story_number)
|
25
|
+
when 'update'
|
26
|
+
api = Rallycat::Connection.new(options[:username], options[:password]).api
|
27
|
+
|
28
|
+
task_number = @argv.shift
|
29
|
+
|
30
|
+
abort 'The "update" command requires a task number.' unless task_number
|
31
|
+
|
32
|
+
opts = {}
|
33
|
+
opts[:blocked] = true if options[:blocked]
|
34
|
+
opts[:state] = "In-Progress" if options[:in_progress]
|
35
|
+
opts[:state] = "Completed" if options[:completed]
|
36
|
+
opts[:state] = "Defined" if options[:defined]
|
37
|
+
opts[:owner] = options[:owner] if options[:owner]
|
38
|
+
|
39
|
+
@stdout.puts Rallycat::Update.new(api).task(task_number, opts)
|
40
|
+
when 'list'
|
41
|
+
api = Rallycat::Connection.new(options[:username], options[:password]).api
|
42
|
+
project = options[:project]
|
43
|
+
|
44
|
+
if options[:iteration]
|
45
|
+
@stdout.puts Rallycat::List.new(api).stories(project, options[:iteration])
|
46
|
+
else
|
47
|
+
@stdout.puts Rallycat::List.new(api).iterations(project)
|
48
|
+
end
|
49
|
+
when 'help'
|
50
|
+
# `puts` calls `to_s`
|
51
|
+
@stdout.puts Rallycat::Help.new
|
52
|
+
else
|
53
|
+
@stdout.puts "'#{@command}' is not a supported command. See 'rallycat help'."
|
54
|
+
end
|
55
|
+
|
56
|
+
rescue Rallycat::RallycatError, ArgumentError => e
|
57
|
+
abort e.message
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
15
62
|
def parse_global_options!
|
16
63
|
@options ||= {}
|
17
64
|
|
18
65
|
global = OptionParser.new do |opts|
|
19
|
-
opts.on('-u USERNAME', '--username') do |username|
|
66
|
+
opts.on('-u USERNAME', '--username', 'Your Rally username') do |username|
|
20
67
|
@options[:username] = username
|
21
68
|
end
|
22
69
|
|
23
|
-
opts.on('-p PASSWORD', '--password') do |password|
|
70
|
+
opts.on('-p PASSWORD', '--password', 'Your Rally password') do |password|
|
24
71
|
@options[:password] = password
|
25
72
|
end
|
26
73
|
|
27
|
-
opts.on('-h', '--help') do
|
74
|
+
opts.on('-h', '--help', 'Show this message') do
|
28
75
|
@stdout.puts Rallycat::Help.new
|
29
76
|
exit
|
30
77
|
end
|
@@ -37,78 +84,53 @@ module Rallycat
|
|
37
84
|
@options ||= {}
|
38
85
|
|
39
86
|
commands = {
|
40
|
-
'cat' => OptionParser.new
|
87
|
+
'cat' => OptionParser.new do |opts|
|
88
|
+
opts.banner = 'Usage: rallycat story <story number>'
|
89
|
+
end,
|
41
90
|
|
42
91
|
'update' => OptionParser.new do |opts|
|
43
|
-
opts.banner = 'Usage: rallycat update <
|
92
|
+
opts.banner = 'Usage: rallycat update <task number> [options]'
|
44
93
|
|
45
|
-
opts.on('-b', '--blocked') do |blocked|
|
94
|
+
opts.on('-b', '--blocked', 'Block the current state of a task') do |blocked|
|
46
95
|
@options[:blocked] = true
|
47
96
|
end
|
48
97
|
|
49
|
-
opts.on('-p', '--in-progress') do |in_progress|
|
98
|
+
opts.on('-p', '--in-progress', 'Set the state of a task to "In-Progress"') do |in_progress|
|
50
99
|
@options[:in_progress] = true
|
51
100
|
end
|
52
101
|
|
53
|
-
opts.on('-c', '--completed') do |completed|
|
102
|
+
opts.on('-c', '--completed', 'Set the state of a task to "Completed"') do |completed|
|
54
103
|
@options[:completed] = true
|
55
104
|
end
|
56
105
|
|
57
|
-
opts.on('-d', '--defined') do |defined|
|
106
|
+
opts.on('-d', '--defined', 'Set the state of a task to "Defined"') do |defined|
|
58
107
|
@options[:defined] = true
|
59
108
|
end
|
60
109
|
|
61
|
-
opts.on('-o OWNER', '--owner') do |owner|
|
110
|
+
opts.on('-o OWNER', '--owner', 'Set the owner of a task') do |owner|
|
62
111
|
@options[:owner] = owner
|
63
112
|
end
|
64
113
|
end,
|
65
114
|
|
66
|
-
'
|
67
|
-
|
115
|
+
'list' => OptionParser.new do |opts|
|
116
|
+
opts.banner = 'Usage: rallycat list [options]'
|
68
117
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def run
|
74
|
-
case @command
|
75
|
-
when 'cat'
|
76
|
-
api = Rallycat::Connection.new(options[:username], options[:password]).api
|
77
|
-
|
78
|
-
story_number = @argv.shift
|
118
|
+
opts.on('-p [PROJECT]', '--project', 'The project whose iterations you want to list') do |project|
|
119
|
+
@options[:project] = project
|
120
|
+
end
|
79
121
|
|
80
|
-
|
122
|
+
opts.on('-i ITERATION', '--iteration', 'The iteration whose stories you want to list') do |iteration|
|
123
|
+
@options[:iteration] = iteration
|
124
|
+
end
|
125
|
+
end,
|
81
126
|
|
82
|
-
|
83
|
-
|
84
|
-
rescue Rallycat::Cat::StoryNotFound => e
|
85
|
-
abort e.message
|
127
|
+
'help' => OptionParser.new do |opts|
|
128
|
+
opts.banner = 'Usage: rallycat help'
|
86
129
|
end
|
87
|
-
|
88
|
-
api = Rallycat::Connection.new(options[:username], options[:password]).api
|
89
|
-
|
90
|
-
task_number = @argv.shift
|
91
|
-
|
92
|
-
abort 'The "update" command requires a task number.' unless task_number
|
93
|
-
|
94
|
-
opts = {}
|
95
|
-
opts[:blocked] = true if options[:blocked]
|
96
|
-
opts[:state] = "In-Progress" if options[:in_progress]
|
97
|
-
opts[:state] = "Completed" if options[:completed]
|
98
|
-
opts[:state] = "Defined" if options[:defined]
|
99
|
-
opts[:owner] = options[:owner] if options[:owner]
|
130
|
+
}
|
100
131
|
|
101
|
-
|
102
|
-
|
103
|
-
rescue Rallycat::Update::UserNotFound, Rallycat::Update::TaskNotFound => e
|
104
|
-
abort e.message
|
105
|
-
end
|
106
|
-
when 'help'
|
107
|
-
# `puts` calls `to_s`
|
108
|
-
@stdout.puts Rallycat::Help.new
|
109
|
-
else
|
110
|
-
@stdout.puts "'#{@command}' is not a supported command. See 'rallycat help'."
|
111
|
-
end
|
132
|
+
@command = @argv.shift
|
133
|
+
commands[@command].parse! @argv if commands.has_key? @command
|
112
134
|
end
|
113
135
|
end
|
114
136
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rallycat
|
2
|
+
class Config
|
3
|
+
def initialize
|
4
|
+
begin
|
5
|
+
@config = YAML.load_file File.expand_path('~/.rallycatrc')
|
6
|
+
rescue StandardError
|
7
|
+
message = "Your rallycat config file is missing or invalid. See 'rallycat help'."
|
8
|
+
raise InvalidConfigError.new message
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
@config[key]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/rallycat/connection.rb
CHANGED
@@ -2,46 +2,29 @@ require 'yaml'
|
|
2
2
|
require 'rally_rest_api'
|
3
3
|
require 'logger'
|
4
4
|
|
5
|
-
module Rallycat
|
6
|
-
class InvalidConfigError < StandardError; end
|
7
|
-
class InvalidCredentialsError < StandardError; end
|
8
|
-
|
5
|
+
module Rallycat
|
9
6
|
class Connection
|
10
7
|
attr_reader :api
|
11
8
|
|
12
9
|
def initialize(username=nil, password=nil)
|
13
|
-
@
|
14
|
-
@password = password
|
10
|
+
@config = Config.new
|
15
11
|
|
16
|
-
|
12
|
+
username ||= @config['username']
|
13
|
+
password ||= @config['password']
|
17
14
|
|
18
15
|
begin
|
16
|
+
logger = ENV['RALLY_DEBUG'] ? Logger.new(STDOUT) : Logger.new(nil)
|
17
|
+
|
19
18
|
@api = RallyRestAPI.new \
|
20
19
|
base_url: 'https://rally1.rallydev.com/slm',
|
21
|
-
username:
|
22
|
-
password:
|
23
|
-
logger:
|
20
|
+
username: username,
|
21
|
+
password: password,
|
22
|
+
logger: logger
|
24
23
|
|
25
24
|
rescue Rally::NotAuthenticatedError
|
26
25
|
raise InvalidCredentialsError.new('Your Rally credentials are invalid.')
|
27
26
|
end
|
28
27
|
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def parse_config
|
33
|
-
rc_file_path = File.expand_path '~/.rallycatrc'
|
34
|
-
|
35
|
-
if @username || @password
|
36
|
-
{ 'username' => @username, 'password' => @password }
|
37
|
-
else
|
38
|
-
begin
|
39
|
-
YAML.load_file(rc_file_path)
|
40
|
-
rescue StandardError
|
41
|
-
message = "Your rallycat config file is missing or invalid. Please RTFM."
|
42
|
-
raise InvalidConfigError.new message
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
28
|
end
|
47
29
|
end
|
30
|
+
|
data/lib/rallycat/help.rb
CHANGED
@@ -2,27 +2,39 @@ module Rallycat
|
|
2
2
|
class Help
|
3
3
|
def to_s
|
4
4
|
<<-HELP
|
5
|
-
Rallycat is a command
|
5
|
+
Rallycat is a command-line utility for interacting with Rally. It should be
|
6
|
+
used to support a command-line centric workflow. Rallycat has the simple goal
|
7
|
+
of being a lightweight alternative to Rally's web interface. It provides quick
|
8
|
+
access to Rally features that are important to developers in their day-to-day
|
9
|
+
work.
|
6
10
|
|
7
11
|
Configuration:
|
8
12
|
Configuration is available through a ~/.rallycatrc file formatted as YAML.
|
9
13
|
The file should have two keys: `username` and `password` for authenticating
|
10
|
-
with the Rally API.
|
14
|
+
with the Rally API. An additional project attribute can be added to make some
|
15
|
+
commands easiser to use. Example:
|
16
|
+
|
17
|
+
username: email@host.com
|
18
|
+
password: pass1234
|
19
|
+
project: SuperBad
|
11
20
|
|
12
21
|
Additionally, the `-u [USERNAME]` and `-p [PASSWORD]` flags may be provided
|
13
22
|
at runtime and will take higher precedence than the configuration file.
|
14
23
|
|
15
24
|
Global Options:
|
16
|
-
-u [USERNAME] #
|
17
|
-
-p [PASSWORD] #
|
25
|
+
-u [USERNAME] # Your Rally username
|
26
|
+
-p [PASSWORD] # Your Rally password
|
18
27
|
-h, --help # Displays this help text
|
19
28
|
|
20
29
|
Commands:
|
21
|
-
rallycat cat <story number> # Displays
|
22
|
-
rallycat update <task number> #
|
23
|
-
[--blocked | -b] [--in-progress | -
|
30
|
+
rallycat cat <story number> # Displays a user story or defect
|
31
|
+
rallycat update <task number> # Updates the state of a task
|
32
|
+
[--blocked | -b] [--in-progress | -p]
|
24
33
|
[--completed | -c] [--defined | -d]
|
25
34
|
[--owner | -o <fullname>]
|
35
|
+
rallycat list # List iterations and stories
|
36
|
+
[--project | -p <project name>]
|
37
|
+
[--iteration | -i <iteration name>]
|
26
38
|
rallycat help # Displays this help text
|
27
39
|
|
28
40
|
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Rallycat
|
2
|
+
class List
|
3
|
+
|
4
|
+
def initialize(api)
|
5
|
+
@api = api
|
6
|
+
@config = Config.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def iterations(project_name)
|
10
|
+
project_name ||= @config['project']
|
11
|
+
|
12
|
+
validate_arg project_name, 'Project name is required.'
|
13
|
+
|
14
|
+
project = find_project(project_name)
|
15
|
+
|
16
|
+
iterations = @api.find_all(:iteration, {
|
17
|
+
project: project,
|
18
|
+
order: 'StartDate desc',
|
19
|
+
pagesize: 10
|
20
|
+
}).results
|
21
|
+
|
22
|
+
if iterations.count == 0
|
23
|
+
return "No iterations could be found for project #{project_name}."
|
24
|
+
end
|
25
|
+
|
26
|
+
iteration_names = iterations.map(&:name).uniq
|
27
|
+
|
28
|
+
<<-LIST
|
29
|
+
# 5 Most recent iterations for "#{project_name}"
|
30
|
+
|
31
|
+
#{iteration_names.join("\n")}
|
32
|
+
|
33
|
+
LIST
|
34
|
+
end
|
35
|
+
|
36
|
+
def stories(project_name, iteration_name)
|
37
|
+
project_name ||= @config['project']
|
38
|
+
|
39
|
+
validate_arg project_name, 'Project name is required.'
|
40
|
+
validate_arg iteration_name, 'Iteration name is required.'
|
41
|
+
|
42
|
+
project = find_project(project_name)
|
43
|
+
iteration = find_iteration(iteration_name, project)
|
44
|
+
|
45
|
+
stories = @api.find(:hierarchical_requirement, {
|
46
|
+
project: project,
|
47
|
+
pagesize: 100,
|
48
|
+
fetch: true
|
49
|
+
}) { equal :iteration, iteration }.results
|
50
|
+
|
51
|
+
stories += @api.find(:defect, {
|
52
|
+
project: project,
|
53
|
+
pagesize: 100,
|
54
|
+
fetch: true
|
55
|
+
}) { equal :iteration, iteration }.results
|
56
|
+
|
57
|
+
if stories.count == 0
|
58
|
+
return %{No stories could be found for iteration "#{iteration_name}".}
|
59
|
+
end
|
60
|
+
|
61
|
+
list = %{# Stories for iteration "#{iteration_name}" - "#{project_name}"\n\n}
|
62
|
+
|
63
|
+
stories.each do |story|
|
64
|
+
state = story.schedule_state == 'In-Progress' ? 'P' : story.schedule_state[0]
|
65
|
+
list += "* [#{story.formatted_i_d}] [#{state}] #{story.name}\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
list += "\n"
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def validate_arg(val, message)
|
74
|
+
raise ArgumentError, message if val.nil? || val.empty?
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_project(project_name)
|
78
|
+
project = @api.find(:project) { equal(:name, project_name) }.first
|
79
|
+
|
80
|
+
raise ProjectNotFound, "Project (#{project_name}) does not exist." unless project
|
81
|
+
|
82
|
+
project
|
83
|
+
end
|
84
|
+
|
85
|
+
def find_iteration(iteration_name, project)
|
86
|
+
iteration = @api.find(:iteration, :project => project) {
|
87
|
+
equal(:name, iteration_name)
|
88
|
+
}.first
|
89
|
+
|
90
|
+
raise IterationNotFound, "Iteration (#{iteration_name}) does not exist." unless iteration
|
91
|
+
|
92
|
+
iteration
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
data/lib/rallycat/update.rb
CHANGED
data/lib/rallycat/version.rb
CHANGED
data/spec/integration_spec.rb
CHANGED
@@ -57,7 +57,7 @@ describe 'Rallycat' do
|
|
57
57
|
expected = string_io.read
|
58
58
|
|
59
59
|
expected.should include("rallycat cat <story number>")
|
60
|
-
expected.should include("Displays
|
60
|
+
expected.should include("Displays a user story or defect")
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -183,4 +183,108 @@ describe 'Rallycat' do
|
|
183
183
|
end
|
184
184
|
end
|
185
185
|
end
|
186
|
+
|
187
|
+
context 'list' do
|
188
|
+
|
189
|
+
it 'outputs the last 5 iterations for the users configured project' do
|
190
|
+
sout = StringIO.new
|
191
|
+
|
192
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password list }, sout
|
193
|
+
|
194
|
+
Artifice.activate_with RallyIterationsResponder.new do
|
195
|
+
rc_file = File.expand_path("~/.rallycatrc")
|
196
|
+
YAML.stub(:load_file).with(rc_file).and_return({
|
197
|
+
'project' => 'SuperBad'
|
198
|
+
})
|
199
|
+
|
200
|
+
cli.run
|
201
|
+
end
|
202
|
+
|
203
|
+
sout.rewind
|
204
|
+
sout.read.should include('# 5 Most recent iterations for "SuperBad"')
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'outputs the last 5 iterations for the project provided' do
|
208
|
+
sout = StringIO.new
|
209
|
+
|
210
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password list -p SuperBad }, sout
|
211
|
+
|
212
|
+
Artifice.activate_with RallyIterationsResponder.new do
|
213
|
+
cli.run
|
214
|
+
end
|
215
|
+
|
216
|
+
sout.rewind
|
217
|
+
sout.read.should include('# 5 Most recent iterations for "SuperBad"')
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'outputs the stories for an iteration constrained by the configured project' do
|
221
|
+
sout = StringIO.new
|
222
|
+
|
223
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password list -i 25\ (2012-05-01\ to\ 2012-05-05) }, sout
|
224
|
+
|
225
|
+
Artifice.activate_with RallyStoriesResponder.new do
|
226
|
+
rc_file = File.expand_path("~/.rallycatrc")
|
227
|
+
YAML.stub(:load_file).with(rc_file).and_return({
|
228
|
+
'project' => 'SuperBad'
|
229
|
+
})
|
230
|
+
|
231
|
+
cli.run
|
232
|
+
end
|
233
|
+
|
234
|
+
sout.rewind
|
235
|
+
sout.read.should include('# Stories for iteration "25 (2012-05-01 to 2012-05-05)" - "SuperBad"')
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'outputs the stories for an iteration constrained by the project provided' do
|
239
|
+
sout = StringIO.new
|
240
|
+
|
241
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password list -p SuperBad -i 25\ (2012-05-01\ to\ 2012-05-05) }, sout
|
242
|
+
|
243
|
+
Artifice.activate_with RallyStoriesResponder.new do
|
244
|
+
cli.run
|
245
|
+
end
|
246
|
+
|
247
|
+
sout.rewind
|
248
|
+
sout.read.should include('# Stories for iteration "25 (2012-05-01 to 2012-05-05)" - "SuperBad"')
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'aborts when project is not found' do
|
252
|
+
sout = StringIO.new
|
253
|
+
|
254
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password list -p WebFarts -i 25\ (2012-05-01\ to\ 2012-05-05) }, sout
|
255
|
+
|
256
|
+
lambda {
|
257
|
+
Artifice.activate_with RallyStoriesResponder.new do
|
258
|
+
cli.run
|
259
|
+
end
|
260
|
+
}.should raise_error(SystemExit, 'Project (WebFarts) does not exist.')
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'aborts when iteration is not found' do
|
264
|
+
sout = StringIO.new
|
265
|
+
|
266
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password list -p SuperBad -i Sprint\ 0 }, sout
|
267
|
+
|
268
|
+
lambda {
|
269
|
+
Artifice.activate_with RallyStoriesResponder.new do
|
270
|
+
cli.run
|
271
|
+
end
|
272
|
+
}.should raise_error(SystemExit, 'Iteration (Sprint 0) does not exist.')
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'aborts when project is not configured or provided' do
|
276
|
+
sout = StringIO.new
|
277
|
+
|
278
|
+
cli = Rallycat::CLI.new %w{ -u foo.bar@rallycat.com -p password list }, sout
|
279
|
+
|
280
|
+
lambda {
|
281
|
+
Artifice.activate_with RallyStoriesResponder.new do
|
282
|
+
rc_file = File.expand_path("~/.rallycatrc")
|
283
|
+
YAML.stub(:load_file).with(rc_file).and_return({ })
|
284
|
+
|
285
|
+
cli.run
|
286
|
+
end
|
287
|
+
}.should raise_error(SystemExit, 'Project name is required.')
|
288
|
+
end
|
289
|
+
end
|
186
290
|
end
|
File without changes
|
@@ -23,7 +23,7 @@ describe Rallycat::Connection do
|
|
23
23
|
@connection = Rallycat::Connection.new
|
24
24
|
end
|
25
25
|
}.should raise_error Rallycat::InvalidConfigError,
|
26
|
-
|
26
|
+
"Your rallycat config file is missing or invalid. See 'rallycat help'."
|
27
27
|
end
|
28
28
|
|
29
29
|
it "raises when the credentials are invalid" do
|
@@ -88,4 +88,4 @@ describe Rallycat::Connection do
|
|
88
88
|
end
|
89
89
|
|
90
90
|
end
|
91
|
-
end
|
91
|
+
end
|
File without changes
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rallycat::List, '#iterations' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
Artifice.activate_with RallyAuthResponder.new do
|
7
|
+
@api = Rallycat::Connection.new('foo.bar@rallycat.com', 'password').api
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns last five iterations for the project provided' do
|
12
|
+
expected = <<-LIST
|
13
|
+
# 5 Most recent iterations for "SuperBad"
|
14
|
+
|
15
|
+
25 (2012-05-01 to 2012-05-05)
|
16
|
+
24 (2012-04-01 to 2012-04-05)
|
17
|
+
23 (2012-03-01 to 2012-03-05)
|
18
|
+
22 (2012-02-01 to 2012-02-05)
|
19
|
+
21 (2012-01-01 to 2012-01-05)
|
20
|
+
|
21
|
+
LIST
|
22
|
+
|
23
|
+
Artifice.activate_with RallyIterationsResponder.new do
|
24
|
+
project = 'SuperBad'
|
25
|
+
list = Rallycat::List.new(@api)
|
26
|
+
list.iterations(project).should == expected
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns last five iterations for the configured project in .rallycatrc' do
|
31
|
+
expected = <<-LIST
|
32
|
+
# 5 Most recent iterations for "SuperBad"
|
33
|
+
|
34
|
+
25 (2012-05-01 to 2012-05-05)
|
35
|
+
24 (2012-04-01 to 2012-04-05)
|
36
|
+
23 (2012-03-01 to 2012-03-05)
|
37
|
+
22 (2012-02-01 to 2012-02-05)
|
38
|
+
21 (2012-01-01 to 2012-01-05)
|
39
|
+
|
40
|
+
LIST
|
41
|
+
|
42
|
+
Artifice.activate_with RallyIterationsResponder.new do
|
43
|
+
rc_file = File.expand_path("~/.rallycatrc")
|
44
|
+
YAML.stub(:load_file).with(rc_file).and_return({
|
45
|
+
'project' => 'SuperBad'
|
46
|
+
})
|
47
|
+
list = Rallycat::List.new(@api)
|
48
|
+
list.iterations(nil).should == expected
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'raises when project name is empty' do
|
53
|
+
lambda {
|
54
|
+
list = Rallycat::List.new(@api)
|
55
|
+
list.iterations('').should == expected
|
56
|
+
}.should raise_error(ArgumentError, 'Project name is required.')
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'raises when project name is nil' do
|
60
|
+
lambda {
|
61
|
+
rc_file = File.expand_path("~/.rallycatrc")
|
62
|
+
YAML.stub(:load_file).with(rc_file).and_return({})
|
63
|
+
|
64
|
+
list = Rallycat::List.new(@api)
|
65
|
+
list.iterations(nil).should == expected
|
66
|
+
}.should raise_error(ArgumentError, 'Project name is required.')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'raises when project cannot be found' do
|
70
|
+
Artifice.activate_with RallyNoResultsResponder.new do
|
71
|
+
lambda {
|
72
|
+
list = Rallycat::List.new(@api)
|
73
|
+
list.iterations('WebFarts').should == expected
|
74
|
+
}.should raise_error(Rallycat::ProjectNotFound, 'Project (WebFarts) does not exist.')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'returns a user friendly message when project has no iterations' do
|
79
|
+
Artifice.activate_with RallyIterationsResponder.new do
|
80
|
+
project = 'SuperAwful'
|
81
|
+
list = Rallycat::List.new(@api)
|
82
|
+
list.iterations(project).should == 'No iterations could be found for project SuperAwful.'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe Rallycat::List, '#stories' do
|
88
|
+
before do
|
89
|
+
Artifice.activate_with RallyAuthResponder.new do
|
90
|
+
@api = Rallycat::Connection.new('foo.bar@rallycat.com', 'password').api
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'returns the stories for an iteration filtered by sprint' do
|
95
|
+
expected = <<-LIST
|
96
|
+
# Stories for iteration "25 (2012-05-01 to 2012-05-05)" - "SuperBad"
|
97
|
+
|
98
|
+
* [US123] [C] This is story number one
|
99
|
+
* [US456] [P] This is story number two
|
100
|
+
* [DE789] [D] This is defect number one
|
101
|
+
|
102
|
+
LIST
|
103
|
+
|
104
|
+
Artifice.activate_with RallyStoriesResponder.new do
|
105
|
+
project = 'SuperBad'
|
106
|
+
iteration = '25 (2012-05-01 to 2012-05-05)'
|
107
|
+
list = Rallycat::List.new(@api)
|
108
|
+
list.stories(project, iteration).should == expected
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'raises when project name is empty' do
|
113
|
+
lambda {
|
114
|
+
list = Rallycat::List.new(@api)
|
115
|
+
list.stories('', '25 (2012-05-01 to 2012-05-05)').should == expected
|
116
|
+
}.should raise_error(ArgumentError, 'Project name is required.')
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'raises when project name is nil' do
|
120
|
+
lambda {
|
121
|
+
rc_file = File.expand_path("~/.rallycatrc")
|
122
|
+
YAML.stub(:load_file).with(rc_file).and_return({})
|
123
|
+
|
124
|
+
list = Rallycat::List.new(@api)
|
125
|
+
list.stories(nil, '25 (2012-05-01 to 2012-05-05)').should == expected
|
126
|
+
}.should raise_error(ArgumentError, 'Project name is required.')
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'raises when iteration name is empty' do
|
130
|
+
lambda {
|
131
|
+
list = Rallycat::List.new(@api)
|
132
|
+
list.stories('SuperBad', '').should == expected
|
133
|
+
}.should raise_error(ArgumentError, 'Iteration name is required.')
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'raises when iteration name is nil' do
|
137
|
+
lambda {
|
138
|
+
list = Rallycat::List.new(@api)
|
139
|
+
list.stories('SuperBad', nil).should == expected
|
140
|
+
}.should raise_error(ArgumentError, 'Iteration name is required.')
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'raises when project cannot be found' do
|
144
|
+
Artifice.activate_with RallyNoResultsResponder.new do
|
145
|
+
lambda {
|
146
|
+
list = Rallycat::List.new(@api)
|
147
|
+
list.stories('WebFarts', '25 (2012-05-01 to 2012-05-05)')
|
148
|
+
}.should raise_error(Rallycat::ProjectNotFound, 'Project (WebFarts) does not exist.')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'raises when iteration cannot be found' do
|
153
|
+
Artifice.activate_with RallyStoriesResponder.new do
|
154
|
+
lambda {
|
155
|
+
list = Rallycat::List.new(@api)
|
156
|
+
list.stories('SuperBad', 'Sprint 0')
|
157
|
+
}.should raise_error(Rallycat::IterationNotFound, 'Iteration (Sprint 0) does not exist.')
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'returns a user friendly message when iteration has no stories' do
|
162
|
+
Artifice.activate_with RallyStoriesResponder.new do
|
163
|
+
project = 'SuperBad'
|
164
|
+
iteration = '26 (2012-06-01 to 2012-06-05)'
|
165
|
+
list = Rallycat::List.new(@api)
|
166
|
+
list.stories(project, iteration).should == 'No stories could be found for iteration "26 (2012-06-01 to 2012-06-05)".'
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
@@ -119,7 +119,7 @@ describe Rallycat::Update do
|
|
119
119
|
|
120
120
|
lambda {
|
121
121
|
update.task(task_num, state: 'Completed', owner: 'Norman Notreal')
|
122
|
-
}.should raise_error(Rallycat::
|
122
|
+
}.should raise_error(Rallycat::UserNotFound, 'User (Norman Notreal) does not exist.')
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
@@ -132,8 +132,8 @@ describe Rallycat::Update do
|
|
132
132
|
|
133
133
|
lambda {
|
134
134
|
update.task(task_num, state: 'Completed')
|
135
|
-
}.should raise_error(Rallycat::
|
135
|
+
}.should raise_error(Rallycat::TaskNotFound, 'Task (TA6666) does not exist.')
|
136
136
|
end
|
137
137
|
|
138
138
|
end
|
139
|
-
end
|
139
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class RallyAuthResponder < GenericResponder
|
2
|
+
|
3
|
+
def call(env)
|
4
|
+
@requests << request = Rack::Request.new(env)
|
5
|
+
if request.url == 'https://rally1.rallydev.com/slm/webservice/current/user'
|
6
|
+
[200, {}, ['<User><DisplayName></DisplayName></User>']]
|
7
|
+
else
|
8
|
+
[500, {}, ['oh noes']]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
class RallyIterationsResponder < GenericResponder
|
2
|
+
|
3
|
+
def call(env)
|
4
|
+
@requests << request = Rack::Request.new(env)
|
5
|
+
|
6
|
+
case request.url
|
7
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/Project?query=%28Name+%3D+SuperBad%29'
|
8
|
+
[200, {}, [
|
9
|
+
<<-XML
|
10
|
+
<QueryResult>
|
11
|
+
<Results>
|
12
|
+
<Object ref="https://rally1.rallydev.com/slm/webservice/1.36/project/777555" type="Project">
|
13
|
+
<Name>SuperBad</Name>
|
14
|
+
</Object>
|
15
|
+
</Results>
|
16
|
+
</QueryResult>
|
17
|
+
XML
|
18
|
+
]]
|
19
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/Iteration?query=%28ObjectID+%3E+0%29&project=https%3A%2F%2Frally1.rallydev.com%2Fslm%2Fwebservice%2F1.36%2Fproject%2F777555&order=Startdate+desc&pagesize=10'
|
20
|
+
[200, {}, [
|
21
|
+
<<-XML
|
22
|
+
<QueryResult>
|
23
|
+
<Results>
|
24
|
+
<Object type="Iteration">
|
25
|
+
<Name>25 (2012-05-01 to 2012-05-05)</Name>
|
26
|
+
</Object>
|
27
|
+
<Object type="Iteration">
|
28
|
+
<Name>25 (2012-05-01 to 2012-05-05)</Name>
|
29
|
+
</Object>
|
30
|
+
<Object type="Iteration">
|
31
|
+
<Name>24 (2012-04-01 to 2012-04-05)</Name>
|
32
|
+
</Object>
|
33
|
+
<Object type="Iteration">
|
34
|
+
<Name>24 (2012-04-01 to 2012-04-05)</Name>
|
35
|
+
</Object>
|
36
|
+
<Object type="Iteration">
|
37
|
+
<Name>23 (2012-03-01 to 2012-03-05)</Name>
|
38
|
+
</Object>
|
39
|
+
<Object type="Iteration">
|
40
|
+
<Name>23 (2012-03-01 to 2012-03-05)</Name>
|
41
|
+
</Object>
|
42
|
+
<Object type="Iteration">
|
43
|
+
<Name>22 (2012-02-01 to 2012-02-05)</Name>
|
44
|
+
</Object>
|
45
|
+
<Object type="Iteration">
|
46
|
+
<Name>22 (2012-02-01 to 2012-02-05)</Name>
|
47
|
+
</Object>
|
48
|
+
<Object type="Iteration">
|
49
|
+
<Name>21 (2012-01-01 to 2012-01-05)</Name>
|
50
|
+
</Object>
|
51
|
+
<Object type="Iteration">
|
52
|
+
<Name>21 (2012-01-01 to 2012-01-05)</Name>
|
53
|
+
</Object>
|
54
|
+
</Results>
|
55
|
+
</QueryResult>
|
56
|
+
XML
|
57
|
+
]]
|
58
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/Project?query=%28Name+%3D+SuperAwful%29'
|
59
|
+
[200, {}, [
|
60
|
+
<<-XML
|
61
|
+
<QueryResult>
|
62
|
+
<Results>
|
63
|
+
<Object ref="https://rally1.rallydev.com/slm/webservice/1.36/project/888444" type="Project">
|
64
|
+
<Name>SuperAwful</Name>
|
65
|
+
</Object>
|
66
|
+
</Results>
|
67
|
+
</QueryResult>
|
68
|
+
XML
|
69
|
+
]]
|
70
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/Iteration?query=%28ObjectID+%3E+0%29&project=https%3A%2F%2Frally1.rallydev.com%2Fslm%2Fwebservice%2F1.36%2Fproject%2F888444&order=Startdate+desc&pagesize=10'
|
71
|
+
RallyNoResultsResponder.new.call(env)
|
72
|
+
else
|
73
|
+
RallyNoResultsResponder.new.call(env)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
class RallyStoriesResponder < GenericResponder
|
2
|
+
|
3
|
+
def call(env)
|
4
|
+
@requests << request = Rack::Request.new(env)
|
5
|
+
|
6
|
+
case request.url
|
7
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/Project?query=%28Name+%3D+SuperBad%29'
|
8
|
+
[200, {}, [
|
9
|
+
<<-XML
|
10
|
+
<QueryResult>
|
11
|
+
<Results>
|
12
|
+
<Object ref="https://rally1.rallydev.com/slm/webservice/1.17/project/777555" type="Project" />
|
13
|
+
</Results>
|
14
|
+
</QueryResult>
|
15
|
+
XML
|
16
|
+
]]
|
17
|
+
when 'https://rally1.rallydev.com/slm/webservice/1.17/project/777555'
|
18
|
+
[200, {}, [
|
19
|
+
<<-XML
|
20
|
+
<Project ref="https://rally1.rallydev.com/slm/webservice/1.17/project/777555">
|
21
|
+
<Name>SuperBad</Name>
|
22
|
+
</Project>
|
23
|
+
XML
|
24
|
+
]]
|
25
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/Iteration?query=%28Name+%3D+%2225+%282012-05-01+to+2012-05-05%29%22%29&project=https%3A%2F%2Frally1.rallydev.com%2Fslm%2Fwebservice%2F1.17%2Fproject%2F777555'
|
26
|
+
[200, {}, [
|
27
|
+
# NOTE: Always getting two results back from Rally, not sure why.
|
28
|
+
<<-XML
|
29
|
+
<QueryResult>
|
30
|
+
<Results>
|
31
|
+
<Object ref="https://rally1.rallydev.com/slm/webservice/1.17/iteration/1234" type="Iteration" />
|
32
|
+
<Object ref="https://rally1.rallydev.com/slm/webservice/1.17/iteration/4567" type="Iteration" />
|
33
|
+
</Results>
|
34
|
+
</QueryResult>
|
35
|
+
XML
|
36
|
+
]]
|
37
|
+
when 'https://rally1.rallydev.com/slm/webservice/1.17/iteration/1234'
|
38
|
+
[200, {}, [
|
39
|
+
<<-XML
|
40
|
+
<Iteration>
|
41
|
+
<Name>25 (2012-05-01 to 2012-05-05)</Name>
|
42
|
+
</Iteration>
|
43
|
+
XML
|
44
|
+
]]
|
45
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/HierarchicalRequirement?query=%28Iteration+%3D+%29&project=https%3A%2F%2Frally1.rallydev.com%2Fslm%2Fwebservice%2F1.17%2Fproject%2F777555&pagesize=100&fetch=true'
|
46
|
+
[200, {}, [
|
47
|
+
<<-XML
|
48
|
+
<QueryResult>
|
49
|
+
<Results>
|
50
|
+
<Object type="HierarchicalRequirement">
|
51
|
+
<FormattedID>US123</FormattedID>
|
52
|
+
<Name>This is story number one</Name>
|
53
|
+
<ScheduleState>Completed</ScheduleState>
|
54
|
+
</Object>
|
55
|
+
<Object type="HierarchicalRequirement">
|
56
|
+
<FormattedID>US456</FormattedID>
|
57
|
+
<Name>This is story number two</Name>
|
58
|
+
<ScheduleState>In-Progress</ScheduleState>
|
59
|
+
</Object>
|
60
|
+
</Results>
|
61
|
+
</QueryResult>
|
62
|
+
XML
|
63
|
+
]]
|
64
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/Defect?query=%28Iteration+%3D+%29&project=https%3A%2F%2Frally1.rallydev.com%2Fslm%2Fwebservice%2F1.17%2Fproject%2F777555&pagesize=100&fetch=true'
|
65
|
+
[200, {}, [
|
66
|
+
<<-XML
|
67
|
+
<QueryResult>
|
68
|
+
<Results>
|
69
|
+
<Object type="Defect">
|
70
|
+
<FormattedID>DE789</FormattedID>
|
71
|
+
<Name>This is defect number one</Name>
|
72
|
+
<ScheduleState>Defined</ScheduleState>
|
73
|
+
</Object>
|
74
|
+
</Results>
|
75
|
+
</QueryResult>
|
76
|
+
XML
|
77
|
+
]]
|
78
|
+
|
79
|
+
# These are for testing the not so happy path.
|
80
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/Iteration?query=%28Name+%3D+%22Sprint+0%22%29&project=https%3A%2F%2Frally1.rallydev.com%2Fslm%2Fwebservice%2F1.17%2Fproject%2F777555'
|
81
|
+
RallyNoResultsResponder.new.call(env)
|
82
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/Iteration?query=%28Name+%3D+%2226+%282012-06-01+to+2012-06-05%29%22%29&project=https%3A%2F%2Frally1.rallydev.com%2Fslm%2Fwebservice%2F1.17%2Fproject%2F777555'
|
83
|
+
[200, {}, [
|
84
|
+
<<-XML
|
85
|
+
<QueryResult>
|
86
|
+
<Results>
|
87
|
+
<Object ref="https://rally1.rallydev.com/slm/webservice/1.17/iteration/7890" type="Iteration" />
|
88
|
+
</Results>
|
89
|
+
</QueryResult>
|
90
|
+
XML
|
91
|
+
]]
|
92
|
+
when 'https://rally1.rallydev.com/slm/webservice/1.17/iteration/7890'
|
93
|
+
# does nothing WTF(but will make tests fail if not here)???
|
94
|
+
when 'https://rally1.rallydev.com/slm/webservice/current/HierarchicalRequirement?query=%28Iteration+%3D+https%3A%2F%2Frally1.rallydev.com%2Fslm%2Fwebservice%2F1.17%2Fiteration%2F7890%29&project=https%3A%2F%2Frally1.rallydev.com%2Fslm%2Fwebservice%2F1.17%2Fproject%2F777555&pagesize=100&fetch=true'
|
95
|
+
RallyNoResultsResponder.new.call(env)
|
96
|
+
else
|
97
|
+
RallyNoResultsResponder.new.call(env)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
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.
|
4
|
+
version: 0.4.0
|
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-26 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: builder
|
17
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirement: &70357622298460 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,15 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements:
|
26
|
-
none: false
|
27
|
-
requirements:
|
28
|
-
- - ! '>='
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
version: '0'
|
25
|
+
version_requirements: *70357622298460
|
31
26
|
- !ruby/object:Gem::Dependency
|
32
27
|
name: rally_rest_api
|
33
|
-
requirement: !ruby/object:Gem::Requirement
|
28
|
+
requirement: &70357622297960 !ruby/object:Gem::Requirement
|
34
29
|
none: false
|
35
30
|
requirements:
|
36
31
|
- - ! '>='
|
@@ -38,15 +33,10 @@ dependencies:
|
|
38
33
|
version: '0'
|
39
34
|
type: :runtime
|
40
35
|
prerelease: false
|
41
|
-
version_requirements:
|
42
|
-
none: false
|
43
|
-
requirements:
|
44
|
-
- - ! '>='
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '0'
|
36
|
+
version_requirements: *70357622297960
|
47
37
|
- !ruby/object:Gem::Dependency
|
48
38
|
name: nokogiri
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
39
|
+
requirement: &70357622297540 !ruby/object:Gem::Requirement
|
50
40
|
none: false
|
51
41
|
requirements:
|
52
42
|
- - ! '>='
|
@@ -54,12 +44,7 @@ dependencies:
|
|
54
44
|
version: '0'
|
55
45
|
type: :runtime
|
56
46
|
prerelease: false
|
57
|
-
version_requirements:
|
58
|
-
none: false
|
59
|
-
requirements:
|
60
|
-
- - ! '>='
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
47
|
+
version_requirements: *70357622297540
|
63
48
|
description: The Rally website sucks. CLI is better.
|
64
49
|
email:
|
65
50
|
- adam@adamtanner.org
|
@@ -81,22 +66,28 @@ files:
|
|
81
66
|
- lib/rallycat.rb
|
82
67
|
- lib/rallycat/cat.rb
|
83
68
|
- lib/rallycat/cli.rb
|
69
|
+
- lib/rallycat/config.rb
|
84
70
|
- lib/rallycat/connection.rb
|
85
71
|
- lib/rallycat/help.rb
|
86
72
|
- lib/rallycat/html_to_text_converter.rb
|
73
|
+
- lib/rallycat/list.rb
|
87
74
|
- lib/rallycat/update.rb
|
88
75
|
- lib/rallycat/version.rb
|
89
76
|
- rallycat.gemspec
|
90
77
|
- spec/integration_spec.rb
|
91
|
-
- spec/
|
92
|
-
- spec/
|
93
|
-
- spec/
|
94
|
-
- spec/
|
95
|
-
- spec/
|
78
|
+
- spec/rallycat/cat_spec.rb
|
79
|
+
- spec/rallycat/cli_spec.rb
|
80
|
+
- spec/rallycat/connection_spec.rb
|
81
|
+
- spec/rallycat/html_to_text_converter_spec.rb
|
82
|
+
- spec/rallycat/list_spec.rb
|
83
|
+
- spec/rallycat/update_spec.rb
|
96
84
|
- spec/spec_helper.rb
|
97
85
|
- spec/support/generic_responder.rb
|
86
|
+
- spec/support/rally_auth_responder.rb
|
98
87
|
- spec/support/rally_defect_responder.rb
|
88
|
+
- spec/support/rally_iterations_responder.rb
|
99
89
|
- spec/support/rally_no_results_responder.rb
|
90
|
+
- spec/support/rally_stories_responder.rb
|
100
91
|
- spec/support/rally_story_responder.rb
|
101
92
|
- spec/support/rally_task_update_responder.rb
|
102
93
|
homepage: https://github.com/adamtanner/rallycat
|
@@ -113,7 +104,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
104
|
version: '0'
|
114
105
|
segments:
|
115
106
|
- 0
|
116
|
-
hash:
|
107
|
+
hash: -2821747029306890735
|
117
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
109
|
none: false
|
119
110
|
requirements:
|
@@ -122,23 +113,27 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
113
|
version: '0'
|
123
114
|
segments:
|
124
115
|
- 0
|
125
|
-
hash:
|
116
|
+
hash: -2821747029306890735
|
126
117
|
requirements: []
|
127
118
|
rubyforge_project:
|
128
|
-
rubygems_version: 1.8.
|
119
|
+
rubygems_version: 1.8.7
|
129
120
|
signing_key:
|
130
121
|
specification_version: 3
|
131
122
|
summary: The Rally website sucks. CLI is better.
|
132
123
|
test_files:
|
133
124
|
- spec/integration_spec.rb
|
134
|
-
- spec/
|
135
|
-
- spec/
|
136
|
-
- spec/
|
137
|
-
- spec/
|
138
|
-
- spec/
|
125
|
+
- spec/rallycat/cat_spec.rb
|
126
|
+
- spec/rallycat/cli_spec.rb
|
127
|
+
- spec/rallycat/connection_spec.rb
|
128
|
+
- spec/rallycat/html_to_text_converter_spec.rb
|
129
|
+
- spec/rallycat/list_spec.rb
|
130
|
+
- spec/rallycat/update_spec.rb
|
139
131
|
- spec/spec_helper.rb
|
140
132
|
- spec/support/generic_responder.rb
|
133
|
+
- spec/support/rally_auth_responder.rb
|
141
134
|
- spec/support/rally_defect_responder.rb
|
135
|
+
- spec/support/rally_iterations_responder.rb
|
142
136
|
- spec/support/rally_no_results_responder.rb
|
137
|
+
- spec/support/rally_stories_responder.rb
|
143
138
|
- spec/support/rally_story_responder.rb
|
144
139
|
- spec/support/rally_task_update_responder.rb
|