napcs-pivotalrecord 0.0.1

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/README.rdoc ADDED
@@ -0,0 +1,31 @@
1
+ =PivotalRecord
2
+
3
+ Provides a simple interface to PivotalTracker using syntax similar to ActiveRecord.
4
+
5
+ @projects = Pivotal::Project.find(246)
6
+ @stories = @projects.first.stories
7
+
8
+ == Configuring
9
+
10
+ Require the library in your project and then configure it with your API key.
11
+
12
+ require 'pivotal'
13
+ Pivotal::Configuration.options[:api_key] = "asdfagjasd"
14
+
15
+
16
+ ==Known Issues
17
+
18
+ This is extremely rudimentary and not meant for any sort of production use.
19
+ There is no write support yet, nor are there any search filters.
20
+
21
+
22
+ == Changelog
23
+
24
+ ===0.0.1
25
+ * Basic record retrieval from PivotalTracker.
26
+
27
+
28
+ ==License
29
+ Copyright 2009 Brian Hogan
30
+ Licensed under the MIT license.
31
+ Use of this library must be in accordance with Pivotal Tracker's terms of service and requires an API key.
data/Rakefile ADDED
@@ -0,0 +1,121 @@
1
+
2
+
3
+ require "rubygems"
4
+ require "rake/gempackagetask"
5
+ require "rake/rdoctask"
6
+
7
+ task :default => :spec
8
+
9
+ require "spec"
10
+ require "spec/rake/spectask"
11
+ Spec::Rake::SpecTask.new do |t|
12
+ t.spec_opts = %w(--format specdoc --colour)
13
+ t.libs = ["spec"]
14
+ end
15
+
16
+ # This builds the actual gem. For details of what all these options
17
+ # mean, and other ones you can add, check the documentation here:
18
+ #
19
+ # http://rubygems.org/read/chapter/20
20
+ #
21
+ spec = Gem::Specification.new do |s|
22
+
23
+ # Change these as appropriate
24
+ s.name = "pivotalrecord"
25
+ s.version = "0.0.1"
26
+ s.summary = "Simple ActiveRecord-style interface for PivotalTracker"
27
+ s.author = "Brian Hogan"
28
+ s.email = "brianhogan@naopcs.com"
29
+ s.homepage = "http://www.napcs.com"
30
+
31
+ s.has_rdoc = true
32
+
33
+ # You should probably have a README of some kind. Change the filename
34
+ # as appropriate
35
+ s.extra_rdoc_files = %w(README.rdoc)
36
+ s.rdoc_options = %w(--main README.rdoc)
37
+
38
+ # Add any extra files to include in the gem (like your README)
39
+ s.files = %w(Rakefile) + Dir.glob("{spec,lib}/**/*")
40
+
41
+ s.require_paths = ["lib"]
42
+
43
+ # If you want to depend on other gems, add them here, along with any
44
+ # relevant versions
45
+ s.add_dependency("rest-client", "~> 1.0.3")
46
+ s.add_dependency("xml-simple", "~> 1.0.12")
47
+ s.add_dependency("activesupport", "~> 2.0.2")
48
+
49
+ s.add_development_dependency("rspec") # add any other gems for testing/development
50
+
51
+ # If you want to publish automatically to rubyforge, you'll may need
52
+ # to tweak this, and the publishing task below too.
53
+ s.rubyforge_project = "pivotalrecord"
54
+ end
55
+
56
+ # This task actually builds the gem. We also regenerate a static
57
+ # .gemspec file, which is useful if something (i.e. GitHub) will
58
+ # be automatically building a gem for this project. If you're not
59
+ # using GitHub, edit as appropriate.
60
+ Rake::GemPackageTask.new(spec) do |pkg|
61
+ pkg.gem_spec = spec
62
+
63
+ # Generate the gemspec file for github.
64
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
65
+ File.open(file, "w") {|f| f << spec.to_ruby }
66
+ end
67
+
68
+ # Generate documentation
69
+ Rake::RDocTask.new do |rd|
70
+
71
+ rd.rdoc_files.include("lib/**/*.rb")
72
+ rd.rdoc_dir = "rdoc"
73
+ end
74
+
75
+ desc 'Clear out RDoc and generated packages'
76
+ task :clean => [:clobber_rdoc, :clobber_package] do
77
+ rm "#{spec.name}.gemspec"
78
+ end
79
+
80
+ # If you want to publish to RubyForge automatically, here's a simple
81
+ # task to help do that. If you don't, just get rid of this.
82
+ # Be sure to set up your Rubyforge account details with the Rubyforge
83
+ # gem; you'll need to run `rubyforge setup` and `rubyforge config` at
84
+ # the very least.
85
+ begin
86
+ require "rake/contrib/sshpublisher"
87
+ namespace :rubyforge do
88
+
89
+ desc "Release gem and RDoc documentation to RubyForge"
90
+ task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
91
+
92
+ namespace :release do
93
+ desc "Release a new version of this gem"
94
+ task :gem => [:package] do
95
+ require 'rubyforge'
96
+ rubyforge = RubyForge.new
97
+ rubyforge.configure
98
+ rubyforge.login
99
+ rubyforge.userconfig['release_notes'] = spec.summary
100
+ path_to_gem = File.join(File.dirname(__FILE__), "pkg", "#{spec.name}-#{spec.version}.gem")
101
+ puts "Publishing #{spec.name}-#{spec.version.to_s} to Rubyforge..."
102
+ rubyforge.add_release(spec.rubyforge_project, spec.name, spec.version.to_s, path_to_gem)
103
+ end
104
+
105
+ desc "Publish RDoc to RubyForge."
106
+ task :docs => [:rdoc] do
107
+ config = YAML.load(
108
+ File.read(File.expand_path('~/.rubyforge/user-config.yml'))
109
+ )
110
+
111
+ host = "#{config['username']}@rubyforge.org"
112
+ remote_dir = "/var/www/gforge-projects/pivotalrecord/" # Should be the same as the rubyforge project name
113
+ local_dir = 'rdoc'
114
+
115
+ Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
116
+ end
117
+ end
118
+ end
119
+ rescue LoadError
120
+ puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
121
+ end
data/lib/pivotal.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'activesupport'
3
+ require 'rest_client'
4
+ require 'xmlsimple'
5
+
6
+ require 'lib/pivotal/base'
7
+ require 'lib/pivotal/configuration'
8
+ require 'lib/pivotal/project'
9
+ require 'lib/pivotal/story'
10
+ require 'lib/pivotal/iteration'
@@ -0,0 +1,155 @@
1
+ module Pivotal
2
+ class Base
3
+
4
+ def initialize(args = {})
5
+ args.each do |key, value|
6
+ self.send("#{key}=", value) if self.respond_to?("#{key}=")
7
+ end
8
+ end
9
+
10
+ class << self
11
+
12
+ # parses the collection from Pivotal. Invokes the class's parse_result method
13
+ # which the class should implement.
14
+ def parse_results(results)
15
+ return [] if results[self.resource].nil?
16
+ results[self.resource].collect do |item|
17
+ klass = self.name.constantize
18
+ klass.parse_result(item)
19
+ end
20
+ end
21
+
22
+
23
+ # get_content_for is used to convert the strings in the xml fil
24
+ # into actual Ruby types.
25
+ # The element can come in as a string, an array with a string inside
26
+ # or a hash, so I'm covering all bases here.
27
+ # Don't change without using the specs!
28
+ def get_content_for(element)
29
+ result = if element.is_a? String
30
+ element.to_s
31
+ elsif element.nil?
32
+ nil #doesn't exist
33
+ else
34
+ if element[0].is_a? Hash
35
+ type = element[0]["type"]
36
+ content = element[0]["content"]
37
+ cast_value(content, type)
38
+ else
39
+ element[0].to_s
40
+ end
41
+
42
+ end
43
+ end
44
+
45
+ def cast_value(content, type)
46
+ value = case type
47
+ when "integer"
48
+ content.to_i
49
+ when "datetime"
50
+ content.to_time
51
+ else
52
+ content
53
+ end
54
+ end
55
+
56
+ def resource
57
+ name.split('::').last.downcase
58
+ end
59
+
60
+ # Declares the relationship between the models.
61
+ #
62
+ # has_many :stories
63
+ #
64
+ # Creates a <tt>stories</tt> method to retrive the related records.
65
+ def has_many(things)
66
+ parent_resource = resource
67
+ object = things.to_s.classify
68
+ class_eval <<-EOF
69
+
70
+ def #{things}
71
+ Pivotal::#{object}.find_all_by_parent_id("#{parent_resource.pluralize}", self.id)
72
+ end
73
+
74
+ EOF
75
+ end
76
+
77
+ # Creates a reverse association, making it easy to grab
78
+ # the parent object.
79
+ def belongs_to(parent)
80
+ object = parent.to_s.classify
81
+ parent_id = "#{parent.to_s.downcase}_id"
82
+ class_eval <<-EOF
83
+
84
+ def #{parent.to_s}
85
+ Pivotal::#{object}.find_by_id(#{parent_id})
86
+ end
87
+
88
+ def self.find_all_by_#{parent.to_s}_id(id)
89
+ find_all_by_parent_id("#{parent.to_s.pluralize}", id)
90
+ end
91
+
92
+ EOF
93
+ end
94
+
95
+ # Retrives the token from the configuration
96
+ def token
97
+ @@token = Pivotal::Configuration.options[:api_key]
98
+ end
99
+
100
+ def debugging?
101
+ @@debugging = Pivotal::Configuration.options[:debug] == true
102
+ end
103
+
104
+ # retrieves the service url
105
+ # If not set in the configuration, the default
106
+ # value is used.
107
+ def url
108
+ @@url = Pivotal::Configuration.options[:url] || "http://www.pivotaltracker.com/services/v2"
109
+ end
110
+
111
+ # Base find method
112
+ def find(type)
113
+ if type == :all
114
+ find_all
115
+ else
116
+ find_by_id(type)
117
+ end
118
+ end
119
+
120
+ # find a specific record by id
121
+ def find_by_id(id)
122
+ query = "#{self.url}/#{self.resource.pluralize}/#{id}?token=#{self.token}"
123
+ puts "query : #{query}" if self.debugging?
124
+ results = RestClient.get query
125
+ result = XmlSimple.xml_in(results.to_s)
126
+ parse_result(result)
127
+ end
128
+
129
+ # generic finder, used by associations
130
+ def find_all_by_parent_id(parent, id)
131
+ return [] if id.nil?
132
+ query = "#{self.url}/#{parent}/#{id}/#{self.resource.pluralize}?token=#{self.token}"
133
+ puts "query : #{query}" if self.debugging?
134
+ results = RestClient.get query
135
+ results = XmlSimple.xml_in(results.to_s)
136
+ parse_results(results)
137
+ end
138
+
139
+ # find all records for the given object.
140
+ def find_all
141
+ query ="#{self.url}/#{self.resource.pluralize}?token=#{self.token}"
142
+ puts "query : #{query}" if self.debugging?
143
+ results = RestClient.get query
144
+ puts results.inspect if self.debugging?
145
+ results = XmlSimple.xml_in(results.to_s)
146
+ parse_results(results)
147
+ end
148
+
149
+
150
+
151
+
152
+ end
153
+
154
+ end
155
+ end
@@ -0,0 +1,11 @@
1
+ module Pivotal
2
+ # The class that keeps track of the global options for Haml within Rails.
3
+ module Configuration
4
+ extend self
5
+
6
+ @options = {}
7
+ # The options hash for the Pivotal library.
8
+ # Pivotal::Configuration.options[:api_key] = "something"
9
+ attr_accessor :options
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ module Pivotal
2
+
3
+ #==Iteration
4
+ # An iteration maps to a Pivotal Project and represents the sprint.
5
+ class Iteration < Pivotal::Base
6
+
7
+ belongs_to :project
8
+
9
+ attr_accessor :start, :finish, :id, :stories, :number
10
+
11
+ def self.parse_result(iteration)
12
+ Pivotal::Iteration.new( :id => get_content_for(iteration["id"]),
13
+ :number => get_content_for(iteration["number"]),
14
+ :start => get_content_for(iteration["start"]),
15
+ :finish => get_content_for(iteration["finish"]),
16
+ :stories => Pivotal::Story.parse_results(iteration["stories"].first)
17
+ )
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ module Pivotal
2
+ #==Project
3
+ # Your project in PivotalTracker. Use the project to get basic info
4
+ # and to get its stories.
5
+ class Project < Pivotal::Base
6
+
7
+ attr_accessor :id, :name
8
+
9
+ has_many :stories
10
+
11
+
12
+ def self.parse_result(project)
13
+ Pivotal::Project.new( :id => get_content_for(project["id"]),
14
+ :name => get_content_for(project["name"]) )
15
+ end
16
+
17
+
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ module Pivotal
2
+ class Story < Pivotal::Base
3
+
4
+ attr_accessor :id, :project_id, :name, :description, :url, :estimate, :story_type, :accepted_at
5
+
6
+
7
+
8
+ belongs_to :project
9
+
10
+ private
11
+
12
+
13
+ def self.parse_result(story)
14
+ Pivotal::Story.new(
15
+ :id => get_content_for(story["id"]),
16
+ :name => get_content_for(story["name"]),
17
+ :description => get_content_for(story["description"]),
18
+ :url => get_content_for(story["url"]),
19
+ :estimate => get_content_for(story["estimate"]),
20
+ :story_type => get_content_for(story["story_type"]),
21
+ :accepted_at => get_content_for(story["accepted_at"]),
22
+ :current_state => get_content_for(story["current_state"])
23
+ )
24
+ end
25
+
26
+
27
+
28
+
29
+ end
30
+ end
@@ -0,0 +1,357 @@
1
+ require File.join(File.dirname(__FILE__), "/../spec_helper")
2
+
3
+ describe Pivotal::Iteration do
4
+ before(:each) do
5
+ @service = "http://www.pivotaltracker.com/services/v2"
6
+ @token = "abcd"
7
+ Pivotal::Configuration.options[:api_key] = @token
8
+ FakeWeb.clean_registry
9
+ FakeWeb.allow_net_connect = false
10
+ end
11
+
12
+
13
+ after(:each) do
14
+ FakeWeb.clean_registry
15
+ FakeWeb.allow_net_connect = true
16
+ end
17
+
18
+
19
+ describe "with many iterations" do
20
+ before(:each) do
21
+ xml = %Q{<?xml version="1.0" encoding="UTF-8"?>
22
+ <iterations type="array">
23
+ <iteration>
24
+ <id type="integer">1</id>
25
+ <number type="integer">1</number>
26
+ <start type="datetime">2009/01/12 00:00:00 UTC</start>
27
+ <finish type="datetime">2009/01/19 00:00:00 UTC</finish>
28
+ <stories type="array">
29
+ <story>
30
+ <id type="integer">300970</id>
31
+ <story_type>release</story_type>
32
+ <url>http://www.pivotaltracker.com/story/show/300970</url>
33
+ <current_state>accepted</current_state>
34
+ <description></description>
35
+ <name>rel_1-1-5</name>
36
+ <requested_by>Brian Hogan</requested_by>
37
+ <created_at type="datetime">2008/12/04 03:00:57 UTC</created_at>
38
+ <accepted_at type="datetime">2009/01/14 06:32:15 UTC</accepted_at>
39
+ <deadline type="datetime">2008/12/12 20:00:00 UTC</deadline>
40
+ </story>
41
+ </stories>
42
+ </iteration>
43
+ <iteration>
44
+ <id type="integer">2</id>
45
+ <number type="integer">2</number>
46
+ <start type="datetime">2009/01/19 00:00:00 UTC</start>
47
+ <finish type="datetime">2009/01/26 00:00:00 UTC</finish>
48
+ <stories type="array">
49
+ <story>
50
+ <id type="integer">302045</id>
51
+ <story_type>bug</story_type>
52
+ <url>http://www.pivotaltracker.com/story/show/302045</url>
53
+ <current_state>accepted</current_state>
54
+ <description>CSS stylesheets for greybox are still referenced in templates causing errors in the logs</description>
55
+ <name>Remove references to Greybox</name>
56
+ <requested_by>Brian Hogan</requested_by>
57
+ <owned_by>Brian Hogan</owned_by>
58
+ <created_at type="datetime">2008/12/04 18:46:43 UTC</created_at>
59
+ <accepted_at type="datetime">2009/01/19 17:22:53 UTC</accepted_at>
60
+ </story>
61
+ <story>
62
+ <id type="integer">300931</id>
63
+ <story_type>bug</story_type>
64
+ <url>http://www.pivotaltracker.com/story/show/300931</url>
65
+ <current_state>accepted</current_state>
66
+ <description>The home page has no title set.</description>
67
+ <name>Get a title on the home page</name>
68
+ <requested_by>Brian Hogan</requested_by>
69
+ <owned_by>Brian Hogan</owned_by>
70
+ <created_at type="datetime">2008/12/04 02:22:00 UTC</created_at>
71
+ <accepted_at type="datetime">2009/01/19 17:23:11 UTC</accepted_at>
72
+ </story>
73
+ </stories>
74
+ </iteration>
75
+ <iteration>
76
+ <id type="integer">3</id>
77
+ <number type="integer">3</number>
78
+ <start type="datetime">2009/01/26 00:00:00 UTC</start>
79
+ <finish type="datetime">2009/02/02 00:00:00 UTC</finish>
80
+ <stories type="array">
81
+ </stories>
82
+ </iteration>
83
+ <iteration>
84
+ <id type="integer">4</id>
85
+ <number type="integer">4</number>
86
+ <start type="datetime">2009/02/02 00:00:00 UTC</start>
87
+ <finish type="datetime">2009/02/09 00:00:00 UTC</finish>
88
+ <stories type="array">
89
+ </stories>
90
+ </iteration>
91
+ <iteration>
92
+ <id type="integer">5</id>
93
+ <number type="integer">5</number>
94
+ <start type="datetime">2009/02/09 00:00:00 UTC</start>
95
+ <finish type="datetime">2009/02/16 00:00:00 UTC</finish>
96
+ <stories type="array">
97
+ </stories>
98
+ </iteration>
99
+ <iteration>
100
+ <id type="integer">6</id>
101
+ <number type="integer">6</number>
102
+ <start type="datetime">2009/02/16 00:00:00 UTC</start>
103
+ <finish type="datetime">2009/02/23 00:00:00 UTC</finish>
104
+ <stories type="array">
105
+ </stories>
106
+ </iteration>
107
+ <iteration>
108
+ <id type="integer">7</id>
109
+ <number type="integer">7</number>
110
+ <start type="datetime">2009/02/23 00:00:00 UTC</start>
111
+ <finish type="datetime">2009/03/02 00:00:00 UTC</finish>
112
+ <stories type="array">
113
+ </stories>
114
+ </iteration>
115
+ <iteration>
116
+ <id type="integer">8</id>
117
+ <number type="integer">8</number>
118
+ <start type="datetime">2009/03/02 00:00:00 UTC</start>
119
+ <finish type="datetime">2009/03/09 00:00:00 UTC</finish>
120
+ <stories type="array">
121
+ </stories>
122
+ </iteration>
123
+ <iteration>
124
+ <id type="integer">9</id>
125
+ <number type="integer">9</number>
126
+ <start type="datetime">2009/03/09 00:00:00 UTC</start>
127
+ <finish type="datetime">2009/03/16 00:00:00 UTC</finish>
128
+ <stories type="array">
129
+ </stories>
130
+ </iteration>
131
+ <iteration>
132
+ <id type="integer">10</id>
133
+ <number type="integer">10</number>
134
+ <start type="datetime">2009/03/16 00:00:00 UTC</start>
135
+ <finish type="datetime">2009/03/23 00:00:00 UTC</finish>
136
+ <stories type="array">
137
+ </stories>
138
+ </iteration>
139
+ <iteration>
140
+ <id type="integer">11</id>
141
+ <number type="integer">11</number>
142
+ <start type="datetime">2009/03/23 00:00:00 UTC</start>
143
+ <finish type="datetime">2009/03/30 00:00:00 UTC</finish>
144
+ <stories type="array">
145
+ </stories>
146
+ </iteration>
147
+ <iteration>
148
+ <id type="integer">12</id>
149
+ <number type="integer">12</number>
150
+ <start type="datetime">2009/03/30 00:00:00 UTC</start>
151
+ <finish type="datetime">2009/04/06 00:00:00 UTC</finish>
152
+ <stories type="array">
153
+ </stories>
154
+ </iteration>
155
+ <iteration>
156
+ <id type="integer">13</id>
157
+ <number type="integer">13</number>
158
+ <start type="datetime">2009/04/06 00:00:00 UTC</start>
159
+ <finish type="datetime">2009/04/13 00:00:00 UTC</finish>
160
+ <stories type="array">
161
+ </stories>
162
+ </iteration>
163
+ <iteration>
164
+ <id type="integer">14</id>
165
+ <number type="integer">14</number>
166
+ <start type="datetime">2009/04/13 00:00:00 UTC</start>
167
+ <finish type="datetime">2009/04/20 00:00:00 UTC</finish>
168
+ <stories type="array">
169
+ </stories>
170
+ </iteration>
171
+ <iteration>
172
+ <id type="integer">15</id>
173
+ <number type="integer">15</number>
174
+ <start type="datetime">2009/04/20 00:00:00 UTC</start>
175
+ <finish type="datetime">2009/04/27 00:00:00 UTC</finish>
176
+ <stories type="array">
177
+ </stories>
178
+ </iteration>
179
+ <iteration>
180
+ <id type="integer">16</id>
181
+ <number type="integer">16</number>
182
+ <start type="datetime">2009/04/27 00:00:00 UTC</start>
183
+ <finish type="datetime">2009/05/04 00:00:00 UTC</finish>
184
+ <stories type="array">
185
+ </stories>
186
+ </iteration>
187
+ <iteration>
188
+ <id type="integer">17</id>
189
+ <number type="integer">17</number>
190
+ <start type="datetime">2009/05/04 00:00:00 UTC</start>
191
+ <finish type="datetime">2009/05/11 00:00:00 UTC</finish>
192
+ <stories type="array">
193
+ </stories>
194
+ </iteration>
195
+ <iteration>
196
+ <id type="integer">18</id>
197
+ <number type="integer">18</number>
198
+ <start type="datetime">2009/05/11 00:00:00 UTC</start>
199
+ <finish type="datetime">2009/05/18 00:00:00 UTC</finish>
200
+ <stories type="array">
201
+ </stories>
202
+ </iteration>
203
+ <iteration>
204
+ <id type="integer">19</id>
205
+ <number type="integer">19</number>
206
+ <start type="datetime">2009/05/18 00:00:00 UTC</start>
207
+ <finish type="datetime">2009/05/25 00:00:00 UTC</finish>
208
+ <stories type="array">
209
+ </stories>
210
+ </iteration>
211
+ <iteration>
212
+ <id type="integer">20</id>
213
+ <number type="integer">20</number>
214
+ <start type="datetime">2009/05/25 00:00:00 UTC</start>
215
+ <finish type="datetime">2009/06/01 00:00:00 UTC</finish>
216
+ <stories type="array">
217
+ </stories>
218
+ </iteration>
219
+ <iteration>
220
+ <id type="integer">21</id>
221
+ <number type="integer">21</number>
222
+ <start type="datetime">2009/06/01 00:00:00 UTC</start>
223
+ <finish type="datetime">2009/06/08 00:00:00 UTC</finish>
224
+ <stories type="array">
225
+ </stories>
226
+ </iteration>
227
+ <iteration>
228
+ <id type="integer">22</id>
229
+ <number type="integer">22</number>
230
+ <start type="datetime">2009/06/08 00:00:00 UTC</start>
231
+ <finish type="datetime">2009/06/15 00:00:00 UTC</finish>
232
+ <stories type="array">
233
+ </stories>
234
+ </iteration>
235
+ <iteration>
236
+ <id type="integer">23</id>
237
+ <number type="integer">23</number>
238
+ <start type="datetime">2009/06/15 00:00:00 UTC</start>
239
+ <finish type="datetime">2009/06/22 00:00:00 UTC</finish>
240
+ <stories type="array">
241
+ </stories>
242
+ </iteration>
243
+ <iteration>
244
+ <id type="integer">24</id>
245
+ <number type="integer">24</number>
246
+ <start type="datetime">2009/06/22 00:00:00 UTC</start>
247
+ <finish type="datetime">2009/06/29 00:00:00 UTC</finish>
248
+ <stories type="array">
249
+ </stories>
250
+ </iteration>
251
+ <iteration>
252
+ <id type="integer">25</id>
253
+ <number type="integer">25</number>
254
+ <start type="datetime">2009/06/29 00:00:00 UTC</start>
255
+ <finish type="datetime">2009/07/06 00:00:00 UTC</finish>
256
+ <stories type="array">
257
+ </stories>
258
+ </iteration>
259
+ <iteration>
260
+ <id type="integer">26</id>
261
+ <number type="integer">26</number>
262
+ <start type="datetime">2009/07/06 00:00:00 UTC</start>
263
+ <finish type="datetime">2009/07/13 00:00:00 UTC</finish>
264
+ <stories type="array">
265
+ </stories>
266
+ </iteration>
267
+ <iteration>
268
+ <id type="integer">27</id>
269
+ <number type="integer">27</number>
270
+ <start type="datetime">2009/07/13 00:00:00 UTC</start>
271
+ <finish type="datetime">2009/07/20 00:00:00 UTC</finish>
272
+ <stories type="array">
273
+ </stories>
274
+ </iteration>
275
+ <iteration>
276
+ <id type="integer">28</id>
277
+ <number type="integer">28</number>
278
+ <start type="datetime">2009/07/20 00:00:00 UTC</start>
279
+ <finish type="datetime">2009/07/27 00:00:00 UTC</finish>
280
+ <stories type="array">
281
+ <story>
282
+ <id type="integer">300939</id>
283
+ <story_type>feature</story_type>
284
+ <url>http://www.pivotaltracker.com/story/show/300939</url>
285
+ <estimate type="integer">1</estimate>
286
+ <current_state>started</current_state>
287
+ <description>Images need to be linked and modalbox needs testing in other browsers besides FF and Safari.</description>
288
+ <name>Finish modalbox implementation</name>
289
+ <requested_by>Brian Hogan</requested_by>
290
+ <owned_by>Brian Hogan</owned_by>
291
+ <created_at type="datetime">2008/12/04 02:33:33 UTC</created_at>
292
+ </story>
293
+ <story>
294
+ <id type="integer">300495</id>
295
+ <story_type>feature</story_type>
296
+ <url>http://www.pivotaltracker.com/story/show/300495</url>
297
+ <estimate type="integer">3</estimate>
298
+ <current_state>started</current_state>
299
+ <description>Choose an item, edit it, and notice that the dropdown for file / url is not selected on load, causing the file or url field to not display.</description>
300
+ <name>Editing an item does not select the correct type</name>
301
+ <requested_by>Brian Hogan</requested_by>
302
+ <owned_by>Brian Hogan</owned_by>
303
+ <created_at type="datetime">2008/12/04 01:55:39 UTC</created_at>
304
+ </story>
305
+ <story>
306
+ <id type="integer">300423</id>
307
+ <story_type>feature</story_type>
308
+ <url>http://www.pivotaltracker.com/story/show/300423</url>
309
+ <estimate type="integer">3</estimate>
310
+ <current_state>started</current_state>
311
+ <description>A profile should be viewable using the iPhone or Touch.
312
+ The profile page should link to the portfolios.
313
+ items should display within the portfolios.</description>
314
+ <name>iPhone interface for viewing profiles</name>
315
+ <requested_by>Brian Hogan</requested_by>
316
+ <owned_by>Brian Hogan</owned_by>
317
+ <created_at type="datetime">2008/12/04 01:48:53 UTC</created_at>
318
+ </story>
319
+ <story>
320
+ <id type="integer">300923</id>
321
+ <story_type>feature</story_type>
322
+ <url>http://www.pivotaltracker.com/story/show/300923</url>
323
+ <estimate type="integer">1</estimate>
324
+ <current_state>unstarted</current_state>
325
+ <description>Add a &quot;contact us&quot; form that sends an email</description>
326
+ <name>Contact Us form</name>
327
+ <requested_by>Brian Hogan</requested_by>
328
+ <created_at type="datetime">2008/12/04 02:14:51 UTC</created_at>
329
+ </story>
330
+ <story>
331
+ <id type="integer">300518</id>
332
+ <story_type>feature</story_type>
333
+ <url>http://www.pivotaltracker.com/story/show/300518</url>
334
+ <estimate type="integer">1</estimate>
335
+ <current_state>unstarted</current_state>
336
+ <description>Add a send to friend link on portfolios</description>
337
+ <name>Send To Friend linke</name>
338
+ <requested_by>Brian Hogan</requested_by>
339
+ <created_at type="datetime">2008/12/04 01:57:22 UTC</created_at>
340
+ </story>
341
+ </stories>
342
+ </iteration>
343
+ </iterations>}
344
+ url = "#{@service}/projects/4860/iterations?token=#{@token}"
345
+ FakeWeb.register_uri(:get, url, :string => xml, :content_type => "application/xml", :status => ["200", "OK"])
346
+ @project = Pivotal::Iteration.new("id" => "4860", :name => "FeelMySkills")
347
+ end
348
+ it "should get iterations" do
349
+ iterations = Pivotal::Iteration.find_all_by_project_id(@project.id)
350
+ iterations.each{|i| i.should be_a(Pivotal::Iteration)}
351
+ iterations.detect{|i| i.number == 28}.should be_true
352
+
353
+ end
354
+ end
355
+
356
+
357
+ end