google-spreadsheet-ruby 0.1.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|