box-api 0.1.9 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/Gemfile.lock +1 -1
  2. data/box-api.gemspec +1 -1
  3. data/doc/Box.html +5 -5
  4. data/doc/Box/Account.html +430 -188
  5. data/doc/Box/Api.html +731 -428
  6. data/doc/Box/Api/AccountExceeded.html +3 -3
  7. data/doc/Box/Api/EmailInvalid.html +6 -6
  8. data/doc/Box/Api/EmailTaken.html +3 -3
  9. data/doc/Box/Api/ErrorStatus.html +3 -3
  10. data/doc/Box/Api/Exception.html +3 -3
  11. data/doc/Box/Api/Generic.html +3 -3
  12. data/doc/Box/Api/InvalidFolder.html +6 -6
  13. data/doc/Box/Api/InvalidInput.html +3 -3
  14. data/doc/Box/Api/InvalidName.html +3 -3
  15. data/doc/Box/Api/NameTaken.html +3 -3
  16. data/doc/Box/Api/NoAccess.html +3 -3
  17. data/doc/Box/Api/NoParent.html +3 -3
  18. data/doc/Box/Api/NotAuthorized.html +3 -3
  19. data/doc/Box/Api/Restricted.html +6 -6
  20. data/doc/Box/Api/SizeExceeded.html +3 -3
  21. data/doc/Box/Api/Unknown.html +3 -3
  22. data/doc/Box/Api/UnknownResponse.html +6 -6
  23. data/doc/Box/Api/UploadFailed.html +6 -6
  24. data/doc/Box/Comment.html +560 -0
  25. data/doc/Box/File.html +318 -53
  26. data/doc/Box/Folder.html +298 -136
  27. data/doc/Box/Item.html +398 -348
  28. data/doc/_index.html +22 -7
  29. data/doc/class_list.html +1 -1
  30. data/doc/css/full_list.css +2 -0
  31. data/doc/css/style.css +2 -0
  32. data/doc/file.README.html +3 -3
  33. data/doc/frames.html +1 -1
  34. data/doc/index.html +3 -3
  35. data/doc/js/full_list.js +23 -6
  36. data/doc/method_list.html +233 -89
  37. data/doc/top-level-namespace.html +3 -3
  38. data/examples/files.rb +3 -5
  39. data/examples/login.rb +14 -18
  40. data/lib/box/account.rb +57 -16
  41. data/lib/box/api.rb +34 -1
  42. data/lib/box/api/exceptions.rb +3 -0
  43. data/lib/box/comment.rb +50 -0
  44. data/lib/box/file.rb +31 -0
  45. data/lib/box/folder.rb +57 -1
  46. data/lib/box/item.rb +20 -4
  47. data/spec/account_spec.rb +4 -0
  48. data/spec/file_spec.rb +37 -0
  49. data/spec/folder_spec.rb +78 -0
  50. data/spec/helper/account.rb +1 -1
  51. metadata +55 -61
@@ -6,7 +6,7 @@
6
6
  <title>
7
7
  Top Level Namespace
8
8
 
9
- &mdash; Documentation by YARD 0.7.2
9
+ &mdash; Documentation by YARD 0.7.3
10
10
 
11
11
  </title>
12
12
 
@@ -94,9 +94,9 @@
94
94
  </div>
95
95
 
96
96
  <div id="footer">
97
- Generated on Mon Aug 8 13:05:22 2011 by
97
+ Generated on Tue Nov 8 15:32:24 2011 by
98
98
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
99
- 0.7.2 (ruby-1.9.2).
99
+ 0.7.3 (ruby-1.9.3).
100
100
  </div>
101
101
 
102
102
  </body>
@@ -1,5 +1,6 @@
1
- # log in using the login example, so we don't have to duplicate code
2
1
  $: << File.dirname(__FILE__) # for 1.9
2
+
3
+ # log in using the login example, so we don't have to duplicate code
3
4
  require 'login'
4
5
 
5
6
  # get the root of the folder structure
@@ -16,18 +17,15 @@ index = gets
16
17
 
17
18
  begin
18
19
  # grab the folder they selected
19
- # to_i or [] will throw an exception if the index is invalid or out of range respectively
20
20
  folder = root.folders[index.to_i]
21
21
  rescue
22
- # they picked an invalid folder!
22
+ # they picked a folder that broke our script!
23
23
  puts "You picked an invalid folder, please try again."
24
24
  exit
25
25
  end
26
26
 
27
- # the folder they picked was valid
28
27
  puts "Excellent choice, here are the contents of that folder"
29
28
 
30
- # show the selected folder
31
29
  puts "FOLDER: #{ folder.name } (#{ folder.id })"
32
30
 
33
31
  # loop through and show each of the sub files and folders
@@ -1,12 +1,11 @@
1
- # add the lib directory to our require path (you do not need this if you install the box-api gem)
1
+ # add the lib directory to our require path (you do not need this if you use the box-api gem)
2
2
  $: << File.dirname(__FILE__) + "/../lib"
3
3
 
4
4
  # we use bundler to keep all of our gems up to date, but it is optional
5
5
  require 'rubygems'
6
6
  require 'bundler/setup'
7
7
 
8
- # we're only using account specific features in this example, so we don't need to require the entire gem
9
- require 'box/account'
8
+ require 'box-api'
10
9
 
11
10
  # launchy is a simple gem that opens a browser, but is completely optional
12
11
  require 'launchy'
@@ -19,18 +18,17 @@ app_data = YAML.load_file(app_data_file)
19
18
  # you need to get your own API key at http://www.box.net/developers/services
20
19
  account = Box::Account.new(app_data['api_key'])
21
20
 
22
- # the user has to authorize your app on the box website, at which point you'll get an auth token
23
- # this auth token works between sessions, so read it from disk if the user has authed before
21
+ # read any auth tokens if they are saved, so we don't have to re-authenticate on Box
24
22
  auth_token = app_data['auth_token']
25
23
 
26
- # now we have enough information to log into the box api, so we try to authorize using the auth token
27
- account.authorize(auth_token) do |auth_url|
24
+ # try to authorize using the auth token, or request a new one if not
25
+ account.authorize(:auth_token => auth_token) do |auth_url|
28
26
  # this block is called if the auth_token is invalid or missing
29
27
 
30
- # we use launchy to open a new browser, but you can do it however you want
28
+ # we use launchy to open a new browser to the given url
31
29
  Launchy.open(auth_url)
32
30
 
33
- # make sure you pause until the user has authorized, or just tell them to restart the program
31
+ # wait until the user authenticates on box before continuing
34
32
  puts "Please press the enter key once you have authorized this application to use your account"
35
33
  gets
36
34
  end
@@ -41,17 +39,15 @@ unless account.authorized?
41
39
  exit
42
40
  end
43
41
 
44
- # this auth token will let us instantly authorize next time this app is run, so we want to save it
45
- # keep in mind that an auth token only works with the same api key, and will expire if the user logs out
42
+ # we managed to log in successfully!
43
+ puts "Logged in as #{ account.login }"
44
+
45
+ # this is so the other example can access the account variable
46
+ @account = account
47
+
48
+ # this auth token will let us instantly authorize next time this app is run, so we save it
46
49
  app_data['auth_token'] = account.auth_token
47
50
 
48
- # we write the auth_token to file so it can be accessed on next run
49
51
  File.open(app_data_file, 'w') do |file|
50
52
  YAML.dump(app_data, file)
51
53
  end
52
-
53
- # we managed to log in successfully!
54
- puts "Logged in as #{ account.info['login'] }"
55
-
56
- # this is so the other example can access the account variable (bad practice)
57
- @account = account
@@ -42,7 +42,7 @@ module Box
42
42
  true
43
43
  end
44
44
 
45
- # Authorize the account using the given auth token, or request
45
+ # Authorize the account using the given auth token/ticket, or request
46
46
  # permission from the user to let this application use their account.
47
47
  #
48
48
  # An auth token can be reused from previous authorizations provided the
@@ -51,8 +51,18 @@ module Box
51
51
  # normally and requires the user to log in and provide access for their
52
52
  # account.
53
53
  #
54
- # @param [Optional, String] auth_token Uses an existing token or requests
55
- # a new one if nil.
54
+ # A ticket can be used for applications that do not block on the user,
55
+ # such as a website, where specifying a redirection url is not possible.
56
+ #
57
+ # In order to maintain backwards compatibility, a ticket can only be
58
+ # specified in the hash syntax, while an auth token can be used in
59
+ # either the hash or string syntax.
60
+ #
61
+ # @param [Optional, String, Hash{:ticket,:auth_token => String}] details
62
+ # Uses an existing auth token or ticket. If nil, a new ticket
63
+ # will be generated and used. If a String, it is assumed to be
64
+ # an auth_token (depreciated). If a Hash, then any values of
65
+ # the :ticket and :auth_token keys will be used to authenticate.
56
66
  # @yield [authorize_url] This block called when the user has not yet
57
67
  # granted this application permission to use their account. You
58
68
  # must have the user navigate to the passed url and authorize
@@ -68,15 +78,24 @@ module Box
68
78
  #
69
79
  # @example Authorize an account using an existing auth token.
70
80
  # auth_token = "saved auth token" # load from file ideally
71
- # account.authorize(auth_token)
81
+ # account.authorize(:auth_token => auth_token)
72
82
  #
73
83
  # @example Combining the above two for the best functionality.
74
84
  # auth_token = "saved auth token" # load from file if possible
75
- # account.authorize(auth_token) do |auth_url|
85
+ # account.authorize(:auth_token => auth_token) do |auth_url|
76
86
  # # auth token was invalid or nil, have the user visit auth_url
77
87
  # end
78
88
  #
79
- def authorize(auth_token = nil)
89
+ def authorize(details = nil)
90
+ # for backwards compatibility
91
+ if details.is_a?(Hash)
92
+ auth_token = details[:auth_token]
93
+ ticket = details[:ticket]
94
+ else
95
+ auth_token = details
96
+ ticket = nil
97
+ end
98
+
80
99
  # use a saved auth token if it is given
81
100
  if auth_token
82
101
  return true if authorize_token(auth_token)
@@ -84,12 +103,12 @@ module Box
84
103
 
85
104
  # the auth token either failed or was not provided
86
105
  # we must try to authorize a ticket, and call the block if that fails
87
- if not authorize_ticket and block_given?
106
+ if not authorize_ticket(ticket) and block_given?
88
107
  # the supplied block should instruct the user to visit this url
89
- yield authorize_url
108
+ yield authorize_url(ticket)
90
109
 
91
110
  # try authorizing once more
92
- authorize_ticket
111
+ authorize_ticket(ticket)
93
112
  end
94
113
 
95
114
  # return our authorized status
@@ -152,17 +171,36 @@ module Box
152
171
  @info != nil
153
172
  end
154
173
 
155
- protected
156
-
157
- # @return [Api] The api currently in use.
158
- attr_reader :api
159
-
160
174
  # Get the cached ticket or request a new one from the Box api.
161
175
  # @return [String] The authorization ticket.
162
176
  def ticket
163
177
  @ticket ||= @api.get_ticket['ticket']
164
178
  end
165
179
 
180
+ # Provides an easy way to access this account's info.
181
+ #
182
+ # @example
183
+ # account.login # returns @info['login']
184
+ def method_missing(sym, *args, &block)
185
+ super unless authorized?
186
+
187
+ # TODO: Use symbols instead of strings
188
+ str = sym.to_s
189
+
190
+ return @info[str] if @info.key?(str)
191
+
192
+ super
193
+ end
194
+
195
+ def respond_to?(sym)
196
+ @info.key?(sym.to_s) or super
197
+ end
198
+
199
+ protected
200
+
201
+ # @return [Api] The api currently in use.
202
+ attr_reader :api
203
+
166
204
  # The url the user needs to visit in order to grant this application
167
205
  # permission to use their account. This requires a ticket, which
168
206
  # is either pulled from the cache or requested.
@@ -171,7 +209,8 @@ module Box
171
209
  # If no ticket is provided, one will be requested and cached.
172
210
  # @return [String] the url used for authorizing this account.
173
211
  #
174
- def authorize_url(ticket = self.ticket)
212
+ def authorize_url(ticket = nil)
213
+ ticket = self.ticket unless ticket
175
214
  "#{ api.base_url }/auth/#{ ticket }"
176
215
  end
177
216
 
@@ -183,7 +222,9 @@ module Box
183
222
  # If no ticket is provided, one will be requested and cached.
184
223
  # @return [String, nil] The auth token if successful otherwise, nil.
185
224
  #
186
- def authorize_ticket(ticket = self.ticket)
225
+ def authorize_ticket(ticket = nil)
226
+ ticket = self.ticket unless ticket
227
+
187
228
  begin
188
229
  response = @api.get_auth_token(ticket)
189
230
 
@@ -129,7 +129,7 @@ module Box
129
129
  end
130
130
  end
131
131
 
132
- raise ErrorStatus, response.code unless response.success? # when the http return code is not normal
132
+ raise ErrorStatus, "HTTP code #{ response.code }" unless response.success? # when the http return code is not normal
133
133
  response
134
134
  end
135
135
 
@@ -298,5 +298,38 @@ module Box
298
298
  def new_copy(path, file_id, name = nil)
299
299
  query_upload('new_copy', file_id, 'upload_ok', :file => ::File.new(path), :new_file_name => name)
300
300
  end
301
+
302
+ # Gets the comments posted on the given item.
303
+ #
304
+ # @param ["file"] target The type of item.
305
+ # @param [String] target_id The id of the item to get.
306
+ def get_comments(target, target_id)
307
+ query_rest('get_comments_ok', :action => :get_comments, :target => target, :target_id => target_id)
308
+ end
309
+
310
+ # Adds a new comment to the given item.
311
+ #
312
+ # @param ["file"] target The type of item.
313
+ # @param [String] target_id The id of the item to add to.
314
+ # @param [String] message The message to use.
315
+ def add_comment(target, target_id, message)
316
+ query_rest('add_comment_ok', :action => :add_comment, :target => target, :target_id => target_id, :message => message)
317
+ end
318
+
319
+ # Deletes a given comment.
320
+ #
321
+ # @param [String] comment_id The id of the comment to delete.
322
+ def delete_comment(comment_id)
323
+ query_rest('delete_comment_ok', :action => :delete_comment, :target_id => comment_id)
324
+ end
325
+
326
+ # Request the HTML embed code for a file.
327
+ #
328
+ # @param [String] id The id of the file to use.
329
+ # @param [Hash] options The properties for the generated preview code.
330
+ # See File#embed_code for a more detailed list of options.
331
+ def file_embed(id, options = Hash.new)
332
+ query_rest('s_create_file_embed', { :action => :create_file_embed, :file_id => id }.merge(options))
333
+ end
301
334
  end
302
335
  end
@@ -71,6 +71,9 @@ module Box
71
71
  AccountExceeded
72
72
  when "filesize_limit_exceeded"
73
73
  SizeExceeded
74
+ # Comment specific responses
75
+ when "get_comments_error", "add_comment_error", "delete_comment_error"
76
+ Generic
74
77
  else
75
78
  Unknown
76
79
  end
@@ -0,0 +1,50 @@
1
+ module Box
2
+ class Comment
3
+ def self.create(api, comments)
4
+ if comments
5
+ comments = [ comments ] if comments.class == Hash
6
+
7
+ comments.collect do |comment|
8
+ sub_comments = comment.delete('reply_comments')
9
+
10
+ comment['id'] = comment.delete('comment_id')
11
+ comment['comments'] = self.create(api, sub_comments['item']) if sub_comments
12
+
13
+ Comment.new(api, comment)
14
+ end
15
+ else
16
+ Array.new
17
+ end
18
+ end
19
+
20
+ # Create a new comment object for a file.
21
+ #
22
+ # @param [Api] api The {Api} instance used to generate requests.
23
+ # @param [Hash] data The hash of initial info for this item, like 'id' and 'message'.
24
+ def initialize(api, data)
25
+ @api = api
26
+ @data = data
27
+ end
28
+
29
+ def id; @data['id']; end
30
+
31
+ def method_missing(sym, *args, &block)
32
+ # TODO: Use symbols instead of strings for keys.
33
+ sym = sym.to_s
34
+
35
+ return @data[sym] if @data.key?(sym)
36
+
37
+ super
38
+ end
39
+
40
+ def respond_to?(sym)
41
+ @data.key?(sym.to_s) or super
42
+ end
43
+
44
+ def delete
45
+ @api.delete_comment(id)
46
+
47
+ true
48
+ end
49
+ end
50
+ end
@@ -40,6 +40,37 @@ module Box
40
40
  self.class.new(api, parent, info)
41
41
  end
42
42
 
43
+ # Get the comments left on this file.
44
+ #
45
+ # @return [Array] An array of {Comment}s.
46
+ def get_comments
47
+ comments = @api.get_comments(type, id)['comments']
48
+
49
+ comments = Comment.create(@api, comments['comment']) if comments
50
+
51
+ @data['comments'] = comments || Array.new
52
+ end
53
+
54
+ # Add a comment to the file.
55
+ #
56
+ # @return [Comment] The created comment.
57
+ def add_comment(message)
58
+ response = @api.add_comment(type, id, message)
59
+
60
+ Comment.create(@api, response['comment']).first
61
+ end
62
+
63
+ # Request the HTML embed code for this file.
64
+ #
65
+ # @param [Optional, Hash{:allow_download,:allow_print,:allow_share,
66
+ # :width,:height,:color => String}] options Options to use
67
+ # when generating the embed code.
68
+ # @return [String] HTML code to use to embed the file.
69
+ #
70
+ def embed_code(options = Hash.new)
71
+ @api.file_embed(id, options)['file_embed_html']
72
+ end
73
+
43
74
  protected
44
75
 
45
76
  # (see Item#get_info)
@@ -122,6 +122,62 @@ module Box
122
122
  end
123
123
  end
124
124
 
125
+ # Get the item at the given path.
126
+ #
127
+ # @param [String] The path to search for. This follows the typical unix
128
+ # path syntax, in that the root folder is '/'. Supports
129
+ # the dot sytax, where '.' is the current folder and
130
+ # '..' is the parent folder.
131
+ #
132
+ # @return [Item] The item that exists at this path, or nil.
133
+ #
134
+ # @example Find a folder based on its absolute path.
135
+ # folder.at('/box/is/awesome')
136
+ #
137
+ # @example Find a file based on a relative path.
138
+ # folder.at('awesome/file.pdf')
139
+ #
140
+ # @example Find a folder using the parent.
141
+ # folder.at('../other/folder')
142
+ def at(target_path)
143
+ # start with this folder
144
+ current = self
145
+
146
+ if target_path.start_with?('/')
147
+ # absolute path, find the root folder
148
+ current = current.parent while current.parent != nil
149
+ end
150
+
151
+ # split each part of the target path
152
+ target_path.split('/').each do |target_name|
153
+ # update current based on the target name
154
+ current = case target_name
155
+ when "", "."
156
+ # no-op
157
+ current
158
+ when ".."
159
+ # use the parent folder
160
+ parent
161
+ else
162
+ # must be a file/folder name, so make sure this is a folder
163
+ return nil unless current and current.type == 'folder'
164
+
165
+ # search for an item with that name
166
+ current.find(:name => target_name, :recursive => false).first
167
+ end
168
+ end
169
+
170
+ if current
171
+ # ends with a slash, so it has to be a folder
172
+ if target_path.end_with?('/') and current.type != 'folder'
173
+ # get the folder with the same name (if it exists)
174
+ current = parent.find(:type => 'folder', :name => name, :recursive => false).first
175
+ end
176
+ end
177
+
178
+ current
179
+ end
180
+
125
181
  protected
126
182
 
127
183
  attr_accessor :cached_tree
@@ -180,7 +236,7 @@ module Box
180
236
  def find!(criteria, recursive)
181
237
  matches = (files + folders).collect do |item| # search over our files and folders
182
238
  match = criteria.all? do |key, value| # make sure all criteria pass
183
- item.send(key) == value.to_s rescue false
239
+ value === item.send(key) rescue false
184
240
  end
185
241
 
186
242
  item if match # use the item if it is a match