track-r 1.9.1 → 1.9.2
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/lib/track-r/comment.rb +18 -16
- data/lib/track-r/project.rb +125 -123
- data/lib/track-r/story.rb +103 -101
- data/lib/track-r/token.rb +27 -25
- data/lib/track-r/tracker.rb +48 -46
- data/test/unit/project_test.rb +2 -2
- data/test/unit/story_test.rb +1 -1
- data/test/unit/token_test.rb +1 -1
- data/test/unit/tracker_test.rb +8 -8
- data/track-r.gemspec +1 -1
- metadata +1 -1
data/lib/track-r/comment.rb
CHANGED
@@ -1,22 +1,24 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module TrackR
|
2
|
+
class Comment
|
3
|
+
attr_accessor :id, :text, :author, :noted_at, :story_id, :project_id, :token
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
def initialize(options = {})
|
6
|
+
@token = options[:token].to_s
|
7
|
+
@project_id = options[:project_id]
|
8
|
+
@story_id = options[:story_id]
|
9
|
+
@comment = Hpricot(options[:comment])
|
10
|
+
build_comment
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
+
private
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
def build_comment
|
16
|
+
if @comment
|
17
|
+
@id = @comment.at('id').inner_html.chomp unless @comment.at('id').nil?
|
18
|
+
@text = @comment.at('text').inner_html.chomp unless @comment.at('text').nil?
|
19
|
+
@author = @comment.at('author').inner_html.chomp unless @comment.at('author').nil?
|
20
|
+
@noted_at = @comment.at('noted_at').inner_html.chomp unless @comment.at('noted_at').nil?
|
21
|
+
end
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|
data/lib/track-r/project.rb
CHANGED
@@ -1,130 +1,132 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
4
|
-
|
5
|
-
|
6
|
-
:
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
1
|
+
module TrackR
|
2
|
+
# Container for project's attributes.
|
3
|
+
# Receives a hash with either :project key pointing to an hpricot object or a
|
4
|
+
# project_id and a token with which to fetch and build the project object
|
5
|
+
class TrackR::Project
|
6
|
+
attr_reader :name, :iteration_length, :id, :week_start_day, :point_scale,
|
7
|
+
:api_url, :url, :token
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
if options.include?(:project_id) && options.include?(:token)
|
11
|
+
@id = options[:project_id]
|
12
|
+
@token = options[:token].to_s
|
13
|
+
@api_url = "#{CONFIG[:api_url]}projects/#{@id}"
|
14
|
+
@url = "http://www.pivotaltracker.com/projects/#{@id}"
|
15
|
+
@project = Hpricot(open(@api_url, {"X-TrackerToken" => @token}))
|
16
|
+
@stories = nil
|
17
|
+
elsif options.include?(:project) && options.include?(:token)
|
18
|
+
@project = options[:project]
|
19
|
+
@token = options[:token].to_s
|
20
|
+
else
|
21
|
+
raise ArgumentError, "Valid options are: :project (receives an Hpricot Object) OR :project_id + :token"
|
22
|
+
end
|
23
|
+
build_project
|
21
24
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def story(id)
|
30
|
-
Story.new(:story_id => id, :project_id => @id, :token => @token)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Creates a story for this project. Receives a set of valid attributes.
|
34
|
-
# Returns a Story object
|
35
|
-
# TODO: Validate attributes
|
36
|
-
def create_story(attributes = {})
|
37
|
-
api_url = URI.parse("#{CONFIG[:api_url]}projects/#{@id}/stories")
|
38
|
-
query_string = attributes.map { |key, value| "story[#{key}]=#{CGI::escape(value)}"}.join('&')
|
39
|
-
begin
|
40
|
-
http = Net::HTTP.new(api_url.host, api_url.port)
|
41
|
-
http.use_ssl = true
|
42
|
-
response, data = http.post(api_url.path, query_string.concat("&token=#{@token}"))
|
43
|
-
|
44
|
-
raise ResponseError, "Wrong response code" unless response.code.to_i == 200
|
45
|
-
story = (Hpricot(response.body)/:story)
|
46
|
-
Story.new(:story => story, :project_id => @id, :token => @token)
|
47
|
-
rescue ResponseError => e
|
48
|
-
print "Got response code [#{response.code}]:\t"
|
49
|
-
puts response.message
|
50
|
-
raise
|
25
|
+
|
26
|
+
# Builds an array containing the project's story
|
27
|
+
def stories ; @stories || get_stories ; end
|
28
|
+
|
29
|
+
# Fetches a story with given id
|
30
|
+
def story(id)
|
31
|
+
TrackR::Story.new(:story_id => id, :project_id => @id, :token => @token)
|
51
32
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
api_url = URI.parse("#{CONFIG[:api_url]}projects/#{@id}/stories
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
33
|
+
|
34
|
+
# Creates a story for this project. Receives a set of valid attributes.
|
35
|
+
# Returns a TrackR::Story object
|
36
|
+
# TODO: Validate attributes
|
37
|
+
def create_story(attributes = {})
|
38
|
+
api_url = URI.parse("#{CONFIG[:api_url]}projects/#{@id}/stories")
|
39
|
+
query_string = attributes.map { |key, value| "story[#{key}]=#{CGI::escape(value)}"}.join('&')
|
40
|
+
begin
|
41
|
+
http = Net::HTTP.new(api_url.host, api_url.port)
|
42
|
+
http.use_ssl = true
|
43
|
+
response, data = http.post(api_url.path, query_string.concat("&token=#{@token}"))
|
44
|
+
|
45
|
+
raise ResponseError, "Wrong response code" unless response.code.to_i == 200
|
46
|
+
story = (Hpricot(response.body)/:story)
|
47
|
+
TrackR::Story.new(:story => story, :project_id => @id, :token => @token)
|
48
|
+
rescue ResponseError => e
|
49
|
+
print "Got response code [#{response.code}]:\t"
|
50
|
+
puts response.message
|
51
|
+
raise
|
52
|
+
end
|
62
53
|
end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
story
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
54
|
+
|
55
|
+
# Deletes a story given a TrackR::Story object or a story_id
|
56
|
+
def delete_story(story)
|
57
|
+
if story.is_a?(TrackR::Story)
|
58
|
+
api_url = URI.parse("#{CONFIG[:api_url]}projects/#{@id}/stories/#{story.id}")
|
59
|
+
elsif story.is_a?(Integer) || story.to_i.is_a?(Integer)
|
60
|
+
api_url = URI.parse("#{CONFIG[:api_url]}projects/#{@id}/stories/#{story}")
|
61
|
+
else
|
62
|
+
raise ArgumentError, "Should receive a story id or a TrackR::Story object."
|
63
|
+
end
|
64
|
+
begin
|
65
|
+
http = Net::HTTP.new(api_url.host, api_url.port)
|
66
|
+
http.use_ssl = true
|
67
|
+
response, data = http.delete(api_url.path, {"X-TrackerToken" => @token})
|
68
|
+
raise ResponseError, "Wrong response code" unless response.code.to_i == 200
|
69
|
+
story = (Hpricot(response.body)/:story)
|
70
|
+
TrackR::Story.new(:story => story, :project_id => @id, :token => @token)
|
71
|
+
rescue ResponseError => e
|
72
|
+
print "Got response code [#{response.code}]:\t"
|
73
|
+
puts response.message
|
74
|
+
raise
|
75
|
+
end
|
74
76
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
77
|
+
|
78
|
+
# Gets the backlog's stories
|
79
|
+
def backlog
|
80
|
+
get_stories_by_iteration("backlog")
|
81
|
+
end
|
82
|
+
|
83
|
+
# Gets the current iteration's stories
|
84
|
+
def current
|
85
|
+
get_stories_by_iteration("current")
|
86
|
+
end
|
87
|
+
|
88
|
+
# Gets the icebox iteration's stories
|
89
|
+
def icebox
|
90
|
+
get_stories_by_iteration("icebox")
|
91
|
+
end
|
92
|
+
|
93
|
+
# Gets the done iteration's stories
|
94
|
+
def done
|
95
|
+
get_stories_by_iteration("done")
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
# Builds a project given an hpricot object stored at instance variable
|
101
|
+
# @project
|
102
|
+
def build_project
|
103
|
+
@id ||= @project.at('id').inner_html
|
104
|
+
@api_url ||= "#{CONFIG[:api_url]}projects/#{@id}"
|
105
|
+
@url ||= "http://www.pivotaltracker.com/projects/#{@id}"
|
106
|
+
@name = @project.at('name').inner_html
|
107
|
+
@iteration_length = @project.at('iteration_length').inner_html
|
108
|
+
@week_start_day = @project.at('week_start_day').inner_html
|
109
|
+
@point_scale = @project.at('point_scale').inner_html.split(',')
|
110
|
+
end
|
111
|
+
|
112
|
+
# Builds an array containing the project's stories
|
113
|
+
def get_stories
|
114
|
+
api_url = "#{CONFIG[:api_url]}projects/#{@id}/stories"
|
115
|
+
@stories = (Hpricot(open(api_url, {"X-TrackerToken" => @token.to_s}))/:story).map {|story| TrackR::Story.new(:story => story, :project_id => @id, :token => @token)}
|
116
|
+
end
|
117
|
+
|
118
|
+
# Builds an array containing the project's stories for a given iteration
|
119
|
+
def get_stories_by_iteration(name)
|
120
|
+
case name
|
121
|
+
when "icebox"
|
122
|
+
api_url = "#{CONFIG[:api_url]}projects/#{@id}/stories?filter=current_state%3Aunscheduled"
|
123
|
+
else
|
124
|
+
api_url = "#{CONFIG[:api_url]}projects/#{@id}/iterations/#{name}"
|
125
|
+
end
|
126
|
+
@stories = (Hpricot(open(api_url, {"X-TrackerToken" => @token.to_s}))/:story).map {|story| TrackR::Story.new(:story => story, :project_id => @id, :token => @token)}
|
124
127
|
end
|
125
|
-
@stories = (Hpricot(open(api_url, {"X-TrackerToken" => @token.to_s}))/:story).map {|story| Story.new(:story => story, :project_id => @id, :token => @token)}
|
126
|
-
end
|
127
128
|
|
128
|
-
end # class Tracker::Project
|
129
|
+
end # class TrackR::Tracker::TrackR::Project
|
129
130
|
|
130
|
-
class ResponseError < Exception ;end
|
131
|
+
class ResponseError < Exception ;end
|
132
|
+
end
|
data/lib/track-r/story.rb
CHANGED
@@ -1,122 +1,123 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module TrackR
|
2
|
+
#Typhoon TODO: Documentation ☻
|
3
|
+
class TrackR::Story
|
4
|
+
attr_accessor :story_type, :estimate, :current_state,
|
5
|
+
:description, :name, :requested_by, :owned_by, :created_at, :accepted_at,
|
6
|
+
:labels, :project_id, :comments, :story
|
6
7
|
|
7
|
-
|
8
|
+
attr_reader :id, :url
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
10
|
+
def initialize(options = {})
|
11
|
+
@token = options[:token].to_s
|
12
|
+
if options.include?(:project_id) && options.include?(:story_id) && options.include?(:token)
|
13
|
+
@id = options[:story_id]
|
14
|
+
@project_id = options[:project_id]
|
15
|
+
@url = "http://www.pivotaltracker.com/story/show/#{@id}"
|
16
|
+
@api_url = "#{CONFIG[:api_url]}projects/#{@project_id}/stories/#{@id}"
|
17
|
+
@story = Hpricot(open(@api_url, {"X-TrackerToken" => @token}))
|
18
|
+
elsif options.include?(:story) && options.include?(:project_id) && options.include?(:token)
|
19
|
+
@project_id = options[:project_id]
|
20
|
+
@story = options[:story]
|
21
|
+
else
|
22
|
+
raise ArgumentError, "Valid options are: :story (receives an Hpricot Object) + :project_id OR :project_id + :story_id + :token"
|
23
|
+
end
|
24
|
+
build_story
|
22
25
|
end
|
23
|
-
build_story
|
24
|
-
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
27
|
+
def build_story
|
28
|
+
@id ||= @story.at('id').inner_html if @story.at('id')
|
29
|
+
@url ||= "http://www.pivotaltracker.com/story/show/#{@id}"
|
30
|
+
@api_url ||= "#{CONFIG[:api_url]}projects/#{@project_id}/stories/#{@id}"
|
31
|
+
@story_type = @story.at('story_type').inner_html unless @story.at('story_type').nil?
|
32
|
+
@estimate = @story.at('estimate').inner_html unless @story.at('estimate').nil?
|
33
|
+
@current_state = @story.at('current_state').inner_html unless @story.at('current_state').nil?
|
34
|
+
@description = @story.at('description').inner_html unless @story.at('description').nil?
|
35
|
+
@name = @story.at('name').inner_html unless @story.at('name').nil?
|
36
|
+
@requested_by = @story.at('requested_by').inner_html unless @story.at('requested_by').nil?
|
37
|
+
@owned_by = @story.at('owned_by').inner_html unless @story.at('owned_by').nil?
|
38
|
+
@created_at = @story.at('created_at').inner_html unless @story.at('created_at').nil?
|
39
|
+
@accepted_at = @story.at('accepted_at').inner_html unless @story.at('accepted_at').nil?
|
40
|
+
@labels = @story.at('labels').inner_html unless @story.at('labels').nil?
|
41
|
+
@comments ||= build_comments(@story.at('notes').inner_html) unless @story.at('notes').nil?
|
42
|
+
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
44
|
+
# TODO: Test this method
|
45
|
+
def update(attrs = {})
|
46
|
+
unless attrs.empty?
|
47
|
+
if validate_attributes(attrs)
|
48
|
+
attrs.each do |attribute, value|
|
49
|
+
virt_attr = "#{attribute}="
|
50
|
+
self.send(virt_attr, value)
|
51
|
+
end
|
52
|
+
return save
|
53
|
+
else
|
54
|
+
raise ArgumentError
|
50
55
|
end
|
51
|
-
return save
|
52
|
-
else
|
53
|
-
raise ArgumentError
|
54
56
|
end
|
55
57
|
end
|
56
|
-
end
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
59
|
+
def save
|
60
|
+
parameters = build_story_xml
|
61
|
+
api_url = URI.parse("#{CONFIG[:api_url]}projects/#{@project_id}/stories/#{@id}")
|
62
|
+
begin
|
63
|
+
http = Net::HTTP.new(api_url.host, api_url.port)
|
64
|
+
http.use_ssl = true
|
65
|
+
response, data = http.put(api_url.path, parameters, {'X-TrackerToken' => @token, 'Content-Type' => 'application/xml'})
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
67
|
+
raise ResponseError, "Wrong response code" unless response.code.to_i == 200
|
68
|
+
rescue ResponseError => e
|
69
|
+
print "Got response code [#{response.code}]:\t"
|
70
|
+
puts response.message
|
71
|
+
raise
|
72
|
+
end
|
72
73
|
|
73
|
-
|
74
|
-
|
75
|
-
|
74
|
+
@story = (Hpricot(response.body)/:story)
|
75
|
+
build_story
|
76
|
+
end
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
78
|
+
# TODO: test this method:
|
79
|
+
def destroy
|
80
|
+
api_url = URI.parse("#{CONFIG[:api_url]}projects/#{@project_id}/stories/#{@id}")
|
81
|
+
begin
|
82
|
+
http = Net::HTTP.new(api_url.host, api_url.port)
|
83
|
+
http.use_ssl = true
|
84
|
+
response, data = http.delete(api_url.path, {"X-TrackerToken" => @token})
|
85
|
+
raise ResponseError, "Wrong response code" unless response.code.to_i == 200
|
86
|
+
rescue ResponseError => e
|
87
|
+
print "Got response code [#{response.code}]:\t"
|
88
|
+
puts response.message
|
89
|
+
raise
|
90
|
+
end
|
89
91
|
end
|
90
|
-
end
|
91
92
|
|
92
|
-
|
93
|
+
protected
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
|
95
|
+
def build_comments(xml_comments)
|
96
|
+
(Hpricot(xml_comments)/:note).map {|comment| Comment.new(:comment => comment.to_s, :project_id => @project_id, :token => @token, :story_id => @id)}
|
97
|
+
end
|
97
98
|
|
98
|
-
|
99
|
-
|
100
|
-
|
99
|
+
def to_param
|
100
|
+
query_string = attributes.map { |key, value| "story[#{key}]=#{CGI::escape(value)}" unless value.nil?}.compact.join('&')
|
101
|
+
end
|
101
102
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
103
|
+
def build_story_xml
|
104
|
+
story_xml = "<story>"
|
105
|
+
attributes.each do |key, value|
|
106
|
+
story_xml << "<#{key}>#{(value.to_s)}</#{key}>" unless value.nil?
|
107
|
+
end
|
108
|
+
story_xml << "</story>"
|
106
109
|
end
|
107
|
-
story_xml << "</story>"
|
108
|
-
end
|
109
110
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
111
|
+
def validate_attributes(attrs)
|
112
|
+
valid_attributes = attributes
|
113
|
+
attrs.each_key do |k|
|
114
|
+
return false unless valid_attributes.include? k
|
115
|
+
end
|
116
|
+
return true
|
114
117
|
end
|
115
|
-
return true
|
116
|
-
end
|
117
118
|
|
118
|
-
|
119
|
-
|
119
|
+
def attributes
|
120
|
+
{
|
120
121
|
"story_type" => @story_type,
|
121
122
|
"estimate" => @estimate,
|
122
123
|
"current_state" => @current_state,
|
@@ -125,6 +126,7 @@ class Story
|
|
125
126
|
"requested_by" => @requested_by,
|
126
127
|
"owned_by" => @owned_by,
|
127
128
|
"labels" => @labels
|
128
|
-
|
129
|
-
|
130
|
-
end # class Tracker::Story
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end # class TrackR::Tracker::TrackR::Story
|
132
|
+
end
|
data/lib/track-r/token.rb
CHANGED
@@ -1,29 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
options
|
6
|
-
|
7
|
-
options.include?(:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
1
|
+
module TrackR
|
2
|
+
class TrackR::Token
|
3
|
+
def initialize(options = {})
|
4
|
+
@token = case
|
5
|
+
when options.include?(:token)
|
6
|
+
options[:token]
|
7
|
+
when options.include?(:username) &&
|
8
|
+
options.include?(:password)
|
9
|
+
# If a token is not provided, it can be generated by passing a
|
10
|
+
# hash with :user and :password keys
|
11
|
+
get_token(options[:username],
|
12
|
+
options[:password]
|
13
|
+
)
|
14
|
+
else
|
15
|
+
raise ArgumentError, "Invalid argument, expecting either <:token> or <:username> and <:password>"
|
16
|
+
end
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
def to_s
|
20
|
+
@token
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
+
protected
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
# According to http://www.pivotaltracker.com/help/api#retrieve_token this should work:
|
26
|
+
def get_token(username, password)
|
27
|
+
(Hpricot(open("https://www.pivotaltracker.com/services/tokens/active", :http_basic_authentication => [username, password])).at('guid')).inner_html
|
28
|
+
end
|
28
29
|
|
29
|
-
end # class Tracker::Token
|
30
|
+
end # class TrackR::Tracker::TrackR::Token
|
31
|
+
end
|
data/lib/track-r/tracker.rb
CHANGED
@@ -1,50 +1,52 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# Fetches project with given ID
|
15
|
-
# Returns a Project object
|
16
|
-
def project(project_id)
|
17
|
-
@project = Project.new(:project_id => project_id , :token => @token)
|
18
|
-
end
|
19
|
-
|
20
|
-
# Refresh the projects from the server
|
21
|
-
# Returns an array of projects
|
22
|
-
def sync
|
23
|
-
@projects = nil
|
24
|
-
get_projects
|
25
|
-
end
|
26
|
-
|
27
|
-
# Alias for get_projects
|
28
|
-
# Returns an array of projects
|
29
|
-
def projects ; get_projects ; end
|
30
|
-
|
31
|
-
# Receives a block with the condition to find a project. Should work the
|
32
|
-
# same as Enumerable.find method
|
33
|
-
# Returns a Project object
|
34
|
-
def find_project
|
35
|
-
get_projects unless defined?(@projects) && @projects.is_a?(Array)
|
36
|
-
@projects.find do |project|
|
37
|
-
yield(project)
|
1
|
+
module TrackR
|
2
|
+
# The class is a wrapper for getting projects, stories and using tokens. It's
|
3
|
+
# main purpose is relating the three allowing to interact with a project, and
|
4
|
+
# its stories using a token.
|
5
|
+
# Expects a token when initialized.
|
6
|
+
class TrackR::Tracker
|
7
|
+
|
8
|
+
# To generate a token, use the "Create New TrackR::Token" link on the My Profile
|
9
|
+
# page (http://www.pivotaltracker.com/profile).
|
10
|
+
def initialize(token)
|
11
|
+
@token = token
|
12
|
+
raise TypeError unless @token.is_a? TrackR::Token
|
38
13
|
end
|
39
|
-
end
|
40
14
|
|
41
|
-
|
15
|
+
# Fetches project with given ID
|
16
|
+
# Returns a TrackR::Project object
|
17
|
+
def project(project_id)
|
18
|
+
@project = TrackR::Project.new(:project_id => project_id , :token => @token)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Refresh the projects from the server
|
22
|
+
# Returns an array of projects
|
23
|
+
def sync
|
24
|
+
@projects = nil
|
25
|
+
get_projects
|
26
|
+
end
|
42
27
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
28
|
+
# Alias for get_projects
|
29
|
+
# Returns an array of projects
|
30
|
+
def projects ; get_projects ; end
|
31
|
+
|
32
|
+
# Receives a block with the condition to find a project. Should work the
|
33
|
+
# same as Enumerable.find method
|
34
|
+
# Returns a TrackR::Project object
|
35
|
+
def find_project
|
36
|
+
get_projects unless defined?(@projects) && @projects.is_a?(Array)
|
37
|
+
@projects.find do |project|
|
38
|
+
yield(project)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
# Fills @projects. NOTE: call sync method to refill/sync @projects
|
45
|
+
# Returns an Array stored in @projects
|
46
|
+
def get_projects
|
47
|
+
api_url = "#{CONFIG[:api_url]}projects/"
|
48
|
+
@projects ||= (Hpricot(open(api_url, {"X-TrackerToken" => @token.to_s}))/:project).map {|project| TrackR::Project.new(:project => project, :token => @token)}
|
49
|
+
end
|
49
50
|
|
50
|
-
end # class Tracker::Tracker
|
51
|
+
end # class TrackR::Tracker::TrackR::Tracker
|
52
|
+
end
|
data/test/unit/project_test.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../test_helper'
|
2
2
|
|
3
|
-
class ProjectTest < Test::Unit::TestCase
|
3
|
+
class TrackR::ProjectTest < Test::Unit::TestCase
|
4
4
|
context "A given project" do
|
5
5
|
|
6
6
|
setup do
|
7
7
|
@project_id = $config[:project_1][:id]
|
8
8
|
token_options = {:username => $config[:username], :password => $config[:password]}
|
9
|
-
@tracker = Tracker.new(Token.new(token_options))
|
9
|
+
@tracker = TrackR::TrackR::Tracker.new(TrackR::TrackR::Token.new(token_options))
|
10
10
|
@project = @tracker.project(@project_id)
|
11
11
|
end
|
12
12
|
|
data/test/unit/story_test.rb
CHANGED
@@ -6,7 +6,7 @@ class TestStoryTest < Test::Unit::TestCase
|
|
6
6
|
|
7
7
|
setup do
|
8
8
|
token_options = {:username => $config[:username], :password => $config[:password]}
|
9
|
-
@tracker = Tracker.new(Token.new(token_options))
|
9
|
+
@tracker = Tracker::Tracker.new(Tracker::Token.new(token_options))
|
10
10
|
@project_id = $config[:project_1][:id]
|
11
11
|
@project = @tracker.project(@project_id)
|
12
12
|
attributes = { :name => "Finish Track-R (sorry for cluttering :))",
|
data/test/unit/token_test.rb
CHANGED
data/test/unit/tracker_test.rb
CHANGED
@@ -2,11 +2,11 @@ require File.dirname(__FILE__) + '/../test_helper'
|
|
2
2
|
|
3
3
|
class TestTrackerTest < Test::Unit::TestCase
|
4
4
|
|
5
|
-
context "with an invalid token" do
|
5
|
+
context "with an invalid token" do
|
6
6
|
|
7
|
-
should "raise an TypeError if token is other than Token object" do
|
7
|
+
should "raise an TypeError if token is other than TrackR::Token object" do
|
8
8
|
token = "token"
|
9
|
-
assert_raise(TypeError) { Tracker.new(token) }
|
9
|
+
assert_raise(TypeError) { TrackR::Tracker.new(token) }
|
10
10
|
end
|
11
11
|
|
12
12
|
end
|
@@ -17,12 +17,12 @@ class TestTrackerTest < Test::Unit::TestCase
|
|
17
17
|
@project_id = $config[:project_1][:id]
|
18
18
|
def get_token
|
19
19
|
token_options = {:username => $config[:username], :password => $config[:password]}
|
20
|
-
Token.new(token_options)
|
20
|
+
TrackR::Token.new(token_options)
|
21
21
|
end
|
22
|
-
@tracker = Tracker.new(get_token)
|
22
|
+
@tracker = TrackR::Tracker.new(get_token)
|
23
23
|
end
|
24
24
|
|
25
|
-
should "return a valid project by passing the id with correct attributes" do
|
25
|
+
should "return a valid project by passing the id with correct attributes" do
|
26
26
|
project = @tracker.project(@project_id)
|
27
27
|
assert_equal $config[:project_1][:name], project.name
|
28
28
|
assert_equal $config[:project_1][:point_scale], project.point_scale
|
@@ -30,12 +30,12 @@ class TestTrackerTest < Test::Unit::TestCase
|
|
30
30
|
assert_equal $config[:project_1][:iteration_length], project.iteration_length
|
31
31
|
end
|
32
32
|
|
33
|
-
should "return an array of all my projects" do
|
33
|
+
should "return an array of all my projects" do
|
34
34
|
projects = @tracker.projects
|
35
35
|
assert_equal $config[:project_count], projects.size
|
36
36
|
end
|
37
37
|
|
38
|
-
should "be able to find a project using Enumerable.find method" do
|
38
|
+
should "be able to find a project using Enumerable.find method" do
|
39
39
|
project_1 = (@tracker.find_project {|project| project.name == $config[:project_1][:name]})
|
40
40
|
project_2 = (@tracker.find_project {|project| project.id == $config[:project_2][:id]})
|
41
41
|
assert_equal $config[:project_1][:name], project_1.name, "Failed to find by name"
|
data/track-r.gemspec
CHANGED