gcloud 0.3.0 → 0.3.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.
Files changed (42) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +21 -0
  3. data/lib/gcloud.rb +0 -5
  4. data/lib/gcloud/bigquery.rb +31 -62
  5. data/lib/gcloud/bigquery/connection.rb +58 -35
  6. data/lib/gcloud/bigquery/dataset.rb +147 -18
  7. data/lib/gcloud/bigquery/dataset/access.rb +477 -0
  8. data/lib/gcloud/bigquery/dataset/list.rb +1 -1
  9. data/lib/gcloud/bigquery/errors.rb +2 -0
  10. data/lib/gcloud/bigquery/job.rb +30 -6
  11. data/lib/gcloud/bigquery/job/list.rb +1 -1
  12. data/lib/gcloud/bigquery/project.rb +47 -8
  13. data/lib/gcloud/bigquery/query_job.rb +1 -5
  14. data/lib/gcloud/bigquery/table.rb +185 -47
  15. data/lib/gcloud/bigquery/table/list.rb +1 -1
  16. data/lib/gcloud/bigquery/table/schema.rb +252 -0
  17. data/lib/gcloud/bigquery/view.rb +25 -0
  18. data/lib/gcloud/datastore/connection.rb +4 -0
  19. data/lib/gcloud/datastore/dataset.rb +5 -2
  20. data/lib/gcloud/datastore/errors.rb +1 -1
  21. data/lib/gcloud/datastore/properties.rb +1 -0
  22. data/lib/gcloud/datastore/proto.rb +3 -0
  23. data/lib/gcloud/errors.rb +23 -0
  24. data/lib/gcloud/gce.rb +62 -0
  25. data/lib/gcloud/pubsub/connection.rb +4 -0
  26. data/lib/gcloud/pubsub/errors.rb +2 -0
  27. data/lib/gcloud/pubsub/project.rb +5 -3
  28. data/lib/gcloud/pubsub/subscription/list.rb +1 -1
  29. data/lib/gcloud/pubsub/topic.rb +1 -1
  30. data/lib/gcloud/pubsub/topic/list.rb +1 -1
  31. data/lib/gcloud/storage.rb +16 -0
  32. data/lib/gcloud/storage/bucket.rb +31 -1
  33. data/lib/gcloud/storage/bucket/acl.rb +12 -10
  34. data/lib/gcloud/storage/bucket/list.rb +1 -1
  35. data/lib/gcloud/storage/connection.rb +4 -0
  36. data/lib/gcloud/storage/errors.rb +2 -0
  37. data/lib/gcloud/storage/file.rb +13 -0
  38. data/lib/gcloud/storage/file/acl.rb +6 -5
  39. data/lib/gcloud/storage/file/list.rb +1 -1
  40. data/lib/gcloud/storage/project.rb +4 -2
  41. data/lib/gcloud/version.rb +1 -1
  42. metadata +6 -2
@@ -35,7 +35,7 @@ module Gcloud
35
35
 
36
36
  ##
37
37
  # New Dataset::List from a response object.
38
- def self.from_resp resp, conn #:nodoc:
38
+ def self.from_response resp, conn #:nodoc:
39
39
  datasets = List.new(Array(resp.data["datasets"]).map do |gapi_object|
40
40
  Dataset.from_gapi gapi_object, conn
41
41
  end)
@@ -13,6 +13,8 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ require "gcloud/errors"
17
+
16
18
  module Gcloud
17
19
  module Bigquery
18
20
  ##
@@ -44,11 +44,7 @@ module Gcloud
44
44
  # q = "SELECT COUNT(word) as count FROM publicdata:samples.shakespeare"
45
45
  # job = bigquery.query_job q
46
46
  #
47
- # loop do
48
- # break if job.done?
49
- # sleep 1
50
- # job.refresh!
51
- # end
47
+ # job.wait_until_done!
52
48
  #
53
49
  # if job.failed?
54
50
  # puts job.error
@@ -219,7 +215,7 @@ module Gcloud
219
215
 
220
216
  ##
221
217
  # Reloads the job with current data from the BigQuery service.
222
- def refresh!
218
+ def reload!
223
219
  ensure_connection!
224
220
  resp = connection.get_job job_id
225
221
  if resp.success?
@@ -228,6 +224,34 @@ module Gcloud
228
224
  fail ApiError.from_response(resp)
229
225
  end
230
226
  end
227
+ alias_method :refresh!, :reload!
228
+
229
+ ##
230
+ # Refreshes the job until the job is +DONE+.
231
+ # The delay between refreshes will incrementally increase.
232
+ #
233
+ # === Example
234
+ #
235
+ # require "gcloud"
236
+ #
237
+ # gcloud = Gcloud.new
238
+ # bigquery = gcloud.bigquery
239
+ # dataset = bigquery.dataset "my_dataset"
240
+ # table = dataset.table "my_table"
241
+ #
242
+ # extract_job = table.extract "gs://my-bucket/file-name.json",
243
+ # format: "json"
244
+ # extract_job.wait_until_done!
245
+ # extract_job.done? #=> true
246
+ def wait_until_done!
247
+ backoff = ->(retries) { sleep 2 * retries + 5 }
248
+ retries = 0
249
+ until done?
250
+ backoff.call retries
251
+ retries += 1
252
+ reload!
253
+ end
254
+ end
231
255
 
232
256
  ##
233
257
  # New Job from a Google API Client object.
@@ -38,7 +38,7 @@ module Gcloud
38
38
 
39
39
  ##
40
40
  # New Job::List from a response object.
41
- def self.from_resp resp, conn #:nodoc:
41
+ def self.from_response resp, conn #:nodoc:
42
42
  jobs = List.new(Array(resp.data["jobs"]).map do |gapi_object|
43
43
  Job.from_gapi gapi_object, conn
44
44
  end)
@@ -13,6 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ require "gcloud/gce"
16
17
  require "gcloud/bigquery/connection"
17
18
  require "gcloud/bigquery/credentials"
18
19
  require "gcloud/bigquery/errors"
@@ -77,7 +78,8 @@ module Gcloud
77
78
  def self.default_project #:nodoc:
78
79
  ENV["BIGQUERY_PROJECT"] ||
79
80
  ENV["GCLOUD_PROJECT"] ||
80
- ENV["GOOGLE_CLOUD_PROJECT"]
81
+ ENV["GOOGLE_CLOUD_PROJECT"] ||
82
+ Gcloud::GCE.project_id
81
83
  end
82
84
 
83
85
  ##
@@ -143,11 +145,7 @@ module Gcloud
143
145
  #
144
146
  # job = bigquery.query_job "SELECT name FROM [my_proj:my_data.my_table]"
145
147
  #
146
- # loop do
147
- # break if job.done?
148
- # sleep 1
149
- # job.refresh!
150
- # end
148
+ # job.wait_until_done!
151
149
  # if !job.failed?
152
150
  # job.query_results.each do |row|
153
151
  # puts row["name"]
@@ -268,6 +266,10 @@ module Gcloud
268
266
  end
269
267
  end
270
268
 
269
+ # rubocop:disable Metrics/AbcSize
270
+ # rubocop:disable Metrics/MethodLength
271
+ # Disabled rubocop because the level of abstraction is not violated here
272
+
271
273
  ##
272
274
  # Creates a new dataset.
273
275
  #
@@ -286,6 +288,11 @@ module Gcloud
286
288
  # <code>options[:expiration]</code>::
287
289
  # The default lifetime of all tables in the dataset, in milliseconds.
288
290
  # The minimum value is 3600000 milliseconds (one hour). (+Integer+)
291
+ # <code>options[:access]</code>::
292
+ # The access rules for a Dataset using the Google Cloud Datastore API
293
+ # data structure of an array of hashes. See {BigQuery Access
294
+ # Control}[https://cloud.google.com/bigquery/access-control] for more
295
+ # information. (+Array of Hashes+)
289
296
  #
290
297
  # === Returns
291
298
  #
@@ -311,7 +318,36 @@ module Gcloud
311
318
  # name: "My Dataset",
312
319
  # description: "This is my Dataset"
313
320
  #
321
+ # Access rules can be provided with the +access+ option:
322
+ #
323
+ # require "gcloud"
324
+ #
325
+ # gcloud = Gcloud.new
326
+ # bigquery = gcloud.bigquery
327
+ #
328
+ # dataset = bigquery.create_dataset "my_dataset",
329
+ # access: [{"role"=>"WRITER", "userByEmail"=>"writers@example.com"}]
330
+ #
331
+ # Or access rules can be configured by using the block syntax:
332
+ # (See Dataset::Access)
333
+ #
334
+ # require "gcloud"
335
+ #
336
+ # gcloud = Gcloud.new
337
+ # bigquery = gcloud.bigquery
338
+ #
339
+ # dataset = bigquery.create_dataset "my_dataset" do |access|
340
+ # access.add_writer_user "writers@example.com"
341
+ # end
342
+ #
314
343
  def create_dataset dataset_id, options = {}
344
+ if block_given?
345
+ access_builder = Dataset::Access.new connection.default_access_rules,
346
+ "projectId" => project
347
+ yield access_builder
348
+ options[:access] = access_builder.access if access_builder.changed?
349
+ end
350
+
315
351
  ensure_connection!
316
352
  resp = connection.insert_dataset dataset_id, options
317
353
  if resp.success?
@@ -321,6 +357,9 @@ module Gcloud
321
357
  end
322
358
  end
323
359
 
360
+ # rubocop:enable Metrics/AbcSize
361
+ # rubocop:enable Metrics/MethodLength
362
+
324
363
  ##
325
364
  # Retrieves the list of datasets belonging to the project.
326
365
  #
@@ -387,7 +426,7 @@ module Gcloud
387
426
  ensure_connection!
388
427
  resp = connection.list_datasets options
389
428
  if resp.success?
390
- Dataset::List.from_resp resp, connection
429
+ Dataset::List.from_response resp, connection
391
430
  else
392
431
  fail ApiError.from_response(resp)
393
432
  end
@@ -494,7 +533,7 @@ module Gcloud
494
533
  ensure_connection!
495
534
  resp = connection.list_jobs options
496
535
  if resp.success?
497
- Job::List.from_resp resp, connection
536
+ Job::List.from_response resp, connection
498
537
  else
499
538
  fail ApiError.from_response(resp)
500
539
  end
@@ -126,11 +126,7 @@ module Gcloud
126
126
  # q = "SELECT word FROM publicdata:samples.shakespeare"
127
127
  # job = bigquery.query_job q
128
128
  #
129
- # loop do
130
- # break if job.done?
131
- # sleep 1
132
- # job.refresh!
133
- # end
129
+ # job.wait_until_done!
134
130
  # data = job.query_results
135
131
  # data.each do |row|
136
132
  # puts row["word"]
@@ -16,6 +16,7 @@
16
16
  require "gcloud/bigquery/view"
17
17
  require "gcloud/bigquery/data"
18
18
  require "gcloud/bigquery/table/list"
19
+ require "gcloud/bigquery/table/schema"
19
20
  require "gcloud/bigquery/errors"
20
21
  require "gcloud/bigquery/insert_response"
21
22
  require "gcloud/upload"
@@ -36,46 +37,25 @@ module Gcloud
36
37
  # gcloud = Gcloud.new
37
38
  # bigquery = gcloud.bigquery
38
39
  # dataset = bigquery.dataset "my_dataset"
39
- # table = dataset.create_table "my_table"
40
40
  #
41
- # schema = {
42
- # "fields" => [
43
- # {
44
- # "name" => "first_name",
45
- # "type" => "STRING",
46
- # "mode" => "REQUIRED"
47
- # },
48
- # {
49
- # "name" => "cities_lived",
50
- # "type" => "RECORD",
51
- # "mode" => "REPEATED",
52
- # "fields" => [
53
- # {
54
- # "name" => "place",
55
- # "type" => "STRING",
56
- # "mode" => "REQUIRED"
57
- # },
58
- # {
59
- # "name" => "number_of_years",
60
- # "type" => "INTEGER",
61
- # "mode" => "REQUIRED"
62
- # }
63
- # ]
64
- # }
65
- # ]
66
- # }
67
- # table.schema = schema
41
+ # table = dataset.create_table "my_table" do |schema|
42
+ # schema.string "first_name", mode: :required
43
+ # schema.record "cities_lived", mode: :repeated do |nested_schema|
44
+ # nested_schema.string "place", mode: :required
45
+ # nested_schema.integer "number_of_years", mode: :required
46
+ # end
47
+ # end
68
48
  #
69
49
  # row = {
70
50
  # "first_name" => "Alice",
71
51
  # "cities_lived" => [
72
52
  # {
73
- # "place": "Seattle",
74
- # "number_of_years": 5
53
+ # "place" => "Seattle",
54
+ # "number_of_years" => 5
75
55
  # },
76
56
  # {
77
- # "place": "Stockholm",
78
- # "number_of_years": 6
57
+ # "place" => "Stockholm",
58
+ # "number_of_years" => 6
79
59
  # }
80
60
  # ]
81
61
  # }
@@ -126,6 +106,51 @@ module Gcloud
126
106
  @gapi["tableReference"]["projectId"]
127
107
  end
128
108
 
109
+ ##
110
+ # The gapi fragment containing the Project ID, Dataset ID, and Table ID as
111
+ # a camel-cased hash.
112
+ def table_ref #:nodoc:
113
+ table_ref = @gapi["tableReference"]
114
+ table_ref = table_ref.to_hash if table_ref.respond_to? :to_hash
115
+ table_ref
116
+ end
117
+
118
+ ##
119
+ # The combined Project ID, Dataset ID, and Table ID for this table, in the
120
+ # format specified by the {Query
121
+ # Reference}[https://cloud.google.com/bigquery/query-reference#from]:
122
+ # +project_name:datasetId.tableId+. To use this value in queries see
123
+ # #query_id.
124
+ #
125
+ # :category: Attributes
126
+ #
127
+ def id
128
+ @gapi["id"]
129
+ end
130
+
131
+ ##
132
+ # The value returned by #id, wrapped in square brackets if the Project ID
133
+ # contains dashes, as specified by the {Query
134
+ # Reference}[https://cloud.google.com/bigquery/query-reference#from].
135
+ # Useful in queries.
136
+ #
137
+ # === Example
138
+ #
139
+ # require "gcloud"
140
+ #
141
+ # gcloud = Gcloud.new
142
+ # bigquery = gcloud.bigquery
143
+ # dataset = bigquery.dataset "my_dataset"
144
+ # table = dataset.table "my_table"
145
+ #
146
+ # data = bigquery.query "SELECT name FROM #{table.query_id}"
147
+ #
148
+ # :category: Attributes
149
+ #
150
+ def query_id
151
+ project_id["-"] ? "[#{id}]" : id
152
+ end
153
+
129
154
  ##
130
155
  # The name of the table.
131
156
  #
@@ -266,20 +291,66 @@ module Gcloud
266
291
  end
267
292
 
268
293
  ##
269
- # The schema of the table.
294
+ # Returns the table's schema as hash containing the keys and values
295
+ # returned by the Google Cloud BigQuery {Rest API
296
+ # }[https://cloud.google.com/bigquery/docs/reference/v2/tables#resource].
297
+ # This method can also be used to set, replace, or add to the schema by
298
+ # passing a block. See Table::Schema for available methods. To set the
299
+ # schema by passing a hash instead, use #schema=.
300
+ #
301
+ # === Parameters
302
+ #
303
+ # +options+::
304
+ # An optional Hash for controlling additional behavior. (+Hash+)
305
+ # <code>options[:replace]</code>::
306
+ # Whether to replace the existing schema with the new schema. If
307
+ # +true+, the fields will replace the existing schema. If
308
+ # +false+, the fields will be added to the existing schema. When a table
309
+ # already contains data, schema changes must be additive. Thus, the
310
+ # default value is +false+. (+Boolean+)
311
+ #
312
+ # === Examples
313
+ #
314
+ # require "gcloud"
315
+ #
316
+ # gcloud = Gcloud.new
317
+ # bigquery = gcloud.bigquery
318
+ # dataset = bigquery.dataset "my_dataset"
319
+ # table = dataset.create_table "my_table"
320
+ #
321
+ # table.schema do |schema|
322
+ # schema.string "first_name", mode: :required
323
+ # schema.record "cities_lived", mode: :repeated do |nested_schema|
324
+ # nested_schema.string "place", mode: :required
325
+ # nested_schema.integer "number_of_years", mode: :required
326
+ # end
327
+ # end
270
328
  #
271
329
  # :category: Attributes
272
330
  #
273
- def schema
331
+ def schema options = {}
274
332
  ensure_full_data!
275
- s = @gapi["schema"]
276
- s = s.to_hash if s.respond_to? :to_hash
277
- s = {} if s.nil?
278
- s
333
+ g = @gapi
334
+ g = g.to_hash if g.respond_to? :to_hash
335
+ s = g["schema"] ||= {}
336
+ return s unless block_given?
337
+ s = nil if options[:replace]
338
+ schema_builder = Schema.new s
339
+ yield schema_builder
340
+ self.schema = schema_builder.schema if schema_builder.changed?
279
341
  end
280
342
 
281
343
  ##
282
344
  # Updates the schema of the table.
345
+ # To update the schema using a block instead, use #schema.
346
+ #
347
+ # === Parameters
348
+ #
349
+ # +schema+::
350
+ # A hash containing keys and values as specified by the Google Cloud
351
+ # BigQuery {Rest API
352
+ # }[https://cloud.google.com/bigquery/docs/reference/v2/tables#resource]
353
+ # . (+Hash+)
283
354
  #
284
355
  # === Example
285
356
  #
@@ -385,7 +456,7 @@ module Gcloud
385
456
  # === Parameters
386
457
  #
387
458
  # +destination_table+::
388
- # The destination for the copied data. (+Table+)
459
+ # The destination for the copied data. (+Table+ or +String+)
389
460
  # +options+::
390
461
  # An optional Hash for controlling additional behavior. (+Hash+)
391
462
  # <code>options[:create]</code>::
@@ -409,7 +480,7 @@ module Gcloud
409
480
  #
410
481
  # Gcloud::Bigquery::CopyJob
411
482
  #
412
- # === Example
483
+ # === Examples
413
484
  #
414
485
  # require "gcloud"
415
486
  #
@@ -421,11 +492,28 @@ module Gcloud
421
492
  #
422
493
  # copy_job = table.copy destination_table
423
494
  #
495
+ # The destination table argument can also be a string identifier as
496
+ # specified by the {Query
497
+ # Reference}[https://cloud.google.com/bigquery/query-reference#from]:
498
+ # +project_name:datasetId.tableId+. This is useful for referencing tables
499
+ # in other projects and datasets.
500
+ #
501
+ # require "gcloud"
502
+ #
503
+ # gcloud = Gcloud.new
504
+ # bigquery = gcloud.bigquery
505
+ # dataset = bigquery.dataset "my_dataset"
506
+ # table = dataset.table "my_table"
507
+ #
508
+ # copy_job = table.copy "other-project:other_dataset.other_table"
509
+ #
424
510
  # :category: Data
425
511
  #
426
512
  def copy destination_table, options = {}
427
513
  ensure_connection!
428
- resp = connection.copy_table gapi, destination_table.gapi, options
514
+ resp = connection.copy_table table_ref,
515
+ get_table_ref(destination_table),
516
+ options
429
517
  if resp.success?
430
518
  Job.from_gapi resp.data, connection
431
519
  else
@@ -467,7 +555,7 @@ module Gcloud
467
555
  #
468
556
  def link source_url, options = {} #:nodoc:
469
557
  ensure_connection!
470
- resp = connection.link_table gapi, source_url, options
558
+ resp = connection.link_table table_ref, source_url, options
471
559
  if resp.success?
472
560
  Job.from_gapi resp.data, connection
473
561
  else
@@ -516,7 +604,7 @@ module Gcloud
516
604
  #
517
605
  def extract extract_url, options = {}
518
606
  ensure_connection!
519
- resp = connection.extract_table gapi, extract_url, options
607
+ resp = connection.extract_table table_ref, extract_url, options
520
608
  if resp.success?
521
609
  Job.from_gapi resp.data, connection
522
610
  else
@@ -542,6 +630,7 @@ module Gcloud
542
630
  # * +csv+ - CSV
543
631
  # * +json+ - {Newline-delimited JSON}[http://jsonlines.org/]
544
632
  # * +avro+ - {Avro}[http://avro.apache.org/]
633
+ # * +datastore_backup+ - Cloud Datastore backup
545
634
  # <code>options[:create]</code>::
546
635
  # Specifies whether the job is allowed to create new tables. (+String+)
547
636
  #
@@ -558,6 +647,12 @@ module Gcloud
558
647
  # * +append+ - BigQuery appends the data to the table.
559
648
  # * +empty+ - An error will be returned if the table already contains
560
649
  # data.
650
+ # <code>options[:projection_fields]</code>::
651
+ # If the +format+ option is set to +datastore_backup+, indicates which
652
+ # entity properties to load from a Cloud Datastore backup. Property
653
+ # names are case sensitive and must be top-level properties. If not set,
654
+ # BigQuery loads all properties. If any named property isn't found in
655
+ # the Cloud Datastore backup, an invalid error is returned. (+Array+)
561
656
  #
562
657
  # === Returns
563
658
  #
@@ -589,7 +684,7 @@ module Gcloud
589
684
  # file = bucket.file "file-name.csv"
590
685
  # load_job = table.load file
591
686
  #
592
- # Or, you can upload a smaller file directly.
687
+ # Or, you can upload a file directly.
593
688
  # See {Loading Data with a POST Request}[
594
689
  # https://cloud.google.com/bigquery/loading-data-post-request#multipart].
595
690
  #
@@ -603,6 +698,23 @@ module Gcloud
603
698
  # file = File.open "my_data.csv"
604
699
  # load_job = table.load file
605
700
  #
701
+ # === A note about large direct uploads
702
+ #
703
+ # You may encounter a broken pipe error while attempting to upload large
704
+ # files. To avoid this problem, add
705
+ # {httpclient}[https://rubygems.org/gems/httpclient] as a dependency to
706
+ # your project, and configure {Faraday}[https://rubygems.org/gems/faraday]
707
+ # to use it, after requiring Gcloud, but before initiating your Gcloud
708
+ # connection.
709
+ #
710
+ # require "gcloud"
711
+ #
712
+ # Faraday.default_adapter = :httpclient
713
+ #
714
+ # gcloud = Gcloud.new
715
+ # bigquery = gcloud.bigquery
716
+ # dataset = bigquery.dataset "my_dataset"
717
+ #
606
718
  # :category: Data
607
719
  #
608
720
  def load file, options = {}
@@ -700,6 +812,22 @@ module Gcloud
700
812
  end
701
813
  end
702
814
 
815
+ ##
816
+ # Reloads the table with current data from the BigQuery service.
817
+ #
818
+ # :category: Lifecycle
819
+ #
820
+ def reload!
821
+ ensure_connection!
822
+ resp = connection.get_table dataset_id, table_id
823
+ if resp.success?
824
+ @gapi = resp.data
825
+ else
826
+ fail ApiError.from_response(resp)
827
+ end
828
+ end
829
+ alias_method :refresh!, :reload!
830
+
703
831
  ##
704
832
  # New Table from a Google API Client object.
705
833
  def self.from_gapi gapi, conn #:nodoc:
@@ -737,7 +865,7 @@ module Gcloud
737
865
  # Convert to storage URL
738
866
  file = file.to_gs_url if file.respond_to? :to_gs_url
739
867
 
740
- resp = connection.load_table gapi, file, options
868
+ resp = connection.load_table table_ref, file, options
741
869
  if resp.success?
742
870
  Job.from_gapi resp.data, connection
743
871
  else
@@ -755,7 +883,7 @@ module Gcloud
755
883
 
756
884
  def load_resumable file, options = {}
757
885
  chunk_size = verify_chunk_size! options[:chunk_size]
758
- resp = connection.load_resumable gapi, file, chunk_size, options
886
+ resp = connection.load_resumable table_ref, file, chunk_size, options
759
887
  if resp.success?
760
888
  Job.from_gapi resp.data, connection
761
889
  else
@@ -764,7 +892,7 @@ module Gcloud
764
892
  end
765
893
 
766
894
  def load_multipart file, options = {}
767
- resp = connection.load_multipart gapi, file, options
895
+ resp = connection.load_multipart table_ref, file, options
768
896
  if resp.success?
769
897
  Job.from_gapi resp.data, connection
770
898
  else
@@ -822,6 +950,16 @@ module Gcloud
822
950
  def data_complete?
823
951
  !@gapi["creationTime"].nil?
824
952
  end
953
+
954
+ private
955
+
956
+ def get_table_ref table
957
+ if table.respond_to? :table_ref
958
+ table.table_ref
959
+ else
960
+ Connection.table_ref_from_s table, table_ref
961
+ end
962
+ end
825
963
  end
826
964
  end
827
965
  end