Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial implementation of Project retrieval

  • Loading branch information...
commit 3d9b34b3a7b12e66da76e9d83c90bf6d20c7bc35 0 parents
@reagent reagent authored
3  .gitignore
@@ -0,0 +1,3 @@
+/pkg/
+/doc/
+/coverage/
66 README.rdoc
@@ -0,0 +1,66 @@
+= Unfuzzle
+
+== Description
+
+The Unfuzzle gem provides an interface to the Unfuddle JSON API
+
+== Installation
+
+ sudo gem install vigetlabs-unfuzzle --source=http://gems.github.com
+
+== Usage
+
+To get started, you'll need your Unfuddle subdomain and a valid username /
+password combination:
+
+ require 'unfuzzle'
+
+ Unfuzzle.subdomain = 'viget'
+ Unfuzzle.username = 'bopbip'
+ Unfuzzle.password = 'bleep'
+
+Once that is configured, you can start accessing data from the API.
+
+=== Projects
+
+Pulling back a list of projects is simple. Based on the currently logged-in
+user, you can see which ones he has access to:
+
+ projects = Unfuzzle::Project.all # => [#<Unfuzzle::Project:0x11e9280 ...> , ...]
+ project = projects.first
+
+There are a few attributes available for a project:
+
+ project.id # => 123
+ project.slug # => "salty"
+ project.name # => "Salty Co."
+ project.archived? # => false
+ project.created_at.strftime('%Y-%m-%d') # => "2008-07-28"
+
+To see a list of additional attributes, take a look at the documentation for
+Unfuzzle::Project.
+
+== License
+
+Copyright (c) 2009 Patrick Reagan of Viget Labs (mailto:patrick.reagan@viget.com)
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
39 Rakefile
@@ -0,0 +1,39 @@
+require 'rubygems'
+require 'rake/gempackagetask'
+require 'rake/testtask'
+
+require 'lib/unfuzzle/version'
+
+task :default => :test
+
+spec = Gem::Specification.new do |s|
+ s.name = 'unfuzzle'
+ s.version = Unfuzzle::Version.to_s
+ s.has_rdoc = true
+ s.extra_rdoc_files = %w(README.rdoc)
+ s.rdoc_options = %w(--main README.rdoc)
+ s.summary = "This gem does ... "
+ s.author = 'First Last'
+ s.email = 'user@example.com'
+ s.homepage = 'http://my-site.net'
+ s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,test}/**/*")
+
+ s.add_dependency('json', '>= 1.1.6')
+end
+
+Rake::GemPackageTask.new(spec) do |pkg|
+ pkg.gem_spec = spec
+end
+
+Rake::TestTask.new do |t|
+ t.libs << 'test'
+ t.test_files = FileList["test/**/*_test.rb"]
+ t.verbose = true
+end
+
+desc 'Generate the gemspec to serve this Gem from Github'
+task :github do
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
+ File.open(file, 'w') {|f| f << spec.to_ruby }
+ puts "Created gemspec: #{file}"
+end
59 lib/unfuzzle.rb
@@ -0,0 +1,59 @@
+$:.unshift File.dirname(__FILE__)
+
+require 'uri'
+require 'net/http'
+require 'json'
+
+require 'unfuzzle/request'
+require 'unfuzzle/response'
+require 'unfuzzle/project'
+
+# = Unfuzzle: A simple wrapper around the Unfuddle JSON API
+#
+# == Quick Start
+#
+# To get started, you need to set the subdomain and a valid username /
+# password combination:
+#
+# require 'rubygems'
+# require 'unfuzzle'
+#
+# Unfuzzle.subdomain = 'viget'
+# Unfuzzle.username = 'bopbip'
+# Unfuzzle.password = 'bleep'
+#
+# From there, you can start accessing a list of projects:
+#
+# Project.all
+#
+module Unfuzzle
+
+ # Set the subdomain for all requests
+ def self.subdomain=(subdomain)
+ @subdomain = subdomain
+ end
+
+ # Set the username for all requests. Data retrieved from the API will be
+ # scoped to the data that this user has access to.
+ def self.username=(username)
+ @username = username
+ end
+
+ # Set the password for the supplied username
+ def self.password=(password)
+ @password = password
+ end
+
+ def self.subdomain # :nodoc:
+ @subdomain
+ end
+
+ def self.username # :nodoc:
+ @username
+ end
+
+ def self.password # :nodoc:
+ @password
+ end
+
+end
59 lib/unfuzzle/project.rb
@@ -0,0 +1,59 @@
+module Unfuzzle
+
+ # = Project
+ #
+ # Represents an Unfuddle project. Has the following attributes:
+ #
+ # [id] The unique identifier for this project
+ # [slug] The "short name" for this project
+ # [name] The name of this project
+ # [description] The description for the project
+ #
+ class Project
+
+ def self.attribute(name, options = {}) # :nodoc:
+ key = options.delete(:from) || name
+
+ class_eval %(
+ def #{name}
+ @response_data['#{key}']
+ end
+ )
+ end
+
+ attribute :id
+ attribute :slug, :from => :short_name
+ attribute :archived
+ attribute :name, :from => :title
+ attribute :description
+ attribute :created_timestamp, :from => :created_at
+ attribute :updated_timestamp, :from => :updated_at
+
+ # Return a list of all projects to which the current user has access
+ def self.all
+ response = Request.get('/projects')
+ response.parse.map {|data| Project.new(data) }
+ end
+
+ # Create a new project from JSON response data
+ def initialize(response_data)
+ @response_data = response_data
+ end
+
+ # Has this project been archived?
+ def archived?
+ archived == true
+ end
+
+ # The DateTime that this project was created
+ def created_at
+ DateTime.parse(created_timestamp)
+ end
+
+ # The DateTime that this project was last updated
+ def updated_at
+ DateTime.parse(updated_timestamp)
+ end
+
+ end
+end
37 lib/unfuzzle/request.rb
@@ -0,0 +1,37 @@
+module Unfuzzle
+
+ # = Request
+ #
+ # A basic wrapper for GET requests to the Unfuddle API
+ #
+ class Request
+
+ # Retrieve a response from the given resource path
+ def self.get(resource_path)
+ request = new(resource_path)
+ request.get
+ end
+
+ # Create a new request for the given resource path
+ def initialize(resource_path)
+ @resource_path = resource_path
+ end
+
+ def endpoint_uri # :nodoc:
+ URI.parse("http://#{Unfuzzle.subdomain}.unfuddle.com/api/v1#{@resource_path}.json")
+ end
+
+ def client # :nodoc:
+ Net::HTTP.new(endpoint_uri.host)
+ end
+
+ # Retrieve a response from the current resource path
+ def get
+ request = Net::HTTP::Get.new(endpoint_uri.path)
+ request.basic_auth Unfuzzle.username, Unfuzzle.password
+
+ Response.new(client.request(request))
+ end
+
+ end
+end
32 lib/unfuzzle/response.rb
@@ -0,0 +1,32 @@
+module Unfuzzle
+
+ # = Response
+ #
+ # A simple wrapper around an HTTP response from the Unfuddle API
+ #
+ class Response
+
+ # Create a new response from an HTTP response object
+ def initialize(http_response)
+ @http_response = http_response
+ end
+
+ # Was there an error produced as part of the request?
+ def error?
+ !@http_response.is_a?(Net::HTTPSuccess)
+ end
+
+ # Raw body of the HTTP response
+ def body
+ @http_response.body
+ end
+
+ # Parsed JSON response body
+ def parse
+ if !error?
+ @parsed_data ||= JSON.parse(body)
+ end
+ end
+
+ end
+end
13 lib/unfuzzle/version.rb
@@ -0,0 +1,13 @@
+module Unfuzzle
+ module Version
+
+ MAJOR = 0
+ MINOR = 1
+ TINY = 0
+
+ def self.to_s # :nodoc:
+ [MAJOR, MINOR, TINY].join('.')
+ end
+
+ end
+end
32 test/fixtures/projects.json
@@ -0,0 +1,32 @@
+[{
+ "archived": false,
+ "short_name": "aa",
+ "created_at": "2008-07-28T16:57:10Z",
+ "title": "A Client",
+ "close_ticket_simultaneously_default": true,
+ "account_id": 12345,
+ "description": "A great project",
+ "enable_time_tracking": true,
+ "default_ticket_report_id": 0,
+ "updated_at": "2009-04-28T18:48:52Z",
+ "id": 1,
+ "theme": "teal",
+ "disk_usage": 1416,
+ "assignee_on_resolve": "none"
+},
+{
+ "archived": true,
+ "short_name": "zz",
+ "created_at": "2008-06-23T21:06:01Z",
+ "title": "Zee Client",
+ "close_ticket_simultaneously_default": false,
+ "account_id": 12345,
+ "description": null,
+ "enable_time_tracking": true,
+ "default_ticket_report_id": null,
+ "updated_at": "2009-05-20T23:15:24Z",
+ "id": 2,
+ "theme": "grey",
+ "disk_usage": 14008,
+ "assignee_on_resolve": "reporter"
+}]
37 test/test_helper.rb
@@ -0,0 +1,37 @@
+# http://sneaq.net/textmate-wtf
+$:.reject! { |e| e.include? 'TextMate' }
+
+require 'rubygems'
+require 'throat_punch'
+
+require File.dirname(__FILE__) + '/../lib/unfuzzle'
+
+
+class Test::Unit::TestCase
+
+ def self.read_fixture(method_name)
+ file = File.dirname(__FILE__) + "/fixtures/#{method_name}.json"
+ JSON.parse(File.read(file))
+ end
+
+ def read_fixture(method_name)
+ self.class.read_fixture(method_name)
+ end
+
+ def self.when_populating(klass, options, &block)
+ # data = options[:from].is_a?(String) ? read_fixture(options[:from])[0] : options[:from].call
+
+ context "with data populated for #{klass}" do
+ setup { @object = klass.new(read_fixture(options[:from])[0]) }
+ merge_block(&block)
+ end
+
+ end
+
+ def self.value_for(method_name, options)
+ should "have a value for :#{method_name}" do
+ @object.send(method_name).should == options[:is]
+ end
+ end
+
+end
76 test/unit/unfuzzle/project_test.rb
@@ -0,0 +1,76 @@
+require File.dirname(__FILE__) + '/../../test_helper'
+
+module Unfuzzle
+ class ProjectTest < Test::Unit::TestCase
+
+ context "The Project class" do
+
+ should "be able to return a list of all projects" do
+ data = read_fixture('projects')
+ response = stub(:parse => data)
+
+ Unfuzzle::Request.expects(:get).with('/projects').returns(response)
+
+ elements = Array.new
+
+ data.each do |node|
+ element = stub()
+ Unfuzzle::Project.expects(:new).with(node).returns(element)
+ elements << element
+ end
+
+ Project.all.should == elements
+ end
+
+ end
+
+ context "An instance of the Project class" do
+
+ when_populating Project, :from => 'projects' do
+
+ value_for :id, :is => 1
+ value_for :archived, :is => false
+ value_for :slug, :is => 'aa'
+ value_for :name, :is => 'A Client'
+ value_for :description, :is => 'A great project'
+ value_for :created_timestamp, :is => '2008-07-28T16:57:10Z'
+ value_for :updated_timestamp, :is => '2009-04-28T18:48:52Z'
+
+ end
+
+ should "know that it's archived" do
+ project = Project.new(stub())
+ project.stubs(:archived).with().returns(true)
+
+ project.archived?.should be(true)
+ end
+
+ should "know that it's not archived" do
+ project = Project.new(stub())
+ project.stubs(:archived).with().returns(false)
+
+ project.archived?.should be(false)
+ end
+
+ should "have a create date" do
+ project = Project.new(stub())
+ project.stubs(:created_timestamp).with().returns('2008-07-28T16:57:10Z')
+
+ DateTime.expects(:parse).with('2008-07-28T16:57:10Z').returns('create_date')
+
+ project.created_at.should == 'create_date'
+ end
+
+ should "have an update date" do
+ project = Project.new(stub())
+ project.stubs(:updated_timestamp).with().returns('2009-04-28T18:48:52Z')
+
+ DateTime.expects(:parse).with('2009-04-28T18:48:52Z').returns('update_date')
+
+ project.updated_at.should == 'update_date'
+ end
+
+ end
+
+ end
+end
63 test/unit/unfuzzle/request_test.rb
@@ -0,0 +1,63 @@
+require File.dirname(__FILE__) + '/../../test_helper'
+
+module Unfuzzle
+ class RequestTest < Test::Unit::TestCase
+
+ context "The Request class" do
+
+ should "be able to perform a GET request" do
+ request = mock() {|r| r.expects(:get).with().returns('response') }
+ Unfuzzle::Request.expects(:new).with('/projects').returns(request)
+
+ Unfuzzle::Request.get('/projects').should == 'response'
+ end
+
+ end
+
+ context "An instance of the Request class" do
+
+ should "have an endpoint URI" do
+ Unfuzzle.stubs(:subdomain).with().returns('viget')
+
+ request = Unfuzzle::Request.new('/projects')
+ request.endpoint_uri.should == URI.parse('http://viget.unfuddle.com/api/v1/projects.json')
+ end
+
+ should "have a client" do
+ client = stub()
+
+ request = Unfuzzle::Request.new('/projects')
+ request.stubs(:endpoint_uri).with().returns(URI.parse('http://example.com'))
+
+ Net::HTTP.expects(:new).with('example.com').returns(client)
+
+ request.client.should == client
+ end
+
+ should "be able to perform a GET request" do
+ Unfuzzle.stubs(:username).with().returns('username')
+ Unfuzzle.stubs(:password).with().returns('password')
+
+ request = Unfuzzle::Request.new('/projects')
+ request.stubs(:endpoint_uri).returns(URI.parse('http://example.com/projects'))
+
+ get_request = mock() do |g|
+ g.expects(:basic_auth).with('username', 'password')
+ end
+
+ client = mock() {|c| c.expects(:request).with(get_request).returns('response') }
+
+ response = stub()
+ Unfuzzle::Response.expects(:new).with('response').returns(response)
+
+ Net::HTTP::Get.expects(:new).with('/projects').returns(get_request)
+
+ request.stubs(:client).with().returns(client)
+
+ request.get.should == response
+ end
+
+ end
+
+ end
+end
65 test/unit/unfuzzle/response_test.rb
@@ -0,0 +1,65 @@
+require File.dirname(__FILE__) + '/../../test_helper'
+
+module Unfuzzle
+ class ResponseTest < Test::Unit::TestCase
+
+ context "An instance of the Response class" do
+
+ should "delegate to the HTTP response to determine the body" do
+ http_response = mock() {|r| r.expects(:body).with().returns('check mah boday') }
+
+ response = Unfuzzle::Response.new(http_response)
+
+ response.body.should == 'check mah boday'
+ end
+
+ should "know that there are no errors" do
+ http_response = mock() do |r|
+ r.expects(:is_a?).with(Net::HTTPSuccess).returns(true)
+ end
+
+ response = Unfuzzle::Response.new(http_response)
+ response.error?.should be(false)
+ end
+
+ should "know if there are errors" do
+ http_response = mock() do |r|
+ r.expects(:is_a?).with(Net::HTTPSuccess).returns(false)
+ end
+
+ response = Unfuzzle::Response.new(http_response)
+ response.error?.should be(true)
+ end
+
+ should "be able to parse the response" do
+ response = Unfuzzle::Response.new(stub())
+ response.expects(:body).with().returns('json')
+ response.stubs(:error?).with().returns(false)
+
+ JSON.expects(:parse).with('json').returns('data')
+
+ response.parse.should == 'data'
+ end
+
+ should "cache the parsed data from the response" do
+ response = Unfuzzle::Response.new(stub())
+ response.stubs(:body).with().returns('json')
+ response.stubs(:error?).with().returns(false)
+
+ JSON.stubs(:parse).with('json').once.returns('data')
+
+ 2.times { response.parse }
+ end
+
+ should "return nil when parsing data if there are errors in the response" do
+ response = Unfuzzle::Response.new(stub())
+ response.expects(:error?).with().returns(true)
+
+ response.parse.should be(nil)
+
+ end
+
+ end
+
+ end
+end
24 test/unit/unfuzzle_test.rb
@@ -0,0 +1,24 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class UnfuzzleTest < Test::Unit::TestCase
+
+ context "The Unfuzzle module" do
+
+ should "be able to set the subdomain" do
+ Unfuzzle.subdomain = 'viget'
+ Unfuzzle.subdomain.should == 'viget'
+ end
+
+ should "be able to set the username" do
+ Unfuzzle.username = 'username'
+ Unfuzzle.username.should == 'username'
+ end
+
+ should "be able to set the password" do
+ Unfuzzle.password = 'password'
+ Unfuzzle.password.should == 'password'
+ end
+
+ end
+
+end
Please sign in to comment.
Something went wrong with that request. Please try again.