Skip to content

Instantly share code, notes, and snippets.

@nakajima
Created October 31, 2008 05:53
Show Gist options
  • Save nakajima/21233 to your computer and use it in GitHub Desktop.
Save nakajima/21233 to your computer and use it in GitHub Desktop.
=begin
Pivotal Tracker API client (note: super-not-ready)
USAGE
Finding a project:
project = PivotalTracker::Project.find(1)
project.name # => Some Name
project.point_scale # => "0,1,2,4,8"
etc.
Finding a project's stories
stories = project.stories
story = stories.first
story.id # => 123
story.url # => "http://www.pivotaltracker.com/story/show/123"
story.story_type # => "feature"
story.requested_by # => "Pat Nakajima"
story.description # => "some sort of description"
etc.
You can find more information about things by looking at the raw
XML response. This stuff is just a way to access that info more
conviently.
TODO
- flesh out models some more
- rewrite from scratch with specs
=end
%w(rubygems nokogiri net/http uri).each { |lib| require lib }
module PivotalTracker
BASE_URI = "http://www.pivotaltracker.com/services/v1"
class << self
attr_writer :token
def token
@token ||= begin
print "Enter your Pivotal Tracker token: "
@token = $stdin.gets.chomp
end
end
end
class Base
attr_writer :id
class << self
def request(path, options={})
uri = URI.parse(BASE_URI + path)
response = Net::HTTP.start(uri.host, uri.port) { |http| http.get(uri.path, 'Token' => PivotalTracker.token) }
Nokogiri::XML(response.body)
end
def entity_as(name, options={})
entity_parser(name, options) do
options[:map] ?
doc.search(options[:tag]).map { |node| options[:klass].new(node) } :
options[:klass].new(doc.at(options[:tag]))
end
end
def entity_attribute(name, options={})
entity_parser(name, options) do
entity_attributes << name
options[:type] ||= :to_s
value = doc.at(options[:tag]).inner_text
value.send(options[:type])
end
end
private
def entity_parser(name, options={}, &block)
options[:tag] ||= name.to_s
define_method(name) do
instance_variable_get("@#{name}") || begin
instance_variable_set "@#{name}", instance_eval(&block)
end
end
end
end
def id
doc.at('id').inner_text
end
def entity_attributes
@entity_attributes ||= []
end
def initialize(doc)
@doc = doc
end
def request(*args)
self.class.request(*args)
end
private
def doc
@doc
end
end
class Project < Base
entity_attribute :name
entity_attribute :iteration_length
entity_attribute :week_start_day
entity_attribute :point_scale
def self.find(id)
res = request("/projects/#{id}").at('project')
project = new(res)
project.id = id
project
end
def stories(refresh=false)
@stories = nil if refresh
@stories ||= begin
request("/projects/#{@id}/stories").search('story').map { |node| Story.new(node) }
end
end
end
class Note < Base
entity_attribute :text
entity_attribute :author
entity_attribute :date
end
class Iteration < Base
entity_attribute :number
entity_attribute :start
entity_attribute :finish
end
class Story < Base
entity_as :notes, :klass => Note, :map => true, :tag => 'note'
entity_as :iteration, :klass => Iteration
entity_attribute :id, :type => :integer
entity_attribute :url
entity_attribute :estimate, :type => :integer
entity_attribute :current_state
entity_attribute :description
# still implemented here because I haven't fleshed out
# the entity_attributeibute list yet.
def method_missing(sym, *args)
doc.at(sym.to_s).inner_text
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment