turingstudio-basecamp-rb 0.0.1 → 0.0.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 (37) hide show
  1. data/Rakefile +21 -0
  2. data/lib/basecamp/attachment.rb +23 -23
  3. data/lib/basecamp/attachment_category.rb +10 -0
  4. data/lib/basecamp/base.rb +5 -109
  5. data/lib/basecamp/company.rb +10 -0
  6. data/lib/basecamp/message.rb +24 -22
  7. data/lib/basecamp/milestone.rb +50 -0
  8. data/lib/basecamp/person.rb +18 -0
  9. data/lib/basecamp/post_category.rb +10 -0
  10. data/lib/basecamp/project.rb +22 -0
  11. data/lib/basecamp/record.rb +42 -13
  12. data/lib/basecamp/resource.rb +9 -9
  13. data/lib/basecamp/time_entry.rb +11 -9
  14. data/lib/basecamp/todoitem.rb +6 -6
  15. data/lib/basecamp/todolist.rb +16 -14
  16. data/lib/basecamp/version.rb +1 -1
  17. data/lib/basecamp.rb +7 -1
  18. data/spec/lib/basecamp/attachment_category_spec.rb +12 -0
  19. data/spec/lib/basecamp/attachment_spec.rb +26 -0
  20. data/spec/lib/basecamp/base_spec.rb +44 -0
  21. data/spec/lib/basecamp/company_spec.rb +13 -0
  22. data/spec/lib/basecamp/message_spec.rb +45 -0
  23. data/spec/lib/basecamp/milestone_spec.rb +44 -0
  24. data/spec/lib/basecamp/person_spec.rb +18 -0
  25. data/spec/lib/basecamp/post_category_spec.rb +13 -0
  26. data/spec/lib/basecamp/project_spec.rb +12 -0
  27. data/spec/lib/basecamp/record_spec.rb +57 -0
  28. data/spec/lib/basecamp/time_entry_spec.rb +23 -0
  29. data/spec/lib/basecamp/todo_item_spec.rb +38 -0
  30. data/spec/lib/basecamp/todo_list_spec.rb +36 -0
  31. data/spec/spec_helper.rb +68 -1
  32. metadata +44 -18
  33. data/spec/basecamp_spec.rb +0 -61
  34. data/spec/lib/basecamp_base.rb +0 -75
  35. data/spec/lib/basecamp_message_spec.rb +0 -42
  36. data/spec/lib/basecamp_todo_item_spec.rb +0 -40
  37. data/spec/lib/basecamp_todo_list_spec.rb +0 -32
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/basecamp/version'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('basecamp-rb', Basecamp::VERSION) do |p|
7
+ p.developer('The Turing Studio, Inc.', 'support@turingstudio.com')
8
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
9
+ p.extra_deps = [
10
+ ['xml-simple','>= 1.0.11'],
11
+ ['activesupport','>= 2.2.2'],
12
+ ['activeresource','>= 2.2.2']
13
+ ]
14
+ p.extra_dev_deps = [
15
+ ['newgem', ">= #{::Newgem::VERSION}"]
16
+ ]
17
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
18
+ end
19
+
20
+ require 'newgem/tasks'
21
+ Dir['tasks/**/*.rake'].each { |t| load t }
@@ -1,28 +1,20 @@
1
+ # === Attaching Files to a Resource
2
+ #
3
+ # If the resource accepts file attachments, the +attachments+ parameter should
4
+ # be an array of Basecamp::Attachment objects. Example:
5
+ #
6
+ # a1 = Basecamp::Atachment.create('primary', File.read('primary.doc'))
7
+ # a2 = Basecamp::Atachment.create('another', File.read('another.doc'))
8
+ #
9
+ # m = Basecamp::Message.new(:project_id => 1037)
10
+ # ...
11
+ # m.attachments = [a1, a2]
12
+ # m.save # => true
13
+ #
1
14
  module Basecamp
2
- class Attachment
3
-
4
- # === Attaching Files to a Resource
5
- #
6
- # If the resource accepts file attachments, the +attachments+ parameter should
7
- # be an array of Basecamp::Attachment objects. Example:
8
- #
9
- # a1 = Basecamp::Atachment.create('primary', File.read('primary.doc'))
10
- # a2 = Basecamp::Atachment.create('another', File.read('another.doc'))
11
- #
12
- # m = Basecamp::Message.new(:project_id => 1037)
13
- # ...
14
- # m.attachments = [a1, a2]
15
- # m.save # => true
16
- #
17
-
15
+ class Attachment
18
16
  attr_accessor :id, :filename, :content, :content_type
19
-
20
- def self.create(filename, content)
21
- returning new(filename, content) do |attachment|
22
- attachment.save
23
- end
24
- end
25
-
17
+
26
18
  def initialize(filename, content, content_type = 'application/octet-stream')
27
19
  @filename, @content, @content_type = filename, content, content_type
28
20
  end
@@ -49,5 +41,13 @@ module Basecamp
49
41
  raise "Could not save attachment: #{response.message} (#{response.code})"
50
42
  end
51
43
  end
44
+
45
+ class << self
46
+ def create(filename, content)
47
+ returning new(filename, content) do |attachment|
48
+ attachment.save
49
+ end
50
+ end
51
+ end
52
52
  end
53
53
  end
@@ -0,0 +1,10 @@
1
+ module Basecamp
2
+ class AttachmentCategory < Record
3
+ class << self
4
+ # Returns the list of file categories for the given project
5
+ def list(project_id)
6
+ records "/projects/#{project_id}/attachment_categories"
7
+ end
8
+ end
9
+ end
10
+ end
data/lib/basecamp/base.rb CHANGED
@@ -26,128 +26,24 @@ module Basecamp
26
26
  @use_xml = false
27
27
  end
28
28
 
29
- # ==========================================================================
30
- # GENERAL
31
- # ==========================================================================
32
-
33
- # Return the list of all accessible projects
34
- def projects
35
- records "project", "/project/list"
36
- end
37
-
38
- # Returns the list of message categories for the given project
39
- def message_categories(project_id)
40
- records "post-category", "/projects/#{project_id}/post_categories"
41
- end
42
-
43
- # Returns the list of file categories for the given project
44
- def file_categories(project_id)
45
- records "attachment-category", "/projects/#{project_id}/attachment_categories"
46
- end
47
-
48
- # ==========================================================================
49
- # CONTACT MANAGEMENT
50
- # ==========================================================================
51
-
52
- # Return information for the company with the given id
53
- def company(id)
54
- record "/contacts/company/#{id}"
55
- end
56
-
57
- # Return an array of the people in the given company. If the project-id is
58
- # given, only people who have access to the given project will be returned.
59
- def people(company_id, project_id=nil)
60
- url = project_id ? "/projects/#{project_id}" : ""
61
- url << "/contacts/people/#{company_id}"
62
- records "person", url
63
- end
64
-
65
- # Return information about the person with the given id
66
- def person(id)
67
- record "/contacts/person/#{id}"
68
- end
69
-
70
- # ==========================================================================
71
- # MILESTONES
72
- # ==========================================================================
73
-
74
- # Complete the milestone with the given id
75
- def complete_milestone(id)
76
- record "/milestones/complete/#{id}"
77
- end
78
-
79
- # Create a new milestone for the given project. +data+ must be hash of the
80
- # values to set, including +title+, +deadline+, +responsible_party+, and
81
- # +notify+.
82
- def create_milestone(project_id, data)
83
- create_milestones(project_id, [data]).first
84
- end
85
-
86
- # As #create_milestone, but can create multiple milestones in a single
87
- # request. The +milestones+ parameter must be an array of milestone values as
88
- # descrbed in #create_milestone.
89
- def create_milestones(project_id, milestones)
90
- records "milestone", "/projects/#{project_id}/milestones/create", :milestone => milestones
91
- end
92
-
93
- # Destroys the milestone with the given id.
94
- def delete_milestone(id)
95
- record "/milestones/delete/#{id}"
96
- end
97
-
98
- # Returns a list of all milestones for the given project, optionally filtered
99
- # by whether they are completed, late, or upcoming.
100
- def milestones(project_id, find="all")
101
- records "milestone", "/projects/#{project_id}/milestones/list", :find => find
102
- end
103
-
104
- # Uncomplete the milestone with the given id
105
- def uncomplete_milestone(id)
106
- record "/milestones/uncomplete/#{id}"
107
- end
108
-
109
- # Updates an existing milestone.
110
- def update_milestone(id, data, move=false, move_off_weekends=false)
111
- record "/milestones/update/#{id}", :milestone => data,
112
- :move_upcoming_milestones => move,
113
- :move_upcoming_milestones_off_weekends => move_off_weekends
114
- end
115
-
116
- private
117
-
118
29
  # Make a raw web-service request to Basecamp. This will return a Hash of
119
30
  # Arrays of the response, and may seem a little odd to the uninitiated.
120
31
  def request(path, parameters = {})
121
32
  response = Base.connection.post(path, convert_body(parameters), "Content-Type" => content_type)
122
-
33
+
123
34
  if response.code.to_i / 100 == 2
124
35
  result = XmlSimple.xml_in(response.body, 'keeproot' => true, 'contentkey' => '__content__', 'forcecontent' => true)
125
- typecast_value(result)
36
+ typecast_value(result)
126
37
  else
127
38
  raise "#{response.message} (#{response.code})"
128
39
  end
129
40
  end
130
41
 
131
- # A convenience method for wrapping the result of a query in a Record
132
- # object. This assumes that the result is a singleton, not a collection.
133
- def record(path, parameters={})
134
- result = request(path, parameters)
135
- (result && !result.empty?) ? Record.new(result.keys.first, result.values.first) : nil
136
- end
137
-
138
- # A convenience method for wrapping the result of a query in Record
139
- # objects. This assumes that the result is a collection--any singleton
140
- # result will be wrapped in an array.
141
- def records(node, path, parameters={})
142
- result = request(path, parameters).values.first or return []
143
- result = result[node] or return []
144
- result = [result] unless Array === result
145
- result.map { |row| Record.new(node, row) }
146
- end
147
-
42
+ private
43
+
148
44
  def convert_body(body)
149
45
  body = use_xml ? body.to_legacy_xml : body.to_yaml
150
- end
46
+ end
151
47
 
152
48
  def content_type
153
49
  use_xml ? "application/xml" : "application/x-yaml"
@@ -0,0 +1,10 @@
1
+ module Basecamp
2
+ class Company < Record
3
+ class << self
4
+ def find(id)
5
+ # Return information for the company with the given id
6
+ record "/contacts/company/#{id}"
7
+ end
8
+ end
9
+ end
10
+ end
@@ -3,30 +3,32 @@ module Basecamp
3
3
  parent_resources :project
4
4
  self.element_name = 'post'
5
5
 
6
- # Returns the most recent 25 messages in the given project (and category,
7
- # if specified). If you need to retrieve older messages, use the archive
8
- # method instead. Example:
9
- #
10
- # Basecamp::Message.list(1037)
11
- # Basecamp::Message.list(1037, :category_id => 7301)
12
- #
13
- def self.list(project_id, options = {})
14
- find(:all, :params => options.merge(:project_id => project_id))
15
- end
16
-
17
- # Returns a summary of all messages in the given project (and category, if
18
- # specified). The summary is simply the title and category of the message,
19
- # as well as the number of attachments (if any). Example:
20
- #
21
- # Basecamp::Message.archive(1037)
22
- # Basecamp::Message.archive(1037, :category_id => 7301)
23
- #
24
- def self.archive(project_id, options = {})
25
- find(:all, :params => options.merge(:project_id => project_id), :from => :archive)
26
- end
27
-
28
6
  def comments(options = {})
29
7
  @comments ||= Comment.find(:all, :params => options.merge(:post_id => id))
30
8
  end
9
+
10
+ class << self
11
+ # Returns the most recent 25 messages in the given project (and category,
12
+ # if specified). If you need to retrieve older messages, use the archive
13
+ # method instead. Example:
14
+ #
15
+ # Basecamp::Message.list(1037)
16
+ # Basecamp::Message.list(1037, :category_id => 7301)
17
+ #
18
+ def list(project_id, options = {})
19
+ find(:all, :params => options.merge(:project_id => project_id))
20
+ end
21
+
22
+ # Returns a summary of all messages in the given project (and category, if
23
+ # specified). The summary is simply the title and category of the message,
24
+ # as well as the number of attachments (if any). Example:
25
+ #
26
+ # Basecamp::Message.archive(1037)
27
+ # Basecamp::Message.archive(1037, :category_id => 7301)
28
+ #
29
+ def archive(project_id, options = {})
30
+ find(:all, :params => options.merge(:project_id => project_id), :from => :archive)
31
+ end
32
+ end
31
33
  end
32
34
  end
@@ -0,0 +1,50 @@
1
+ module Basecamp
2
+ class Milestone < Record
3
+ # Updates an existing milestone.
4
+ def update_attributes(params = {})
5
+ data = params.dup
6
+ move = data.delete :move
7
+ move_off_weekends = data.delete :move_off_weekends
8
+ record "/milestones/update/#{id}",
9
+ :milestone => data,
10
+ :move_upcoming_milestones => move,
11
+ :move_upcoming_milestones_off_weekends => move_off_weekends
12
+ end
13
+
14
+ # Destroys the milestone
15
+ def destroy
16
+ record "/milestones/delete/#{id}"
17
+ end
18
+
19
+ # Complete the milestone
20
+ def complete!
21
+ record "/milestones/complete/#{id}"
22
+ end
23
+
24
+ # Uncomplete the milestone
25
+ def uncomplete!
26
+ record "/milestones/uncomplete/#{id}"
27
+ end
28
+
29
+ class << self
30
+ def list(project_id, find = "all")
31
+ records "/projects/#{project_id}/milestones/list", :find => find
32
+ end
33
+
34
+ # Create a new milestone for the given project. +data+ must be hash of the
35
+ # values to set, including +title+, +deadline+, +responsible_party+, and
36
+ # +notify+.
37
+ def create(project_id, data = {})
38
+ create_milestones(project_id, [data]).first
39
+ end
40
+
41
+
42
+ # As #create_milestone, but can create multiple milestones in a single
43
+ # request. The +milestones+ parameter must be an array of milestone values as
44
+ # descrbed in #create_milestone.
45
+ def create_milestones(project_id, milestones)
46
+ records "/projects/#{project_id}/milestones/create", :milestone => milestones
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,18 @@
1
+ module Basecamp
2
+ class Person < Record
3
+ class << self
4
+ # Return an array of the people in the given company. If the project-id is
5
+ # given, only people who have access to the given project will be returned.
6
+ def list(company_id, project_id = nil)
7
+ url = project_id ? "/projects/#{project_id}" : ""
8
+ url << "/contacts/people/#{company_id}"
9
+ records url
10
+ end
11
+
12
+ # Return information about the person with the given id
13
+ def find(id)
14
+ record "/contacts/person/#{id}"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ module Basecamp
2
+ class PostCategory < Record
3
+ class << self
4
+ # Returns the list of message categories for the given project
5
+ def list(project_id)
6
+ records "/projects/#{project_id}/post_categories"
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ module Basecamp
2
+ class Project < Record
3
+ class << self
4
+ # Return the list of all accessible projects
5
+ def list
6
+ records "/project/list"
7
+ end
8
+ end
9
+
10
+ # def messages(options = {})
11
+ # Message.list(id, options)
12
+ # end
13
+ #
14
+ # def message_archive(options = {})
15
+ # @message_archive ||= Message.archive(id, options)
16
+ # end
17
+ #
18
+ # def milestones(find="all")
19
+ # Milestone.list(id, find)
20
+ # end
21
+ end
22
+ end
@@ -1,20 +1,22 @@
1
1
  module Basecamp
2
2
  class Record #:nodoc:
3
3
  attr_reader :type
4
-
5
- def initialize(type, hash)
6
- @type, @hash = type, hash
4
+
5
+ def initialize(hash)
6
+ # type = self.class.to_s
7
+ @hash = hash
7
8
  end
8
9
 
9
10
  def [](name)
10
- name = dashify(name)
11
+ name = name.to_s.dasherize
11
12
 
12
13
  case @hash[name]
13
14
  when Hash then
15
+ puts @hash[name].inspect
14
16
  @hash[name] = if (@hash[name].keys.length == 1 && @hash[name].values.first.is_a?(Array))
15
- @hash[name].values.first.map { |v| Record.new(@hash[name].keys.first, v) }
17
+ @hash[name].values.first.map { |v| self.class.new_with_type(@hash[name].keys.first, v) }
16
18
  else
17
- Record.new(name, @hash[name])
19
+ self.class.new_with_type(name, @hash[name])
18
20
  end
19
21
  else
20
22
  @hash[name]
@@ -30,7 +32,7 @@ module Basecamp
30
32
  end
31
33
 
32
34
  def respond_to?(sym)
33
- super || @hash.has_key?(dashify(sym))
35
+ super || @hash.has_key?(sym.to_s.dasherize)
34
36
  end
35
37
 
36
38
  def method_missing(sym, *args)
@@ -42,17 +44,44 @@ module Basecamp
42
44
  end
43
45
 
44
46
  def to_s
45
- "\#<Record(#{@type}) #{@hash.inspect[1..-2]}>"
47
+ "\#<Record(#{self.class}) #{@hash.inspect[1..-2]}>"
46
48
  end
47
49
 
48
50
  def inspect
49
51
  to_s
50
52
  end
51
53
 
52
- private
53
-
54
- def dashify(name)
55
- name.to_s.tr("_", "-")
54
+ # A convenience method for wrapping the result of a query in a Record
55
+ # object. This assumes that the result is a singleton, not a collection.
56
+ def record(path, parameters = {})
57
+ self.class.record(path, parameters)
56
58
  end
57
- end
59
+
60
+ class << self
61
+ def new_with_type(type, hash)
62
+ "Basecamp::#{type.classify}".constantize.new(hash)
63
+ end
64
+
65
+ def record(path, parameters = {})
66
+ result = Basecamp::Base.new.request(path, parameters)
67
+ (result && !result.empty?) ? new(result.values.first) : nil
68
+ end
69
+
70
+ # A convenience method for wrapping the result of a query in Record
71
+ # objects. This assumes that the result is a collection--any singleton
72
+ # result will be wrapped in an array.
73
+ def records(path, parameters = {})
74
+ result = Basecamp::Base.new.request(path, parameters)
75
+ node_singular = name.demodulize.underscore.dasherize
76
+ node_plural = node_singular.pluralize
77
+
78
+ # FIX: rename FileCategory class to AttachmentCategory
79
+ # node_plural = 'attachment-categories' if node_plural == 'file-categories'
80
+
81
+ result = result[node_plural][node_singular] or return []
82
+ result = [result] unless Array === result
83
+ result.map { |row| new(row) }
84
+ end
85
+ end
86
+ end
58
87
  end
@@ -1,29 +1,29 @@
1
1
  module Basecamp
2
2
  class Resource < ActiveResource::Base #:nodoc:
3
+ def prefix_options
4
+ id ? {} : super
5
+ end
6
+
3
7
  class << self
4
8
  def parent_resources(*parents)
5
9
  @parent_resources = parents
6
10
  end
7
-
11
+
8
12
  def element_name
9
13
  name.split(/::/).last.underscore
10
14
  end
11
-
15
+
12
16
  def prefix_source
13
17
  @parent_resources.map { |resource| "/#{resource.to_s.pluralize}/:#{resource}_id/" }.join
14
18
  end
15
-
19
+
16
20
  def prefix(options={})
17
21
  if options.any?
18
22
  options.map { |name, value| "/#{name.to_s.chomp('_id').pluralize}/#{value}/" }.join
19
23
  else
20
- super
24
+ '/'
21
25
  end
22
- end
23
- end
24
-
25
- def prefix_options
26
- id ? {} : super
26
+ end
27
27
  end
28
28
  end
29
29
  end
@@ -1,17 +1,19 @@
1
1
  module Basecamp
2
2
  class TimeEntry < Resource
3
3
  parent_resources :project, :todo_item
4
-
5
- def self.all(project_id, page=0)
6
- find(:all, :params => { :project_id => project_id, :page => page })
4
+
5
+ def todo_item(options={})
6
+ @todo_item ||= todo_item_id && TodoItem.find(todo_item_id, options)
7
7
  end
8
+
9
+ class << self
10
+ def all(project_id, page=0)
11
+ find(:all, :params => { :project_id => project_id, :page => page })
12
+ end
8
13
 
9
- def self.report(options={})
10
- find(:all, :from => :report, :params => options)
14
+ def report(options={})
15
+ find(:all, :from => :report, :params => options)
16
+ end
11
17
  end
12
-
13
- def todo_item(options={})
14
- @todo_item ||= todo_item_id && TodoItem.find(todo_item_id, options)
15
- end
16
18
  end
17
19
  end
@@ -1,23 +1,23 @@
1
1
  module Basecamp
2
2
  class TodoItem < Resource
3
3
  parent_resources :todo_list
4
-
4
+
5
5
  def todo_list(options={})
6
- @todo_list ||= TodoList.find(todo_list_id, options)
6
+ @todo_list ||= TodoList.find(self.todo_list_id, options)
7
7
  end
8
-
8
+
9
9
  def time_entries(options={})
10
10
  @time_entries ||= TimeEntry.find(:all, :params => options.merge(:todo_item_id => id))
11
11
  end
12
-
12
+
13
13
  def comments(options = {})
14
14
  @comments ||= Comment.find(:all, :params => options.merge(:todo_item_id => id))
15
15
  end
16
-
16
+
17
17
  def complete!
18
18
  put(:complete)
19
19
  end
20
-
20
+
21
21
  def uncomplete!
22
22
  put(:uncomplete)
23
23
  end
@@ -1,22 +1,24 @@
1
1
  module Basecamp
2
2
  class TodoList < Resource
3
3
  parent_resources :project
4
-
5
- # Returns all lists for a project. If complete is true, only completed lists
6
- # are returned. If complete is false, only uncompleted lists are returned.
7
- def self.all(project_id, complete=nil)
8
- filter = case complete
9
- when nil then "all"
10
- when true then "finished"
11
- when false then "pending"
12
- else raise ArgumentError, "invalid value for `complete'"
13
- end
14
-
15
- find(:all, :params => { :project_id => project_id, :filter => filter })
16
- end
17
-
4
+
18
5
  def todo_items(options={})
19
6
  @todo_items ||= TodoItem.find(:all, :params => options.merge(:todo_list_id => id))
20
7
  end
8
+
9
+ class << self
10
+ # Returns all lists for a project. If complete is true, only completed lists
11
+ # are returned. If complete is false, only uncompleted lists are returned.
12
+ def all(project_id, complete=nil)
13
+ filter = case complete
14
+ when nil then "all"
15
+ when true then "finished"
16
+ when false then "pending"
17
+ else raise ArgumentError, "invalid value for `complete'"
18
+ end
19
+
20
+ find(:all, :params => { :project_id => project_id, :filter => filter })
21
+ end
22
+ end
21
23
  end
22
24
  end
@@ -1,3 +1,3 @@
1
1
  module Basecamp #:nodoc:
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
data/lib/basecamp.rb CHANGED
@@ -8,11 +8,17 @@ require 'activeresource'
8
8
 
9
9
  require 'basecamp/base'
10
10
  require 'basecamp/resource'
11
+ require 'basecamp/record'
11
12
  require 'basecamp/attachment'
12
13
  require 'basecamp/comment'
14
+ require 'basecamp/company'
13
15
  require 'basecamp/connection'
16
+ require 'basecamp/file_category'
14
17
  require 'basecamp/message'
15
- require 'basecamp/record'
18
+ require 'basecamp/milestone'
19
+ require 'basecamp/project'
20
+ require 'basecamp/person'
21
+ require 'basecamp/post_category'
16
22
  require 'basecamp/time_entry'
17
23
  require 'basecamp/todoitem'
18
24
  require 'basecamp/todolist'
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ describe Basecamp::AttachmentCategory do
4
+ before(:each) do
5
+ establish_connection
6
+ end
7
+
8
+ it "should return the list of file categories for the given project" do
9
+ list = Basecamp::AttachmentCategory.list(TEST_PROJECT_ID)
10
+ list.should be_kind_of(Array)
11
+ end
12
+ end