google-spreadsheet-ruby 0.1.8 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +6 -3
- data/doc_src/google_spreadsheet/acl.rb +20 -0
- data/doc_src/google_spreadsheet/acl_entry.rb +33 -0
- data/lib/google_spreadsheet.rb +8 -1237
- data/lib/google_spreadsheet/acl.rb +124 -0
- data/lib/google_spreadsheet/acl_entry.rb +58 -0
- data/lib/google_spreadsheet/authentication_error.rb +14 -0
- data/lib/google_spreadsheet/client_login_fetcher.rb +56 -0
- data/lib/google_spreadsheet/collection.rb +62 -0
- data/lib/google_spreadsheet/error.rb +12 -0
- data/lib/google_spreadsheet/list.rb +115 -0
- data/lib/google_spreadsheet/list_row.rb +84 -0
- data/lib/google_spreadsheet/oauth1_fetcher.rb +26 -0
- data/lib/google_spreadsheet/oauth2_fetcher.rb +29 -0
- data/lib/google_spreadsheet/record.rb +31 -0
- data/lib/google_spreadsheet/session.rb +297 -0
- data/lib/google_spreadsheet/spreadsheet.rb +298 -0
- data/lib/google_spreadsheet/table.rb +60 -0
- data/lib/google_spreadsheet/util.rb +30 -0
- data/lib/google_spreadsheet/worksheet.rb +445 -0
- metadata +46 -9
@@ -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_spreadsheet/acl_entry"
|
7
|
+
|
8
|
+
|
9
|
+
module GoogleSpreadsheet
|
10
|
+
|
11
|
+
# ACL (access control list) of a spreadsheet.
|
12
|
+
#
|
13
|
+
# Use GoogleSpreadsheet::Spreadsheet#acl to get GoogleSpreadsheet::Acl object.
|
14
|
+
# See GoogleSpreadsheet::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 GoogleSpreadsheet::AclEntry or a Hash with keys
|
33
|
+
# :scope_type, :scope and :role. See GoogleSpreadsheet::AclEntry#scope_type and
|
34
|
+
# GoogleSpreadsheet::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 GoogleSpreadsheet
|
10
|
+
|
11
|
+
# An entry of an ACL (access control list) of a spreadsheet.
|
12
|
+
#
|
13
|
+
# Use GoogleSpreadsheet::Acl#[] to get GoogleSpreadsheet::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
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
3
|
+
|
4
|
+
require "google_spreadsheet/error"
|
5
|
+
|
6
|
+
|
7
|
+
module GoogleSpreadsheet
|
8
|
+
|
9
|
+
# Raised when GoogleSpreadsheet.login has failed.
|
10
|
+
class AuthenticationError < GoogleSpreadsheet::Error
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
3
|
+
|
4
|
+
require "net/https"
|
5
|
+
require "uri"
|
6
|
+
Net::HTTP.version_1_2
|
7
|
+
|
8
|
+
|
9
|
+
module GoogleSpreadsheet
|
10
|
+
|
11
|
+
class ClientLoginFetcher #:nodoc:
|
12
|
+
|
13
|
+
def initialize(auth_tokens, proxy)
|
14
|
+
@auth_tokens = auth_tokens
|
15
|
+
if proxy
|
16
|
+
@proxy = proxy
|
17
|
+
elsif ENV["http_proxy"] && !ENV["http_proxy"].empty?
|
18
|
+
proxy_url = URI.parse(ENV["http_proxy"])
|
19
|
+
@proxy = Net::HTTP.Proxy(proxy_url.host, proxy_url.port)
|
20
|
+
else
|
21
|
+
@proxy = Net::HTTP
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor(:auth_tokens)
|
26
|
+
|
27
|
+
def request_raw(method, url, data, extra_header, auth)
|
28
|
+
uri = URI.parse(url)
|
29
|
+
http = @proxy.new(uri.host, uri.port)
|
30
|
+
http.use_ssl = true
|
31
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
32
|
+
http.start() do
|
33
|
+
path = uri.path + (uri.query ? "?#{uri.query}" : "")
|
34
|
+
header = auth_header(auth).merge(extra_header)
|
35
|
+
if method == :delete || method == :get
|
36
|
+
return http.__send__(method, path, header)
|
37
|
+
else
|
38
|
+
return http.__send__(method, path, data, header)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def auth_header(auth)
|
46
|
+
token = auth == :none ? nil : @auth_tokens[auth]
|
47
|
+
if token
|
48
|
+
return {"Authorization" => "GoogleLogin auth=#{token}"}
|
49
|
+
else
|
50
|
+
return {}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
3
|
+
|
4
|
+
require "google_spreadsheet/util"
|
5
|
+
require "google_spreadsheet/error"
|
6
|
+
require "google_spreadsheet/spreadsheet"
|
7
|
+
|
8
|
+
|
9
|
+
module GoogleSpreadsheet
|
10
|
+
|
11
|
+
# Use GoogleSpreadsheet::Session#collection_by_url to get GoogleSpreadsheet::Collection object.
|
12
|
+
class Collection
|
13
|
+
|
14
|
+
include(Util)
|
15
|
+
|
16
|
+
def initialize(session, collection_feed_url) #:nodoc:
|
17
|
+
@session = session
|
18
|
+
@collection_feed_url = collection_feed_url
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader(:collection_feed_url)
|
22
|
+
|
23
|
+
# Adds the given GoogleSpreadsheet::Spreadsheet to the collection.
|
24
|
+
def add(spreadsheet)
|
25
|
+
contents_url = concat_url(@collection_feed_url, "/contents")
|
26
|
+
header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
|
27
|
+
xml = <<-"EOS"
|
28
|
+
<entry xmlns="http://www.w3.org/2005/Atom">
|
29
|
+
<id>#{h(spreadsheet.document_feed_url)}</id>
|
30
|
+
</entry>
|
31
|
+
EOS
|
32
|
+
@session.request(
|
33
|
+
:post, contents_url, :data => xml, :header => header, :auth => :writely)
|
34
|
+
return nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns all the spreadsheets in the collection.
|
38
|
+
def spreadsheets
|
39
|
+
|
40
|
+
contents_url = concat_url(@collection_feed_url, "/contents")
|
41
|
+
header = {"GData-Version" => "3.0", "Content-Type" => "application/atom+xml"}
|
42
|
+
doc = @session.request(:get, contents_url, :header => header, :auth => :writely)
|
43
|
+
|
44
|
+
result = []
|
45
|
+
for entry in doc.css("feed > entry")
|
46
|
+
title = entry.css("title").text
|
47
|
+
worksheets_feed_link = entry.css(
|
48
|
+
"link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]
|
49
|
+
if worksheets_feed_link
|
50
|
+
result.push(GoogleSpreadsheet::Spreadsheet.new(
|
51
|
+
@session, worksheets_feed_link["href"], title))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
return result
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO Add other operations.
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# Author: Hiroshi Ichikawa <http://gimite.net/>
|
2
|
+
# The license of this source is "New BSD Licence"
|
3
|
+
|
4
|
+
require "google_spreadsheet/util"
|
5
|
+
require "google_spreadsheet/error"
|
6
|
+
require "google_spreadsheet/list_row"
|
7
|
+
|
8
|
+
|
9
|
+
module GoogleSpreadsheet
|
10
|
+
|
11
|
+
# Provides access to cells using column names.
|
12
|
+
# Use GoogleSpreadsheet::Worksheet#list to get GoogleSpreadsheet::List object.
|
13
|
+
#--
|
14
|
+
# This is implemented as wrapper of GoogleSpreadsheet::Worksheet i.e. using cells
|
15
|
+
# feed, not list feed. In this way, we can easily provide consistent API as
|
16
|
+
# GoogleSpreadsheet::Worksheet using save()/reload().
|
17
|
+
class List
|
18
|
+
|
19
|
+
include(Enumerable)
|
20
|
+
|
21
|
+
def initialize(worksheet) #:nodoc:
|
22
|
+
@worksheet = worksheet
|
23
|
+
end
|
24
|
+
|
25
|
+
# Number of non-empty rows in the worksheet excluding the first row.
|
26
|
+
def size
|
27
|
+
return @worksheet.num_rows - 1
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns Hash-like object (GoogleSpreadsheet::ListRow) for the row with the
|
31
|
+
# index. Keys of the object are colum names (the first row).
|
32
|
+
# The second row has index 0.
|
33
|
+
#
|
34
|
+
# Note that updates to the returned object are not sent to the server until
|
35
|
+
# you call GoogleSpreadsheet::Worksheet#save().
|
36
|
+
def [](index)
|
37
|
+
return ListRow.new(self, index)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Updates the row with the index with the given Hash object.
|
41
|
+
# Keys of +hash+ are colum names (the first row).
|
42
|
+
# The second row has index 0.
|
43
|
+
#
|
44
|
+
# Note that update is not sent to the server until
|
45
|
+
# you call GoogleSpreadsheet::Worksheet#save().
|
46
|
+
def []=(index, hash)
|
47
|
+
self[index].replace(hash)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Iterates over Hash-like object (GoogleSpreadsheet::ListRow) for each row
|
51
|
+
# (except for the first row).
|
52
|
+
# Keys of the object are colum names (the first row).
|
53
|
+
def each(&block)
|
54
|
+
for i in 0...self.size
|
55
|
+
yield(self[i])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Column names i.e. the contents of the first row.
|
60
|
+
# Duplicates are removed.
|
61
|
+
def keys
|
62
|
+
return (1..@worksheet.num_cols).map(){ |i| @worksheet[1, i] }.uniq()
|
63
|
+
end
|
64
|
+
|
65
|
+
# Updates column names i.e. the contents of the first row.
|
66
|
+
#
|
67
|
+
# Note that update is not sent to the server until
|
68
|
+
# you call GoogleSpreadsheet::Worksheet#save().
|
69
|
+
def keys=(ary)
|
70
|
+
for i in 1..ary.size
|
71
|
+
@worksheet[1, i] = ary[i - 1]
|
72
|
+
end
|
73
|
+
for i in (ary.size + 1)..@worksheet.num_cols
|
74
|
+
@worksheet[1, i] = ""
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Adds a new row to the bottom.
|
79
|
+
# Keys of +hash+ are colum names (the first row).
|
80
|
+
# Returns GoogleSpreadsheet::ListRow for the new row.
|
81
|
+
#
|
82
|
+
# Note that update is not sent to the server until
|
83
|
+
# you call GoogleSpreadsheet::Worksheet#save().
|
84
|
+
def push(hash)
|
85
|
+
row = self[self.size]
|
86
|
+
row.update(hash)
|
87
|
+
return row
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns all rows (except for the first row) as Array of Hash.
|
91
|
+
# Keys of Hash objects are colum names (the first row).
|
92
|
+
def to_hash_array()
|
93
|
+
return self.map(){ |r| r.to_hash() }
|
94
|
+
end
|
95
|
+
|
96
|
+
def get(index, key) #:nodoc:
|
97
|
+
return @worksheet[index + 2, key_to_col(key)]
|
98
|
+
end
|
99
|
+
|
100
|
+
def set(index, key, value) #:nodoc:
|
101
|
+
@worksheet[index + 2, key_to_col(key)] = value
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def key_to_col(key)
|
107
|
+
key = key.to_s()
|
108
|
+
col = (1..@worksheet.num_cols).find(){ |c| @worksheet[1, c] == key }
|
109
|
+
raise(GoogleSpreadsheet::Error, "Colunm doesn't exist: %p" % key) if !col
|
110
|
+
return col
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|