gcloud 0.2.0 → 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.
- checksums.yaml +8 -8
- data/AUTHENTICATION.md +3 -3
- data/CHANGELOG.md +12 -0
- data/OVERVIEW.md +30 -0
- data/lib/gcloud.rb +126 -9
- data/lib/gcloud/bigquery.rb +399 -0
- data/lib/gcloud/bigquery/connection.rb +592 -0
- data/lib/gcloud/bigquery/copy_job.rb +98 -0
- data/lib/gcloud/bigquery/credentials.rb +29 -0
- data/lib/gcloud/bigquery/data.rb +134 -0
- data/lib/gcloud/bigquery/dataset.rb +662 -0
- data/lib/gcloud/bigquery/dataset/list.rb +51 -0
- data/lib/gcloud/bigquery/errors.rb +62 -0
- data/lib/gcloud/bigquery/extract_job.rb +117 -0
- data/lib/gcloud/bigquery/insert_response.rb +80 -0
- data/lib/gcloud/bigquery/job.rb +283 -0
- data/lib/gcloud/bigquery/job/list.rb +55 -0
- data/lib/gcloud/bigquery/load_job.rb +199 -0
- data/lib/gcloud/bigquery/project.rb +512 -0
- data/lib/gcloud/bigquery/query_data.rb +135 -0
- data/lib/gcloud/bigquery/query_job.rb +151 -0
- data/lib/gcloud/bigquery/table.rb +827 -0
- data/lib/gcloud/bigquery/table/list.rb +55 -0
- data/lib/gcloud/bigquery/view.rb +419 -0
- data/lib/gcloud/credentials.rb +3 -3
- data/lib/gcloud/datastore.rb +15 -3
- data/lib/gcloud/datastore/credentials.rb +3 -2
- data/lib/gcloud/datastore/dataset.rb +5 -1
- data/lib/gcloud/datastore/transaction.rb +1 -1
- data/lib/gcloud/pubsub.rb +14 -3
- data/lib/gcloud/pubsub/credentials.rb +4 -4
- data/lib/gcloud/pubsub/project.rb +5 -1
- data/lib/gcloud/pubsub/topic.rb +5 -0
- data/lib/gcloud/storage.rb +14 -24
- data/lib/gcloud/storage/bucket.rb +10 -4
- data/lib/gcloud/storage/credentials.rb +3 -2
- data/lib/gcloud/storage/file.rb +8 -1
- data/lib/gcloud/storage/project.rb +5 -1
- data/lib/gcloud/upload.rb +54 -0
- data/lib/gcloud/version.rb +1 -1
- metadata +78 -2
@@ -0,0 +1,55 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
module Gcloud
|
17
|
+
module Bigquery
|
18
|
+
class Table
|
19
|
+
##
|
20
|
+
# Table::List is a special case Array with additional values.
|
21
|
+
class List < DelegateClass(::Array)
|
22
|
+
##
|
23
|
+
# If not empty, indicates that there are more records that match
|
24
|
+
# the request and this value should be passed to continue.
|
25
|
+
attr_accessor :token
|
26
|
+
|
27
|
+
# A hash of this page of results.
|
28
|
+
attr_accessor :etag
|
29
|
+
|
30
|
+
# Total number of jobs in this collection.
|
31
|
+
attr_accessor :total
|
32
|
+
|
33
|
+
##
|
34
|
+
# Create a new Table::List with an array of jobs.
|
35
|
+
def initialize arr = []
|
36
|
+
super arr
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# New Table::List from a response object.
|
41
|
+
def self.from_resp resp, conn #:nodoc:
|
42
|
+
tables = List.new(Array(resp.data["tables"]).map do |gapi_object|
|
43
|
+
Table.from_gapi gapi_object, conn
|
44
|
+
end)
|
45
|
+
tables.instance_eval do
|
46
|
+
@token = resp.data["nextPageToken"]
|
47
|
+
@etag = resp.data["etag"]
|
48
|
+
@total = resp.data["totalItems"]
|
49
|
+
end
|
50
|
+
tables
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,419 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2015 Google Inc. All rights reserved.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require "gcloud/bigquery/data"
|
17
|
+
require "gcloud/bigquery/table/list"
|
18
|
+
require "gcloud/bigquery/errors"
|
19
|
+
|
20
|
+
module Gcloud
|
21
|
+
module Bigquery
|
22
|
+
##
|
23
|
+
# = View
|
24
|
+
#
|
25
|
+
# A view is a virtual table defined by a SQL query. You can query views in
|
26
|
+
# the browser tool, or by using a query job.
|
27
|
+
#
|
28
|
+
# BigQuery's views are logical views, not materialized views, which means
|
29
|
+
# that the query that defines the view is re-executed every time the view is
|
30
|
+
# queried. Queries are billed according to the total amount of data in all
|
31
|
+
# table fields referenced directly or indirectly by the top-level query.
|
32
|
+
#
|
33
|
+
# require "gcloud"
|
34
|
+
#
|
35
|
+
# gcloud = Gcloud.new
|
36
|
+
# bigquery = gcloud.bigquery
|
37
|
+
# dataset = bigquery.dataset "my_dataset"
|
38
|
+
# view = dataset.create_view "my_view",
|
39
|
+
# "SELECT name, age FROM [proj:dataset.users]"
|
40
|
+
#
|
41
|
+
class View
|
42
|
+
##
|
43
|
+
# The Connection object.
|
44
|
+
attr_accessor :connection #:nodoc:
|
45
|
+
|
46
|
+
##
|
47
|
+
# The Google API Client object.
|
48
|
+
attr_accessor :gapi #:nodoc:
|
49
|
+
|
50
|
+
##
|
51
|
+
# Create an empty Table object.
|
52
|
+
def initialize #:nodoc:
|
53
|
+
@connection = nil
|
54
|
+
@gapi = {}
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# A unique ID for this table.
|
59
|
+
# The ID must contain only letters (a-z, A-Z), numbers (0-9),
|
60
|
+
# or underscores (_). The maximum length is 1,024 characters.
|
61
|
+
#
|
62
|
+
# :category: Attributes
|
63
|
+
#
|
64
|
+
def table_id
|
65
|
+
@gapi["tableReference"]["tableId"]
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# The ID of the +Dataset+ containing this table.
|
70
|
+
#
|
71
|
+
# :category: Attributes
|
72
|
+
#
|
73
|
+
def dataset_id
|
74
|
+
@gapi["tableReference"]["datasetId"]
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# The ID of the +Project+ containing this table.
|
79
|
+
#
|
80
|
+
# :category: Attributes
|
81
|
+
#
|
82
|
+
def project_id
|
83
|
+
@gapi["tableReference"]["projectId"]
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# The name of the table.
|
88
|
+
#
|
89
|
+
# :category: Attributes
|
90
|
+
#
|
91
|
+
def name
|
92
|
+
@gapi["friendlyName"]
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Updates the name of the table.
|
97
|
+
#
|
98
|
+
# :category: Lifecycle
|
99
|
+
#
|
100
|
+
def name= new_name
|
101
|
+
patch_gapi! name: new_name
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# A string hash of the dataset.
|
106
|
+
#
|
107
|
+
# :category: Attributes
|
108
|
+
#
|
109
|
+
def etag
|
110
|
+
ensure_full_data!
|
111
|
+
@gapi["etag"]
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# A URL that can be used to access the dataset using the REST API.
|
116
|
+
#
|
117
|
+
# :category: Attributes
|
118
|
+
#
|
119
|
+
def url
|
120
|
+
ensure_full_data!
|
121
|
+
@gapi["selfLink"]
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# The description of the table.
|
126
|
+
#
|
127
|
+
# :category: Attributes
|
128
|
+
#
|
129
|
+
def description
|
130
|
+
ensure_full_data!
|
131
|
+
@gapi["description"]
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# Updates the description of the table.
|
136
|
+
#
|
137
|
+
# :category: Lifecycle
|
138
|
+
#
|
139
|
+
def description= new_description
|
140
|
+
patch_gapi! description: new_description
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# The time when this table was created.
|
145
|
+
#
|
146
|
+
# :category: Attributes
|
147
|
+
#
|
148
|
+
def created_at
|
149
|
+
ensure_full_data!
|
150
|
+
Time.at(@gapi["creationTime"] / 1000.0)
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# The time when this table expires.
|
155
|
+
# If not present, the table will persist indefinitely.
|
156
|
+
# Expired tables will be deleted and their storage reclaimed.
|
157
|
+
#
|
158
|
+
# :category: Attributes
|
159
|
+
#
|
160
|
+
def expires_at
|
161
|
+
ensure_full_data!
|
162
|
+
return nil if @gapi["expirationTime"].nil?
|
163
|
+
Time.at(@gapi["expirationTime"] / 1000.0)
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# The date when this table was last modified.
|
168
|
+
#
|
169
|
+
# :category: Attributes
|
170
|
+
#
|
171
|
+
def modified_at
|
172
|
+
ensure_full_data!
|
173
|
+
Time.at(@gapi["lastModifiedTime"] / 1000.0)
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# Checks if the table's type is "TABLE".
|
178
|
+
#
|
179
|
+
# :category: Attributes
|
180
|
+
#
|
181
|
+
def table?
|
182
|
+
@gapi["type"] == "TABLE"
|
183
|
+
end
|
184
|
+
|
185
|
+
##
|
186
|
+
# Checks if the table's type is "VIEW".
|
187
|
+
#
|
188
|
+
# :category: Attributes
|
189
|
+
#
|
190
|
+
def view?
|
191
|
+
@gapi["type"] == "VIEW"
|
192
|
+
end
|
193
|
+
|
194
|
+
##
|
195
|
+
# The geographic location where the table should reside. Possible
|
196
|
+
# values include EU and US. The default value is US.
|
197
|
+
#
|
198
|
+
# :category: Attributes
|
199
|
+
#
|
200
|
+
def location
|
201
|
+
ensure_full_data!
|
202
|
+
@gapi["location"]
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# The schema of the table.
|
207
|
+
#
|
208
|
+
# :category: Attributes
|
209
|
+
#
|
210
|
+
def schema
|
211
|
+
ensure_full_data!
|
212
|
+
s = @gapi["schema"]
|
213
|
+
s = s.to_hash if s.respond_to? :to_hash
|
214
|
+
s = {} if s.nil?
|
215
|
+
s
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# The fields of the table.
|
220
|
+
#
|
221
|
+
# :category: Attributes
|
222
|
+
#
|
223
|
+
def fields
|
224
|
+
f = schema["fields"]
|
225
|
+
f = f.to_hash if f.respond_to? :to_hash
|
226
|
+
f = [] if f.nil?
|
227
|
+
f
|
228
|
+
end
|
229
|
+
|
230
|
+
##
|
231
|
+
# The names of the columns in the table.
|
232
|
+
#
|
233
|
+
# :category: Attributes
|
234
|
+
#
|
235
|
+
def headers
|
236
|
+
fields.map { |f| f["name"] }
|
237
|
+
end
|
238
|
+
|
239
|
+
##
|
240
|
+
# The query that executes each time the view is loaded.
|
241
|
+
#
|
242
|
+
# :category: Attributes
|
243
|
+
#
|
244
|
+
def query
|
245
|
+
@gapi["view"]["query"] if @gapi["view"]
|
246
|
+
end
|
247
|
+
|
248
|
+
##
|
249
|
+
# Updates the query that executes each time the view is loaded.
|
250
|
+
# See the BigQuery {Query Reference
|
251
|
+
# }[https://cloud.google.com/bigquery/query-reference].
|
252
|
+
#
|
253
|
+
# === Parameters
|
254
|
+
#
|
255
|
+
# +new_query+::
|
256
|
+
# The query that defines the view. (+String+)
|
257
|
+
#
|
258
|
+
# === Example
|
259
|
+
#
|
260
|
+
# require "gcloud"
|
261
|
+
#
|
262
|
+
# gcloud = Gcloud.new
|
263
|
+
# bigquery = gcloud.bigquery
|
264
|
+
# dataset = bigquery.dataset "my_dataset"
|
265
|
+
# view = dataset.table "my_view"
|
266
|
+
#
|
267
|
+
# view.query = "SELECT first_name FROM [my_project:my_dataset.my_table]"
|
268
|
+
#
|
269
|
+
# :category: Lifecycle
|
270
|
+
#
|
271
|
+
def query= new_query
|
272
|
+
patch_gapi! query: new_query
|
273
|
+
end
|
274
|
+
|
275
|
+
##
|
276
|
+
# Runs a query to retrieve all data from the view.
|
277
|
+
#
|
278
|
+
# === Parameters
|
279
|
+
#
|
280
|
+
# +options+::
|
281
|
+
# An optional Hash for controlling additional behavior. (+Hash+)
|
282
|
+
# <code>options[:max]</code>::
|
283
|
+
# The maximum number of rows of data to return per page of results.
|
284
|
+
# Setting this flag to a small value such as 1000 and then paging
|
285
|
+
# through results might improve reliability when the query result set is
|
286
|
+
# large. In addition to this limit, responses are also limited to 10 MB.
|
287
|
+
# By default, there is no maximum row count, and only the byte limit
|
288
|
+
# applies. (+Integer+)
|
289
|
+
# <code>options[:timeout]</code>::
|
290
|
+
# How long to wait for the query to complete, in milliseconds, before
|
291
|
+
# the request times out and returns. Note that this is only a timeout
|
292
|
+
# for the request, not the query. If the query takes longer to run than
|
293
|
+
# the timeout value, the call returns without any results and with
|
294
|
+
# QueryData#complete? set to false. The default value is 10000
|
295
|
+
# milliseconds (10 seconds). (+Integer+)
|
296
|
+
# <code>options[:dryrun]</code>::
|
297
|
+
# If set to +true+, BigQuery doesn't run the job. Instead, if the query
|
298
|
+
# is valid, BigQuery returns statistics about the job such as how many
|
299
|
+
# bytes would be processed. If the query is invalid, an error returns.
|
300
|
+
# The default value is +false+. (+Boolean+)
|
301
|
+
# <code>options[:cache]</code>::
|
302
|
+
# Whether to look for the result in the query cache. The query cache is
|
303
|
+
# a best-effort cache that will be flushed whenever tables in the query
|
304
|
+
# are modified. The default value is true. For more information, see
|
305
|
+
# {query caching}[https://developers.google.com/bigquery/querying-data].
|
306
|
+
# (+Boolean+)
|
307
|
+
#
|
308
|
+
# === Returns
|
309
|
+
#
|
310
|
+
# Gcloud::Bigquery::QueryData
|
311
|
+
#
|
312
|
+
# === Example
|
313
|
+
#
|
314
|
+
# require "gcloud"
|
315
|
+
#
|
316
|
+
# gcloud = Gcloud.new
|
317
|
+
# bigquery = gcloud.bigquery
|
318
|
+
# dataset = bigquery.dataset "my_dataset"
|
319
|
+
# view = dataset.table "my_view"
|
320
|
+
#
|
321
|
+
# data = view.data
|
322
|
+
# data.each do |row|
|
323
|
+
# puts row["first_name"]
|
324
|
+
# end
|
325
|
+
# more_data = data.next if data.next?
|
326
|
+
#
|
327
|
+
# :category: Data
|
328
|
+
#
|
329
|
+
def data options = {}
|
330
|
+
sql = "SELECT * FROM #{@gapi['id']}"
|
331
|
+
ensure_connection!
|
332
|
+
resp = connection.query sql, options
|
333
|
+
if resp.success?
|
334
|
+
QueryData.from_gapi resp.data, connection
|
335
|
+
else
|
336
|
+
fail ApiError.from_response(resp)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
##
|
341
|
+
# Permanently deletes the table.
|
342
|
+
#
|
343
|
+
# === Returns
|
344
|
+
#
|
345
|
+
# +true+ if the table was deleted.
|
346
|
+
#
|
347
|
+
# === Example
|
348
|
+
#
|
349
|
+
# require "gcloud"
|
350
|
+
#
|
351
|
+
# gcloud = Gcloud.new
|
352
|
+
# bigquery = gcloud.bigquery
|
353
|
+
# dataset = bigquery.dataset "my_dataset"
|
354
|
+
# table = dataset.table "my_table"
|
355
|
+
#
|
356
|
+
# table.delete
|
357
|
+
#
|
358
|
+
# :category: Lifecycle
|
359
|
+
#
|
360
|
+
def delete
|
361
|
+
ensure_connection!
|
362
|
+
resp = connection.delete_table dataset_id, table_id
|
363
|
+
if resp.success?
|
364
|
+
true
|
365
|
+
else
|
366
|
+
fail ApiError.from_response(resp)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
##
|
371
|
+
# New Table from a Google API Client object.
|
372
|
+
def self.from_gapi gapi, conn #:nodoc:
|
373
|
+
new.tap do |f|
|
374
|
+
f.gapi = gapi
|
375
|
+
f.connection = conn
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
protected
|
380
|
+
|
381
|
+
##
|
382
|
+
# Raise an error unless an active connection is available.
|
383
|
+
def ensure_connection!
|
384
|
+
fail "Must have active connection" unless connection
|
385
|
+
end
|
386
|
+
|
387
|
+
def patch_gapi! options = {}
|
388
|
+
ensure_connection!
|
389
|
+
resp = connection.patch_table dataset_id, table_id, options
|
390
|
+
if resp.success?
|
391
|
+
@gapi = resp.data
|
392
|
+
else
|
393
|
+
fail ApiError.from_response(resp)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
##
|
398
|
+
# Load the complete representation of the table if it has been
|
399
|
+
# only partially loaded by a request to the API list method.
|
400
|
+
def ensure_full_data!
|
401
|
+
reload_gapi! unless data_complete?
|
402
|
+
end
|
403
|
+
|
404
|
+
def reload_gapi!
|
405
|
+
ensure_connection!
|
406
|
+
resp = connection.get_table dataset_id, table_id
|
407
|
+
if resp.success?
|
408
|
+
@gapi = resp.data
|
409
|
+
else
|
410
|
+
fail ApiError.from_response(resp)
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
def data_complete?
|
415
|
+
!@gapi["creationTime"].nil?
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|