google_drive 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +89 -0
- data/doc_src/google_drive/acl.rb +20 -0
- data/doc_src/google_drive/acl_entry.rb +33 -0
- data/lib/google_drive.rb +120 -0
- data/lib/google_drive/acl.rb +124 -0
- data/lib/google_drive/acl_entry.rb +58 -0
- data/lib/google_drive/authentication_error.rb +14 -0
- data/lib/google_drive/client_login_fetcher.rb +56 -0
- data/lib/google_drive/collection.rb +54 -0
- data/lib/google_drive/error.rb +12 -0
- data/lib/google_drive/file.rb +217 -0
- data/lib/google_drive/list.rb +115 -0
- data/lib/google_drive/list_row.rb +84 -0
- data/lib/google_drive/oauth1_fetcher.rb +26 -0
- data/lib/google_drive/oauth2_fetcher.rb +47 -0
- data/lib/google_drive/record.rb +31 -0
- data/lib/google_drive/session.rb +436 -0
- data/lib/google_drive/spreadsheet.rb +220 -0
- data/lib/google_drive/table.rb +60 -0
- data/lib/google_drive/util.rb +55 -0
- data/lib/google_drive/worksheet.rb +445 -0
- metadata +121 -0
data/README.rdoc
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
This is a Ruby 1.8/1.9 library to read/write files/spreadsheets in Google Drive/Docs.
|
2
|
+
|
3
|
+
|
4
|
+
= How to install
|
5
|
+
|
6
|
+
$ sudo gem install google_drive
|
7
|
+
|
8
|
+
|
9
|
+
= How to use
|
10
|
+
|
11
|
+
Example to read/write files in Google Drive:
|
12
|
+
|
13
|
+
require "rubygems"
|
14
|
+
require "google_drive"
|
15
|
+
|
16
|
+
# Logs in.
|
17
|
+
# You can also use OAuth. See document of
|
18
|
+
# GoogleDrive.login_with_oauth for details.
|
19
|
+
session = GoogleDrive.login("username@gmail.com", "mypassword")
|
20
|
+
|
21
|
+
# Gets list of remote files.
|
22
|
+
for file in session.files
|
23
|
+
p file.title
|
24
|
+
end
|
25
|
+
|
26
|
+
# Uploads a local file.
|
27
|
+
session.upload_from_file("/path/to/hello.txt", "hello.txt", :convert => false)
|
28
|
+
|
29
|
+
# Downloads to a local file.
|
30
|
+
file = session.file_by_title("hello.txt")
|
31
|
+
file.download_to_file("/path/to/hello.txt")
|
32
|
+
|
33
|
+
# Updates content of the remote file.
|
34
|
+
file.update_from_file("/path/to/hello.txt")
|
35
|
+
|
36
|
+
Example to read/write spreadsheets:
|
37
|
+
|
38
|
+
require "rubygems"
|
39
|
+
require "google_drive"
|
40
|
+
|
41
|
+
# Logs in.
|
42
|
+
# You can also use OAuth. See document of
|
43
|
+
# GoogleDrive.login_with_oauth for details.
|
44
|
+
session = GoogleDrive.login("username@gmail.com", "mypassword")
|
45
|
+
|
46
|
+
# First worksheet of
|
47
|
+
# https://docs.google.com/spreadsheet/ccc?key=pz7XtlQC-PYx-jrVMJErTcg
|
48
|
+
ws = session.spreadsheet_by_key("pz7XtlQC-PYx-jrVMJErTcg").worksheets[0]
|
49
|
+
|
50
|
+
# Gets content of A2 cell.
|
51
|
+
p ws[2, 1] #==> "hoge"
|
52
|
+
|
53
|
+
# Changes content of cells.
|
54
|
+
# Changes are not sent to the server until you call ws.save().
|
55
|
+
ws[2, 1] = "foo"
|
56
|
+
ws[2, 2] = "bar"
|
57
|
+
ws.save()
|
58
|
+
|
59
|
+
# Dumps all cells.
|
60
|
+
for row in 1..ws.num_rows
|
61
|
+
for col in 1..ws.num_cols
|
62
|
+
p ws[row, col]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Yet another way to do so.
|
67
|
+
p ws.rows #==> [["fuga", ""], ["foo", "bar]]
|
68
|
+
|
69
|
+
# Reloads the worksheet to get changes by other clients.
|
70
|
+
ws.reload()
|
71
|
+
|
72
|
+
API document: http://gimite.net/doc/google-drive-ruby/
|
73
|
+
|
74
|
+
|
75
|
+
= Source code
|
76
|
+
|
77
|
+
http://github.com/gimite/google-drive-ruby
|
78
|
+
|
79
|
+
The license of this source is "New BSD Licence"
|
80
|
+
|
81
|
+
|
82
|
+
= Supported environments
|
83
|
+
|
84
|
+
Ruby 1.8.x and Ruby 1.9.x. Checked with Ruby 1.8.7 and Ruby 1.9.3.
|
85
|
+
|
86
|
+
|
87
|
+
= Author
|
88
|
+
|
89
|
+
Hiroshi Ichikawa - http://gimite.net/en/index.php?Contact
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module GoogleDrive
|
2
|
+
|
3
|
+
class Acl
|
4
|
+
|
5
|
+
# Returns the number of entries.
|
6
|
+
def size
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns GoogleDrive::AclEntry object at +index+.
|
10
|
+
def [](index)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Iterates over GoogleDrive::AclEntry objects.
|
14
|
+
def each(&block)
|
15
|
+
yield(entry)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module GoogleDrive
|
2
|
+
|
3
|
+
class AclEntry
|
4
|
+
|
5
|
+
# Type of the scope. One of:
|
6
|
+
#
|
7
|
+
# - "user": scope is a user's email address.
|
8
|
+
# - "group": scope is a Google Group email address.
|
9
|
+
# - "domain": scope is a Google Apps domain.
|
10
|
+
# - "default": Publicly shared with all users. scope is +nil+.
|
11
|
+
attr_reader(:scope_type)
|
12
|
+
|
13
|
+
# The scope. See scope_type.
|
14
|
+
attr_reader(:scope)
|
15
|
+
|
16
|
+
# The role given to the scope. One of:
|
17
|
+
# - "owner": The owner.
|
18
|
+
# - "writer": With read/write access.
|
19
|
+
# - "reader": With read-only access.
|
20
|
+
attr_reader(:role)
|
21
|
+
|
22
|
+
# Title of the entry.
|
23
|
+
attr_reader(:title)
|
24
|
+
|
25
|
+
# Edit URL of the entry.
|
26
|
+
attr_reader(:edit_url)
|
27
|
+
|
28
|
+
# E-tag of the entry.
|
29
|
+
attr_reader(:etag)
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/google_drive.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
3
|
+
|
4
|
+
require "google_drive/session"
|
5
|
+
|
6
|
+
|
7
|
+
module GoogleDrive
|
8
|
+
|
9
|
+
# Authenticates with given +mail+ and +password+, and returns GoogleDrive::Session
|
10
|
+
# if succeeds. Raises GoogleDrive::AuthenticationError if fails.
|
11
|
+
# Google Apps account is supported.
|
12
|
+
#
|
13
|
+
# +proxy+ can be nil or return value of Net::HTTP.Proxy. If +proxy+ is specified, all
|
14
|
+
# HTTP access in the session uses the proxy. If +proxy+ is nil, it uses the proxy
|
15
|
+
# specified by http_proxy environment variable if available. Otherwise it performs direct
|
16
|
+
# access.
|
17
|
+
def self.login(mail, password, proxy = nil)
|
18
|
+
return Session.login(mail, password, proxy)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Authenticates with given OAuth1 or OAuth2 token.
|
22
|
+
#
|
23
|
+
# OAuth2 code example:
|
24
|
+
#
|
25
|
+
# client = OAuth2::Client.new(
|
26
|
+
# your_client_id, your_client_secret,
|
27
|
+
# :site => "https://accounts.google.com",
|
28
|
+
# :token_url => "/o/oauth2/token",
|
29
|
+
# :authorize_url => "/o/oauth2/auth")
|
30
|
+
# auth_url = client.auth_code.authorize_url(
|
31
|
+
# :redirect_uri => "http://example.com/",
|
32
|
+
# "scope" => "https://spreadsheets.google.com/feeds https://docs.google.com/feeds/")
|
33
|
+
# # Redirect the user to auth_url and get authorization code from redirect URL.
|
34
|
+
# auth_token = client.auth_code.get_token(
|
35
|
+
# authorization_code, :redirect_uri => "http://example.com/")
|
36
|
+
# session = GoogleDrive.login_with_oauth(auth_token)
|
37
|
+
#
|
38
|
+
# Or, from existing refresh token:
|
39
|
+
#
|
40
|
+
# access_token = OAuth2::AccessToken.from_hash(client,
|
41
|
+
# {:refresh_token => refresh_token, :expires_at => expires_at})
|
42
|
+
# access_token = access_token.refresh!
|
43
|
+
# session = GoogleDrive.login_with_oauth(access_token)
|
44
|
+
#
|
45
|
+
# OAuth1 code example:
|
46
|
+
#
|
47
|
+
# 1) First generate OAuth consumer object with key and secret for your site by registering site
|
48
|
+
# with Google.
|
49
|
+
# @consumer = OAuth::Consumer.new( "key","secret", {:site=>"https://agree2"})
|
50
|
+
# 2) Request token with OAuth.
|
51
|
+
# @request_token = @consumer.get_request_token
|
52
|
+
# session[:request_token] = @request_token
|
53
|
+
# redirect_to @request_token.authorize_url
|
54
|
+
# 3) Create an oauth access token.
|
55
|
+
# @oauth_access_token = @request_token.get_access_token
|
56
|
+
# @access_token = OAuth::AccessToken.new(
|
57
|
+
# @consumer, @oauth_access_token.token, @oauth_access_token.secret)
|
58
|
+
#
|
59
|
+
# See these documents for details:
|
60
|
+
#
|
61
|
+
# - https://github.com/intridea/oauth2
|
62
|
+
# - http://code.google.com/apis/accounts/docs/OAuth2.html
|
63
|
+
# - http://oauth.rubyforge.org/
|
64
|
+
# - http://code.google.com/apis/accounts/docs/OAuth.html
|
65
|
+
def self.login_with_oauth(oauth_token)
|
66
|
+
return Session.login_with_oauth(oauth_token)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Restores session using return value of auth_tokens method of previous session.
|
70
|
+
#
|
71
|
+
# See GoogleDrive.login for description of parameter +proxy+.
|
72
|
+
def self.restore_session(auth_tokens, proxy = nil)
|
73
|
+
return Session.restore_session(auth_tokens, proxy)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Restores GoogleDrive::Session from +path+ and returns it.
|
77
|
+
# If +path+ doesn't exist or authentication has failed, prompts mail and password on console,
|
78
|
+
# authenticates with them, stores the session to +path+ and returns it.
|
79
|
+
#
|
80
|
+
# See login for description of parameter +proxy+.
|
81
|
+
#
|
82
|
+
# This method requires Highline library: http://rubyforge.org/projects/highline/
|
83
|
+
def self.saved_session(path = ENV["HOME"] + "/.ruby_google_drive.token", proxy = nil)
|
84
|
+
tokens = {}
|
85
|
+
if ::File.exist?(path)
|
86
|
+
open(path) do |f|
|
87
|
+
for auth in [:wise, :writely]
|
88
|
+
line = f.gets()
|
89
|
+
tokens[auth] = line && line.chomp()
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
session = Session.new(tokens, nil, proxy)
|
94
|
+
session.on_auth_fail = proc() do
|
95
|
+
begin
|
96
|
+
require "highline"
|
97
|
+
rescue LoadError
|
98
|
+
raise(LoadError,
|
99
|
+
"GoogleDrive.saved_session requires Highline library.\n" +
|
100
|
+
"Run\n" +
|
101
|
+
" \$ sudo gem install highline\n" +
|
102
|
+
"to install it.")
|
103
|
+
end
|
104
|
+
highline = HighLine.new()
|
105
|
+
mail = highline.ask("Mail: ")
|
106
|
+
password = highline.ask("Password: "){ |q| q.echo = false }
|
107
|
+
session.login(mail, password)
|
108
|
+
open(path, "w", 0600) do |f|
|
109
|
+
f.puts(session.auth_token(:wise))
|
110
|
+
f.puts(session.auth_token(:writely))
|
111
|
+
end
|
112
|
+
true
|
113
|
+
end
|
114
|
+
if !session.auth_token
|
115
|
+
session.on_auth_fail.call()
|
116
|
+
end
|
117
|
+
return session
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# Author: Guy Boertje <https://github.com/guyboertje>
|
2
|
+
# Author: David R. Albrecht <https://github.com/eldavido>
|
3
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
4
|
+
# The license of this source is "New BSD Licence"
|
5
|
+
|
6
|
+
require "google_drive/acl_entry"
|
7
|
+
|
8
|
+
|
9
|
+
module GoogleDrive
|
10
|
+
|
11
|
+
# ACL (access control list) of a spreadsheet.
|
12
|
+
#
|
13
|
+
# Use GoogleDrive::Spreadsheet#acl to get GoogleDrive::Acl object.
|
14
|
+
# See GoogleDrive::Spreadsheet#acl for usage example.
|
15
|
+
#
|
16
|
+
# This code is based on https://github.com/guyboertje/gdata-spreadsheet-ruby .
|
17
|
+
class Acl
|
18
|
+
|
19
|
+
include(Util)
|
20
|
+
extend(Forwardable)
|
21
|
+
|
22
|
+
def initialize(session, acls_feed_url) #:nodoc:
|
23
|
+
@session = session
|
24
|
+
@acls_feed_url = acls_feed_url
|
25
|
+
header = {"GData-Version" => "3.0"}
|
26
|
+
doc = @session.request(:get, @acls_feed_url, :header => header, :auth => :writely)
|
27
|
+
@acls = doc.css("entry").map(){ |e| AclEntry.new(entry_to_params(e)) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def_delegators(:@acls, :size, :[], :each)
|
31
|
+
|
32
|
+
# Adds a new entry. +entry+ is either a GoogleDrive::AclEntry or a Hash with keys
|
33
|
+
# :scope_type, :scope and :role. See GoogleDrive::AclEntry#scope_type and
|
34
|
+
# GoogleDrive::AclEntry#role for the document of the fields.
|
35
|
+
#
|
36
|
+
# NOTE: This sends email to the new people.
|
37
|
+
#
|
38
|
+
# e.g.
|
39
|
+
# spreadsheet.acl.push(
|
40
|
+
# {:scope_type => "user", :scope => "example2@gmail.com", :role => "reader"})
|
41
|
+
# spreadsheet.acl.push(
|
42
|
+
# {:scope_type => "user", :scope => "example3@gmail.com", :role => "writer"})
|
43
|
+
def push(entry)
|
44
|
+
|
45
|
+
entry = AclEntry.new(entry) if entry.is_a?(Hash)
|
46
|
+
|
47
|
+
header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
|
48
|
+
value_attr = entry.scope ? "value='#{h(entry.scope)}'" : ""
|
49
|
+
xml = <<-EOS
|
50
|
+
<entry
|
51
|
+
xmlns='http://www.w3.org/2005/Atom'
|
52
|
+
xmlns:gAcl='http://schemas.google.com/acl/2007'>
|
53
|
+
<category scheme='http://schemas.google.com/g/2005#kind'
|
54
|
+
term='http://schemas.google.com/acl/2007#accessRule'/>
|
55
|
+
<gAcl:role value='#{h(entry.role)}'/>
|
56
|
+
<gAcl:scope type='#{h(entry.scope_type)}' #{value_attr}/>
|
57
|
+
</entry>
|
58
|
+
EOS
|
59
|
+
doc = @session.request(
|
60
|
+
:post, @acls_feed_url, :data => xml, :header => header, :auth => :writely)
|
61
|
+
|
62
|
+
entry.params = entry_to_params(doc.root)
|
63
|
+
@acls.push(entry)
|
64
|
+
return entry
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
# Deletes an ACL entry.
|
69
|
+
#
|
70
|
+
# e.g.
|
71
|
+
# spreadsheet.acl.delete(spreadsheet.acl[1])
|
72
|
+
def delete(entry)
|
73
|
+
header = {"GData-Version" => "3.0"}
|
74
|
+
@session.request(:delete, entry.edit_url, :header => header, :auth => :writely)
|
75
|
+
@acls.delete(entry)
|
76
|
+
end
|
77
|
+
|
78
|
+
def update_role(entry, role) #:nodoc:
|
79
|
+
|
80
|
+
header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
|
81
|
+
value_attr = entry.scope ? "value='#{h(entry.scope)}'" : ""
|
82
|
+
xml = <<-EOS
|
83
|
+
<entry
|
84
|
+
xmlns='http://www.w3.org/2005/Atom'
|
85
|
+
xmlns:gAcl='http://schemas.google.com/acl/2007'
|
86
|
+
xmlns:gd='http://schemas.google.com/g/2005'
|
87
|
+
gd:etag='#{h(entry.etag)}'>
|
88
|
+
<category
|
89
|
+
scheme='http://schemas.google.com/g/2005#kind'
|
90
|
+
term='http://schemas.google.com/acl/2007#accessRule'/>
|
91
|
+
<gAcl:role value='#{h(role)}'/>
|
92
|
+
<gAcl:scope type='#{h(entry.scope_type)}' #{value_attr}/>
|
93
|
+
</entry>
|
94
|
+
EOS
|
95
|
+
doc = @session.request(
|
96
|
+
:put, entry.edit_url, :data => xml, :header => header, :auth => :writely)
|
97
|
+
|
98
|
+
entry.params = entry_to_params(doc.root)
|
99
|
+
return entry
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def inspect
|
104
|
+
return "\#<%p %p>" % [self.class, @acls]
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def entry_to_params(entry)
|
110
|
+
# TODO Support with-link roles.
|
111
|
+
return {
|
112
|
+
:acl => self,
|
113
|
+
:scope_type => entry.css("gAcl|scope")[0]["type"],
|
114
|
+
:scope => entry.css("gAcl|scope")[0]["value"],
|
115
|
+
:role => entry.css("gAcl|role")[0]["value"],
|
116
|
+
:title => entry.css("title").text,
|
117
|
+
:edit_url => entry.css("link[rel='edit']")[0]["href"],
|
118
|
+
:etag => entry["etag"],
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Author: Guy Boertje <https://github.com/guyboertje>
|
2
|
+
# Author: David R. Albrecht <https://github.com/eldavido>
|
3
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
4
|
+
# The license of this source is "New BSD Licence"
|
5
|
+
|
6
|
+
# acl.rb, derived from https://github.com/guyboertje/gdata-spreadsheet-ruby/blob/master/lib/document.rb
|
7
|
+
# more frankensteining of the original library
|
8
|
+
|
9
|
+
module GoogleDrive
|
10
|
+
|
11
|
+
# An entry of an ACL (access control list) of a spreadsheet.
|
12
|
+
#
|
13
|
+
# Use GoogleDrive::Acl#[] to get GoogleDrive::AclEntry object.
|
14
|
+
#
|
15
|
+
# This code is based on https://github.com/guyboertje/gdata-spreadsheet-ruby .
|
16
|
+
class AclEntry
|
17
|
+
|
18
|
+
include(Util)
|
19
|
+
|
20
|
+
PARAM_NAMES = [:acl, :scope_type, :scope, :role, :title, :edit_url, :etag] #:nodoc:
|
21
|
+
|
22
|
+
# +params+ is a Hash object with keys +:scope_type+, +:scope+ and +:role+.
|
23
|
+
# See scope_type and role for the document of the fields.
|
24
|
+
def initialize(params)
|
25
|
+
@params = {:role => "reader"}
|
26
|
+
for name, value in params
|
27
|
+
if !PARAM_NAMES.include?(name)
|
28
|
+
raise(ArgumentError, "Invalid key: %p" % name)
|
29
|
+
end
|
30
|
+
@params[name] = value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_accessor(:params) #:nodoc:
|
35
|
+
|
36
|
+
PARAM_NAMES.each() do |name|
|
37
|
+
define_method(name) do
|
38
|
+
return @params[name]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Changes the role of the scope.
|
43
|
+
#
|
44
|
+
# e.g.
|
45
|
+
# spreadsheet.acl[1].role = "writer"
|
46
|
+
def role=(role)
|
47
|
+
@params[:acl].update_role(self, role)
|
48
|
+
end
|
49
|
+
|
50
|
+
def inspect
|
51
|
+
return "\#<%p scope_type=%p, scope=%p, role=%p>" %
|
52
|
+
[self.class, @params[:scope_type], @params[:scope], @params[:role]]
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|