ruby-box 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ module RubyBox
2
+ class Comment < Item
3
+
4
+ private
5
+
6
+ def resource_name
7
+ 'comments'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module RubyBox
2
+ class Discussion < Item
3
+
4
+ def comments
5
+ url = "#{RubyBox::API_URL}/#{resource_name}/#{id}/comments"
6
+ resp = @session.get( url )
7
+ resp['entries'].map {|i| Comment.new(@session, i)}
8
+ end
9
+
10
+ private
11
+
12
+ def resource_name
13
+ 'discussions'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module RubyBox
2
+ class RubyBoxError < StandardError
3
+ def initialize(error_json)
4
+ @error_json = error_json
5
+ end
6
+
7
+ def [](key)
8
+ @error_json[key]
9
+ end
10
+ end
11
+
12
+ class ObjectNotFound < StandardError; end
13
+ class AuthError < RubyBoxError; end
14
+ class RequestError < RubyBoxError; end
15
+ class ServerError < StandardError; end
16
+ class ItemNameInUse < RubyBoxError; end
17
+ end
@@ -0,0 +1,70 @@
1
+ module RubyBox
2
+ class File < Item
3
+
4
+ def download
5
+ resp = stream.read
6
+ end
7
+
8
+ def stream( opts={} )
9
+ url = "#{RubyBox::API_URL}/#{resource_name}/#{id}/content"
10
+ @session.do_stream( url, opts )
11
+ end
12
+
13
+ def comments
14
+ url = "#{RubyBox::API_URL}/#{resource_name}/#{id}/comments"
15
+ resp = @session.get( url )
16
+ resp['entries'].map {|i| Comment.new(@session, i)}
17
+ end
18
+
19
+ def upload_content( data )
20
+ url = "#{RubyBox::UPLOAD_URL}/#{resource_name}/content"
21
+ uri = URI.parse(url)
22
+ request = Net::HTTP::Post::Multipart.new(uri.path, {
23
+ "filename" => UploadIO.new(data, "application/pdf", name),
24
+ "folder_id" => parent.id
25
+ })
26
+
27
+ resp = @session.request(uri, request)
28
+ if resp['entries']
29
+ @raw_item = resp['entries'][0]
30
+ else
31
+ @raw_item = resp
32
+ end
33
+ self
34
+ end
35
+
36
+ def update_content( data )
37
+
38
+ url = "#{RubyBox::UPLOAD_URL}/#{resource_name}/#{id}/content"
39
+ uri = URI.parse(url)
40
+
41
+ request = Net::HTTP::Post::Multipart.new(uri.path, {
42
+ "filename" => prepare_upload(data, name),
43
+ "folder_id" => parent.id
44
+ }, {"if-match" => etag })
45
+
46
+ resp = @session.request(uri, request)
47
+ if resp['entries']
48
+ @raw_item = resp['entries'][0]
49
+ else
50
+ @raw_item = resp
51
+ end
52
+ self
53
+ end
54
+
55
+ private
56
+
57
+ def resource_name
58
+ 'files'
59
+ end
60
+
61
+ def update_fields
62
+ ['name', 'description']
63
+ end
64
+
65
+ def prepare_upload(data, fname)
66
+ UploadIO.new(data, "application/pdf", fname)
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,71 @@
1
+ module RubyBox
2
+ class Folder < Item
3
+ def items(item_limit=100, offset=0)
4
+ Enumerator.new do |yielder|
5
+ while true
6
+ url = "#{RubyBox::API_URL}/#{resource_name}/#{id}/items?limit=#{item_limit}&offset=#{offset}"
7
+ resp = @session.get( url )
8
+ resp['entries'].each do |entry|
9
+ yielder.yield(RubyBox::Item.factory(@session, entry))
10
+ end
11
+ offset += resp['entries'].count
12
+ break if resp['offset'].to_i + resp['limit'].to_i >= resp['total_count'].to_i
13
+ end
14
+ end
15
+ end
16
+
17
+ def files(name=nil, item_limit=100, offset=0)
18
+ items(item_limit, offset).select do |item|
19
+ item.kind_of? RubyBox::File and (name.nil? or item.name == name)
20
+ end
21
+ end
22
+
23
+ def folders(name=nil, item_limit=100, offset=0)
24
+ items(item_limit, offset).select do |item|
25
+ item.kind_of? RubyBox::Folder and (name.nil? or item.name == name)
26
+ end
27
+ end
28
+
29
+ def discussions
30
+ url = "#{RubyBox::API_URL}/#{resource_name}/#{id}/discussions"
31
+ resp = @session.get( url )
32
+ resp['entries'].map {|i| Discussion.new(@session, i)}
33
+ end
34
+
35
+ def upload_file(filename, data)
36
+ file = RubyBox::File.new(@session, {
37
+ 'name' => filename,
38
+ 'parent' => RubyBox::User.new(@session, {'id' => id})
39
+ })
40
+
41
+ begin
42
+ resp = file.upload_content(data) #write a new file. If there is a conflict, update the conflicted file.
43
+ rescue RubyBox::ItemNameInUse => e
44
+ file = RubyBox::File.new(@session, {
45
+ 'id' => e['context_info']['conflicts'][0]['id']
46
+ })
47
+ data.rewind
48
+ resp = file.update_content( data )
49
+ end
50
+ end
51
+
52
+ def create_subfolder(name)
53
+ url = "#{RubyBox::API_URL}/#{resource_name}"
54
+ uri = URI.parse(url)
55
+ request = Net::HTTP::Post.new( uri.request_uri )
56
+ request.body = JSON.dump({ "name" => name, "parent" => {"id" => id} })
57
+ resp = @session.request(uri, request)
58
+ RubyBox::Folder.new(@session, resp)
59
+ end
60
+
61
+ private
62
+
63
+ def resource_name
64
+ 'folders'
65
+ end
66
+
67
+ def update_fields
68
+ ['name', 'description']
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,83 @@
1
+ require 'time'
2
+
3
+ module RubyBox
4
+ class Item
5
+
6
+ def initialize( session, raw_item )
7
+ @session = session
8
+ @raw_item = raw_item
9
+ end
10
+
11
+ def update
12
+ reload_meta unless etag
13
+
14
+ url = "#{RubyBox::API_URL}/#{resource_name}/#{id}"
15
+ uri = URI.parse(url)
16
+
17
+ request = Net::HTTP::Put.new(uri.path, {
18
+ "if-match" => etag,
19
+ "Content-Type" => 'application/json'
20
+ })
21
+ request.body = JSON.dump(serialize)
22
+
23
+ @raw_item = @session.request(uri, request)
24
+ self
25
+ end
26
+
27
+ def delete
28
+ url = "#{RubyBox::API_URL}/#{resource_name}/#{id}"
29
+ resp = @session.delete( url )
30
+ end
31
+
32
+ def reload_meta
33
+ url = "#{RubyBox::API_URL}/#{resource_name}/#{@raw_item['id']}"
34
+ @raw_item = @session.get( url )
35
+ self
36
+ end
37
+
38
+ def method_missing(method, *args, &block)
39
+ key = method.to_s
40
+
41
+ # update @raw_item hash if this appears to be a setter.
42
+ setter = method.to_s.end_with?('=')
43
+ key = key[0...-1] if setter
44
+ @raw_item[key] = args[0] if setter and update_fields.include?(key)
45
+
46
+ # we may have a mini version of the object loaded, fix this.
47
+ reload_meta if @raw_item[key].nil?
48
+
49
+ if @raw_item[key].kind_of?(Hash)
50
+ return RubyBox::Item.factory(@session, @raw_item[key])
51
+ elsif RubyBox::ISO_8601_TEST.match(@raw_item[key].to_s)
52
+ return Time.parse(@raw_item[key])
53
+ else
54
+ return @raw_item[key]
55
+ end
56
+ end
57
+
58
+ protected
59
+
60
+ def self.factory(session, entry)
61
+ case entry['type']
62
+ when 'folder'
63
+ return RubyBox::Folder.new(session, entry)
64
+ when 'file'
65
+ return RubyBox::File.new(session, entry)
66
+ when 'comment'
67
+ return RubyBox::Comment.new(session, entry)
68
+ when 'user'
69
+ return RubyBox::User.new(session, entry)
70
+ when 'discussion'
71
+ return RubyBox::Discussion.new(session, entry)
72
+ end
73
+ entry
74
+ end
75
+
76
+ private
77
+
78
+ def serialize
79
+ update_fields.inject({}) {|hash, field| hash[field] = @raw_item[field]; hash}
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,102 @@
1
+ require 'oauth2'
2
+
3
+ module RubyBox
4
+ class Session
5
+ attr_accessor :api_key, :auth_token, :oauth2_client, :access_token
6
+
7
+ OAUTH2_URLS = {
8
+ :site => 'https://www.box.com',
9
+ :authorize_url => "/api/oauth2/authorize",
10
+ :token_url => "/api/oauth2/token"
11
+ }
12
+
13
+ def initialize(opts={})
14
+ if opts[:client_id]
15
+ @oauth2_client = OAuth2::Client.new(opts[:client_id], opts[:client_secret], OAUTH2_URLS)
16
+ @access_token = OAuth2::AccessToken.new(oauth2_client, opts[:access_token]) if opts[:access_token]
17
+ else # Support legacy API for historical reasons.
18
+ @api_key = opts[:api_key]
19
+ @auth_token = opts[:auth_token]
20
+ end
21
+ end
22
+
23
+ def authorize_url(redirect_uri)
24
+ @redirect_uri = redirect_uri
25
+ @oauth2_client.auth_code.authorize_url(:redirect_uri => redirect_uri)
26
+ end
27
+
28
+ def get_access_token(code)
29
+ @access_token = @oauth2_client.auth_code.get_token(code, { :redirect_uri => @redirect_uri, :token_method => :post })
30
+ @access_token.token
31
+ end
32
+
33
+ def build_auth_header
34
+ "BoxAuth api_key=#{@api_key}&auth_token=#{@auth_token}"
35
+ end
36
+
37
+ def get(url, raw=false)
38
+ uri = URI.parse(url)
39
+ request = Net::HTTP::Get.new( uri.request_uri )
40
+ resp = request( uri, request, raw )
41
+ end
42
+
43
+ def delete(url, raw=false)
44
+ uri = URI.parse(url)
45
+ request = Net::HTTP::Delete.new( uri.request_uri )
46
+ resp = request( uri, request, raw )
47
+ end
48
+
49
+ def request(uri, request, raw=false)
50
+
51
+ http = Net::HTTP.new(uri.host, uri.port)
52
+ http.use_ssl = true
53
+ http.ssl_version = :SSLv3
54
+
55
+ if @access_token
56
+ request.add_field('Authorization', "Bearer #{@access_token.token}")
57
+ else
58
+ request.add_field('Authorization', build_auth_header)
59
+ end
60
+
61
+ response = http.request(request)
62
+
63
+ if response.is_a? Net::HTTPNotFound
64
+ raise RubyBox::ObjectNotFound
65
+ end
66
+ handle_errors( response.code.to_i, response.body, raw )
67
+ end
68
+
69
+ def do_stream(url, opts)
70
+ params = {
71
+ :content_length_proc => opts[:content_length_proc],
72
+ :progress_proc => opts[:progress_proc]
73
+ }
74
+
75
+ if @access_token
76
+ params['Authorization'] = "Bearer #{@access_token.token}"
77
+ else
78
+ params['Authorization'] = build_auth_header
79
+ end
80
+
81
+ open(url, params)
82
+ end
83
+
84
+ def handle_errors( status, body, raw )
85
+ begin
86
+ parsed_body = JSON.parse(body)
87
+ rescue
88
+ msg = body.nil? || body.empty? ? "no data returned" : body
89
+ parsed_body = { "message" => msg }
90
+ end
91
+ case status / 100
92
+ when 4
93
+ raise(RubyBox::ItemNameInUse.new(parsed_body), parsed_body["message"]) if parsed_body["code"] == "item_name_in_use"
94
+ raise(RubyBox::AuthError.new(parsed_body), parsed_body["message"]) if parsed_body["code"] == "unauthorized" || status == 401
95
+ raise(RubyBox::RequestError.new(parsed_body), parsed_body["message"])
96
+ when 5
97
+ raise RubyBox::ServerError, parsed_body["message"]
98
+ end
99
+ raw ? body : parsed_body
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,10 @@
1
+ module RubyBox
2
+ class User < Item
3
+
4
+ private
5
+
6
+ def resource_name
7
+ 'users'
8
+ end
9
+ end
10
+ end
data/ruby-box.gemspec ADDED
@@ -0,0 +1,50 @@
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 = "ruby-box"
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Benjamin Coe", "Jesse Miller", "Larry Kang"]
12
+ s.date = "2013-03-02"
13
+ s.description = "ruby gem for box.com 2.0 api"
14
+ s.email = "ben@attachments.me"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.markdown"
18
+ ]
19
+ s.files = `git ls-files`.gsub('"spec/fixtures/\351\201\240\345\277\227\346\225\231\346\216\210.jpg"', '').split($\)
20
+ s.homepage = "http://github.com/attachmentsme/ruby-box"
21
+ s.licenses = ["MIT"]
22
+ s.require_paths = ["lib"]
23
+ s.rubygems_version = "1.8.24"
24
+ s.summary = "ruby gem for box.com 2.0 api"
25
+
26
+ if s.respond_to? :specification_version then
27
+ s.specification_version = 3
28
+
29
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
30
+ s.add_runtime_dependency(%q<multipart-post>, ["~> 1.1.5"])
31
+ s.add_development_dependency(%q<rspec>, [">= 0"])
32
+ s.add_development_dependency(%q<bundler>, [">= 0"])
33
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
34
+ s.add_development_dependency(%q<webmock>, [">= 0"])
35
+ else
36
+ s.add_dependency(%q<multipart-post>, ["~> 1.1.5"])
37
+ s.add_dependency(%q<rspec>, [">= 0"])
38
+ s.add_dependency(%q<bundler>, [">= 0"])
39
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
40
+ s.add_dependency(%q<webmock>, [">= 0"])
41
+ end
42
+ else
43
+ s.add_dependency(%q<multipart-post>, ["~> 1.1.5"])
44
+ s.add_dependency(%q<rspec>, [">= 0"])
45
+ s.add_dependency(%q<bundler>, [">= 0"])
46
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
47
+ s.add_dependency(%q<webmock>, [">= 0"])
48
+ end
49
+ end
50
+
@@ -0,0 +1,28 @@
1
+ #encoding: UTF-8
2
+
3
+ require 'helper/account'
4
+ require 'ruby-box'
5
+ require 'webmock/rspec'
6
+
7
+ describe RubyBox::Client do
8
+ before do
9
+ @session = RubyBox::Session.new
10
+ end
11
+
12
+ describe '#split_path' do
13
+ it "returns the appropriate path" do
14
+ client = RubyBox::Client.new(@session)
15
+ client.split_path('foo/bar').should == ['foo', 'bar']
16
+ end
17
+
18
+ it "leading / is ignored" do
19
+ client = RubyBox::Client.new(@session)
20
+ client.split_path('/foo/bar').should == ['foo', 'bar']
21
+ end
22
+
23
+ it "trailing / is ignored" do
24
+ client = RubyBox::Client.new(@session)
25
+ client.split_path('foo/bar/').should == ['foo', 'bar']
26
+ end
27
+ end
28
+ end