google-spreadsheet-ruby 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,62 +1,4 @@
1
1
  # Author: Hiroshi Ichikawa <http://gimite.net/>
2
2
  # The license of this source is "New BSD Licence"
3
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
4
+ require "google_spreadsheet"
@@ -1,12 +1,4 @@
1
1
  # Author: Hiroshi Ichikawa <http://gimite.net/>
2
2
  # The license of this source is "New BSD Licence"
3
3
 
4
-
5
- module GoogleSpreadsheet
6
-
7
- # Raised when spreadsheets.google.com has returned error.
8
- class Error < RuntimeError
9
-
10
- end
11
-
12
- end
4
+ require "google_spreadsheet"
@@ -0,0 +1,4 @@
1
+ # Author: Hiroshi Ichikawa <http://gimite.net/>
2
+ # The license of this source is "New BSD Licence"
3
+
4
+ require "google_spreadsheet"
@@ -1,115 +1,4 @@
1
1
  # Author: Hiroshi Ichikawa <http://gimite.net/>
2
2
  # The license of this source is "New BSD Licence"
3
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
4
+ require "google_spreadsheet"
@@ -1,84 +1,4 @@
1
1
  # Author: Hiroshi Ichikawa <http://gimite.net/>
2
2
  # The license of this source is "New BSD Licence"
3
3
 
4
- require "forwardable"
5
-
6
- require "google_spreadsheet/util"
7
- require "google_spreadsheet/error"
8
-
9
-
10
- module GoogleSpreadsheet
11
-
12
- # Hash-like object returned by GoogleSpreadsheet::List#[].
13
- class ListRow
14
-
15
- include(Enumerable)
16
- extend(Forwardable)
17
-
18
- def_delegators(:to_hash,
19
- :keys, :values, :each_key, :each_value, :each, :each_pair, :hash,
20
- :assoc, :fetch, :flatten, :key, :invert, :size, :length, :rassoc,
21
- :merge, :reject, :select, :sort, :to_a, :values_at)
22
-
23
- def initialize(list, index) #:nodoc:
24
- @list = list
25
- @index = index
26
- end
27
-
28
- def [](key)
29
- return @list.get(@index, key)
30
- end
31
-
32
- def []=(key, value)
33
- @list.set(@index, key, value)
34
- end
35
-
36
- def has_key?(key)
37
- return @list.keys.include?(key)
38
- end
39
-
40
- alias include? has_key?
41
- alias key? has_key?
42
- alias member? has_key?
43
-
44
- def update(hash)
45
- for k, v in hash
46
- self[k] = v
47
- end
48
- end
49
-
50
- alias merge! update
51
-
52
- def replace(hash)
53
- clear()
54
- update(hash)
55
- end
56
-
57
- def clear()
58
- for key in @list.keys
59
- self[key] = ""
60
- end
61
- end
62
-
63
- def to_hash()
64
- result = {}
65
- for key in @list.keys
66
- result[key] = self[key]
67
- end
68
- return result
69
- end
70
-
71
- def ==(other)
72
- return self.class == other.class && self.to_hash() == other.to_hash()
73
- end
74
-
75
- alias === ==
76
- alias eql? ==
77
-
78
- def inspect
79
- return "\#<%p %p>" % [self.class, to_hash()]
80
- end
81
-
82
- end
83
-
84
- end
4
+ require "google_spreadsheet"
@@ -1,26 +1,4 @@
1
1
  # Author: Hiroshi Ichikawa <http://gimite.net/>
2
2
  # The license of this source is "New BSD Licence"
3
3
 
4
- require "rubygems"
5
- require "oauth"
6
-
7
-
8
- module GoogleSpreadsheet
9
-
10
- class OAuth1Fetcher #:nodoc:
11
-
12
- def initialize(oauth1_token)
13
- @oauth1_token = oauth1_token
14
- end
15
-
16
- def request_raw(method, url, data, extra_header, auth)
17
- if method == :delete || method == :get
18
- return @oauth1_token.__send__(method, url, extra_header)
19
- else
20
- return @oauth1_token.__send__(method, url, data, extra_header)
21
- end
22
- end
23
-
24
- end
25
-
26
- end
4
+ require "google_spreadsheet"
@@ -1,29 +1,4 @@
1
1
  # Author: Hiroshi Ichikawa <http://gimite.net/>
2
2
  # The license of this source is "New BSD Licence"
3
3
 
4
- require "rubygems"
5
- require "oauth2"
6
-
7
-
8
- module GoogleSpreadsheet
9
-
10
- class OAuth2Fetcher #:nodoc:
11
-
12
- Response = Struct.new(:code, :body)
13
-
14
- def initialize(oauth2_token)
15
- @oauth2_token = oauth2_token
16
- end
17
-
18
- def request_raw(method, url, data, extra_header, auth)
19
- if method == :delete || method == :get
20
- raw_res = @oauth2_token.request(method, url, {:header => extra_header})
21
- else
22
- raw_res = @oauth2_token.request(method, url, {:header => extra_header, :body => data})
23
- end
24
- return Response.new(raw_res.status.to_s(), raw_res.body)
25
- end
26
-
27
- end
28
-
29
- end
4
+ require "google_spreadsheet"
@@ -1,31 +1,4 @@
1
1
  # Author: Hiroshi Ichikawa <http://gimite.net/>
2
2
  # The license of this source is "New BSD Licence"
3
3
 
4
- require "google_spreadsheet/util"
5
- require "google_spreadsheet/error"
6
-
7
-
8
- module GoogleSpreadsheet
9
-
10
- # DEPRECATED: Table and Record feeds are deprecated and they will not be available after
11
- # March 2012.
12
- #
13
- # Use GoogleSpreadsheet::Table#records to get GoogleSpreadsheet::Record objects.
14
- class Record < Hash
15
- include(Util)
16
-
17
- def initialize(session, entry) #:nodoc:
18
- @session = session
19
- entry.css("gs|field").each() do |field|
20
- self[field["name"]] = field.inner_text
21
- end
22
- end
23
-
24
- def inspect #:nodoc:
25
- content = self.map(){ |k, v| "%p => %p" % [k, v] }.join(", ")
26
- return "\#<%p:{%s}>" % [self.class, content]
27
- end
28
-
29
- end
30
-
31
- end
4
+ require "google_spreadsheet"
@@ -1,297 +1,4 @@
1
1
  # Author: Hiroshi Ichikawa <http://gimite.net/>
2
2
  # The license of this source is "New BSD Licence"
3
3
 
4
- require "cgi"
5
-
6
- require "rubygems"
7
- require "nokogiri"
8
-
9
- require "google_spreadsheet/util"
10
- require "google_spreadsheet/client_login_fetcher"
11
- require "google_spreadsheet/oauth1_fetcher"
12
- require "google_spreadsheet/oauth2_fetcher"
13
- require "google_spreadsheet/error"
14
- require "google_spreadsheet/authentication_error"
15
- require "google_spreadsheet/spreadsheet"
16
- require "google_spreadsheet/worksheet"
17
- require "google_spreadsheet/collection"
18
-
19
-
20
- module GoogleSpreadsheet
21
-
22
- # Use GoogleSpreadsheet.login or GoogleSpreadsheet.saved_session to get
23
- # GoogleSpreadsheet::Session object.
24
- class Session
25
-
26
- include(Util)
27
- extend(Util)
28
-
29
- # The same as GoogleSpreadsheet.login.
30
- def self.login(mail, password, proxy = nil)
31
- session = Session.new(nil, ClientLoginFetcher.new({}, proxy))
32
- session.login(mail, password)
33
- return session
34
- end
35
-
36
- # The same as GoogleSpreadsheet.login_with_oauth.
37
- def self.login_with_oauth(oauth_token)
38
- case oauth_token
39
- when OAuth::AccessToken
40
- fetcher = OAuth1Fetcher.new(oauth_token)
41
- when OAuth2::AccessToken
42
- fetcher = OAuth2Fetcher.new(oauth_token)
43
- else
44
- raise(GoogleSpreadsheet::Error,
45
- "oauth_token is neither OAuth::Token nor OAuth2::Token: %p" % oauth_token)
46
- end
47
- return Session.new(nil, fetcher)
48
- end
49
-
50
- # The same as GoogleSpreadsheet.restore_session.
51
- def self.restore_session(auth_tokens, proxy = nil)
52
- return Session.new(auth_tokens, nil, proxy)
53
- end
54
-
55
- # Creates a dummy GoogleSpreadsheet::Session object for testing.
56
- def self.new_dummy()
57
- return Session.new(nil, Object.new())
58
- end
59
-
60
- # DEPRECATED: Use GoogleSpreadsheet.restore_session instead.
61
- def initialize(auth_tokens = nil, fetcher = nil, proxy = nil)
62
- if fetcher
63
- @fetcher = fetcher
64
- else
65
- @fetcher = ClientLoginFetcher.new(auth_tokens || {}, proxy)
66
- end
67
- end
68
-
69
- # Authenticates with given +mail+ and +password+, and updates current session object
70
- # if succeeds. Raises GoogleSpreadsheet::AuthenticationError if fails.
71
- # Google Apps account is supported.
72
- def login(mail, password)
73
- if !@fetcher.is_a?(ClientLoginFetcher)
74
- raise(GoogleSpreadsheet::Error,
75
- "Cannot call login for session created by login_with_oauth.")
76
- end
77
- begin
78
- @fetcher.auth_tokens = {
79
- :wise => authenticate(mail, password, :wise),
80
- :writely => authenticate(mail, password, :writely),
81
- }
82
- rescue GoogleSpreadsheet::Error => ex
83
- return true if @on_auth_fail && @on_auth_fail.call()
84
- raise(AuthenticationError, "Authentication failed for #{mail}: #{ex.message}")
85
- end
86
- end
87
-
88
- # Authentication tokens.
89
- def auth_tokens
90
- if !@fetcher.is_a?(ClientLoginFetcher)
91
- raise(GoogleSpreadsheet::Error,
92
- "Cannot call auth_tokens for session created by " +
93
- "login_with_oauth.")
94
- end
95
- return @fetcher.auth_tokens
96
- end
97
-
98
- # Authentication token.
99
- def auth_token(auth = :wise)
100
- return self.auth_tokens[auth]
101
- end
102
-
103
- # Proc or Method called when authentication has failed.
104
- # When this function returns +true+, it tries again.
105
- attr_accessor :on_auth_fail
106
-
107
- # Returns list of spreadsheets for the user as array of GoogleSpreadsheet::Spreadsheet.
108
- # You can specify query parameters described at
109
- # http://code.google.com/apis/spreadsheets/docs/2.0/reference.html#Parameters
110
- #
111
- # e.g.
112
- # session.spreadsheets
113
- # session.spreadsheets("title" => "hoge")
114
- def spreadsheets(params = {})
115
- query = encode_query(params)
116
- doc = request(
117
- :get, "https://spreadsheets.google.com/feeds/spreadsheets/private/full?#{query}")
118
- result = []
119
- doc.css("feed > entry").each() do |entry|
120
- title = entry.css("title").text
121
- url = entry.css(
122
- "link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"]
123
- result.push(Spreadsheet.new(self, url, title))
124
- end
125
- return result
126
- end
127
-
128
- # Returns GoogleSpreadsheet::Spreadsheet with given +key+.
129
- #
130
- # e.g.
131
- # # http://spreadsheets.google.com/ccc?key=pz7XtlQC-PYx-jrVMJErTcg&hl=ja
132
- # session.spreadsheet_by_key("pz7XtlQC-PYx-jrVMJErTcg")
133
- def spreadsheet_by_key(key)
134
- url = "https://spreadsheets.google.com/feeds/worksheets/#{key}/private/full"
135
- return Spreadsheet.new(self, url)
136
- end
137
-
138
- # Returns GoogleSpreadsheet::Spreadsheet with given +url+. You must specify either of:
139
- # - URL of the page you open to access the spreadsheet in your browser
140
- # - URL of worksheet-based feed of the spreadseet
141
- #
142
- # e.g.
143
- # session.spreadsheet_by_url(
144
- # "https://docs.google.com/spreadsheet/ccc?key=pz7XtlQC-PYx-jrVMJErTcg")
145
- # session.spreadsheet_by_url(
146
- # "https://spreadsheets.google.com/feeds/" +
147
- # "worksheets/pz7XtlQC-PYx-jrVMJErTcg/private/full")
148
- def spreadsheet_by_url(url)
149
- # Tries to parse it as URL of human-readable spreadsheet.
150
- uri = URI.parse(url)
151
- if ["spreadsheets.google.com", "docs.google.com"].include?(uri.host) &&
152
- uri.path =~ /\/ccc$/
153
- if (uri.query || "").split(/&/).find(){ |s| s=~ /^key=(.*)$/ }
154
- return spreadsheet_by_key($1)
155
- end
156
- end
157
- # Assumes the URL is worksheets feed URL.
158
- return Spreadsheet.new(self, url)
159
- end
160
-
161
- # Returns GoogleSpreadsheet::Spreadsheet with given +title+.
162
- # Returns nil if not found. If multiple spreadsheets with the +title+ are found, returns
163
- # one of them.
164
- def spreadsheet_by_title(title)
165
- return spreadsheets({"title" => title})[0]
166
- end
167
-
168
- # Returns GoogleSpreadsheet::Worksheet with given +url+.
169
- # You must specify URL of cell-based feed of the worksheet.
170
- #
171
- # e.g.
172
- # session.worksheet_by_url(
173
- # "http://spreadsheets.google.com/feeds/" +
174
- # "cells/pz7XtlQC-PYxNmbBVgyiNWg/od6/private/full")
175
- def worksheet_by_url(url)
176
- return Worksheet.new(self, nil, url)
177
- end
178
-
179
- # Returns GoogleSpreadsheet::Collection with given +url+.
180
- # You must specify either of:
181
- # - URL of the page you get when you go to https://docs.google.com/ with your browser and
182
- # open a collection
183
- # - URL of collection (folder) feed
184
- #
185
- # e.g.
186
- # session.collection_by_url(
187
- # "https://docs.google.com/?pli=1&authuser=0#folders/" +
188
- # "0B9GfDpQ2pBVUODNmOGE0NjIzMWU3ZC00NmUyLTk5NzEtYaFkZjY1MjAyxjMc")
189
- # session.collection_by_url(
190
- # "http://docs.google.com/feeds/default/private/full/folder%3A" +
191
- # "0B9GfDpQ2pBVUODNmOGE0NjIzMWU3ZC00NmUyLTk5NzEtYaFkZjY1MjAyxjMc")
192
- def collection_by_url(url)
193
- uri = URI.parse(url)
194
- if uri.host == "docs.google.com" && uri.fragment =~ /^folders\/(.+)$/
195
- # Looks like a URL of human-readable collection page. Converts to collection feed URL.
196
- url = "https://docs.google.com/feeds/default/private/full/folder%3A#{$1}"
197
- end
198
- return Collection.new(self, url)
199
- end
200
-
201
- # Creates new spreadsheet and returns the new GoogleSpreadsheet::Spreadsheet.
202
- #
203
- # e.g.
204
- # session.create_spreadsheet("My new sheet")
205
- def create_spreadsheet(
206
- title = "Untitled",
207
- feed_url = "https://docs.google.com/feeds/documents/private/full")
208
-
209
- xml = <<-"EOS"
210
- <atom:entry
211
- xmlns:atom="http://www.w3.org/2005/Atom"
212
- xmlns:docs="http://schemas.google.com/docs/2007">
213
- <atom:category
214
- scheme="http://schemas.google.com/g/2005#kind"
215
- term="http://schemas.google.com/docs/2007#spreadsheet"
216
- label="spreadsheet"/>
217
- <atom:title>#{h(title)}</atom:title>
218
- </atom:entry>
219
- EOS
220
-
221
- doc = request(:post, feed_url, :data => xml, :auth => :writely)
222
- ss_url = doc.css(
223
- "link[rel='http://schemas.google.com/spreadsheets/2006#worksheetsfeed']")[0]["href"]
224
- return Spreadsheet.new(self, ss_url, title)
225
-
226
- end
227
-
228
- def request(method, url, params = {}) #:nodoc:
229
-
230
- # Always uses HTTPS.
231
- url = url.gsub(%r{^http://}, "https://")
232
- data = params[:data]
233
- auth = params[:auth] || :wise
234
- if params[:header]
235
- extra_header = params[:header]
236
- elsif data
237
- extra_header = {"Content-Type" => "application/atom+xml"}
238
- else
239
- extra_header = {}
240
- end
241
- response_type = params[:response_type] || :xml
242
-
243
- while true
244
- response = @fetcher.request_raw(method, url, data, extra_header, auth)
245
- if response.code == "401" && @on_auth_fail && @on_auth_fail.call()
246
- next
247
- end
248
- if !(response.code =~ /^2/)
249
- raise(
250
- response.code == "401" ? AuthenticationError : GoogleSpreadsheet::Error,
251
- "Response code #{response.code} for #{method} #{url}: " +
252
- CGI.unescapeHTML(response.body))
253
- end
254
- return convert_response(response, response_type)
255
- end
256
-
257
- end
258
-
259
- def inspect
260
- return "#<%p:0x%x>" % [self.class, self.object_id]
261
- end
262
-
263
- private
264
-
265
- def convert_response(response, response_type)
266
- case response_type
267
- when :xml
268
- return Nokogiri.XML(response.body)
269
- when :raw
270
- return response.body
271
- else
272
- raise(GoogleSpreadsheet::Error,
273
- "Unknown params[:response_type]: %s" % response_type)
274
- end
275
- end
276
-
277
- def authenticate(mail, password, auth)
278
- params = {
279
- "accountType" => "HOSTED_OR_GOOGLE",
280
- "Email" => mail,
281
- "Passwd" => password,
282
- "service" => auth.to_s(),
283
- "source" => "Gimite-RubyGoogleSpreadsheet-1.00",
284
- }
285
- header = {"Content-Type" => "application/x-www-form-urlencoded"}
286
- response = request(:post,
287
- "https://www.google.com/accounts/ClientLogin",
288
- :data => encode_query(params),
289
- :auth => :none,
290
- :header => header,
291
- :response_type => :raw)
292
- return response.slice(/^Auth=(.*)$/, 1)
293
- end
294
-
295
- end
296
-
297
- end
4
+ require "google_spreadsheet"