confuddle 0.0.1

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 (65) hide show
  1. data/.gitignore +18 -0
  2. data/.passwd_to_unfuddle.example.yml +7 -0
  3. data/README.md +40 -0
  4. data/bin/un +833 -0
  5. data/bin/un.cmd +1 -0
  6. data/confuddle.gemspec +22 -0
  7. data/lib/graft/README.rdoc +138 -0
  8. data/lib/graft/Rakefile +43 -0
  9. data/lib/graft/lib/graft/core_ext/hash.rb +9 -0
  10. data/lib/graft/lib/graft/json.rb +14 -0
  11. data/lib/graft/lib/graft/json/attribute.rb +18 -0
  12. data/lib/graft/lib/graft/json/model.rb +28 -0
  13. data/lib/graft/lib/graft/model.rb +43 -0
  14. data/lib/graft/lib/graft/version.rb +13 -0
  15. data/lib/graft/lib/graft/xml.rb +19 -0
  16. data/lib/graft/lib/graft/xml/attribute.rb +55 -0
  17. data/lib/graft/lib/graft/xml/model.rb +49 -0
  18. data/lib/graft/lib/graft/xml/type.rb +91 -0
  19. data/lib/graft/test/test_helper.rb +38 -0
  20. data/lib/graft/test/unit/core_ext/hash_test.rb +29 -0
  21. data/lib/graft/test/unit/json/attribute_test.rb +51 -0
  22. data/lib/graft/test/unit/json/model_test.rb +86 -0
  23. data/lib/graft/test/unit/xml/attribute_test.rb +161 -0
  24. data/lib/graft/test/unit/xml/model_test.rb +173 -0
  25. data/lib/graft/test/unit/xml/type_test.rb +65 -0
  26. data/lib/unfuzzle/.gitignore +4 -0
  27. data/lib/unfuzzle/README.rdoc +129 -0
  28. data/lib/unfuzzle/Rakefile +39 -0
  29. data/lib/unfuzzle/lib/unfuzzle.rb +87 -0
  30. data/lib/unfuzzle/lib/unfuzzle/comment.rb +37 -0
  31. data/lib/unfuzzle/lib/unfuzzle/component.rb +31 -0
  32. data/lib/unfuzzle/lib/unfuzzle/milestone.rb +54 -0
  33. data/lib/unfuzzle/lib/unfuzzle/person.rb +20 -0
  34. data/lib/unfuzzle/lib/unfuzzle/priority.rb +30 -0
  35. data/lib/unfuzzle/lib/unfuzzle/project.rb +62 -0
  36. data/lib/unfuzzle/lib/unfuzzle/request.rb +75 -0
  37. data/lib/unfuzzle/lib/unfuzzle/response.rb +25 -0
  38. data/lib/unfuzzle/lib/unfuzzle/severity.rb +31 -0
  39. data/lib/unfuzzle/lib/unfuzzle/ticket.rb +156 -0
  40. data/lib/unfuzzle/lib/unfuzzle/ticket_report.rb +29 -0
  41. data/lib/unfuzzle/lib/unfuzzle/time_entry.rb +75 -0
  42. data/lib/unfuzzle/lib/unfuzzle/version.rb +13 -0
  43. data/lib/unfuzzle/test/fixtures/component.xml +8 -0
  44. data/lib/unfuzzle/test/fixtures/components.xml +17 -0
  45. data/lib/unfuzzle/test/fixtures/milestone.xml +12 -0
  46. data/lib/unfuzzle/test/fixtures/milestones.xml +25 -0
  47. data/lib/unfuzzle/test/fixtures/project.xml +17 -0
  48. data/lib/unfuzzle/test/fixtures/projects.xml +35 -0
  49. data/lib/unfuzzle/test/fixtures/severities.xml +24 -0
  50. data/lib/unfuzzle/test/fixtures/severity.xml +8 -0
  51. data/lib/unfuzzle/test/fixtures/ticket.xml +25 -0
  52. data/lib/unfuzzle/test/fixtures/tickets.xml +51 -0
  53. data/lib/unfuzzle/test/test_helper.rb +60 -0
  54. data/lib/unfuzzle/test/unit/unfuzzle/component_test.rb +36 -0
  55. data/lib/unfuzzle/test/unit/unfuzzle/milestone_test.rb +100 -0
  56. data/lib/unfuzzle/test/unit/unfuzzle/priority_test.rb +25 -0
  57. data/lib/unfuzzle/test/unit/unfuzzle/project_test.rb +87 -0
  58. data/lib/unfuzzle/test/unit/unfuzzle/request_test.rb +104 -0
  59. data/lib/unfuzzle/test/unit/unfuzzle/response_test.rb +37 -0
  60. data/lib/unfuzzle/test/unit/unfuzzle/severity_test.rb +36 -0
  61. data/lib/unfuzzle/test/unit/unfuzzle/ticket_test.rb +181 -0
  62. data/lib/unfuzzle/test/unit/unfuzzle_test.rb +39 -0
  63. data/lib/unfuzzle/unfuzzle.gemspec +31 -0
  64. data/lib/version.rb +3 -0
  65. metadata +176 -0
@@ -0,0 +1,29 @@
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
+ # [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
14
+ #
15
+ class TicketReport
16
+
17
+ include Graft
18
+
19
+ attribute :id, :type => :integer
20
+ attribute :relationship
21
+ attribute :title
22
+
23
+ def self.all
24
+ response = Request.get('/ticket_reports')
25
+ collection_from(response.body, 'ticket-report')
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,75 @@
1
+ module Unfuzzle
2
+
3
+ class TimeEntryGroup
4
+ include Graft
5
+ attribute :title
6
+ end
7
+
8
+ class TimeEntry
9
+
10
+ include Graft
11
+
12
+ attribute :date
13
+ attribute :description
14
+ attribute :hours
15
+ attribute :person_id, :from => "person-id", :type => :integer
16
+ attribute :ticket_id, :from => "ticket-id", :time => :integer
17
+
18
+ attr_accessor :title # title of ticket
19
+
20
+ # Hash representation of this time entry's data (for updating)
21
+ def to_hash
22
+ {
23
+ 'date' => date,
24
+ 'description' => description,
25
+ 'hours' => hours,
26
+ 'person-id' => person_id,
27
+ "ticket-id" => ticket_id
28
+ }
29
+ end
30
+
31
+
32
+ # times for project
33
+ def self.time_invested(project_id, start_date, end_date)
34
+ response = Request.get("/projects/#{project_id}/time_invested", query(start_date, end_date))
35
+ parse_group_collection(response.body)
36
+ end
37
+
38
+ # times for account
39
+ def self.all_time_invested(start_date, end_date)
40
+ response = Request.get("/account/time_invested", query(start_date, end_date))
41
+ parse_group_collection(response.body)
42
+ end
43
+
44
+ def self.all_for_ticket(ticket, start_date = nil, end_date = nil)
45
+ response = Request.get("/projects/#{ticket.project_id}/tickets/#{ticket.id}/time_entries", query(start_date, end_date))
46
+ collection_from(response.body, 'time-entries/time-entry')
47
+ end
48
+
49
+ # Create a ticket in unfuddle
50
+ def create(project_id, ticket_id)
51
+ resource_path = "/projects/#{project_id}/tickets/#{ticket_id}/time_entries"
52
+ Request.post(resource_path, self.to_xml('time-entry'))
53
+ end
54
+
55
+ protected
56
+
57
+ def self.query(start_date, end_date)
58
+ # [person, ticket, priority, component, version, severity, milestone, due_on, reporter, assignee, status, resolution]
59
+ group = "ticket"
60
+ query = "?group_by=#{group}"
61
+ query += "&start_date=#{start_date.strftime("%Y/%m/%d")}" if start_date
62
+ query += "&end_date=#{end_date.strftime("%Y/%m/%d")}" if end_date
63
+ query
64
+ end
65
+
66
+ def self.parse_group_collection(body)
67
+ res = collection_from_block(body, "groups/group") do |str|
68
+ group = TimeEntryGroup.new(str)
69
+ times = collection_from(str, 'time-entries/time-entry').map{|time| time.title = group.title; time}
70
+ end
71
+ res.flatten
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,13 @@
1
+ module Unfuzzle
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ TINY = 3
7
+
8
+ def self.to_s # :nodoc:
9
+ [MAJOR, MINOR, TINY].join('.')
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <component>
3
+ <id type="integer">1</id>
4
+ <name>Administration</name>
5
+ <project-id type="integer">2</project-id>
6
+ <created-at>2009-06-15T20:40:29Z</created-at>
7
+ <updated-at>2009-06-15T20:40:29Z</updated-at>
8
+ </component>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <components type="array">
3
+ <component>
4
+ <id type="integer">1</id>
5
+ <name>Administration</name>
6
+ <project-id type="integer">1</project-id>
7
+ <created-at>2009-06-15T20:40:29Z</created-at>
8
+ <updated-at>2009-06-15T20:40:29Z</updated-at>
9
+ </component>
10
+ <component>
11
+ <id type="integer">2</id>
12
+ <name>Notifications</name>
13
+ <project-id type="integer">2</project-id>
14
+ <created-at>2009-06-15T20:40:49Z</created-at>
15
+ <updated-at>2009-06-15T20:40:49Z</updated-at>
16
+ </component>
17
+ </components>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <milestone>
3
+ <archived type="boolean">false</archived>
4
+ <completed type="boolean">false</completed>
5
+ <due-on type="date">2009-04-03</due-on>
6
+ <id type="integer">2</id>
7
+ <person-responsible-id type="integer">123</person-responsible-id>
8
+ <project-id type="integer">1</project-id>
9
+ <title>Milestone #1</title>
10
+ <created-at>2009-04-02T19:43:16Z</created-at>
11
+ <updated-at>2009-07-02T16:40:28Z</updated-at>
12
+ </milestone>
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <milestones type="array">
3
+ <milestone>
4
+ <archived type="boolean">false</archived>
5
+ <completed type="boolean">false</completed>
6
+ <due-on type="date">2009-04-03</due-on>
7
+ <id type="integer">1</id>
8
+ <person-responsible-id type="integer">123</person-responsible-id>
9
+ <project-id type="integer">1</project-id>
10
+ <title>Milestone #1</title>
11
+ <created-at>2009-04-02T19:43:16Z</created-at>
12
+ <updated-at>2009-07-06T18:00:26Z</updated-at>
13
+ </milestone>
14
+ <milestone>
15
+ <archived type="boolean">false</archived>
16
+ <completed type="boolean">false</completed>
17
+ <due-on type="date">2009-04-06</due-on>
18
+ <id type="integer">2</id>
19
+ <person-responsible-id type="integer">123</person-responsible-id>
20
+ <project-id type="integer">1</project-id>
21
+ <title>Milestone #2</title>
22
+ <created-at>2009-04-06T04:45:54Z</created-at>
23
+ <updated-at>2009-04-10T15:29:01Z</updated-at>
24
+ </milestone>
25
+ </milestones>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project>
3
+ <account-id type="integer">1</account-id>
4
+ <archived type="boolean">false</archived>
5
+ <assignee-on-resolve>none</assignee-on-resolve>
6
+ <close-ticket-simultaneously-default type="boolean">true</close-ticket-simultaneously-default>
7
+ <default-ticket-report-id type="integer">0</default-ticket-report-id>
8
+ <description>This is the project for Blip Bleep Co.</description>
9
+ <disk-usage type="integer">1416</disk-usage>
10
+ <enable-time-tracking type="boolean">true</enable-time-tracking>
11
+ <id type="integer">1</id>
12
+ <short-name>blip</short-name>
13
+ <theme>teal</theme>
14
+ <title>Blip Bleep Co.</title>
15
+ <created-at>2008-07-28T16:57:10Z</created-at>
16
+ <updated-at>2009-04-28T18:48:52Z</updated-at>
17
+ </project>
@@ -0,0 +1,35 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projects type="array">
3
+ <project>
4
+ <account-id type="integer">10710</account-id>
5
+ <archived type="boolean">false</archived>
6
+ <assignee-on-resolve>none</assignee-on-resolve>
7
+ <close-ticket-simultaneously-default type="boolean">true</close-ticket-simultaneously-default>
8
+ <default-ticket-report-id type="integer">0</default-ticket-report-id>
9
+ <description></description>
10
+ <disk-usage type="integer">1416</disk-usage>
11
+ <enable-time-tracking type="boolean">true</enable-time-tracking>
12
+ <id type="integer">24541</id>
13
+ <short-name>aa</short-name>
14
+ <theme>teal</theme>
15
+ <title>Agency Access</title>
16
+ <created-at>2008-07-28T16:57:10Z</created-at>
17
+ <updated-at>2009-04-28T18:48:52Z</updated-at>
18
+ </project>
19
+ <project>
20
+ <account-id type="integer">10710</account-id>
21
+ <archived type="boolean">false</archived>
22
+ <assignee-on-resolve>reporter</assignee-on-resolve>
23
+ <close-ticket-simultaneously-default type="boolean">false</close-ticket-simultaneously-default>
24
+ <default-ticket-report-id type="integer" nil="true"></default-ticket-report-id>
25
+ <description nil="true"></description>
26
+ <disk-usage type="integer">28736</disk-usage>
27
+ <enable-time-tracking type="boolean">true</enable-time-tracking>
28
+ <id type="integer">22830</id>
29
+ <short-name>brianregan</short-name>
30
+ <theme>orange</theme>
31
+ <title>Brian Regan</title>
32
+ <created-at>2008-07-02T14:19:32Z</created-at>
33
+ <updated-at>2009-07-09T20:19:51Z</updated-at>
34
+ </project>
35
+ </projects>
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <severities type="array">
3
+ <severity>
4
+ <id type="integer">34942</id>
5
+ <name>Design</name>
6
+ <project-id type="integer">26984</project-id>
7
+ <created-at>2009-04-02T19:44:49Z</created-at>
8
+ <updated-at>2009-04-02T19:44:49Z</updated-at>
9
+ </severity>
10
+ <severity>
11
+ <id type="integer">34943</id>
12
+ <name>Development</name>
13
+ <project-id type="integer">26984</project-id>
14
+ <created-at>2009-04-02T19:44:53Z</created-at>
15
+ <updated-at>2009-04-02T19:44:53Z</updated-at>
16
+ </severity>
17
+ <severity>
18
+ <id type="integer">34944</id>
19
+ <name>Project Management</name>
20
+ <project-id type="integer">26984</project-id>
21
+ <created-at>2009-04-02T19:44:59Z</created-at>
22
+ <updated-at>2009-04-02T19:44:59Z</updated-at>
23
+ </severity>
24
+ </severities>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <severity>
3
+ <id type="integer">1</id>
4
+ <name>Story</name>
5
+ <project-id type="integer">2</project-id>
6
+ <created-at>2009-04-02T19:44:49Z</created-at>
7
+ <updated-at>2009-04-02T19:44:49Z</updated-at>
8
+ </severity>
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ticket>
3
+ <assignee-id type="integer">123</assignee-id>
4
+ <component-id type="integer">1</component-id>
5
+ <description>Do something important</description>
6
+ <description-format>markdown</description-format>
7
+ <due-on type="date"></due-on>
8
+ <hours-estimate-current type="float">0.0</hours-estimate-current>
9
+ <hours-estimate-initial type="float">2.0</hours-estimate-initial>
10
+ <id type="integer">1</id>
11
+ <milestone-id type="integer">1</milestone-id>
12
+ <number type="integer">1</number>
13
+ <priority>3</priority>
14
+ <project-id type="integer">1</project-id>
15
+ <reporter-id type="integer">122</reporter-id>
16
+ <resolution>postponed</resolution>
17
+ <resolution-description>done</resolution-description>
18
+ <resolution-description-format>markdown</resolution-description-format>
19
+ <severity-id type="integer">123</severity-id>
20
+ <status>closed</status>
21
+ <summary>Ticket #1</summary>
22
+ <version-id type="integer">17329</version-id>
23
+ <created-at>2008-11-25T14:00:19Z</created-at>
24
+ <updated-at>2008-12-31T15:51:41Z</updated-at>
25
+ </ticket>
@@ -0,0 +1,51 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <tickets type="array">
3
+ <ticket>
4
+ <assignee-id type="integer">27736</assignee-id>
5
+ <component-id type="integer">47394</component-id>
6
+ <description></description>
7
+ <description-format>markdown</description-format>
8
+ <due-on type="date">2009-04-03</due-on>
9
+ <hours-estimate-current type="float">0.0</hours-estimate-current>
10
+ <hours-estimate-initial type="float">2.0</hours-estimate-initial>
11
+ <id type="integer">557168</id>
12
+ <milestone-id type="integer">52212</milestone-id>
13
+ <number type="integer">5</number>
14
+ <priority>3</priority>
15
+ <project-id type="integer">26984</project-id>
16
+ <reporter-id type="integer">27736</reporter-id>
17
+ <resolution>postponed</resolution>
18
+ <resolution-description>awaiting contracts</resolution-description>
19
+ <resolution-description-format>markdown</resolution-description-format>
20
+ <severity-id type="integer">34943</severity-id>
21
+ <status>closed</status>
22
+ <summary>User can log in to the site</summary>
23
+ <version-id type="integer">17329</version-id>
24
+ <created-at>2009-04-02T19:16:26Z</created-at>
25
+ <updated-at>2009-07-02T16:42:30Z</updated-at>
26
+ </ticket>
27
+ <ticket>
28
+ <assignee-id type="integer">27736</assignee-id>
29
+ <component-id type="integer">47394</component-id>
30
+ <description></description>
31
+ <description-format>markdown</description-format>
32
+ <due-on type="date">2009-04-03</due-on>
33
+ <hours-estimate-current type="float">0.0</hours-estimate-current>
34
+ <hours-estimate-initial type="float">2.0</hours-estimate-initial>
35
+ <id type="integer">557169</id>
36
+ <milestone-id type="integer">52212</milestone-id>
37
+ <number type="integer">6</number>
38
+ <priority>3</priority>
39
+ <project-id type="integer">26984</project-id>
40
+ <reporter-id type="integer">27736</reporter-id>
41
+ <resolution>postponed</resolution>
42
+ <resolution-description>awaiting contracts</resolution-description>
43
+ <resolution-description-format>markdown</resolution-description-format>
44
+ <severity-id type="integer">34943</severity-id>
45
+ <status>closed</status>
46
+ <summary>User can edit profile</summary>
47
+ <version-id type="integer">17329</version-id>
48
+ <created-at>2009-04-02T19:17:40Z</created-at>
49
+ <updated-at>2009-04-10T15:28:54Z</updated-at>
50
+ </ticket>
51
+ </tickets>
@@ -0,0 +1,60 @@
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}.xml"
14
+ 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(:body).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
+ context "with data populated for #{klass}" do
35
+ setup { @object = klass.new(read_fixture(options[:from])) }
36
+ merge_block(&block)
37
+ end
38
+
39
+ end
40
+
41
+ def self.value_for(method_name, options)
42
+ should "have a value for :#{method_name}" do
43
+ @object.send(method_name).should == options[:is]
44
+ end
45
+ end
46
+
47
+ def self.should_set_a_value_for(attribute, value = nil)
48
+ class_name = self.to_s.sub(/^Unfuzzle::(.*)Test$/, '\\1')
49
+ klass = Unfuzzle.const_get(class_name)
50
+
51
+ value = attribute if value.nil?
52
+
53
+ should "be able to set a value for :#{attribute}" do
54
+ object = klass.new
55
+ object.send("#{attribute}=", value)
56
+ object.send(attribute).should == value
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+
3
+ module Unfuzzle
4
+ class ComponentTest < Test::Unit::TestCase
5
+
6
+ context "The Component class" do
7
+
8
+ should "be able to return a severity for a project and component id" do
9
+ project_id = 1
10
+ component_id = 1
11
+
12
+ response = mock_request_cycle :for => "/projects/#{project_id}/components/#{component_id}", :data => 'component'
13
+
14
+ Unfuzzle::Component.expects(:new).with(response.body).returns('component')
15
+
16
+ Unfuzzle::Component.find_by_project_id_and_component_id(project_id, component_id).should == 'component'
17
+ end
18
+
19
+ end
20
+
21
+ context "An instance of the Component class" do
22
+
23
+ when_populating Component, :from => 'component' do
24
+
25
+ value_for :id, :is => 1
26
+ value_for :name, :is => 'Administration'
27
+ value_for :project_id, :is => 2
28
+ value_for :created_at, :is => Time.parse('2009-06-15T20:40:29Z')
29
+ value_for :updated_at, :is => Time.parse('2009-06-15T20:40:29Z')
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end