vigetlabs-unfuzzle 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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