Archive for the ‘Datamapper’ Category

Pivotal Tracker API Ruby Wrapper

November 11th, 2008

I have been playing around with the recently released public API for Pivotal Tracker. In the process of converting this into a Datamapper Adapter, I tested it with some simple Net::HTTP Ruby code. It provides a nice simple, and limited, illustration of what is possible with this public API. One thing I would like to see added to the API however is the ability to limit the number of results when querying with filters. Attached is the slightly modified source code (removed my project id and token key). Tests are included as always, but fair warning, some are brittle! I will post another revision once the Datamapper Adapter is created.


require 'rubygems'
require 'hpricot'
require 'net/http'
require 'uri'
require 'cgi' 

##
# Pivotal Tracker API Ruby Wrapper
# November 11, 2008
# Justin Smestad
# http://www.evalcode.com
##

class Tracker
  def initialize(project_id = changeme, token = changeme)
    @project_id, @token = project_id, token
  end

  def project
    resource_uri = URI.parse("http://www.pivotaltracker.com/services/v1/projects/#{@project_id}")
    response = Net::HTTP.start(resource_uri.host, resource_uri.port) do |http|
      http.get(resource_uri.path, {'Token' => @token})
    end

    doc = Hpricot(response.body).at('project')

    @project = {
      :name             => doc.at('name').innerHTML,
      :iteration_length => doc.at('iteration_length').innerHTML,
      :week_start_day   => doc.at('week_start_day').innerHTML,
      :point_scale      => doc.at('point_scale').innerHTML
    }
  end

  def stories
    resource_uri = URI.parse("http://www.pivotaltracker.com/services/v1/projects/#{@project_id}/stories")
    response = Net::HTTP.start(resource_uri.host, resource_uri.port) do |http|
      http.get(resource_uri.path, {'Token' => @token})
    end

    doc = Hpricot(response.body)

    @stories = []

    doc.search('stories > story').each do |story|
      @stories << {
        :id => story.at('id').innerHTML.to_i,
        :type => story.at('story_type').innerHTML,
        :name => story.at('name').innerHTML
       }
    end
    return @stories
  end

  # would ideally like to pass a size, aka :all to limit search
  def find(filters = {})
    uri = "http://www.pivotaltracker.com/services/v1/projects/#{@project_id}/stories"
    unless filters.empty?
      uri << "?filter="
      filters.each do |key, value|
        uri << CGI::escape("#{key}\"#{value}\""
      end
    end

    resource_uri = URI.parse(uri)
    response = Net::HTTP.start(resource_uri.host, resource_uri.port) do |http|
      http.get(resource_uri.path, {'Token' => @token})
    end

    doc = Hpricot(response.body)

    @stories = []

    doc.search('stories > story').each do |story|
      @stories << {
        :id => story.at('id').innerHTML.to_i,
        :type => story.at('story_type').innerHTML,
        :name => story.at('name').innerHTML
      }
    end
    return @stories
  end

  def find_story(id)
    resource_uri = URI.parse("http://www.pivotaltracker.com/services/v1/projects/#{@project_id}/stories/#{id}")
    response = Net::HTTP.start(resource_uri.host, resource_uri.port) do |http|
      http.get(resource_uri.path, {'Token' => @token, 'Content-Type' => 'application/xml'})
    end

    doc = Hpricot(response.body).at('story')

    @story = {
      :id => doc.at('id').innerHTML.to_i,
      :type => doc.at('story_type').innerHTML,
      :name => doc.at('name').innerHTML
    }
  end

  def create_story(story)
    story_xml = build_story_xml(story)
    resource_uri = URI.parse("http://www.pivotaltracker.com/services/v1/projects/#{@project_id}/stories")
    response = Net::HTTP.start(resource_uri.host, resource_uri.port) do |http|
      http.post(resource_uri.path, story_xml, {'Token' => @token, 'Content-Type' => 'application/xml'})
    end
  end

  def update_story(story)
    story_xml = build_story_xml(story)
    resource_uri = URI.parse("http://www.pivotaltracker.com/services/v1/projects/#{@project_id}/stories/#{story[:id]}")
    response = Net::HTTP.start(resource_uri.host, resource_uri.port) do |http|
      http.put(resource_uri.path, story_xml, {'Token' => @token, 'Content-Type' => 'application/xml'})
    end
  end

  def delete_story(story_id)
    resource_uri = URI.parse("http://www.pivotaltracker.com/services/v1/projects/#{@project_id}/stories/#{story_id}")
    response = Net::HTTP.start(resource_uri.host, resource_uri.port) do |http|
      http.delete(resource_uri.path, {'Token' => @token})
    end
  end

  private

    def build_story_xml(story)
      story_xml = "<story>"
      story.each do |key, value|
        story_xml << "<#{key}>#{value.to_s}</#{key}>"
      end
      story_xml << "</story>"
    end
end

Read the rest of this entry »

Posted in Datamapper, Development, Ruby | Comments (0)

datamapper_fu

August 7th, 2008

I have been discussing the potential of developing a datamapper-native attachment_fu port in the #merb and #datamapper channels. The response seems to be in agreement that this type of plugin needs to be developed, as building an ORM agnostic plugin of this type would add user configuration and confusion. Also in the spirit of Merb, “no code is faster than no code”. This may spawn other merb plugins under the _fu tag, but we have to start somewhere! Its also being discussed as to what happens with attachmerb_fu, and it seems to be heading down the AR-native route.

The project has its initial commit and encourage you to come contribute either through testing or actual code.

Posted in Datamapper, Merb | Comments (0)