vigetlabs-unfuzzle 0.1.1 → 0.1.2

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.
Files changed (39) hide show
  1. data/README.rdoc +21 -1
  2. data/Rakefile +2 -2
  3. data/lib/unfuzzle.rb +13 -2
  4. data/lib/unfuzzle/component.rb +31 -0
  5. data/lib/unfuzzle/milestone.rb +17 -26
  6. data/lib/unfuzzle/priority.rb +30 -0
  7. data/lib/unfuzzle/project.rb +13 -20
  8. data/lib/unfuzzle/request.rb +5 -8
  9. data/lib/unfuzzle/response.rb +0 -7
  10. data/lib/unfuzzle/severity.rb +31 -0
  11. data/lib/unfuzzle/ticket.rb +58 -25
  12. data/lib/unfuzzle/version.rb +1 -1
  13. data/test/fixtures/component.xml +8 -0
  14. data/test/fixtures/components.xml +17 -0
  15. data/test/fixtures/milestone.xml +12 -0
  16. data/test/fixtures/milestones.xml +25 -0
  17. data/test/fixtures/project.xml +17 -0
  18. data/test/fixtures/projects.xml +35 -0
  19. data/test/fixtures/severities.xml +24 -0
  20. data/test/fixtures/severity.xml +8 -0
  21. data/test/fixtures/ticket.xml +25 -0
  22. data/test/fixtures/tickets.xml +51 -0
  23. data/test/test_helper.rb +5 -7
  24. data/test/unit/unfuzzle/component_test.rb +36 -0
  25. data/test/unit/unfuzzle/milestone_test.rb +12 -44
  26. data/test/unit/unfuzzle/priority_test.rb +25 -0
  27. data/test/unit/unfuzzle/project_test.rb +17 -37
  28. data/test/unit/unfuzzle/request_test.rb +3 -13
  29. data/test/unit/unfuzzle/response_test.rb +0 -28
  30. data/test/unit/unfuzzle/severity_test.rb +36 -0
  31. data/test/unit/unfuzzle/ticket_test.rb +108 -47
  32. metadata +21 -12
  33. data/lib/unfuzzle/model.rb +0 -56
  34. data/test/fixtures/milestone.json +0 -11
  35. data/test/fixtures/milestones.json +0 -22
  36. data/test/fixtures/project.json +0 -16
  37. data/test/fixtures/projects.json +0 -32
  38. data/test/fixtures/tickets.json +0 -44
  39. data/test/unit/unfuzzle/model_test.rb +0 -55
data/README.rdoc CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  == Description
4
4
 
5
- The Unfuzzle gem provides an interface to the Unfuddle JSON API
5
+ The Unfuzzle gem provides an interface to the Unfuddle XML API
6
6
 
7
7
  == Installation
8
8
 
@@ -81,8 +81,28 @@ And can also be associated to a milestone for the project:
81
81
  ticket.description # => "Wash my car"
82
82
  ticket.status # => "closed"
83
83
 
84
+ Unfuddle has additional associations for a ticket, including component, severity,
85
+ and priority:
86
+
87
+ ticket.component # => #<Unfuzzle::Component:0x12c5b54 ...>
88
+ ticket.component_name # => "User Accounts"
89
+ ticket.severity # => #<Unfuzzle::Severity:0x12a357c ...>
90
+ ticket.severity_name # => "Development"
91
+ ticket.priority # => #<Unfuzzle::Priority:0x12811fc @id=3>
92
+ ticket.priority_name # => "Normal"
93
+
84
94
  See the Ticket documentation for more information.
85
95
 
96
+ == Updating
97
+
98
+ Currently, only ticket updating is supported and only for a subset of the data:
99
+
100
+ ticket.title = 'This is a new title' # => "This is a new title"
101
+ ticket.update # => #<Unfuzzle::Response:0x1275280 ...>
102
+
103
+ This will update the title of the ticket. Other fields that can be updated include
104
+ description and status.
105
+
86
106
  == License
87
107
 
88
108
  Copyright (c) 2009 Patrick Reagan of Viget Labs (mailto:patrick.reagan@viget.com)
data/Rakefile CHANGED
@@ -12,13 +12,13 @@ spec = Gem::Specification.new do |s|
12
12
  s.has_rdoc = true
13
13
  s.extra_rdoc_files = %w(README.rdoc)
14
14
  s.rdoc_options = %w(--main README.rdoc)
15
- s.summary = "This gem provides an interface to the Unfuddle JSON API"
15
+ s.summary = "This gem provides an interface to the Unfuddle XML API"
16
16
  s.author = 'Patrick Reagan'
17
17
  s.email = 'patrick.reagan@viget.com'
18
18
  s.homepage = 'http://www.viget.com/extend'
19
19
  s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,test}/**/*")
20
20
 
21
- s.add_dependency('json', '>= 1.1.6')
21
+ s.add_dependency('graft', '>= 0.1.1')
22
22
  end
23
23
 
24
24
  Rake::GemPackageTask.new(spec) do |pkg|
data/lib/unfuzzle.rb CHANGED
@@ -4,13 +4,16 @@ require 'uri'
4
4
  require 'net/http'
5
5
  require 'json'
6
6
  require 'builder'
7
+ require 'graft'
7
8
 
8
9
  require 'unfuzzle/request'
9
10
  require 'unfuzzle/response'
10
- require 'unfuzzle/model'
11
11
  require 'unfuzzle/project'
12
12
  require 'unfuzzle/milestone'
13
13
  require 'unfuzzle/ticket'
14
+ require 'unfuzzle/severity'
15
+ require 'unfuzzle/priority'
16
+ require 'unfuzzle/component'
14
17
 
15
18
  # = Unfuzzle: A simple wrapper around the Unfuddle JSON API
16
19
  #
@@ -28,7 +31,15 @@ require 'unfuzzle/ticket'
28
31
  #
29
32
  # From there, you can start accessing a list of projects:
30
33
  #
31
- # Project.all
34
+ # >> Unfuzzle.projects
35
+ # => [#<Unfuzzle::Project:0x5f5c44 @id=1, @name="BlipCo", ...>, ... ]
36
+ #
37
+ # Or a specific project by its 'short name':
38
+ #
39
+ # >> Unfuzzle.project('sample')
40
+ # => #<Unfuzzle::Project:0x123f888 @id=2, @name="Sample Project", ... >
41
+ #
42
+ # For more usage documentation, see README.doc.
32
43
  #
33
44
  module Unfuzzle
34
45
 
@@ -0,0 +1,31 @@
1
+ module Unfuzzle
2
+
3
+ # = Component
4
+ #
5
+ # Represents a Component in an Unfuddle project. These are user-configurable
6
+ # and are custom for each project you have. Examples include 'Administration',
7
+ # 'User Registration', etc.. A component has the following attributes:
8
+ #
9
+ # [id] The unique id for this component
10
+ # [name] The name of this component (e.g User Registration)
11
+ # [created_at] The date/time that this component was created
12
+ # [updated_at] The date/time that this component was last updated
13
+ #
14
+ class Component
15
+
16
+ include Graft::Model
17
+
18
+ attribute :id, :type => :integer
19
+ attribute :name
20
+ attribute :project_id, :from => 'project-id', :type => :integer
21
+ attribute :created_at, :from => 'created-at', :type => :time
22
+ attribute :updated_at, :from => 'updated-at', :type => :time
23
+
24
+ # Find a component by ID for a given project
25
+ def self.find_by_project_id_and_component_id(project_id, component_id)
26
+ response = Request.get("/projects/#{project_id}/components/#{component_id}")
27
+ new response.body
28
+ end
29
+
30
+ end
31
+ end
@@ -6,28 +6,33 @@ module Unfuzzle
6
6
  #
7
7
  # [id] Unique identifier for this milestone
8
8
  # [name] Name of the milestone
9
- #
9
+ # [archived] The archived status of this milestone (see Milestone#archived?)
10
+ # [due_on] The due date for this milestone
11
+ # [created_at] The date/time that this milestone was created
12
+ # [updated_at] The date/time that this milestone was last updated
13
+ #
10
14
  class Milestone
11
15
 
12
- include Unfuzzle::Model
16
+ include Graft::Model
13
17
 
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
18
+ attribute :id, :type => :integer
19
+ attribute :project_id, :from => 'project-id', :type => :integer
20
+ attribute :archived, :type => :boolean
21
+ attribute :name, :from => 'title'
22
+ attribute :created_at, :from => 'created-at', :type => :time
23
+ attribute :updated_at, :from => 'updated-at', :type => :time
24
+ attribute :due_on, :from => 'due-on', :type => :date
21
25
 
22
26
  # Return a list of all milestones for a given project
23
27
  def self.find_all_by_project_id(project_id)
24
28
  response = Request.get("/projects/#{project_id}/milestones")
25
- response.data.map {|data| new(data) }
29
+ collection_from(response.body, 'milestones/milestone')
26
30
  end
27
31
 
32
+ # Find a milestone by ID for a given project
28
33
  def self.find_by_project_id_and_milestone_id(project_id, milestone_id)
29
34
  response = Request.get("/projects/#{project_id}/milestones/#{milestone_id}")
30
- new response.data
35
+ new response.body
31
36
  end
32
37
 
33
38
  # Has this milestone been archived?
@@ -35,21 +40,7 @@ module Unfuzzle
35
40
  archived == true
36
41
  end
37
42
 
38
- # The DateTime that this milestone was created
39
- def created_at
40
- DateTime.parse(created_timestamp)
41
- end
42
-
43
- # The DateTime that this milestone was last updated
44
- def updated_at
45
- DateTime.parse(updated_timestamp)
46
- end
47
-
48
- # The Date that this milestone is due
49
- def due_on
50
- Date.parse(due_datestamp) unless due_datestamp.nil?
51
- end
52
-
43
+ # Does this milestone occur in the past?
53
44
  def past?
54
45
  due_on < Date.today
55
46
  end
@@ -0,0 +1,30 @@
1
+ module Unfuzzle
2
+
3
+ # = Priority
4
+ #
5
+ # Represents a priority for a ticket.
6
+ #
7
+ class Priority
8
+
9
+ def initialize(id)
10
+ @id = id
11
+ end
12
+
13
+ # The name of the priority based on the supplied ID
14
+ def name
15
+ mapping[@id]
16
+ end
17
+
18
+ private
19
+ def mapping
20
+ {
21
+ 5 => 'Highest',
22
+ 4 => 'High',
23
+ 3 => 'Normal',
24
+ 2 => 'Low',
25
+ 1 => 'Lowest'
26
+ }
27
+ end
28
+
29
+ end
30
+ end
@@ -8,35 +8,38 @@ module Unfuzzle
8
8
  # [slug] The "short name" for this project
9
9
  # [name] The name of this project
10
10
  # [description] The description for the project
11
+ # [archived] The archived status of this project (see Project#archived?)
12
+ # [created_at] The date/time that this project was created
13
+ # [updated_at] The date/time that this project was last updated
11
14
  #
12
15
  class Project
13
16
 
14
- include Unfuzzle::Model
17
+ include Graft::Model
15
18
 
16
- attribute :id
17
- attribute :slug, :from => :short_name
18
- attribute :archived
19
- attribute :name, :from => :title
19
+ attribute :id, :type => :integer
20
+ attribute :slug, :from => 'short-name'
21
+ attribute :archived, :type => :boolean
22
+ attribute :name, :from => 'title'
20
23
  attribute :description
21
- attribute :created_timestamp, :from => :created_at
22
- attribute :updated_timestamp, :from => :updated_at
24
+ attribute :created_at, :from => 'created-at', :type => :time
25
+ attribute :updated_at, :from => 'updated-at', :type => :time
23
26
 
24
27
  # Return a list of all projects to which the current user has access
25
28
  def self.all
26
29
  response = Request.get('/projects')
27
- response.data.map {|data| new(data) }
30
+ collection_from(response.body, 'projects/project')
28
31
  end
29
32
 
30
33
  # Find a single project by its slug (short name)
31
34
  def self.find_by_slug(slug)
32
35
  response = Request.get("/projects/by_short_name/#{slug}")
33
- new(response.data)
36
+ new(response.body)
34
37
  end
35
38
 
36
39
  # Find a single project by its ID
37
40
  def self.find_by_id(id)
38
41
  response = Request.get("/projects/#{id}")
39
- new(response.data)
42
+ new(response.body)
40
43
  end
41
44
 
42
45
  # Has this project been archived?
@@ -44,16 +47,6 @@ module Unfuzzle
44
47
  archived == true
45
48
  end
46
49
 
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
50
  # The collection of Milestones associated to this project
58
51
  def milestones
59
52
  Milestone.find_all_by_project_id(id)
@@ -12,24 +12,20 @@ module Unfuzzle
12
12
  request.get
13
13
  end
14
14
 
15
+ # Send a PUT request with data and retrieve a Response
15
16
  def self.put(resource_path, payload)
16
- request = new(resource_path, payload, :format => :xml)
17
+ request = new(resource_path, payload)
17
18
  request.put
18
19
  end
19
20
 
20
21
  # Create a new request for the given resource path
21
- def initialize(resource_path, payload = nil, options = {})
22
+ def initialize(resource_path, payload = nil)
22
23
  @resource_path = resource_path
23
24
  @payload = payload
24
- @options = options
25
- end
26
-
27
- def request_format
28
- (@options[:format] || 'json').to_s
29
25
  end
30
26
 
31
27
  def endpoint_uri # :nodoc:
32
- URI.parse("http://#{Unfuzzle.subdomain}.unfuddle.com/api/v1#{@resource_path}.#{request_format}")
28
+ URI.parse("http://#{Unfuzzle.subdomain}.unfuddle.com/api/v1#{@resource_path}.xml")
33
29
  end
34
30
 
35
31
  def client # :nodoc:
@@ -44,6 +40,7 @@ module Unfuzzle
44
40
  Response.new(client.request(request))
45
41
  end
46
42
 
43
+ # Send a PUT request to the configured endpoint
47
44
  def put
48
45
  request = Net::HTTP::Put.new(endpoint_uri.path)
49
46
  request.basic_auth Unfuzzle.username, Unfuzzle.password
@@ -21,12 +21,5 @@ module Unfuzzle
21
21
  @http_response.body
22
22
  end
23
23
 
24
- # Parsed JSON response body
25
- def data
26
- if !error?
27
- @parsed_data ||= JSON.parse(body)
28
- end
29
- end
30
-
31
24
  end
32
25
  end
@@ -0,0 +1,31 @@
1
+ module Unfuzzle
2
+
3
+ # = Severity
4
+ #
5
+ # Represents a Severity in an Unfuddle project. These are user-configurable
6
+ # and are custom for each project you have. Examples include 'Story',
7
+ # 'Defect', etc.. A severity has the following attributes:
8
+ #
9
+ # [id] The unique identifier for this severity
10
+ # [name] The name of this severity
11
+ # [created_at] The date/time that this severity was created
12
+ # [updated_at] The date/time that this severity was last updated
13
+ #
14
+ class Severity
15
+
16
+ include Graft::Model
17
+
18
+ attribute :id, :type => :integer
19
+ attribute :name
20
+ attribute :project_id, :from => 'project-id', :type => :integer
21
+ attribute :created_at, :from => 'created-at', :type => :time
22
+ attribute :updated_at, :from => 'updated-at', :type => :time
23
+
24
+ # Find the severity by ID for a given project
25
+ def self.find_by_project_id_and_severity_id(project_id, severity_id)
26
+ response = Request.get("/projects/#{project_id}/severities/#{severity_id}")
27
+ new response.body
28
+ end
29
+
30
+ end
31
+ end
@@ -10,56 +10,89 @@ module Unfuzzle
10
10
  # [title] The title of the ticket (short)
11
11
  # [description] The full description of the ticket
12
12
  # [status] The ticket's status (new / accepted / resolved / closed)
13
+ # [due_on] The due date for this ticket
14
+ # [created_at] The date/time that this ticket was created
15
+ # [updated_at] The date/time that this ticket was last updated
13
16
  #
14
17
  class Ticket
15
18
 
16
- include Unfuzzle::Model
19
+ include Graft::Model
17
20
 
18
- attribute :id
19
- attribute :project_id
20
- attribute :milestone_id
21
+ attribute :id, :type => :integer
22
+ attribute :project_id, :from => 'project-id', :type => :integer
23
+ attribute :milestone_id, :from => 'milestone-id', :type => :integer
24
+ attribute :component_id, :from => 'component-id', :type => :integer
25
+ attribute :priority_id, :from => 'priority', :type => :integer
21
26
  attribute :number
22
- attribute :title, :from => :summary
27
+ attribute :title, :from => 'summary'
23
28
  attribute :description
24
- attribute :due_datestamp, :from => :due_on
25
- attribute :created_timestamp, :from => :created_at
26
- attribute :updated_timestamp, :from => :updated_at
29
+ attribute :due_on, :from => 'due-on', :type => :date
30
+ attribute :created_at, :from => 'created-at', :type => :time
31
+ attribute :updated_at, :from => 'updated-at', :type => :time
32
+ attribute :severity_id, :from => 'severity-id', :type => :integer
27
33
  attribute :status
28
34
 
29
35
  # Return a list of all tickets for an individual project
30
36
  def self.find_all_by_project_id(project_id)
31
37
  response = Request.get("/projects/#{project_id}/tickets")
32
- response.data.map {|data| new(data) }
38
+ collection_from(response.body, 'tickets/ticket')
33
39
  end
34
40
 
35
41
  # Return a list of all tickets for a given milestone as part of a project
36
42
  def self.find_all_by_project_id_and_milestone_id(project_id, milestone_id)
37
43
  response = Request.get("/projects/#{project_id}/milestones/#{milestone_id}/tickets")
38
- response.data.map {|data| new(data) }
44
+ collection_from(response.body, 'tickets/ticket')
39
45
  end
40
46
 
41
- # The DateTime that this milestone was created
42
- def created_at
43
- DateTime.parse(created_timestamp)
47
+ # The Milestone associated with this ticket
48
+ def milestone
49
+ Milestone.find_by_project_id_and_milestone_id(project_id, milestone_id)
44
50
  end
45
-
46
- # The DateTime that this milestone was last updated
47
- def updated_at
48
- DateTime.parse(updated_timestamp)
51
+
52
+ # The Severity associated with this ticket
53
+ def severity
54
+ Severity.find_by_project_id_and_severity_id(project_id, severity_id) unless severity_id.nil?
49
55
  end
50
-
51
- # The Date that this milestone is due
52
- def due_on
53
- Date.parse(due_datestamp) unless due_datestamp.nil?
56
+
57
+ def severity_name
58
+ severity.name unless severity.nil?
54
59
  end
55
-
56
- def milestone
57
- Milestone.find_by_project_id_and_milestone_id(project_id, milestone_id)
60
+
61
+ # The Priority associated with this ticket
62
+ def priority
63
+ Priority.new(priority_id)
64
+ end
65
+
66
+ def priority_name
67
+ priority.name
68
+ end
69
+
70
+ # The Component associated with this ticket
71
+ def component
72
+ Component.find_by_project_id_and_component_id(project_id, component_id) unless component_id.nil?
73
+ end
74
+
75
+ def component_name
76
+ component.name unless component.nil?
77
+ end
78
+
79
+ # Hash representation of this ticket's data (for updating)
80
+ def to_hash
81
+ {
82
+ 'id' => id,
83
+ 'project_id' => project_id,
84
+ 'milestone_id' => milestone_id,
85
+ 'number' => number,
86
+ 'summary' => title,
87
+ 'description' => description,
88
+ 'status' => status
89
+ }
58
90
  end
59
91
 
92
+ # Update the ticket's data in unfuddle
60
93
  def update
61
94
  resource_path = "/projects/#{project_id}/tickets/#{id}"
62
- Request.put(resource_path, self.to_xml)
95
+ Request.put(resource_path, self.to_xml('ticket'))
63
96
  end
64
97
 
65
98
  end