google-spreadsheet-ruby 0.2.1 → 0.3.0

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.
@@ -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"