vigetlabs-unfuzzle 0.1.0

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,109 @@
1
+ = Unfuzzle
2
+
3
+ == Description
4
+
5
+ The Unfuzzle gem provides an interface to the Unfuddle JSON API
6
+
7
+ == Installation
8
+
9
+ sudo gem install vigetlabs-unfuzzle --source=http://gems.github.com
10
+
11
+ == Usage
12
+
13
+ To get started, you'll need your Unfuddle subdomain and a valid username /
14
+ password combination:
15
+
16
+ require 'unfuzzle'
17
+
18
+ Unfuzzle.subdomain = 'viget'
19
+ Unfuzzle.username = 'bopbip'
20
+ Unfuzzle.password = 'bleep'
21
+
22
+ Once that is configured, you can start accessing data from the API.
23
+
24
+ === Projects
25
+
26
+ Pulling back a list of projects is simple. Based on the currently logged-in
27
+ user, you can see which ones he has access to:
28
+
29
+ projects = Unfuzzle.projects # => [#<Unfuzzle::Project:0x11e9280 ...> , ...]
30
+
31
+ If you don't want all projects, you can find one by its slug (or short name):
32
+
33
+ Unfuzzle.project('salty') # => #<Unfuzzle::Project:0x11e9280 ...>
34
+
35
+ Or by ID:
36
+
37
+ Unfuzzle.project(1) # => #<Unfuzzle::Project:0x11e9280 ...>
38
+
39
+ There are a few attributes available for a project:
40
+
41
+ project = Unfuzzle.projects.first
42
+ project.id # => 123
43
+ project.slug # => "salty"
44
+ project.name # => "Salty Co."
45
+ project.archived? # => false
46
+ project.created_at.strftime('%Y-%m-%d') # => "2008-07-28"
47
+
48
+ To see a list of additional attributes, take a look at the documentation for
49
+ Project.
50
+
51
+ === Milestones
52
+
53
+ Each project can have milestones. You can access these from a single project:
54
+
55
+ project.milestones # => [#<Unfuzzle::Milestone:0x10bdca8 ...>, ...]
56
+
57
+ Milestones have attributes:
58
+
59
+ milestone = project.milestones.first
60
+ milestone.id # => 1
61
+ milestone.name # => "Milestone #1"
62
+ milestone.due_on.strftime('%m/%d/%Y') # => "07/30/2008"
63
+
64
+ A full list is available in the Milestone documentation.
65
+
66
+ === Tickets
67
+
68
+ Tickets exist for a project:
69
+
70
+ ticket = project.tickets.first
71
+ ticket.title # => "Ticket #23"
72
+ ticket.number # => 23
73
+ ticket.description # => "Yo dawg, I hear you like tickets in your project ..."
74
+ ticket.status # => "closed"
75
+
76
+ And can also be associated to a milestone for the project:
77
+
78
+ ticket = project.milestones.first.tickets.first
79
+ ticket.title # => "Ticket #1"
80
+ ticket.number # => 1
81
+ ticket.description # => "Wash my car"
82
+ ticket.status # => "closed"
83
+
84
+ See the Ticket documentation for more information.
85
+
86
+ == License
87
+
88
+ Copyright (c) 2009 Patrick Reagan of Viget Labs (mailto:patrick.reagan@viget.com)
89
+
90
+ Permission is hereby granted, free of charge, to any person
91
+ obtaining a copy of this software and associated documentation
92
+ files (the "Software"), to deal in the Software without
93
+ restriction, including without limitation the rights to use,
94
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
95
+ copies of the Software, and to permit persons to whom the
96
+ Software is furnished to do so, subject to the following
97
+ conditions:
98
+
99
+ The above copyright notice and this permission notice shall be
100
+ included in all copies or substantial portions of the Software.
101
+
102
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
103
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
104
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
105
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
106
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
107
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
108
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
109
+ OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+
5
+ require 'lib/unfuzzle/version'
6
+
7
+ task :default => :test
8
+
9
+ spec = Gem::Specification.new do |s|
10
+ s.name = 'unfuzzle'
11
+ s.version = Unfuzzle::Version.to_s
12
+ s.has_rdoc = true
13
+ s.extra_rdoc_files = %w(README.rdoc)
14
+ s.rdoc_options = %w(--main README.rdoc)
15
+ s.summary = "This gem provides an interface to the Unfuddle JSON API"
16
+ s.author = 'Patrick Reagan'
17
+ s.email = 'patrick.reagan@viget.com'
18
+ s.homepage = 'http://www.viget.com/extend'
19
+ s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,test}/**/*")
20
+
21
+ s.add_dependency('json', '>= 1.1.6')
22
+ end
23
+
24
+ Rake::GemPackageTask.new(spec) do |pkg|
25
+ pkg.gem_spec = spec
26
+ end
27
+
28
+ Rake::TestTask.new do |t|
29
+ t.libs << 'test'
30
+ t.test_files = FileList["test/**/*_test.rb"]
31
+ t.verbose = true
32
+ end
33
+
34
+ desc 'Generate the gemspec to serve this Gem from Github'
35
+ task :github do
36
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
37
+ File.open(file, 'w') {|f| f << spec.to_ruby }
38
+ puts "Created gemspec: #{file}"
39
+ end
@@ -0,0 +1,54 @@
1
+ module Unfuzzle
2
+
3
+ # = Milestone
4
+ #
5
+ # A representation of an Unfuddle Milestone. Has the following attributes:
6
+ #
7
+ # [id] Unique identifier for this milestone
8
+ # [name] Name of the milestone
9
+ #
10
+ class Milestone
11
+
12
+ include Unfuzzle::Model
13
+
14
+ attribute :id
15
+ attribute :project_id
16
+ attribute :archived
17
+ attribute :name, :from => :title
18
+ attribute :created_timestamp, :from => :created_at
19
+ attribute :updated_timestamp, :from => :updated_at
20
+ attribute :due_datestamp, :from => :due_on
21
+
22
+ # Return a list of all milestones for a given project
23
+ def self.find_all_by_project_id(project_id)
24
+ response = Request.get("/projects/#{project_id}/milestones")
25
+ response.data.map {|data| new(data) }
26
+ end
27
+
28
+ # Has this milestone been archived?
29
+ def archived?
30
+ archived == true
31
+ end
32
+
33
+ # The DateTime that this milestone was created
34
+ def created_at
35
+ DateTime.parse(created_timestamp)
36
+ end
37
+
38
+ # The DateTime that this milestone was last updated
39
+ def updated_at
40
+ DateTime.parse(updated_timestamp)
41
+ end
42
+
43
+ # The Date that this milestone is due
44
+ def due_on
45
+ Date.parse(due_datestamp) unless due_datestamp.nil?
46
+ end
47
+
48
+ # The collection of Tickets associated to this milestone
49
+ def tickets
50
+ Ticket.find_all_by_project_id_and_milestone_id(project_id, id)
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,30 @@
1
+ module Unfuzzle
2
+ module Model # :nodoc:
3
+
4
+ module ClassMethods
5
+ def attribute(name, options = {}) # :nodoc:
6
+ key = options.delete(:from) || name
7
+
8
+ class_eval %(
9
+ def #{name}
10
+ @response_data['#{key}']
11
+ end
12
+ )
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+
18
+ def initialize(response_data)
19
+ @response_data = response_data
20
+ end
21
+
22
+ end
23
+
24
+ def self.included(other)
25
+ other.send(:extend, Unfuzzle::Model::ClassMethods)
26
+ other.send(:include, Unfuzzle::Model::InstanceMethods)
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,68 @@
1
+ module Unfuzzle
2
+
3
+ # = Project
4
+ #
5
+ # Represents an Unfuddle project. Has the following attributes:
6
+ #
7
+ # [id] The unique identifier for this project
8
+ # [slug] The "short name" for this project
9
+ # [name] The name of this project
10
+ # [description] The description for the project
11
+ #
12
+ class Project
13
+
14
+ include Unfuzzle::Model
15
+
16
+ attribute :id
17
+ attribute :slug, :from => :short_name
18
+ attribute :archived
19
+ attribute :name, :from => :title
20
+ attribute :description
21
+ attribute :created_timestamp, :from => :created_at
22
+ attribute :updated_timestamp, :from => :updated_at
23
+
24
+ # Return a list of all projects to which the current user has access
25
+ def self.all
26
+ response = Request.get('/projects')
27
+ response.data.map {|data| new(data) }
28
+ end
29
+
30
+ # Find a single project by its slug (short name)
31
+ def self.find_by_slug(slug)
32
+ response = Request.get("/projects/by_short_name/#{slug}")
33
+ new(response.data)
34
+ end
35
+
36
+ # Find a single project by its ID
37
+ def self.find_by_id(id)
38
+ response = Request.get("/projects/#{id}")
39
+ new(response.data)
40
+ end
41
+
42
+ # Has this project been archived?
43
+ def archived?
44
+ archived == true
45
+ end
46
+
47
+ # The DateTime that this project was created
48
+ def created_at
49
+ DateTime.parse(created_timestamp)
50
+ end
51
+
52
+ # The DateTime that this project was last updated
53
+ def updated_at
54
+ DateTime.parse(updated_timestamp)
55
+ end
56
+
57
+ # The collection of Milestones associated to this project
58
+ def milestones
59
+ Milestone.find_all_by_project_id(id)
60
+ end
61
+
62
+ # The collection of Tickets associated to this project
63
+ def tickets
64
+ Ticket.find_all_by_project_id(id)
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,37 @@
1
+ module Unfuzzle
2
+
3
+ # = Request
4
+ #
5
+ # A basic wrapper for GET requests to the Unfuddle API
6
+ #
7
+ class Request
8
+
9
+ # Retrieve a response from the given resource path
10
+ def self.get(resource_path)
11
+ request = new(resource_path)
12
+ request.get
13
+ end
14
+
15
+ # Create a new request for the given resource path
16
+ def initialize(resource_path)
17
+ @resource_path = resource_path
18
+ end
19
+
20
+ def endpoint_uri # :nodoc:
21
+ URI.parse("http://#{Unfuzzle.subdomain}.unfuddle.com/api/v1#{@resource_path}.json")
22
+ end
23
+
24
+ def client # :nodoc:
25
+ Net::HTTP.new(endpoint_uri.host)
26
+ end
27
+
28
+ # Retrieve a response from the current resource path
29
+ def get
30
+ request = Net::HTTP::Get.new(endpoint_uri.path)
31
+ request.basic_auth Unfuzzle.username, Unfuzzle.password
32
+
33
+ Response.new(client.request(request))
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,32 @@
1
+ module Unfuzzle
2
+
3
+ # = Response
4
+ #
5
+ # A simple wrapper around an HTTP response from the Unfuddle API
6
+ #
7
+ class Response
8
+
9
+ # Create a new response from an HTTP response object
10
+ def initialize(http_response)
11
+ @http_response = http_response
12
+ end
13
+
14
+ # Was there an error produced as part of the request?
15
+ def error?
16
+ !@http_response.is_a?(Net::HTTPSuccess)
17
+ end
18
+
19
+ # Raw body of the HTTP response
20
+ def body
21
+ @http_response.body
22
+ end
23
+
24
+ # Parsed JSON response body
25
+ def data
26
+ if !error?
27
+ @parsed_data ||= JSON.parse(body)
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,55 @@
1
+ module Unfuzzle
2
+
3
+ # = Ticket
4
+ #
5
+ # Represents a single Unfuddle Ticket - is associated to a project
6
+ # and optionally a project's milestone. Has the following attributes:
7
+ #
8
+ # [id] The unique identifier for this ticket
9
+ # [number] The ticket's number - this is displayed in the web interface
10
+ # [title] The title of the ticket (short)
11
+ # [description] The full description of the ticket
12
+ # [status] The ticket's status (new / accepted / resolved / closed)
13
+ #
14
+ class Ticket
15
+
16
+ include Unfuzzle::Model
17
+
18
+ attribute :id
19
+ attribute :number
20
+ attribute :title, :from => :summary
21
+ attribute :description
22
+ attribute :due_datestamp, :from => :due_on
23
+ attribute :created_timestamp, :from => :created_at
24
+ attribute :updated_timestamp, :from => :updated_at
25
+ attribute :status
26
+
27
+ # Return a list of all tickets for an individual project
28
+ def self.find_all_by_project_id(project_id)
29
+ response = Request.get("/projects/#{project_id}/tickets")
30
+ response.data.map {|data| new(data) }
31
+ end
32
+
33
+ # Return a list of all tickets for a given milestone as part of a project
34
+ def self.find_all_by_project_id_and_milestone_id(project_id, milestone_id)
35
+ response = Request.get("/projects/#{project_id}/milestones/#{milestone_id}/tickets")
36
+ response.data.map {|data| new(data) }
37
+ end
38
+
39
+ # The DateTime that this milestone was created
40
+ def created_at
41
+ DateTime.parse(created_timestamp)
42
+ end
43
+
44
+ # The DateTime that this milestone was last updated
45
+ def updated_at
46
+ DateTime.parse(updated_timestamp)
47
+ end
48
+
49
+ # The Date that this milestone is due
50
+ def due_on
51
+ Date.parse(due_datestamp) unless due_datestamp.nil?
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,13 @@
1
+ module Unfuzzle
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ TINY = 0
7
+
8
+ def self.to_s # :nodoc:
9
+ [MAJOR, MINOR, TINY].join('.')
10
+ end
11
+
12
+ end
13
+ end
data/lib/unfuzzle.rb ADDED
@@ -0,0 +1,72 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'json'
6
+
7
+ require 'unfuzzle/request'
8
+ require 'unfuzzle/response'
9
+ require 'unfuzzle/model'
10
+ require 'unfuzzle/project'
11
+ require 'unfuzzle/milestone'
12
+ require 'unfuzzle/ticket'
13
+
14
+ # = Unfuzzle: A simple wrapper around the Unfuddle JSON API
15
+ #
16
+ # == Quick Start
17
+ #
18
+ # To get started, you need to set the subdomain and a valid username /
19
+ # password combination:
20
+ #
21
+ # require 'rubygems'
22
+ # require 'unfuzzle'
23
+ #
24
+ # Unfuzzle.subdomain = 'viget'
25
+ # Unfuzzle.username = 'bopbip'
26
+ # Unfuzzle.password = 'bleep'
27
+ #
28
+ # From there, you can start accessing a list of projects:
29
+ #
30
+ # Project.all
31
+ #
32
+ module Unfuzzle
33
+
34
+ # Set the subdomain for all requests
35
+ def self.subdomain=(subdomain)
36
+ @subdomain = subdomain
37
+ end
38
+
39
+ # Set the username for all requests. Data retrieved from the API will be
40
+ # scoped to the data that this user has access to.
41
+ def self.username=(username)
42
+ @username = username
43
+ end
44
+
45
+ # Set the password for the supplied username
46
+ def self.password=(password)
47
+ @password = password
48
+ end
49
+
50
+ # Retrieve a project for the current user by its ID or slug
51
+ def self.project(id_or_slug)
52
+ id_or_slug.is_a?(String) ? Project.find_by_slug(id_or_slug) : Project.find_by_id(id_or_slug)
53
+ end
54
+
55
+ # Get a list of all projects for this user
56
+ def self.projects
57
+ Project.all
58
+ end
59
+
60
+ def self.subdomain # :nodoc:
61
+ @subdomain
62
+ end
63
+
64
+ def self.username # :nodoc:
65
+ @username
66
+ end
67
+
68
+ def self.password # :nodoc:
69
+ @password
70
+ end
71
+
72
+ end
@@ -0,0 +1,22 @@
1
+ [{
2
+ "archived": false,
3
+ "created_at": "2008-07-30T22:12:37Z",
4
+ "completed": true,
5
+ "title": "Milestone 1",
6
+ "person_responsible_id": 12345,
7
+ "due_on": "2008-07-30",
8
+ "updated_at": "2008-12-26T22:32:03Z",
9
+ "id": 1,
10
+ "project_id": 1
11
+ },
12
+ {
13
+ "archived": false,
14
+ "created_at": "2009-02-09T15:15:30Z",
15
+ "completed": true,
16
+ "title": "Milestone 2",
17
+ "person_responsible_id": 12344,
18
+ "due_on": "2009-02-13",
19
+ "updated_at": "2009-02-17T15:37:01Z",
20
+ "id": 2,
21
+ "project_id": 1
22
+ }]
@@ -0,0 +1,16 @@
1
+ {
2
+ "archived": false,
3
+ "short_name": "blip",
4
+ "created_at": "2008-07-28T16:57:10Z",
5
+ "title": "Blip Bleep Co.",
6
+ "close_ticket_simultaneously_default": true,
7
+ "account_id": 1,
8
+ "description": "This is the project for Blip Bleep Co.",
9
+ "enable_time_tracking": true,
10
+ "default_ticket_report_id": 0,
11
+ "updated_at": "2009-04-28T18:48:52Z",
12
+ "id": 1,
13
+ "theme": "teal",
14
+ "disk_usage": 1416,
15
+ "assignee_on_resolve": "none"
16
+ }
@@ -0,0 +1,32 @@
1
+ [{
2
+ "archived": false,
3
+ "short_name": "aa",
4
+ "created_at": "2008-07-28T16:57:10Z",
5
+ "title": "A Client",
6
+ "close_ticket_simultaneously_default": true,
7
+ "account_id": 12345,
8
+ "description": "A great project",
9
+ "enable_time_tracking": true,
10
+ "default_ticket_report_id": 0,
11
+ "updated_at": "2009-04-28T18:48:52Z",
12
+ "id": 1,
13
+ "theme": "teal",
14
+ "disk_usage": 1416,
15
+ "assignee_on_resolve": "none"
16
+ },
17
+ {
18
+ "archived": true,
19
+ "short_name": "zz",
20
+ "created_at": "2008-06-23T21:06:01Z",
21
+ "title": "Zee Client",
22
+ "close_ticket_simultaneously_default": false,
23
+ "account_id": 12345,
24
+ "description": null,
25
+ "enable_time_tracking": true,
26
+ "default_ticket_report_id": null,
27
+ "updated_at": "2009-05-20T23:15:24Z",
28
+ "id": 2,
29
+ "theme": "grey",
30
+ "disk_usage": 14008,
31
+ "assignee_on_resolve": "reporter"
32
+ }]
@@ -0,0 +1,44 @@
1
+ [{
2
+ "severity_id": null,
3
+ "hours_estimate_current": 0.0,
4
+ "resolution_description": "",
5
+ "reporter_id": 1,
6
+ "created_at": "2008-11-25T14:00:19Z",
7
+ "priority": "4",
8
+ "number": 1,
9
+ "milestone_id": 1,
10
+ "description": "Do something important",
11
+ "assignee_id": null,
12
+ "due_on": null,
13
+ "status": "closed",
14
+ "summary": "Ticket #1",
15
+ "resolution": "fixed",
16
+ "component_id": null,
17
+ "version_id": null,
18
+ "updated_at": "2008-12-31T15:51:41Z",
19
+ "id": 1,
20
+ "project_id": 1,
21
+ "hours_estimate_initial": 1.75
22
+ },
23
+ {
24
+ "severity_id": null,
25
+ "hours_estimate_current": 0.25,
26
+ "resolution_description": "",
27
+ "reporter_id": 1,
28
+ "created_at": "2009-02-09T15:17:52Z",
29
+ "priority": "3",
30
+ "number": 2,
31
+ "milestone_id": 1,
32
+ "description": "Do something else important",
33
+ "assignee_id": null,
34
+ "due_on": null,
35
+ "status": "closed",
36
+ "summary": "Ticket #2",
37
+ "resolution": "fixed",
38
+ "component_id": null,
39
+ "version_id": null,
40
+ "updated_at": "2009-02-17T15:36:40Z",
41
+ "id": 2,
42
+ "project_id": 1,
43
+ "hours_estimate_initial": 0.0
44
+ }]
@@ -0,0 +1,49 @@
1
+ # http://sneaq.net/textmate-wtf
2
+ $:.reject! { |e| e.include? 'TextMate' }
3
+
4
+ require 'rubygems'
5
+ require 'throat_punch'
6
+
7
+ require File.dirname(__FILE__) + '/../lib/unfuzzle'
8
+
9
+
10
+ class Test::Unit::TestCase
11
+
12
+ def self.read_fixture(method_name)
13
+ file = File.dirname(__FILE__) + "/fixtures/#{method_name}.json"
14
+ JSON.parse(File.read(file))
15
+ end
16
+
17
+ def read_fixture(method_name)
18
+ self.class.read_fixture(method_name)
19
+ end
20
+
21
+ def mock_request_cycle(options)
22
+ response = Unfuzzle::Response.new(stub())
23
+
24
+ data = read_fixture(options[:data])
25
+
26
+ response.stubs(:data).with().returns(data)
27
+
28
+ Unfuzzle::Request.stubs(:get).with(options[:for]).returns(response)
29
+
30
+ response
31
+ end
32
+
33
+ def self.when_populating(klass, options, &block)
34
+ # data = options[:from].is_a?(String) ? read_fixture(options[:from])[0] : options[:from].call
35
+
36
+ context "with data populated for #{klass}" do
37
+ setup { @object = klass.new(read_fixture(options[:from])[0]) }
38
+ merge_block(&block)
39
+ end
40
+
41
+ end
42
+
43
+ def self.value_for(method_name, options)
44
+ should "have a value for :#{method_name}" do
45
+ @object.send(method_name).should == options[:is]
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,97 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+
3
+ module Unfuzzle
4
+ class MilestoneTest < Test::Unit::TestCase
5
+
6
+ context "The Milestone class" do
7
+
8
+ should "be able to find all by project ID" do
9
+ project_id = 1
10
+
11
+ response = mock_request_cycle :for => "/projects/#{project_id}/milestones", :data => 'milestones'
12
+
13
+ milestones = Array.new
14
+
15
+ response.data.each do |data|
16
+ milestone = stub()
17
+ Unfuzzle::Milestone.expects(:new).with(data).returns(milestone)
18
+ milestones << milestone
19
+ end
20
+
21
+ Milestone.find_all_by_project_id(1).should == milestones
22
+ end
23
+
24
+ end
25
+
26
+ context "An instance of the Milestone class" do
27
+
28
+ when_populating Milestone, :from => 'milestones' do
29
+
30
+ value_for :id, :is => 1
31
+ value_for :project_id, :is => 1
32
+ value_for :archived, :is => false
33
+ value_for :created_timestamp, :is => '2008-07-30T22:12:37Z'
34
+ value_for :name, :is => 'Milestone 1'
35
+ value_for :due_datestamp, :is => '2008-07-30'
36
+ value_for :updated_timestamp, :is => '2008-12-26T22:32:03Z'
37
+
38
+ end
39
+
40
+ context "with a new instance" do
41
+
42
+ setup { @milestone = Milestone.new(stub()) }
43
+
44
+ should "know that it is archived" do
45
+ @milestone.stubs(:archived).with().returns(true)
46
+ @milestone.archived?.should be(true)
47
+ end
48
+
49
+ should "know that it isn't archived" do
50
+ @milestone.stubs(:archived).with().returns(false)
51
+ @milestone.archived?.should be(false)
52
+ end
53
+
54
+ should "have a create date/time" do
55
+ DateTime.expects(:parse).with('2008-07-28T16:57:10Z').returns('create_date')
56
+
57
+ @milestone.stubs(:created_timestamp).with().returns('2008-07-28T16:57:10Z')
58
+ @milestone.created_at.should == 'create_date'
59
+ end
60
+
61
+ should "have an update date/time" do
62
+ DateTime.expects(:parse).with('2009-04-28T18:48:52Z').returns('update_date')
63
+
64
+ @milestone.stubs(:updated_timestamp).with().returns('2009-04-28T18:48:52Z')
65
+ @milestone.updated_at.should == 'update_date'
66
+ end
67
+
68
+ should "have a due date" do
69
+ Date.expects(:parse).with('2008-07-30').returns('due_date')
70
+
71
+ @milestone.stubs(:due_datestamp).with().returns('2008-07-30')
72
+ @milestone.due_on.should == 'due_date'
73
+ end
74
+
75
+ should "not have a due date if there isn't one associated" do
76
+ @milestone.stubs(:due_datestamp).with().returns(nil)
77
+ @milestone.due_on.should be(nil)
78
+ end
79
+
80
+ should "have associated tickets" do
81
+ id = 1
82
+ project_id = 1
83
+
84
+ Ticket.expects(:find_all_by_project_id_and_milestone_id).with(project_id, id).returns('tickets')
85
+
86
+ @milestone.stubs(:id).with().returns(id)
87
+ @milestone.stubs(:project_id).with().returns(project_id)
88
+
89
+ @milestone.tickets.should == 'tickets'
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,107 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+
3
+ module Unfuzzle
4
+ class ProjectTest < Test::Unit::TestCase
5
+
6
+ context "The Project class" do
7
+
8
+ should "be able to return a list of all projects" do
9
+ response = mock_request_cycle :for => '/projects', :data => 'projects'
10
+
11
+ elements = Array.new
12
+
13
+ response.data.each do |data|
14
+ element = stub()
15
+ Unfuzzle::Project.expects(:new).with(data).returns(element)
16
+ elements << element
17
+ end
18
+
19
+ Project.all.should == elements
20
+ end
21
+
22
+ should "be able to find a project by its slug" do
23
+ slug = 'blip'
24
+
25
+ response = mock_request_cycle :for => "/projects/by_short_name/#{slug}", :data => 'project'
26
+
27
+ Unfuzzle::Project.expects(:new).with(response.data).returns('project')
28
+
29
+ Project.find_by_slug(slug).should == 'project'
30
+ end
31
+
32
+ should "be able to find a project by its ID" do
33
+ id = 1
34
+
35
+ response = mock_request_cycle :for => "/projects/#{id}", :data => 'project'
36
+
37
+ Unfuzzle::Project.expects(:new).with(response.data).returns('project')
38
+
39
+ Project.find_by_id(id).should == 'project'
40
+ end
41
+
42
+
43
+ end
44
+
45
+ context "An instance of the Project class" do
46
+
47
+ when_populating Project, :from => 'projects' do
48
+
49
+ value_for :id, :is => 1
50
+ value_for :archived, :is => false
51
+ value_for :slug, :is => 'aa'
52
+ value_for :name, :is => 'A Client'
53
+ value_for :description, :is => 'A great project'
54
+ value_for :created_timestamp, :is => '2008-07-28T16:57:10Z'
55
+ value_for :updated_timestamp, :is => '2009-04-28T18:48:52Z'
56
+
57
+ end
58
+
59
+ context "with a new instance" do
60
+
61
+ setup { @project = Project.new(stub()) }
62
+
63
+ should "know that it's archived" do
64
+ @project.stubs(:archived).with().returns(true)
65
+ @project.archived?.should be(true)
66
+ end
67
+
68
+ should "know that it's not archived" do
69
+ @project.stubs(:archived).with().returns(false)
70
+ @project.archived?.should be(false)
71
+ end
72
+
73
+ should "have a create date" do
74
+ DateTime.expects(:parse).with('2008-07-28T16:57:10Z').returns('create_date')
75
+
76
+ @project.stubs(:created_timestamp).with().returns('2008-07-28T16:57:10Z')
77
+ @project.created_at.should == 'create_date'
78
+ end
79
+
80
+ should "have an update date" do
81
+ DateTime.expects(:parse).with('2009-04-28T18:48:52Z').returns('update_date')
82
+
83
+ @project.stubs(:updated_timestamp).with().returns('2009-04-28T18:48:52Z')
84
+ @project.updated_at.should == 'update_date'
85
+ end
86
+
87
+ should "have a list of associated milestones" do
88
+ id = 1
89
+ Milestone.expects(:find_all_by_project_id).with(id).returns('milestones')
90
+
91
+ @project.stubs(:id).with().returns(id)
92
+ @project.milestones.should == 'milestones'
93
+ end
94
+
95
+ should "have a list of associated tickets" do
96
+ id = 1
97
+ Ticket.expects(:find_all_by_project_id).with(id).returns('tickets')
98
+
99
+ @project.stubs(:id).with().returns(id)
100
+ @project.tickets.should == 'tickets'
101
+ end
102
+
103
+ end
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+
3
+ module Unfuzzle
4
+ class RequestTest < Test::Unit::TestCase
5
+
6
+ context "The Request class" do
7
+
8
+ should "be able to perform a GET request" do
9
+ request = mock() {|r| r.expects(:get).with().returns('response') }
10
+ Unfuzzle::Request.expects(:new).with('/projects').returns(request)
11
+
12
+ Unfuzzle::Request.get('/projects').should == 'response'
13
+ end
14
+
15
+ end
16
+
17
+ context "An instance of the Request class" do
18
+
19
+ should "have an endpoint URI" do
20
+ Unfuzzle.stubs(:subdomain).with().returns('viget')
21
+
22
+ request = Unfuzzle::Request.new('/projects')
23
+ request.endpoint_uri.should == URI.parse('http://viget.unfuddle.com/api/v1/projects.json')
24
+ end
25
+
26
+ should "have a client" do
27
+ client = stub()
28
+
29
+ request = Unfuzzle::Request.new('/projects')
30
+ request.stubs(:endpoint_uri).with().returns(URI.parse('http://example.com'))
31
+
32
+ Net::HTTP.expects(:new).with('example.com').returns(client)
33
+
34
+ request.client.should == client
35
+ end
36
+
37
+ should "be able to perform a GET request" do
38
+ Unfuzzle.stubs(:username).with().returns('username')
39
+ Unfuzzle.stubs(:password).with().returns('password')
40
+
41
+ request = Unfuzzle::Request.new('/projects')
42
+ request.stubs(:endpoint_uri).returns(URI.parse('http://example.com/projects'))
43
+
44
+ get_request = mock() do |g|
45
+ g.expects(:basic_auth).with('username', 'password')
46
+ end
47
+
48
+ client = mock() {|c| c.expects(:request).with(get_request).returns('response') }
49
+
50
+ response = stub()
51
+ Unfuzzle::Response.expects(:new).with('response').returns(response)
52
+
53
+ Net::HTTP::Get.expects(:new).with('/projects').returns(get_request)
54
+
55
+ request.stubs(:client).with().returns(client)
56
+
57
+ request.get.should == response
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,65 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+
3
+ module Unfuzzle
4
+ class ResponseTest < Test::Unit::TestCase
5
+
6
+ context "An instance of the Response class" do
7
+
8
+ should "delegate to the HTTP response to determine the body" do
9
+ http_response = mock() {|r| r.expects(:body).with().returns('check mah boday') }
10
+
11
+ response = Unfuzzle::Response.new(http_response)
12
+
13
+ response.body.should == 'check mah boday'
14
+ end
15
+
16
+ should "know that there are no errors" do
17
+ http_response = mock() do |r|
18
+ r.expects(:is_a?).with(Net::HTTPSuccess).returns(true)
19
+ end
20
+
21
+ response = Unfuzzle::Response.new(http_response)
22
+ response.error?.should be(false)
23
+ end
24
+
25
+ should "know if there are errors" do
26
+ http_response = mock() do |r|
27
+ r.expects(:is_a?).with(Net::HTTPSuccess).returns(false)
28
+ end
29
+
30
+ response = Unfuzzle::Response.new(http_response)
31
+ response.error?.should be(true)
32
+ end
33
+
34
+ should "be able to parse the response" do
35
+ response = Unfuzzle::Response.new(stub())
36
+ response.expects(:body).with().returns('json')
37
+ response.stubs(:error?).with().returns(false)
38
+
39
+ JSON.expects(:parse).with('json').returns('data')
40
+
41
+ response.data.should == 'data'
42
+ end
43
+
44
+ should "cache the parsed data from the response" do
45
+ response = Unfuzzle::Response.new(stub())
46
+ response.stubs(:body).with().returns('json')
47
+ response.stubs(:error?).with().returns(false)
48
+
49
+ JSON.stubs(:parse).with('json').once.returns('data')
50
+
51
+ 2.times { response.data }
52
+ end
53
+
54
+ should "return nil when parsing data if there are errors in the response" do
55
+ response = Unfuzzle::Response.new(stub())
56
+ response.expects(:error?).with().returns(true)
57
+
58
+ response.data.should be(nil)
59
+
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,92 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+
3
+ module Unfuzzle
4
+ class TicketTest < Test::Unit::TestCase
5
+
6
+ context "The Ticket class" do
7
+
8
+ should "be able to return a list of tickets for a project" do
9
+ project_id = 1
10
+
11
+ response = mock_request_cycle :for => "/projects/#{project_id}/tickets", :data => 'tickets'
12
+
13
+ tickets = []
14
+
15
+ response.data.each do |data|
16
+ ticket = stub()
17
+ Ticket.expects(:new).with(data).returns(ticket)
18
+ tickets << ticket
19
+ end
20
+
21
+ Ticket.find_all_by_project_id(project_id).should == tickets
22
+ end
23
+
24
+ should "be able to return a list of tickets for a milestone" do
25
+ milestone_id = 1
26
+ project_id = 1
27
+
28
+ response = mock_request_cycle :for => "/projects/#{project_id}/milestones/#{milestone_id}/tickets", :data => 'tickets'
29
+
30
+ tickets = []
31
+
32
+ response.data.each do |data|
33
+ ticket = stub()
34
+ Ticket.expects(:new).with(data).returns(ticket)
35
+ tickets << ticket
36
+ end
37
+
38
+ Ticket.find_all_by_project_id_and_milestone_id(project_id, milestone_id).should == tickets
39
+ end
40
+
41
+ end
42
+
43
+ context "An instance of the Ticket class" do
44
+
45
+ when_populating Ticket, :from => 'tickets' do
46
+
47
+ value_for :id, :is => 1
48
+ value_for :created_timestamp, :is => '2008-11-25T14:00:19Z'
49
+ value_for :updated_timestamp, :is => '2008-12-31T15:51:41Z'
50
+ value_for :number, :is => 1
51
+ value_for :title, :is => 'Ticket #1'
52
+ value_for :description, :is => 'Do something important'
53
+ value_for :due_datestamp, :is => nil
54
+ value_for :status, :is => 'closed'
55
+
56
+ end
57
+
58
+ context "with a new instance" do
59
+ setup { @ticket = Ticket.new(stub()) }
60
+
61
+ should "have a create date/time" do
62
+ DateTime.expects(:parse).with('2008-07-28T16:57:10Z').returns('create_date')
63
+
64
+ @ticket.stubs(:created_timestamp).with().returns('2008-07-28T16:57:10Z')
65
+ @ticket.created_at.should == 'create_date'
66
+ end
67
+
68
+ should "have an update date/time" do
69
+ DateTime.expects(:parse).with('2009-04-28T18:48:52Z').returns('update_date')
70
+
71
+ @ticket.stubs(:updated_timestamp).with().returns('2009-04-28T18:48:52Z')
72
+ @ticket.updated_at.should == 'update_date'
73
+ end
74
+
75
+ should "have a due date" do
76
+ Date.expects(:parse).with('2008-07-30').returns('due_date')
77
+
78
+ @ticket.stubs(:due_datestamp).with().returns('2008-07-30')
79
+ @ticket.due_on.should == 'due_date'
80
+ end
81
+
82
+ should "not have a due date if there isn't one associated" do
83
+ @ticket.stubs(:due_datestamp).with().returns(nil)
84
+ @ticket.due_on.should be(nil)
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class UnfuzzleTest < Test::Unit::TestCase
4
+
5
+ context "The Unfuzzle module" do
6
+
7
+ should "be able to set the subdomain" do
8
+ Unfuzzle.subdomain = 'viget'
9
+ Unfuzzle.subdomain.should == 'viget'
10
+ end
11
+
12
+ should "be able to set the username" do
13
+ Unfuzzle.username = 'username'
14
+ Unfuzzle.username.should == 'username'
15
+ end
16
+
17
+ should "be able to set the password" do
18
+ Unfuzzle.password = 'password'
19
+ Unfuzzle.password.should == 'password'
20
+ end
21
+
22
+ should "be able to retrieve a project by id" do
23
+ Unfuzzle::Project.expects(:find_by_id).with(1).returns('project')
24
+ Unfuzzle.project(1).should == 'project'
25
+ end
26
+
27
+ should "be able to retrieve a project by slug" do
28
+ Unfuzzle::Project.expects(:find_by_slug).with('slug').returns('project')
29
+ Unfuzzle.project('slug').should == 'project'
30
+ end
31
+
32
+ should "be able to retrieve a list of all projects for the current user" do
33
+ Unfuzzle::Project.expects(:all).with().returns('projects')
34
+ Unfuzzle.projects.should == 'projects'
35
+ end
36
+
37
+ end
38
+
39
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vigetlabs-unfuzzle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Patrick Reagan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-24 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.6
24
+ version:
25
+ description:
26
+ email: patrick.reagan@viget.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.rdoc
33
+ files:
34
+ - README.rdoc
35
+ - Rakefile
36
+ - lib/unfuzzle
37
+ - lib/unfuzzle/milestone.rb
38
+ - lib/unfuzzle/model.rb
39
+ - lib/unfuzzle/project.rb
40
+ - lib/unfuzzle/request.rb
41
+ - lib/unfuzzle/response.rb
42
+ - lib/unfuzzle/ticket.rb
43
+ - lib/unfuzzle/version.rb
44
+ - lib/unfuzzle.rb
45
+ - test/fixtures
46
+ - test/fixtures/milestones.json
47
+ - test/fixtures/project.json
48
+ - test/fixtures/projects.json
49
+ - test/fixtures/tickets.json
50
+ - test/test_helper.rb
51
+ - test/unit
52
+ - test/unit/unfuzzle
53
+ - test/unit/unfuzzle/milestone_test.rb
54
+ - test/unit/unfuzzle/project_test.rb
55
+ - test/unit/unfuzzle/request_test.rb
56
+ - test/unit/unfuzzle/response_test.rb
57
+ - test/unit/unfuzzle/ticket_test.rb
58
+ - test/unit/unfuzzle_test.rb
59
+ has_rdoc: true
60
+ homepage: http://www.viget.com/extend
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --main
64
+ - README.rdoc
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.2.0
83
+ signing_key:
84
+ specification_version: 2
85
+ summary: This gem provides an interface to the Unfuddle JSON API
86
+ test_files: []
87
+