confuddle 0.0.1

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