minmb-teambox-client 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/Gemfile +11 -0
  2. data/Gemfile.lock +54 -0
  3. data/History +6 -0
  4. data/License +20 -0
  5. data/README.rdoc +30 -0
  6. data/Rakefile +44 -0
  7. data/VERSION +1 -0
  8. data/examples/conversation.rb +12 -0
  9. data/examples/task.rb +17 -0
  10. data/examples/users.rb +19 -0
  11. data/lib/teambox-client/models/activity.rb +27 -0
  12. data/lib/teambox-client/models/comment.rb +55 -0
  13. data/lib/teambox-client/models/conversation.rb +26 -0
  14. data/lib/teambox-client/models/divider.rb +15 -0
  15. data/lib/teambox-client/models/invitation.rb +21 -0
  16. data/lib/teambox-client/models/membership.rb +15 -0
  17. data/lib/teambox-client/models/note.rb +15 -0
  18. data/lib/teambox-client/models/organization.rb +18 -0
  19. data/lib/teambox-client/models/page.rb +20 -0
  20. data/lib/teambox-client/models/page_slot.rb +16 -0
  21. data/lib/teambox-client/models/person.rb +19 -0
  22. data/lib/teambox-client/models/project.rb +52 -0
  23. data/lib/teambox-client/models/task.rb +66 -0
  24. data/lib/teambox-client/models/task_list.rb +26 -0
  25. data/lib/teambox-client/models/teambox_data.rb +16 -0
  26. data/lib/teambox-client/models/upload.rb +27 -0
  27. data/lib/teambox-client/models/user.rb +11 -0
  28. data/lib/teambox-client/reference_list.rb +49 -0
  29. data/lib/teambox-client/result_set.rb +72 -0
  30. data/lib/teambox-client/teambox.rb +288 -0
  31. data/lib/teambox-client/teambox_oauth.rb +37 -0
  32. data/lib/teambox-client.rb +8 -0
  33. data/minmb-teambox-client.gemspec +96 -0
  34. data/spec/client_spec.rb +17 -0
  35. data/spec/conversation_spec.rb +29 -0
  36. data/spec/project_spec.rb +11 -0
  37. data/spec/resource_spec.rb +77 -0
  38. data/spec/result_set_spec.rb +55 -0
  39. data/spec/spec_helper.rb +7 -0
  40. data/spec/task_lists_spec.rb +17 -0
  41. data/spec/tasks_spec.rb +18 -0
  42. metadata +251 -0
@@ -0,0 +1,27 @@
1
+ module Teambox
2
+ class Upload < Teambox::Resource
3
+ def user
4
+ get_or_make_reference('User', @data, 'user_id')
5
+ end
6
+
7
+ def project
8
+ get_or_make_reference('Project', @data, 'project_id')
9
+ end
10
+
11
+ def page
12
+ get_or_make_reference('Project', @data, 'project_id')
13
+ end
14
+
15
+ def page_slot
16
+ get_or_make_reference('PageSlot', @data, 'page_slot_id')
17
+ end
18
+
19
+ def comment
20
+ get_or_make_reference('Comment', @data, 'comment_id')
21
+ end
22
+
23
+ def url #:nodoc:
24
+ "/projects/#{@data['project_id']}/#{@data['id']}"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module Teambox
2
+ class User < Teambox::Resource
3
+ def name
4
+ "#{@data['first_name']} #{@data['last_name']}"
5
+ end
6
+
7
+ def url #:nodoc:
8
+ "/users/#{@data['id']}"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,49 @@
1
+ # Methods for handling references list
2
+ module ReferenceList
3
+ # Generate references hash from JSON data
4
+ def generate_references(data)
5
+ @references = {}
6
+ @references.tap { |h| data.each {|ref| h[ref['type'] + ref['id'].to_s] = Teambox.create_model(ref['type'], ref, self) } }
7
+ end
8
+
9
+ # References resource in the current Teambox::ResultSet
10
+ def set_reference(klass, resource)
11
+ real_resource = if resource.is_a? Teambox::Resource
12
+ resource
13
+ else
14
+ Teambox.const_get(klass).new(resource, self)
15
+ end
16
+
17
+ @references[klass.to_s + real_resource.id.to_s] = real_resource
18
+ real_resource
19
+ end
20
+
21
+ # Gets a referenced object. e.g:
22
+ # get_reference('User', 1)
23
+ def get_reference(klass, id)
24
+ @references[klass.to_s + (id||'').to_s]
25
+ end
26
+
27
+ # get reference based on data. Makes new resource if reference does not exist. e.g.
28
+ # get_or_make_reference('User', @data, 'user_id')
29
+ def get_or_make_reference(klass, data, field_id)
30
+ get_reference(klass, data[field_id]) || set_reference(klass, {}.merge({'id' => data[field_id]}))
31
+ end
32
+
33
+ # get a list of references based on data. Makes a new resource for each references which doesnot exist.
34
+ def get_or_make_references(klass, data, field_id, field_data=nil)
35
+ if data[field_data]
36
+ data[field_data].map do |object|
37
+ get_reference(klass, object['id']) ||
38
+ set_reference(klass, object)
39
+ end
40
+ elsif data[field_id]
41
+ data[field_id].map do |object_id|
42
+ get_reference(klass, object_id) ||
43
+ set_reference(klass, {'id' => object_id})
44
+ end
45
+ else
46
+ []
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,72 @@
1
+ module Teambox
2
+ # This represents a list of objects along with referenced objects returned by the API.
3
+ #
4
+ # == Basic Usage
5
+ #
6
+ # A Teambox::ResultSet is usually returned from a get request from Teambox::Client. e.g.:
7
+ #
8
+ # list = client.get('/projects') # Teambox::ResultSet
9
+ # puts "Returned #{list.length} projects"
10
+ #
11
+ # The Teambox API only returns a limited amount of objects per request (currently 50),
12
+ # so the methods prev, next, and empty? are provided to navigate the list of objects.
13
+ # e.g. To get all projects, you can use something like:
14
+ #
15
+ # project_list = []
16
+ # list = client.get('/projects')
17
+ # while !list.empty?
18
+ # items += list.objects
19
+ # list = list.prev
20
+ # end
21
+ #
22
+ class ResultSet
23
+ include ReferenceList
24
+ attr_reader :first_id, :last_id, :client, :references, :objects
25
+
26
+ def initialize(client, request, objects, references) #:nodoc:
27
+ @client = client
28
+ @request = request
29
+ generate_references(references)
30
+ @objects = objects.map { |o| Teambox.create_model(o['type'], o, self) }
31
+
32
+ id_list = objects.map{|obj| obj['id']}.sort
33
+ @first_id = id_list[0]
34
+ @last_id = id_list[-1]
35
+ end
36
+
37
+ # Yields for each object
38
+ def each(&block)
39
+ @objects.each(&block)
40
+ end
41
+
42
+ # Yields for each object
43
+ def map(&block)
44
+ @objects.map(&block)
45
+ end
46
+
47
+ # Length of object list
48
+ def length
49
+ @objects.length
50
+ end
51
+
52
+ # Is the object list empty?
53
+ def empty?
54
+ @objects.length == 0
55
+ end
56
+
57
+ # Gets older items as a Teambox::ResultSet
58
+ def prev
59
+ @client.get(@request[:url], {}, :query => (@request[:query]).merge(:max_id => @first_id))
60
+ end
61
+
62
+ # Gets newer items as a Teambox::ResultSet
63
+ def next
64
+ @client.get(@request[:url], {}, :query => (@request[:query]).merge(:since_id => @last_id))
65
+ end
66
+
67
+ # Indexes into object array
68
+ def [](idx)
69
+ @objects[idx]
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,288 @@
1
+ # Teambox API wrapper. See Teambox::Client for more details.
2
+ module Teambox
3
+ # This is the entrypoint to the Teambox API.
4
+ #
5
+ # == Basic Usage
6
+ #
7
+ # Create a Teambox::Client, passing in your authentication detials in :auth.
8
+ # Then use one of the helper methods to get data.
9
+ #
10
+ # client = Teambox::Client.new(:auth => {:user => 'frank', :password => 'papapa'})
11
+ # client.current_user # == frank
12
+ # client.project('earthworks')
13
+ #
14
+ # The methods get, post, put, delete, and safe_get are provided in case you want to
15
+ # access an API method not covered by a helper. e.g.:
16
+ #
17
+ # client.get('/account')
18
+ #
19
+ # The API will either return a Teambox::Resource, or a Teambox::ResultSet in the case of
20
+ # index methods.
21
+ #
22
+ # Errors returned by the api are thrown as a Teambox::APIError, so beware of this when writing
23
+ # your api code.
24
+ #
25
+ # begin
26
+ # client.project('earthworks').create_conversation(:name => 'Serious discussion', :body => 'We need a serious discussion')
27
+ # rescue Teambox::APIError => e
28
+ # puts "Something went wrong: #{e.inspect}"
29
+ # end
30
+ #
31
+ # == OAuth authentication
32
+ #
33
+ # To authenticate via OAuth (currently not supported on teambox.com), you will need
34
+ # to pass in your consumer key, secret, and redirect url in :auth. Then use authorize_url
35
+ # and authorize to complete authentication.
36
+ #
37
+ # client = Teambox::Client.new(:auth => {
38
+ # :oauth_app_id => 'h6K1Ru9sVFbPGEK5v9gQFPHTNZ5IRsVCGNeGENQ3',
39
+ # :oauth_app_secret => 'fec4x9P7atC666JzF6WEZIeY6pVv1lCp6aLfVJBw',
40
+ # :redirect_uri => 'http://www.myapp.com/auth/teambox'})
41
+ # client.authorize_url # Open this in your browser
42
+ # client.authenticate(:oauth_verifier => '1234') # Code returned from teambox
43
+ #
44
+ #
45
+ class Client
46
+ include HTTParty
47
+ include Teambox::OAuth
48
+
49
+ # Initializes the Teambox client
50
+ def initialize(opts = {})
51
+ opts[:base_uri] ||= 'https://teambox.com/api/1'
52
+ self.class.base_uri(opts[:base_uri])
53
+
54
+ @base_uri = opts[:base_uri]
55
+ @auth = opts[:auth] || {}
56
+ @consumer = consumer if oauth?
57
+
58
+ authenticate(opts[:auth])
59
+ end
60
+
61
+ # Returns true if oauth is being used for authentication
62
+ def oauth?
63
+ !@auth[:oauth_app_id].nil?
64
+ end
65
+
66
+ # Returns the URL required to authenticate access if using OAuth
67
+ def authorize_url(opts={})
68
+ if oauth?
69
+ @consumer.web_server.authorize_url({:redirect_uri => @auth[:redirect_uri]}.merge(opts))
70
+ else
71
+ nil
72
+ end
73
+ end
74
+
75
+ # Authenticates the client with teambox
76
+ def authenticate(opts={})
77
+ @current_user = nil
78
+ if oauth?
79
+ if opts[:oauth_verifier]
80
+ authorize_from_request(opts[:oauth_verifier])
81
+ elsif opts[:oauth_token]
82
+ @auth.merge!({:oauth_token => opts[:oauth_token]})
83
+ authorize_from_access
84
+ end
85
+ else
86
+ if opts[:user]
87
+ @auth.merge!({:user => opts[:user], :password => opts[:password]})
88
+ self.class.basic_auth(@auth[:user], @auth[:password])
89
+ end
90
+ end
91
+
92
+ !current_user.nil?
93
+ end
94
+
95
+ # Is the client is authenticated?
96
+ def authenticated?
97
+ !current_user.nil?
98
+ end
99
+
100
+ # Current user the client is logged in as
101
+ def current_user
102
+ @current_user ||= safe_get('/account')
103
+ end
104
+
105
+ # Performs a GET on path
106
+ def get(path, query={}, options={})
107
+ api_unwrap perform_request(:get, path, {:query => query}.merge(options)), {:url => path, :query => query}
108
+ end
109
+
110
+ # Performs a POST on path
111
+ def post(path, query={}, options={})
112
+ api_unwrap perform_request(:post, path, {:body => query}.merge(options)), {:url => path, :body => query}
113
+ end
114
+
115
+ # Performs a PUT on path
116
+ def put(path, query={}, options={})
117
+ api_unwrap perform_request(:put, path, {:body => query}.merge(options)), {:url => path, :body => query}
118
+ end
119
+
120
+ # Performs a DELETE on path
121
+ def delete(path, query={}, options={})
122
+ api_unwrap perform_request(:delete, path, {:body => query}.merge(options)), {:url => path, :body => query}
123
+ end
124
+
125
+ # Performs a GET on path, catching any exceptions
126
+ def safe_get(path, query={}, options={})
127
+ api_unwrap(perform_request(:get, path, {:query => query}.merge(options)), {:url => path, :query => query}) rescue nil
128
+ end
129
+
130
+ # urls
131
+
132
+ # Gets a list Teambox::Project corresponding to the current users project list
133
+ def projects(query={})
134
+ get('/projects', query)
135
+ end
136
+
137
+ # Gets a Teambox::Project by id or permalink
138
+ def project(id)
139
+ get("/projects/#{id}")
140
+ end
141
+
142
+ protected
143
+
144
+ # Internal request handler
145
+ def perform_request(method, path, options)
146
+ options[:headers] ||= {}
147
+ headers = options[:headers]
148
+
149
+ options[:query] ||= {}
150
+ options[:query][:access_token] = @access_token.token if @access_token
151
+ self.class.send(method, path, options)
152
+ end
153
+
154
+ # Decodes response and returns appropriate API object
155
+ def api_unwrap(response, request={})
156
+ data = JSON.parse(response.body) rescue nil
157
+ if [200, 201, 202].include? response.response.code.to_i
158
+ raise StandardError.new('Please update your Teambox') unless response.has_key?('type')
159
+ if data['type'] == 'List'
160
+ ResultSet.new(self, request, data['objects'], data['references'])
161
+ else
162
+ Teambox.create_model(data['type'], data, ResultSet.new(self, request, [], []))
163
+ end
164
+ else
165
+ error_list = data ? data['errors'] : {'type' => 'UnknownError', 'message' => data}
166
+ error_list['type'] = error_list['type'][0] if error_list['type'].is_a? Array
167
+ error_list['message'] = error_list['message'][0] if error_list['message'].is_a? Array
168
+ raise APIError.new(response.response.code, error_list)
169
+ end
170
+ end
171
+
172
+ end
173
+
174
+ # Exception handling for API Errors
175
+ class APIError < StandardError
176
+ attr_reader :error_type # Type of error. See the API Docs for a list of error types
177
+ attr_reader :status_code # HTTP status code returned
178
+ def initialize(status_code, details)
179
+ @error_type = details['type'].to_sym
180
+ @status_code = status_code
181
+ super(details['message'] || details['type'])
182
+ end
183
+ end
184
+
185
+ # Exception handling unknown Teambox::Resource types
186
+ class UnknownResourceError < APIError
187
+ def initialize(type)
188
+ super(200, {'type' => 'UnknownResource', :message => "Unknown Resource #{type}"})
189
+ end
190
+ end
191
+
192
+ def self.create_model(type, data, list=nil) #:nodoc:
193
+ klass = const_get(type.to_sym) rescue nil
194
+ if klass
195
+ klass.new data, list
196
+ else
197
+ throw UnknownResourceError.new(type)
198
+ end
199
+ end
200
+
201
+ # Represents an object located on Teambox.
202
+ #
203
+ # Most resources belonging to a Teambox::Project can be modified, in which case you can use
204
+ # the save, destroy, and reload methods.
205
+ #
206
+ # All attributes are exposed by the method_missing handler.
207
+ class Resource
208
+ include ReferenceList
209
+
210
+ attr_accessor :list, :data
211
+ attr_reader :references
212
+
213
+ def initialize(data, result_list=nil)
214
+ @data = data
215
+ @list = result_list || ResultSet.new(nil, nil, [], [])
216
+ if @data['references']
217
+ generate_references(@data['references'])
218
+ else
219
+ @references = result_list ? result_list.references : {}
220
+ end
221
+ end
222
+
223
+ def id
224
+ @data['id']
225
+ end
226
+
227
+ # The time this object was created
228
+ def created_at
229
+ @data.has_key?('created_at') ? Time.parse(data['created_at']) : nil
230
+ end
231
+
232
+ # The last time this object was updated
233
+ def updated_at
234
+ @data.has_key?('updated_at') ? Time.parse(data['updated_at']) : nil
235
+ end
236
+
237
+ def next #:nodoc:
238
+ nil
239
+ end
240
+
241
+ def prev #:nodoc:
242
+ nil
243
+ end
244
+
245
+ # Location of this object on teambox, used for updating
246
+ def url
247
+ nil
248
+ end
249
+
250
+ # Reloads the resource from source data
251
+ # @returns self
252
+ def reload
253
+ if @list.client && url
254
+ updated = @list.client.get(url) rescue nil
255
+ unless updated.nil?
256
+ @data = updated.data
257
+ end
258
+ end
259
+ self
260
+ end
261
+
262
+ # Saves the resource to teambox
263
+ # @returns true if saved
264
+ def save
265
+ if @list.client && url
266
+ updated = @list.client.put(url, @data) rescue nil
267
+ return true unless updated.nil?
268
+ end
269
+ false
270
+ end
271
+
272
+ # Destroys the resource on teambox
273
+ def destroy
274
+ @list.client.delete(url) if @list.client && url
275
+ true
276
+ end
277
+
278
+ def method_missing(method, *args, &block) #:nodoc:
279
+ @data.include?(method.to_s) ? @data[method.to_s] : super
280
+ end
281
+
282
+ def inspect
283
+ "#<#{self.class} @data=#{@data.inspect} list=#{@list.object_id}>"
284
+ end
285
+ end
286
+ end
287
+
288
+ Dir[File.expand_path('models/*.rb', File.dirname(__FILE__))].each { |f| require f }
@@ -0,0 +1,37 @@
1
+ module Teambox
2
+ # Methods used to authenticate with OAuth on teambox.com. Normally you won't have to call these.
3
+ module OAuth
4
+ attr_accessor :access_token
5
+
6
+ # OAuth consumer required for authentication
7
+ def consumer
8
+ return nil if @auth[:oauth_app_id].nil?
9
+ @consumer ||= OAuth2::Client.new(@auth[:oauth_app_id], @auth[:oauth_app_secret],
10
+ :site => consumer_url,
11
+ :access_token_path => consumer_url+'oauth/token',
12
+ :authorize_path => consumer_url+'oauth/authorize?response_type=code')
13
+ end
14
+
15
+ def consumer_url
16
+ uri = URI.parse(self.class.base_uri)
17
+ uri.path = '/'
18
+ uri.to_s
19
+ end
20
+
21
+ # Is the client authorized via OAuth?
22
+ def authorized?
23
+ !@access_token.nil?
24
+ end
25
+
26
+ def authorize_from_request(verifier)
27
+ @access_token = consumer.web_server.get_access_token(verifier, :redirect_uri => @auth[:redirect_uri], :grant_type => 'authorization_code')
28
+ @auth[:oauth_token] = @access_token.token
29
+ @access_token
30
+ end
31
+
32
+ # Authorizes the client from an existing OAuth token
33
+ def authorize_from_access
34
+ @access_token = ::OAuth2::AccessToken.new(nil, @auth[:oauth_token])
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,8 @@
1
+ require "httparty"
2
+ require "json"
3
+ require "oauth2"
4
+ require "uri"
5
+
6
+
7
+ directory = File.expand_path(File.dirname(__FILE__))
8
+ %w(teambox_oauth reference_list teambox result_set).each { |lib| require File.join(directory, 'teambox-client', lib) }
@@ -0,0 +1,96 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "minmb-teambox-client"
8
+ s.version = "0.4.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Pablo Villalba", "James Urquhart", "Martin H\u{e4}ger"]
12
+ s.date = "2012-08-28"
13
+ s.description = "Provides methods to read and write to Teambox for ruby apps"
14
+ s.email = "martin.hager@minmb.se"
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ "Gemfile",
20
+ "Gemfile.lock",
21
+ "History",
22
+ "License",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "examples/conversation.rb",
27
+ "examples/task.rb",
28
+ "examples/users.rb",
29
+ "lib/teambox-client.rb",
30
+ "lib/teambox-client/models/activity.rb",
31
+ "lib/teambox-client/models/comment.rb",
32
+ "lib/teambox-client/models/conversation.rb",
33
+ "lib/teambox-client/models/divider.rb",
34
+ "lib/teambox-client/models/invitation.rb",
35
+ "lib/teambox-client/models/membership.rb",
36
+ "lib/teambox-client/models/note.rb",
37
+ "lib/teambox-client/models/organization.rb",
38
+ "lib/teambox-client/models/page.rb",
39
+ "lib/teambox-client/models/page_slot.rb",
40
+ "lib/teambox-client/models/person.rb",
41
+ "lib/teambox-client/models/project.rb",
42
+ "lib/teambox-client/models/task.rb",
43
+ "lib/teambox-client/models/task_list.rb",
44
+ "lib/teambox-client/models/teambox_data.rb",
45
+ "lib/teambox-client/models/upload.rb",
46
+ "lib/teambox-client/models/user.rb",
47
+ "lib/teambox-client/reference_list.rb",
48
+ "lib/teambox-client/result_set.rb",
49
+ "lib/teambox-client/teambox.rb",
50
+ "lib/teambox-client/teambox_oauth.rb",
51
+ "minmb-teambox-client.gemspec",
52
+ "spec/client_spec.rb",
53
+ "spec/conversation_spec.rb",
54
+ "spec/project_spec.rb",
55
+ "spec/resource_spec.rb",
56
+ "spec/result_set_spec.rb",
57
+ "spec/spec_helper.rb",
58
+ "spec/task_lists_spec.rb",
59
+ "spec/tasks_spec.rb"
60
+ ]
61
+ s.homepage = "http://github.com/minmb/teambox-ruby-client"
62
+ s.require_paths = ["lib"]
63
+ s.rubygems_version = "1.8.24"
64
+ s.summary = "A ruby gem wrapper for Teambox API"
65
+
66
+ if s.respond_to? :specification_version then
67
+ s.specification_version = 3
68
+
69
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
70
+ s.add_runtime_dependency(%q<minmb-teambox-client>, [">= 0"])
71
+ s.add_development_dependency(%q<rake>, [">= 0"])
72
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
73
+ s.add_development_dependency(%q<rspec>, [">= 0"])
74
+ s.add_runtime_dependency(%q<httparty>, ["~> 0.7.4"])
75
+ s.add_runtime_dependency(%q<oauth2>, ["~> 0.1.1"])
76
+ s.add_runtime_dependency(%q<json>, ["~> 1.5.1"])
77
+ else
78
+ s.add_dependency(%q<minmb-teambox-client>, [">= 0"])
79
+ s.add_dependency(%q<rake>, [">= 0"])
80
+ s.add_dependency(%q<jeweler>, [">= 0"])
81
+ s.add_dependency(%q<rspec>, [">= 0"])
82
+ s.add_dependency(%q<httparty>, ["~> 0.7.4"])
83
+ s.add_dependency(%q<oauth2>, ["~> 0.1.1"])
84
+ s.add_dependency(%q<json>, ["~> 1.5.1"])
85
+ end
86
+ else
87
+ s.add_dependency(%q<minmb-teambox-client>, [">= 0"])
88
+ s.add_dependency(%q<rake>, [">= 0"])
89
+ s.add_dependency(%q<jeweler>, [">= 0"])
90
+ s.add_dependency(%q<rspec>, [">= 0"])
91
+ s.add_dependency(%q<httparty>, ["~> 0.7.4"])
92
+ s.add_dependency(%q<oauth2>, ["~> 0.1.1"])
93
+ s.add_dependency(%q<json>, ["~> 1.5.1"])
94
+ end
95
+ end
96
+
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Teambox::Client do
4
+ before do
5
+ @client = make_teambox_client
6
+ end
7
+
8
+ it "should authenticate" do
9
+ @client.authenticated?.should == true
10
+ end
11
+
12
+ it "should return the current user" do
13
+ user = @client.current_user
14
+ user.class.should == Teambox::User
15
+ user.username.should == 'frank'
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Teambox::Conversation do
4
+ before do
5
+ @client = make_teambox_client
6
+ @project = @client.project('earthworks')
7
+ end
8
+
9
+ it "should query conversations" do
10
+ @project.conversations.length.should_not == 0
11
+ end
12
+
13
+ it "should make a conversation" do
14
+ conversation = @project.create_conversation({:name => 'Hello', :body => 'World'})
15
+ conversation.name.should == 'Hello'
16
+ end
17
+
18
+ it "should list recent comments in a conversation" do
19
+ recent_comments = @project.conversations[0].recent_comments
20
+ recent_comments.length.should > 0
21
+ recent_comments.each {|c| c.class.should == Teambox::Comment}
22
+ end
23
+
24
+ it "should list the first comment in a conversation" do
25
+ comment = @project.conversations[0].first_comment
26
+ comment.should_not == nil
27
+ comment.class.should == Teambox::Comment
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe Teambox::Project do
4
+ before do
5
+ @client = make_teambox_client
6
+ end
7
+
8
+ it "should query a project" do
9
+ @client.project('earthworks').permalink.should == 'earthworks'
10
+ end
11
+ end