pivotal_tracker 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +15 -5
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/pivotal_tracker.rb +46 -10
- data/lib/pivotal_tracker/error.rb +2 -1
- data/pivotal_tracker.gemspec +4 -3
- data/spec/fixtures/activities.xml +28 -8
- data/spec/fixtures/note.xml +7 -0
- data/spec/fixtures/project.xml +23 -3
- data/spec/pivotal_tracker_spec.rb +57 -7
- data/spec/spec_helper.rb +3 -3
- metadata +4 -3
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= pivotal_tracker
|
2
2
|
|
3
|
-
This is a simple ruby wrapper for the Pivotal Tracker
|
3
|
+
This is a simple ruby wrapper for the Pivotal Tracker V3 API utilizing HTTParty and Crack::XML.
|
4
4
|
|
5
5
|
== Setup
|
6
6
|
|
@@ -27,8 +27,8 @@ Notice that all data returned is parsed and built into Mash objects/arrays for y
|
|
27
27
|
|
28
28
|
== Currently implemented methods (More to come!)
|
29
29
|
|
30
|
-
* get_all_activities
|
31
|
-
* get_all_project_activities(project_id)
|
30
|
+
* get_all_activities(options = {})
|
31
|
+
* get_all_project_activities(project_id, options = {})
|
32
32
|
* get_all_projects
|
33
33
|
* get_project(project_id)
|
34
34
|
* create_project(name, options = {})
|
@@ -36,8 +36,18 @@ Notice that all data returned is parsed and built into Mash objects/arrays for y
|
|
36
36
|
* get_project_membership(project_id, membership_id)
|
37
37
|
* add_project_membership(project_id, role, email, options = {})
|
38
38
|
* remove_project_membership(project_id, membership_id)
|
39
|
-
* get_all_project_iterations(project_id)
|
40
|
-
* get_all_project_stories(project_id)
|
39
|
+
* get_all_project_iterations(project_id, options = {})
|
40
|
+
* get_all_project_stories(project_id, options = {})
|
41
|
+
* Filter by passing in a filter hash:
|
42
|
+
{:filter => {:type => "bug", :id => "1,2,5"}}
|
43
|
+
* Read more on filtering here: http://www.pivotaltracker.com/help#howcanasearchberefined
|
44
|
+
* add_project_story(project_id, story)
|
45
|
+
* update_project_story(project_id, story_id, story)
|
46
|
+
* delete_project_story(project_id, story_id)
|
47
|
+
* add_project_story_note(project_id, story_id, text)
|
48
|
+
* move_project_story(project_id, story_id, direction, target_story_id)
|
49
|
+
* direction can be either "before" or "after"
|
50
|
+
|
41
51
|
|
42
52
|
== Note on Patches/Pull Requests
|
43
53
|
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ begin
|
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "pivotal_tracker"
|
8
|
-
gem.summary = %Q{Ruby wrapper for the Pivotal Tracker
|
8
|
+
gem.summary = %Q{Ruby wrapper for the Pivotal Tracker V3 API}
|
9
9
|
gem.email = "joslyn.esser@gmail.com"
|
10
10
|
gem.homepage = "http://github.com/joslynesser/pivotal_tracker"
|
11
11
|
gem.authors = ["Joslyn Esser"]
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/pivotal_tracker.rb
CHANGED
@@ -10,17 +10,17 @@ class PivotalTracker
|
|
10
10
|
def initialize(api_token, options = {})
|
11
11
|
self.class.headers 'X-TrackerToken' => api_token
|
12
12
|
use_ssl = options.delete(:ssl)
|
13
|
-
self.class.base_uri "http#{'s' if use_ssl}://www.pivotaltracker.com/services/
|
13
|
+
self.class.base_uri "http#{'s' if use_ssl}://www.pivotaltracker.com/services/v3"
|
14
14
|
end
|
15
15
|
|
16
|
-
def get_all_activities
|
17
|
-
response = self.class.get("/activities")
|
16
|
+
def get_all_activities(options = {})
|
17
|
+
response = self.class.get("/activities", :query => options)
|
18
18
|
raise_errors(response)
|
19
19
|
parse_response(response, 'activities')
|
20
20
|
end
|
21
21
|
|
22
|
-
def get_all_project_activities(project_id)
|
23
|
-
response = self.class.get("/projects/#{project_id}/activities")
|
22
|
+
def get_all_project_activities(project_id, options = {})
|
23
|
+
response = self.class.get("/projects/#{project_id}/activities", :query => options)
|
24
24
|
raise_errors(response)
|
25
25
|
parse_response(response, 'activities')
|
26
26
|
end
|
@@ -67,17 +67,51 @@ class PivotalTracker
|
|
67
67
|
parse_response(response, 'membership')
|
68
68
|
end
|
69
69
|
|
70
|
-
def get_all_project_iterations(project_id)
|
71
|
-
response = self.class.get("/projects/#{project_id}/iterations")
|
70
|
+
def get_all_project_iterations(project_id, options = {})
|
71
|
+
response = self.class.get("/projects/#{project_id}/iterations", :query => options)
|
72
72
|
raise_errors(response)
|
73
73
|
parse_response(response, 'iterations')
|
74
74
|
end
|
75
75
|
|
76
|
-
def get_all_project_stories(project_id)
|
77
|
-
|
76
|
+
def get_all_project_stories(project_id, options = {})
|
77
|
+
if options[:filter]
|
78
|
+
options[:filter] = options[:filter].inject([]) {|f,(key,value)| f << "#{key}:#{value}"}.join(' ')
|
79
|
+
end
|
80
|
+
|
81
|
+
response = self.class.get("/projects/#{project_id}/stories", :query => options)
|
78
82
|
raise_errors(response)
|
79
83
|
parse_response(response, 'stories')
|
80
84
|
end
|
85
|
+
|
86
|
+
def add_project_story(project_id, story)
|
87
|
+
response = self.class.post("/projects/#{project_id}/stories", :body => {:story => story})
|
88
|
+
raise_errors(response)
|
89
|
+
parse_response(response, 'story')
|
90
|
+
end
|
91
|
+
|
92
|
+
def update_project_story(project_id, story_id, story)
|
93
|
+
response = self.class.put("/projects/#{project_id}/stories/#{story_id}", :body => {:story => story})
|
94
|
+
raise_errors(response)
|
95
|
+
parse_response(response, 'story')
|
96
|
+
end
|
97
|
+
|
98
|
+
def delete_project_story(project_id, story_id)
|
99
|
+
response = self.class.delete("/projects/#{project_id}/stories/#{story_id}")
|
100
|
+
raise_errors(response)
|
101
|
+
parse_response(response, 'story')
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_project_story_note(project_id, story_id, text)
|
105
|
+
response = self.class.post("/projects/#{project_id}/stories/#{story_id}/notes", :body => {:note => {:text => text}})
|
106
|
+
raise_errors(response)
|
107
|
+
parse_response(response, 'note')
|
108
|
+
end
|
109
|
+
|
110
|
+
def move_project_story(project_id, story_id, direction, target_story_id)
|
111
|
+
response = self.class.post("/projects/#{project_id}/stories/#{story_id}/moves", :body => {:move => {:move => direction, :target => target_story_id}})
|
112
|
+
raise_errors(response)
|
113
|
+
parse_response(response, 'story')
|
114
|
+
end
|
81
115
|
|
82
116
|
private
|
83
117
|
|
@@ -91,8 +125,10 @@ class PivotalTracker
|
|
91
125
|
raise PivotalTracker::General, "(#{response.code}): #{response.message}"
|
92
126
|
when 404
|
93
127
|
raise PivotalTracker::ResourceNotFound, "(#{response.code}): #{response.message}"
|
128
|
+
when 422
|
129
|
+
raise PivotalTracker::ResourceInvalid, "(#{response.code}): #{response['errors'].inspect if response['errors']}"
|
94
130
|
when 500
|
95
|
-
raise PivotalTracker::InformPivotal, "Pivotal Tracker had an internal error. Please let them know. (#{response.code}): #{response.message}"
|
131
|
+
raise PivotalTracker::InformPivotal, "Pivotal Tracker had an internal error. Please let them know. (#{response.code}): #{response.message} - #{response['message'] if response}"
|
96
132
|
when 502..503
|
97
133
|
raise PivotalTracker::Unavailable, "(#{response.code}): #{response.message}"
|
98
134
|
end
|
@@ -13,4 +13,5 @@ class PivotalTracker::Unauthorized < PivotalTracker::Error; end
|
|
13
13
|
class PivotalTracker::General < StandardError; end
|
14
14
|
class PivotalTracker::Unavailable < StandardError; end
|
15
15
|
class PivotalTracker::InformPivotal < StandardError; end
|
16
|
-
class PivotalTracker::ResourceNotFound < StandardError; end
|
16
|
+
class PivotalTracker::ResourceNotFound < StandardError; end
|
17
|
+
class PivotalTracker::ResourceInvalid < StandardError; end
|
data/pivotal_tracker.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{pivotal_tracker}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Joslyn Esser"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2010-02-05}
|
13
13
|
s.email = %q{joslyn.esser@gmail.com}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"LICENSE",
|
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
|
|
29
29
|
"spec/fixtures/iterations.xml",
|
30
30
|
"spec/fixtures/membership.xml",
|
31
31
|
"spec/fixtures/memberships.xml",
|
32
|
+
"spec/fixtures/note.xml",
|
32
33
|
"spec/fixtures/project.xml",
|
33
34
|
"spec/fixtures/projects.xml",
|
34
35
|
"spec/fixtures/stories.xml",
|
@@ -41,7 +42,7 @@ Gem::Specification.new do |s|
|
|
41
42
|
s.rdoc_options = ["--charset=UTF-8"]
|
42
43
|
s.require_paths = ["lib"]
|
43
44
|
s.rubygems_version = %q{1.3.5}
|
44
|
-
s.summary = %q{Ruby wrapper for the Pivotal Tracker
|
45
|
+
s.summary = %q{Ruby wrapper for the Pivotal Tracker V3 API}
|
45
46
|
s.test_files = [
|
46
47
|
"spec/pivotal_tracker_spec.rb",
|
47
48
|
"spec/spec_helper.rb"
|
@@ -2,18 +2,38 @@
|
|
2
2
|
<activities type="array">
|
3
3
|
<activity>
|
4
4
|
<id type="integer">1031</id>
|
5
|
-
<
|
6
|
-
<
|
5
|
+
<version type="integer">175</version>
|
6
|
+
<event_type>story_update</event_type>
|
7
|
+
<occurred_at type="datetime">2009/12/14 14:12:09 PST</occurred_at>
|
8
|
+
<author>James Kirk</author>
|
9
|
+
<project_id type="integer">26</project_id>
|
7
10
|
<description>James Kirk accepted "More power to shields"</description>
|
8
|
-
<
|
9
|
-
|
11
|
+
<stories>
|
12
|
+
<story>
|
13
|
+
<id type="integer">109</id>
|
14
|
+
<url>https://www.pivotaltracker.com/services/v3/projects/26/stories/109</url>
|
15
|
+
<lighthouse_id>43</lighthouse_id>
|
16
|
+
<lighthouse_url>http://mylighthouseapp.com/projects/100/tickets/43</lighthouse_url>
|
17
|
+
<accepted_at type="datetime">2009/12/14 22:12:09 UTC</accepted_at>
|
18
|
+
<current_state>accepted</current_state>
|
19
|
+
</story>
|
20
|
+
</stories>
|
10
21
|
</activity>
|
11
22
|
<activity>
|
12
23
|
<id type="integer">1030</id>
|
13
|
-
<
|
14
|
-
<
|
15
|
-
<
|
24
|
+
<version type="integer">175</version>
|
25
|
+
<event_type>story_update</event_type>
|
26
|
+
<occurred_at type="datetime">2009/12/14 14:12:09 PST</occurred_at>
|
16
27
|
<author>Montgomery Scott</author>
|
17
|
-
<
|
28
|
+
<project_id type="integer">28</project_id>
|
29
|
+
<description>Montgomery Scott rejected "Warp speed" with comments: "Warp speed isn't working"</description>
|
30
|
+
<stories>
|
31
|
+
<story>
|
32
|
+
<id type="integer">119</id>
|
33
|
+
<url>https://www.pivotaltracker.com/services/v3/projects/28/stories/119</url>
|
34
|
+
<accepted_at type="datetime">2009/12/14 22:12:09 UTC</accepted_at>
|
35
|
+
<current_state>rejected</current_state>
|
36
|
+
</story>
|
37
|
+
</stories>
|
18
38
|
</activity>
|
19
39
|
</activities>
|
data/spec/fixtures/project.xml
CHANGED
@@ -1,18 +1,38 @@
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
2
|
<project>
|
3
|
-
<id>
|
4
|
-
<name>
|
3
|
+
<id>1</id>
|
4
|
+
<name>Sample Project</name>
|
5
5
|
<iteration_length type="integer">2</iteration_length>
|
6
6
|
<week_start_day>Monday</week_start_day>
|
7
7
|
<point_scale>0,1,2,3</point_scale>
|
8
|
+
<account>James Kirks Account</account>
|
8
9
|
<velocity_scheme>Average of 4 iterations</velocity_scheme>
|
10
|
+
<current_velocity>10</current_velocity>
|
9
11
|
<initial_velocity>10</initial_velocity>
|
10
12
|
<number_of_done_iterations_to_show>12</number_of_done_iterations_to_show>
|
13
|
+
<labels>shields,transporter</labels>
|
11
14
|
<allow_attachments>true</allow_attachments>
|
12
15
|
<public>false</public>
|
13
16
|
<use_https>true</use_https>
|
14
17
|
<bugs_and_chores_are_estimatable>false</bugs_and_chores_are_estimatable>
|
15
18
|
<commit_mode>false</commit_mode>
|
19
|
+
<last_activity_at type="datetime">2010/01/16 17:39:10 CST</last_activity_at>
|
16
20
|
<memberships>
|
21
|
+
<membership>
|
22
|
+
<id>1006</id>
|
23
|
+
<person>
|
24
|
+
<email>kirkybaby@earth.ufp</email>
|
25
|
+
<name>James T. Kirk</name>
|
26
|
+
<initials>JTK</initials>
|
27
|
+
</person>
|
28
|
+
<role>Owner</role>
|
29
|
+
</membership>
|
17
30
|
</memberships>
|
18
|
-
|
31
|
+
<integrations>
|
32
|
+
<integration>
|
33
|
+
<id>4532</id>
|
34
|
+
<title>Lighthouse Sample Integration</title>
|
35
|
+
<active type="boolean">true</active>
|
36
|
+
</integration>
|
37
|
+
</integrations>
|
38
|
+
</project>
|
@@ -35,11 +35,23 @@ describe PivotalTracker do
|
|
35
35
|
activities.first.description.should == "James Kirk accepted \"More power to shields\""
|
36
36
|
end
|
37
37
|
|
38
|
+
it "should filter recent activities" do
|
39
|
+
stub_get('/activities?newer_than_version=170', 'activities.xml')
|
40
|
+
activities = @tracker.get_all_activities(:newer_than_version => 170)
|
41
|
+
activities.first.description.should == "James Kirk accepted \"More power to shields\""
|
42
|
+
end
|
43
|
+
|
38
44
|
it "should get all recent activities for a given project" do
|
39
45
|
stub_get('/projects/1/activities', 'activities.xml')
|
40
46
|
activities = @tracker.get_all_project_activities(1)
|
41
47
|
activities.first.description.should == "James Kirk accepted \"More power to shields\""
|
42
48
|
end
|
49
|
+
|
50
|
+
it "should filter recent activities for a given project" do
|
51
|
+
stub_get('/projects/1/activities?limit=50', 'activities.xml')
|
52
|
+
activities = @tracker.get_all_project_activities(1, :limit => 50)
|
53
|
+
activities.first.description.should == "James Kirk accepted \"More power to shields\""
|
54
|
+
end
|
43
55
|
end
|
44
56
|
|
45
57
|
describe "Projects" do
|
@@ -53,13 +65,19 @@ describe PivotalTracker do
|
|
53
65
|
it "should get a project" do
|
54
66
|
stub_get("/projects/27", 'project.xml')
|
55
67
|
project = @tracker.get_project(27)
|
56
|
-
project.name.should == '
|
68
|
+
project.name.should == 'Sample Project'
|
57
69
|
end
|
58
70
|
|
59
71
|
it "should create a project" do
|
60
72
|
stub_post('/projects', 'project.xml')
|
61
73
|
project = @tracker.create_project('Cardassian War Plans')
|
62
|
-
project.name.should == '
|
74
|
+
project.name.should == 'Sample Project'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should create a project with options" do
|
78
|
+
stub_post('/projects', 'project.xml')
|
79
|
+
project = @tracker.create_project('Cardassian War Plans', :no_owner => true)
|
80
|
+
project.name.should == 'Sample Project'
|
63
81
|
end
|
64
82
|
end
|
65
83
|
|
@@ -101,7 +119,9 @@ describe PivotalTracker do
|
|
101
119
|
end
|
102
120
|
|
103
121
|
it "should get iterations by limit and offset" do
|
104
|
-
|
122
|
+
stub_get('/projects/1/iterations?offset=1&limit=2', 'iterations.xml')
|
123
|
+
iterations = @tracker.get_all_project_iterations(1, :offset => 1, :limit => 2)
|
124
|
+
iterations.first.stories.first.story_type.should == 'feature'
|
105
125
|
end
|
106
126
|
end
|
107
127
|
|
@@ -112,16 +132,46 @@ describe PivotalTracker do
|
|
112
132
|
stories.first.owned_by.should == 'Montgomery Scott'
|
113
133
|
end
|
114
134
|
|
115
|
-
it "should get all stories for a project based on a given filter" do
|
116
|
-
|
135
|
+
it "should get all stories for a project based on a given filter hash" do
|
136
|
+
stub_get("/projects/1/stories\?filter=type%3Afeature", 'stories.xml')
|
137
|
+
stories = @tracker.get_all_project_stories(1, :filter => {:type => 'feature'})
|
138
|
+
stories.first.owned_by.should == 'Montgomery Scott'
|
117
139
|
end
|
118
140
|
|
119
141
|
it "should get all stories for a project paginated by a limit and offset" do
|
120
|
-
|
142
|
+
stub_get("/projects/1/stories\?limit=10&offset=20", 'stories.xml')
|
143
|
+
stories = @tracker.get_all_project_stories(1, :limit => 10, :offset => 20)
|
144
|
+
stories.first.owned_by.should == 'Montgomery Scott'
|
121
145
|
end
|
122
146
|
|
123
147
|
it "should create a new story for a given project" do
|
124
|
-
|
148
|
+
stub_post('/projects/1/stories', 'story.xml')
|
149
|
+
story = @tracker.add_project_story(1, {})
|
150
|
+
story.name.should == 'Fire torpedoes'
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should update an existing story for a given project and story" do
|
154
|
+
stub_put('/projects/1/stories/12', 'story.xml')
|
155
|
+
story = @tracker.update_project_story(1, 12, :name => "Fire torpedoes")
|
156
|
+
story.name.should == 'Fire torpedoes'
|
157
|
+
end
|
158
|
+
|
159
|
+
it "removes an existing story for a given project" do
|
160
|
+
stub_delete('/projects/1/stories/12', 'story.xml')
|
161
|
+
story = @tracker.delete_project_story(1, 12)
|
162
|
+
story.name.should == 'Fire torpedoes'
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should create a new note for a given project and story" do
|
166
|
+
stub_post('/projects/1234/stories/5678/notes', 'note.xml')
|
167
|
+
note = @tracker.add_project_story_note(1234, 5678, 'new note via API')
|
168
|
+
note.text.should == 'new note via API'
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should move a story before or after another story" do
|
172
|
+
stub_post('/projects/1/stories/1234/moves', 'story.xml')
|
173
|
+
story = @tracker.move_project_story(1, 1234, "after", 5678)
|
174
|
+
story.name.should == 'Fire torpedoes'
|
125
175
|
end
|
126
176
|
end
|
127
177
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -9,7 +9,7 @@ require 'fakeweb'
|
|
9
9
|
require 'ruby-debug'
|
10
10
|
|
11
11
|
Spec::Runner.configure do |config|
|
12
|
-
|
12
|
+
config.before(:each) { FakeWeb.clean_registry }
|
13
13
|
end
|
14
14
|
|
15
15
|
FakeWeb.allow_net_connect = false
|
@@ -21,7 +21,7 @@ def fixture_file(filename)
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def pivotal_tracker_url(url)
|
24
|
-
url =~ /^http/ ? url : "http://www.pivotaltracker.com/services/
|
24
|
+
url =~ /^http/ ? url : "http://www.pivotaltracker.com/services/v3#{url}"
|
25
25
|
end
|
26
26
|
|
27
27
|
def stub_get(url, filename, status=nil)
|
@@ -41,4 +41,4 @@ end
|
|
41
41
|
|
42
42
|
def stub_delete(url, filename)
|
43
43
|
FakeWeb.register_uri(:delete, pivotal_tracker_url(url), :body => fixture_file(filename))
|
44
|
-
end
|
44
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pivotal_tracker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joslyn Esser
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-02-05 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- spec/fixtures/iterations.xml
|
76
76
|
- spec/fixtures/membership.xml
|
77
77
|
- spec/fixtures/memberships.xml
|
78
|
+
- spec/fixtures/note.xml
|
78
79
|
- spec/fixtures/project.xml
|
79
80
|
- spec/fixtures/projects.xml
|
80
81
|
- spec/fixtures/stories.xml
|
@@ -109,7 +110,7 @@ rubyforge_project:
|
|
109
110
|
rubygems_version: 1.3.5
|
110
111
|
signing_key:
|
111
112
|
specification_version: 3
|
112
|
-
summary: Ruby wrapper for the Pivotal Tracker
|
113
|
+
summary: Ruby wrapper for the Pivotal Tracker V3 API
|
113
114
|
test_files:
|
114
115
|
- spec/pivotal_tracker_spec.rb
|
115
116
|
- spec/spec_helper.rb
|