rallycat 0.0.1 → 0.1.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/.gitignore CHANGED
@@ -3,7 +3,6 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
- Gemfile.lock
7
6
  InstalledFiles
8
7
  _yardoc
9
8
  coverage
@@ -15,3 +14,4 @@ spec/reports
15
14
  test/tmp
16
15
  test/version_tmp
17
16
  tmp
17
+ vendor
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format progress
3
+ --profile
data/Gemfile CHANGED
@@ -2,3 +2,9 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in rallycat.gemspec
4
4
  gemspec
5
+
6
+ group :development, :test do
7
+ gem 'artifice'
8
+ gem 'pry'
9
+ gem 'rspec'
10
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,44 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rallycat (0.1.0)
5
+ builder
6
+ nokogiri
7
+ rally_rest_api
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ artifice (0.6)
13
+ rack-test
14
+ builder (3.0.0)
15
+ coderay (1.0.6)
16
+ diff-lcs (1.1.3)
17
+ method_source (0.7.1)
18
+ nokogiri (1.5.3)
19
+ pry (0.9.9.6)
20
+ coderay (~> 1.0.5)
21
+ method_source (~> 0.7.1)
22
+ slop (>= 2.4.4, < 3)
23
+ rack (1.4.1)
24
+ rack-test (0.6.1)
25
+ rack (>= 1.0)
26
+ rally_rest_api (1.0.3)
27
+ rspec (2.10.0)
28
+ rspec-core (~> 2.10.0)
29
+ rspec-expectations (~> 2.10.0)
30
+ rspec-mocks (~> 2.10.0)
31
+ rspec-core (2.10.1)
32
+ rspec-expectations (2.10.0)
33
+ diff-lcs (~> 1.1.3)
34
+ rspec-mocks (2.10.1)
35
+ slop (2.4.4)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ artifice
42
+ pry
43
+ rallycat!
44
+ rspec
data/Rakefile CHANGED
@@ -1,2 +1,8 @@
1
1
  #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
data/TODO ADDED
@@ -0,0 +1,50 @@
1
+ # Rallycat TODO
2
+
3
+ * Slow as balls? Make nice wait messages
4
+ * Reconcile USER vs username (cli arg vs config attribute)
5
+ * Add help
6
+ * Add man page
7
+ * Integration specs
8
+
9
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
10
+
11
+ ## Ideas
12
+
13
+ US666 - My Story
14
+ =================
15
+
16
+ ````
17
+ rallycat update --complete --blocked [-cdpsb] -o "Adam Tanner" TA1932`
18
+
19
+ rallycat update -p -o "Emilio Cavazos" TA9832
20
+ rallycat update -b TA1823 -m "Fix specs."
21
+ ````
22
+
23
+ ### Notes
24
+
25
+ * Unblock by setting a new state
26
+ * Don't allow setting a new state and blocking, -b blocks current state
27
+
28
+
29
+ ## Stats
30
+ * Stories in progress
31
+ * Stories accepted
32
+ * Stories not accepted
33
+ * Total To-Do Hours
34
+
35
+
36
+
37
+ rally.vim -> Making User Stories from Vim
38
+
39
+ ## Rallycat Futures
40
+
41
+ cat tasks | rallycat boilerplate
42
+
43
+ [DESCRIPTION] [ESTIMATE] [TO-DO]
44
+
45
+ ### Task Example
46
+
47
+ Show Andrew on frontpage ## 2 ## 2
48
+ Do some shit with Redis ## 3 ## 3
49
+ Redo everything ever ## 140 ## 140
50
+
data/bin/rallycat CHANGED
@@ -1,10 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require "rallycat"
3
+ require 'rallycat'
4
4
 
5
- case ARGV[0]
6
- when "cat"
7
- exec "echo '#{Rallycat::Cat.new.story(ARGV[1])}' | less"
8
- else
9
- puts "YOU DONE FUCKED UP. SEE HELP WHEN AVAILABLE"
10
- end
5
+ Rallycat::CLI.new(ARGV).run
data/lib/rallycat/cat.rb CHANGED
@@ -1,26 +1,69 @@
1
+ require 'nokogiri'
2
+
1
3
  module Rallycat
2
4
  class Cat
3
5
 
6
+ def initialize(rally_api)
7
+ @rally_api = rally_api
8
+ end
9
+
4
10
  def story(story_number)
5
- <<-STORY
11
+ story_type = story_number.start_with?('US') ? :hierarchical_requirement : :defect
12
+
13
+ results = @rally_api.find(story_type, fetch: true) do
14
+ equal :formatted_id, story_number
15
+ end
16
+
17
+ return "Story (#{ story_number }) does not exist." if results.total_result_count == 0
18
+
19
+ consolidate_newlines parse_story(results.first)
20
+ end
21
+
22
+ private
6
23
 
24
+ # NOTE: story.elements.keys => all properties exposed by rally
25
+ def parse_story(story)
26
+ <<-TEXT
7
27
 
8
- ------------------------------------------------
9
- ## [#{story_number}] Rally Title
10
- ------------------------------------------------
28
+ # [#{story.formatted_i_d}] - #{story.name}
11
29
 
12
- As a user
13
- ISBAT be able to update a record
30
+ Plan Estimate: #{story.plan_estimate}
31
+ State: #{story.schedule_state}
32
+ Task Actual: #{story.task_actual_total}
33
+ Task Estimate: #{story.task_estimate_total}
34
+ Task Remaining: #{story.task_remaining_total}
35
+ Owner: #{story.owner}
14
36
 
15
- * [TA1234] I should be able to go to a page
16
- * [TA1234] I should be able to go to a page
17
- * [TA1234] I should be able to go to a page
37
+ ## DESCRIPTION
18
38
 
39
+ #{HtmlToTextConverter.new(story.description).parse}
19
40
 
20
- ------------------------------------------------
41
+ ## TASKS
21
42
 
22
- STORY
43
+ #{parse_tasks(story)}
44
+
45
+ TEXT
23
46
  end
24
47
 
48
+ def parse_tasks(story)
49
+ return '' unless story.tasks
50
+
51
+ tasks = story.tasks
52
+ sorted_tasks = tasks.sort_by{ |t| t.task_index }
53
+
54
+ # This is an example of a task formatted in plain text:
55
+ #
56
+ # [TA12345] [C] The name of the task.
57
+ #
58
+ sorted_tasks.map do |task|
59
+ state = task.state == 'In-Progress' ? 'P' : task.state[0]
60
+ "[#{task.formatted_i_d}] [#{state}] #{task.name}"
61
+ end.join("\n")
62
+ end
63
+
64
+ def consolidate_newlines(story)
65
+ story.gsub(/\n{3,}/, "\n\n")
66
+ end
25
67
  end
26
- end
68
+ end
69
+
@@ -0,0 +1,35 @@
1
+ require 'optparse'
2
+
3
+ module Rallycat
4
+ class CLI
5
+ def initialize(argv, stdout=STDOUT)
6
+ @argv = argv
7
+ @stdout = stdout
8
+ end
9
+
10
+ def run
11
+ options = {}
12
+ option_parser = OptionParser.new do |opts|
13
+ opts.on('-u USER') do |user|
14
+ options[:user] = user
15
+ end
16
+
17
+ opts.on('-p PASSWORD') do |password|
18
+ options[:password] = password
19
+ end
20
+ end
21
+
22
+ option_parser.parse! @argv
23
+
24
+ case @argv.shift
25
+ when 'cat'
26
+ api = Rallycat::Connection.new(options[:user], options[:password]).api
27
+
28
+ @stdout.puts Rallycat::Cat.new(api).story(@argv.shift)
29
+ else
30
+ @stdout.puts 'only support for `cat` exists at the moment.'
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,44 @@
1
+ require 'yaml'
2
+ require 'rally_rest_api'
3
+
4
+ module Rallycat
5
+ class InvalidConfigError < StandardError; end
6
+ class InvalidCredentialsError < StandardError; end
7
+
8
+ class Connection
9
+ attr_reader :api
10
+
11
+ def initialize(username=nil, password=nil)
12
+ @username = username
13
+ @password = password
14
+
15
+ config = parse_config
16
+
17
+ begin
18
+ @api = RallyRestAPI.new \
19
+ base_url: 'https://rally1.rallydev.com/slm',
20
+ username: config.fetch('username'),
21
+ password: config.fetch('password')
22
+ rescue Rally::NotAuthenticatedError
23
+ raise InvalidCredentialsError.new('Your Rally credentials are invalid.')
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def parse_config
30
+ rc_file_path = File.expand_path '~/.rallycatrc'
31
+
32
+ if @username || @password
33
+ { 'username' => @username, 'password' => @password }
34
+ else
35
+ begin
36
+ YAML.load_file(rc_file_path)
37
+ rescue StandardError
38
+ message = "Your rallycat config file is missing or invalid. Please RTFM."
39
+ raise InvalidConfigError.new message
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,56 @@
1
+ require 'nokogiri'
2
+
3
+ class HtmlToTextConverter
4
+
5
+ def initialize(html_string)
6
+ @html_string = html_string
7
+ end
8
+
9
+ def parse
10
+ return '' unless @html_string
11
+
12
+ html = pre_parse
13
+
14
+ @fragment = Nokogiri::HTML.fragment(html)
15
+
16
+ add_newline_after_block_elements
17
+ parse_whitespace
18
+ parse_lists
19
+
20
+ @fragment.content
21
+ end
22
+
23
+ private
24
+
25
+ def pre_parse
26
+ html = @html_string.gsub(/>\s+</, '><') # remove whitespace between tags
27
+ html.gsub('&nbsp;', ' ') # replace nbsp with regular space
28
+ end
29
+
30
+ def add_newline_after_block_elements
31
+ @fragment.css('br, p, div, ul, ol').each { |node| node.after("\n") }
32
+ end
33
+
34
+ def parse_whitespace
35
+ @fragment.xpath('.//text()').each do |node|
36
+ if node.content =~ /\S/ # has non-whitespace characters
37
+ node.content = node.content.squeeze(' ') # consolidate whitespace
38
+ node.content = node.content.lstrip
39
+ else
40
+ # remove nodes that are only whitespace
41
+ node.remove unless node.content.include?("\n")
42
+ end
43
+ end
44
+ end
45
+
46
+ def parse_lists
47
+ # Make lists pretty:
48
+ # * list item 1
49
+ # * list item 2
50
+ # * list item 3
51
+ @fragment.css('li').each do |node|
52
+ node.content = " * #{node.content}\n"
53
+ end
54
+ end
55
+
56
+ end
@@ -1,3 +1,3 @@
1
1
  module Rallycat
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/rallycat.rb CHANGED
@@ -1,5 +1,8 @@
1
- require "rallycat/version"
2
- require "rallycat/cat"
1
+ require 'rallycat/cat'
2
+ require 'rallycat/cli'
3
+ require 'rallycat/connection'
4
+ require 'rallycat/html_to_text_converter'
5
+ require 'rallycat/version'
3
6
 
4
7
  module Rallycat
5
8
  # Your code goes here...
data/rallycat.gemspec CHANGED
@@ -14,4 +14,10 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "rallycat"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Rallycat::VERSION
17
+
18
+ # rally depends on this but does not declare it as a dependency
19
+ gem.add_dependency 'builder'
20
+ gem.add_dependency 'rally_rest_api'
21
+ gem.add_dependency 'nokogiri'
22
+
17
23
  end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Rallycat' do
4
+
5
+ context 'cat' do
6
+
7
+ it 'fetches, parses and outputs the rally story requested by the user' do
8
+ auth_responder = lambda do |env|
9
+ # 'https://rally1.rallydev.com/slm/webservice/current/user'
10
+ [200, {}, ['<foo>bar</foo>']]
11
+ end
12
+
13
+ sout = StringIO.new
14
+ cli = nil
15
+
16
+ Artifice.activate_with auth_responder do
17
+ cli = Rallycat::CLI.new %w{ cat US4567 -u=foo.bar@rallycat.com -p=password'}, sout
18
+ end
19
+
20
+ story_responder = RallyStoryResponder.new
21
+
22
+ Artifice.activate_with story_responder.endpoint do
23
+ cli.run
24
+ end
25
+
26
+ sout.rewind
27
+ sout.read.should include('# [US4567] - [Rework] Change link to button')
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+ require 'cgi'
3
+
4
+ describe Rallycat::Cat, '#story' do
5
+
6
+ before do
7
+ responder = lambda do |env|
8
+ # 'https://rally1.rallydev.com/slm/webservice/current/user'
9
+ [200, {}, ['<foo>bar</foo>']]
10
+ end
11
+
12
+ Artifice.activate_with responder do
13
+ @api = Rallycat::Connection.new('foo.bar@rallycat.com', 'password').api
14
+ end
15
+ end
16
+
17
+ it 'outputs the rally story' do
18
+
19
+ expected = <<-STORY
20
+
21
+ # [US4567] - [Rework] Change link to button
22
+
23
+ Plan Estimate: 1.0
24
+ State: In-Progress
25
+ Task Actual: 0.0
26
+ Task Estimate: 6.5
27
+ Task Remaining: 0.5
28
+ Owner: scootin@fruity.com
29
+
30
+ ## DESCRIPTION
31
+
32
+ This is the story
33
+
34
+ * Remember to do this.
35
+ * And this too.
36
+
37
+ ## TASKS
38
+
39
+ [TA1234] [C] Change link to button
40
+ [TA1235] [P] Add confirmation
41
+ [TA1236] [D] Code Review
42
+ [TA1237] [D] QA Test
43
+
44
+ STORY
45
+
46
+ responder = RallyStoryResponder.new
47
+
48
+ Artifice.activate_with responder.endpoint do
49
+ story_num = 'US7176'
50
+ cat = Rallycat::Cat.new(@api)
51
+ cat.story(story_num).should == expected
52
+ end
53
+ end
54
+
55
+ it 'parses the defect' do
56
+
57
+ expected = <<-STORY
58
+
59
+ # [DE1234] - [Rework] Change link to button
60
+
61
+ Plan Estimate: 1.0
62
+ State: In-Progress
63
+ Task Actual: 0.0
64
+ Task Estimate: 6.5
65
+ Task Remaining: 0.5
66
+ Owner: scootin@fruity.com
67
+
68
+ ## DESCRIPTION
69
+
70
+ This is a defect.
71
+
72
+ ## TASKS
73
+
74
+ STORY
75
+
76
+ responder = RallyDefectResponder.new
77
+
78
+ Artifice.activate_with responder.endpoint do
79
+ story_num = 'DE1234'
80
+ cat = Rallycat::Cat.new(@api)
81
+ cat.story(story_num).should == expected
82
+ end
83
+ end
84
+
85
+ it 'displays nothing under the tasks section when there are no tasks' do
86
+
87
+
88
+ responder = lambda do |env|
89
+ [200, {}, [
90
+ <<-XML
91
+ <QueryResult>
92
+ <Results>
93
+ <Object>
94
+ <FormattedID>US4567</FormattedID>
95
+ <Name>Sky Touch</Name>
96
+ <Description>#{ CGI::escapeHTML('<div>As a user<br /> ISBAT touch the sky.</div>') }</Description>
97
+ </Object>
98
+ </Results>
99
+ <TotalResultCount>1</TotalResultCount>
100
+ </QueryResult>
101
+ XML
102
+ ]]
103
+ end
104
+
105
+ expected = <<-STORY
106
+
107
+ # [US4567] - Sky Touch
108
+
109
+ Plan Estimate:
110
+ State:
111
+ Task Actual:
112
+ Task Estimate:
113
+ Task Remaining:
114
+ Owner:
115
+
116
+ ## DESCRIPTION
117
+
118
+ As a user
119
+ ISBAT touch the sky.
120
+
121
+ ## TASKS
122
+
123
+ STORY
124
+
125
+ Artifice.activate_with responder do
126
+ story_num = 'US4567'
127
+ cat = Rallycat::Cat.new(@api)
128
+ cat.story(story_num).should == expected
129
+ end
130
+ end
131
+
132
+ end
133
+
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rallycat::CLI do
4
+ it 'should default to STDOUT' do
5
+ STDOUT.should_receive(:puts).with 'only support for `cat` exists at the moment.'
6
+ cli = Rallycat::CLI.new []
7
+ cli.run
8
+ end
9
+ end
10
+
11
+ describe Rallycat::CLI, '#run' do
12
+
13
+ it 'should execute command' do
14
+ sout = StringIO.new
15
+ cli = Rallycat::CLI.new ['help'], sout
16
+
17
+ cli.run
18
+
19
+ sout.rewind
20
+ sout.read.should == "only support for `cat` exists at the moment.\n"
21
+ end
22
+ end
@@ -0,0 +1,91 @@
1
+ require "spec_helper"
2
+ require "yaml"
3
+
4
+ describe Rallycat::Connection do
5
+
6
+ before do
7
+ @responder = lambda do |env|
8
+ # 'https://rally1.rallydev.com/slm/webservice/current/user'
9
+ [200, {}, ['<foo>bar</foo>']]
10
+ end
11
+
12
+ @rc_file_path = File.expand_path("~/.rallycatrc")
13
+ end
14
+
15
+ describe '#initialize' do
16
+
17
+ it "raises when no config file is present" do
18
+ # Force file to not exist.
19
+ YAML.stub(:load_file).with(@rc_file_path).and_raise(Errno::ENOENT)
20
+
21
+ lambda {
22
+ Artifice.activate_with @responder do
23
+ @connection = Rallycat::Connection.new
24
+ end
25
+ }.should raise_error Rallycat::InvalidConfigError,
26
+ 'Your rallycat config file is missing or invalid. Please RTFM.'
27
+ end
28
+
29
+ it "raises when the credentials are invalid" do
30
+ responder = lambda do |env|
31
+ [401, {}, "Invalid, homes."]
32
+ end
33
+
34
+ YAML.stub(:load_file).with(@rc_file_path).and_return({
35
+ 'username' => "",
36
+ 'password' => ""
37
+ })
38
+
39
+ lambda {
40
+ Artifice.activate_with responder do
41
+ @connection = Rallycat::Connection.new
42
+ end
43
+ }.should raise_error Rallycat::InvalidCredentialsError,
44
+ "Your Rally credentials are invalid."
45
+ end
46
+ end
47
+
48
+ describe "#api" do
49
+
50
+ before do
51
+ responder = lambda do |env|
52
+ # 'https://rally1.rallydev.com/slm/webservice/current/user'
53
+ [200, {}, ['<foo>bar</foo>']]
54
+ end
55
+
56
+ YAML.stub(:load_file).with(@rc_file_path).and_return({
57
+ 'username' => "bitches@rallydev.com",
58
+ 'password' => "wesuckatsoftware"
59
+ })
60
+
61
+
62
+ end
63
+
64
+ it "creates a rally connection using user's rallycatrc config file" do
65
+ Artifice.activate_with @responder do
66
+ @connection = Rallycat::Connection.new
67
+ end
68
+
69
+ rally_connection = @connection.api
70
+
71
+ rally_connection.should be_kind_of(RallyRestAPI)
72
+
73
+ rally_connection.username.should eq("bitches@rallydev.com")
74
+ rally_connection.password.should eq("wesuckatsoftware")
75
+ end
76
+
77
+ it "creates a rally connection given the username and password provided" do
78
+ Artifice.activate_with @responder do
79
+ @connection = Rallycat::Connection.new("hoes@rallydev.com", "wesuckatpasswords")
80
+ end
81
+
82
+ rally_connection = @connection.api
83
+
84
+ rally_connection.should be_kind_of(RallyRestAPI)
85
+
86
+ rally_connection.username.should eq("hoes@rallydev.com")
87
+ rally_connection.password.should eq("wesuckatpasswords")
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe HtmlToTextConverter do
4
+
5
+ it 'strips away <p> tags and adds a newline to the end of the paragraph' do
6
+ html = '<p>This is a one sentence paragraph.</p>'
7
+ text = HtmlToTextConverter.new(html).parse
8
+ text.should == "This is a one sentence paragraph.\n"
9
+ end
10
+
11
+ it 'returns an empty string when given nil' do
12
+ text = HtmlToTextConverter.new(nil).parse
13
+ text.should == ''
14
+ end
15
+
16
+ it 'strips away <p> tags and adds a newline to the end of each paragraph' do
17
+ html = '<p>Paragraph 1</p><p>Paragraph 2</p>'
18
+ text = HtmlToTextConverter.new(html).parse
19
+ text.should == "Paragraph 1\nParagraph 2\n"
20
+ end
21
+
22
+ it 'strips away spaces in between tags' do
23
+ html = '<p>Paragraph 1</p> <p>Paragraph 2</p>'
24
+ text = HtmlToTextConverter.new(html).parse
25
+ text.should == "Paragraph 1\nParagraph 2\n"
26
+ end
27
+
28
+ it 'consolidates whitespace' do
29
+ html = '<p>I like <b>hot sauce</b>.</p>'
30
+ text = HtmlToTextConverter.new(html).parse
31
+ text.should == "I like hot sauce.\n"
32
+ end
33
+
34
+ it 'converts nbsp to a regular space' do
35
+ html = ' hello&nbsp;world '
36
+ text = HtmlToTextConverter.new(html).parse
37
+ text.should == " hello world "
38
+ end
39
+
40
+ it 'turns <br /> into \n' do
41
+ html = '<div>foo<br /> bar</div>'
42
+ text = HtmlToTextConverter.new(html).parse
43
+ text.should == "foo\nbar\n"
44
+ end
45
+
46
+ it 'indents and stars items in a list' do
47
+ html = '<ol><li>Item 1</li><li>Item 2</li><li>Item 3</li></ol>'
48
+ text = HtmlToTextConverter.new(html).parse
49
+ text.should == " * Item 1\n * Item 2\n * Item 3\n\n"
50
+ end
51
+
52
+ it 'converts complex HTML to clean plain text' do
53
+ html = "<div>This is a story:</div> <div><br /></div> <div> <ul> <li>list item 1</li> <li>list item 2</li> </ul> </div> <div>We need to do something important.</div> <div><br /></div> <div>NOTE:</div> <div>- a note with the word <b><font color=\"#ff0000\">red</font></b>:</div> <div>Some more text.</div> <div><br /></div> <div><b>QA:</b></div> <div>Instructions for QA.</div>"
54
+
55
+ expected = <<-TEXT
56
+ This is a story:
57
+
58
+
59
+ * list item 1
60
+ * list item 2
61
+
62
+
63
+ We need to do something important.
64
+
65
+
66
+ NOTE:
67
+ - a note with the word red:
68
+ Some more text.
69
+
70
+
71
+ QA:
72
+ Instructions for QA.
73
+ TEXT
74
+
75
+ text = HtmlToTextConverter.new(html).parse
76
+ text.should == expected
77
+ end
78
+ end
@@ -0,0 +1,20 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ require 'rallycat'
9
+ require 'pry'
10
+ require 'rally_rest_api'
11
+ require 'artifice'
12
+
13
+ root = File.expand_path('../..', __FILE__)
14
+ Dir["#{root}/spec/support/**/*.rb"].each { |f| require f }
15
+
16
+ RSpec.configure do |config|
17
+ config.treat_symbols_as_metadata_keys_with_true_values = true
18
+ config.run_all_when_everything_filtered = true
19
+ config.filter_run :focus
20
+ end
@@ -0,0 +1,42 @@
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
12
+
13
+ def endpoint
14
+ lambda do |env|
15
+ @requests << request = Rack::Request.new(env)
16
+
17
+ case request.url
18
+ when "https://rally1.rallydev.com/slm/webservice/current/Defect?query=%28FormattedId+%3D+DE1234%29&fetch=true"
19
+ [200, {}, [
20
+ <<-XML
21
+ <QueryResult>
22
+ <Results>
23
+ <Object>
24
+ <FormattedID>DE1234</FormattedID>
25
+ <Name>[Rework] Change link to button</Name>
26
+ <PlanEstimate>1.0</PlanEstimate>
27
+ <ScheduleState>In-Progress</ScheduleState>
28
+ <TaskActualTotal>0.0</TaskActualTotal>
29
+ <TaskEstimateTotal>6.5</TaskEstimateTotal>
30
+ <TaskRemainingTotal>0.5</TaskRemainingTotal>
31
+ <Owner>scootin@fruity.com</Owner>
32
+ <Description>#{ CGI::escapeHTML('<div><p>This is a defect.</p></div>') }</Description>
33
+ </Object>
34
+ </Results>
35
+ <TotalResultCount>1</TotalResultCount>
36
+ </QueryResult>
37
+ XML
38
+ ]]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,91 @@
1
+ require 'cgi'
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
14
+
15
+ def endpoint
16
+ lambda do |env|
17
+ @requests << request = Rack::Request.new(env)
18
+
19
+ case request.url
20
+ when 'https://rally1.rallydev.com/slm/webservice/1.17/task/1'
21
+ [200, {}, [
22
+ <<-XML
23
+ <Task refObjectName="Change link to button">
24
+ <FormattedID>TA1234</FormattedID>
25
+ <State>Complete</State>
26
+ <TaskIndex>1</TaskIndex>
27
+ </Task>
28
+ XML
29
+ ]]
30
+ when 'https://rally1.rallydev.com/slm/webservice/1.17/task/2'
31
+ [200, {}, [
32
+ <<-XML
33
+ <Task refObjectName="Add confirmation">
34
+ <FormattedID>TA1235</FormattedID>
35
+ <State>In-Progress</State>
36
+ <TaskIndex>2</TaskIndex>
37
+ </Task>
38
+ XML
39
+ ]]
40
+ when 'https://rally1.rallydev.com/slm/webservice/1.17/task/3'
41
+ [200, {}, [
42
+ <<-XML
43
+ <Task refObjectName="Code Review">
44
+ <FormattedID>TA1236</FormattedID>
45
+ <State>Defined</State>
46
+ <TaskIndex>3</TaskIndex>
47
+ </Task>
48
+ XML
49
+ ]]
50
+ when 'https://rally1.rallydev.com/slm/webservice/1.17/task/4'
51
+ [200, {}, [
52
+ <<-XML
53
+ <Task refObjectName="QA Test">
54
+ <FormattedID>TA1237</FormattedID>
55
+ <State>Defined</State>
56
+ <TaskIndex>4</TaskIndex>
57
+ </Task>
58
+ XML
59
+ ]]
60
+ else
61
+ # https://rally1.rallydev.com/slm/webservice/current/HierarchicalRequirement?query=%28FormattedId+%3D+US7176%29&fetch=true
62
+ [200, {}, [
63
+ <<-XML
64
+ <QueryResult>
65
+ <Results>
66
+ <Object>
67
+ <FormattedID>US4567</FormattedID>
68
+ <Name>[Rework] Change link to button</Name>
69
+ <PlanEstimate>1.0</PlanEstimate>
70
+ <ScheduleState>In-Progress</ScheduleState>
71
+ <TaskActualTotal>0.0</TaskActualTotal>
72
+ <TaskEstimateTotal>6.5</TaskEstimateTotal>
73
+ <TaskRemainingTotal>0.5</TaskRemainingTotal>
74
+ <Owner>scootin@fruity.com</Owner>
75
+ <Description>#{ CGI::escapeHTML('<div><p>This is the story</p></div><ul><li>Remember to do this.</li><li>And this too.</li></ul>') }</Description>
76
+ <Tasks>
77
+ <Task ref="https://rally1.rallydev.com/slm/webservice/1.17/task/1" />
78
+ <Task ref="https://rally1.rallydev.com/slm/webservice/1.17/task/2" />
79
+ <Task ref="https://rally1.rallydev.com/slm/webservice/1.17/task/3" />
80
+ <Task ref="https://rally1.rallydev.com/slm/webservice/1.17/task/4" />
81
+ </Tasks>
82
+ </Object>
83
+ </Results>
84
+ <TotalResultCount>1</TotalResultCount>
85
+ </QueryResult>
86
+ XML
87
+ ]]
88
+ end
89
+ end
90
+ end
91
+ 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.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,8 +10,41 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-06-11 00:00:00.000000000 Z
14
- dependencies: []
13
+ date: 2012-07-17 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: builder
17
+ requirement: &70363599472260 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *70363599472260
26
+ - !ruby/object:Gem::Dependency
27
+ name: rally_rest_api
28
+ requirement: &70363599471700 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *70363599471700
37
+ - !ruby/object:Gem::Dependency
38
+ name: nokogiri
39
+ requirement: &70363599471120 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *70363599471120
15
48
  description: The Rally website sucks. CLI is better.
16
49
  email:
17
50
  - adam@adamtanner.org
@@ -22,15 +55,29 @@ extensions: []
22
55
  extra_rdoc_files: []
23
56
  files:
24
57
  - .gitignore
58
+ - .rspec
25
59
  - Gemfile
60
+ - Gemfile.lock
26
61
  - LICENSE
27
62
  - README.md
28
63
  - Rakefile
64
+ - TODO
29
65
  - bin/rallycat
30
66
  - lib/rallycat.rb
31
67
  - lib/rallycat/cat.rb
68
+ - lib/rallycat/cli.rb
69
+ - lib/rallycat/connection.rb
70
+ - lib/rallycat/html_to_text_converter.rb
32
71
  - lib/rallycat/version.rb
33
72
  - rallycat.gemspec
73
+ - spec/integration_spec.rb
74
+ - spec/lib/rallycat/cat_spec.rb
75
+ - spec/lib/rallycat/cli_spec.rb
76
+ - spec/lib/rallycat/connection_spec.rb
77
+ - spec/lib/rallycat/html_to_text_converter_spec.rb
78
+ - spec/spec_helper.rb
79
+ - spec/support/rally_defect_responder.rb
80
+ - spec/support/rally_story_responder.rb
34
81
  homepage: https://github.com/adamtanner/rallycat
35
82
  licenses: []
36
83
  post_install_message:
@@ -45,7 +92,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
45
92
  version: '0'
46
93
  segments:
47
94
  - 0
48
- hash: -2187327888201420853
95
+ hash: -2940249692508219253
49
96
  required_rubygems_version: !ruby/object:Gem::Requirement
50
97
  none: false
51
98
  requirements:
@@ -54,11 +101,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
101
  version: '0'
55
102
  segments:
56
103
  - 0
57
- hash: -2187327888201420853
104
+ hash: -2940249692508219253
58
105
  requirements: []
59
106
  rubyforge_project:
60
107
  rubygems_version: 1.8.7
61
108
  signing_key:
62
109
  specification_version: 3
63
110
  summary: The Rally website sucks. CLI is better.
64
- test_files: []
111
+ test_files:
112
+ - spec/integration_spec.rb
113
+ - spec/lib/rallycat/cat_spec.rb
114
+ - spec/lib/rallycat/cli_spec.rb
115
+ - spec/lib/rallycat/connection_spec.rb
116
+ - spec/lib/rallycat/html_to_text_converter_spec.rb
117
+ - spec/spec_helper.rb
118
+ - spec/support/rally_defect_responder.rb
119
+ - spec/support/rally_story_responder.rb