gcloud 0.8.0 → 0.8.2
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/CHANGELOG.md +19 -0
- data/lib/gcloud/datastore/commit.rb +131 -0
- data/lib/gcloud/datastore/connection.rb +19 -5
- data/lib/gcloud/datastore/dataset.rb +97 -64
- data/lib/gcloud/datastore/entity.rb +26 -0
- data/lib/gcloud/datastore/errors.rb +9 -3
- data/lib/gcloud/datastore/properties.rb +9 -2
- data/lib/gcloud/datastore/proto.rb +9 -3
- data/lib/gcloud/datastore/transaction.rb +153 -23
- data/lib/gcloud/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZjM3ZTdhZGU0OWE0NTcxY2M0ZDAxNTY4NDAwNmI4MzNiNjlhZjBjZg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NTU2MWEwNzk1MjhlOWU1Mzc4MzUxNDdhZjEzNmYzZmI5NDViZjBkZA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Y2JlMDZiZGFiZmZkNTQ3N2QxOTRlZjlkOTdjNDk2ZWEwNTQ5NjA1MjUxMTVk
|
10
|
+
NjBhNDcxZjMyOWM1ZjEzMTFjYzQzMzU1YTA2ZmQ1ZGYwMTQ3OTg3OGRhMzhm
|
11
|
+
YTMyNjQzODhlYTVhMThmMjkwY2Q0MjhmMmZhZGIwOTcyODU1Mjc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NjNmMDkzZDhjNWZjYjFlMGVkOWM4MTliOGU4ZjRlN2E4MjAxYjgzNzBiNTgx
|
14
|
+
ZjdiOWY2NmY1YTY0OGE4OWQ5ZTg1MDEzMDE2ZGYwNjg0M2M2ZjNhMmI3YmY2
|
15
|
+
MGE4MDBlNWY4YTU2NGM3Mzc1MThlZDAyMjA0YTkzZGE2NDAxMGI=
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 0.8.2 / 2016-05-04
|
4
|
+
|
5
|
+
#### Changes
|
6
|
+
|
7
|
+
* Datastore
|
8
|
+
* Fix issue with blob values being stored in base64 (bmclean)
|
9
|
+
|
10
|
+
### 0.8.1 / 2016-05-03
|
11
|
+
|
12
|
+
#### Changes
|
13
|
+
|
14
|
+
* Datastore
|
15
|
+
* Add support for blob values (bmclean)
|
16
|
+
* Add support for Date and DateTime values
|
17
|
+
* Add support for setting read consistency
|
18
|
+
* Add support for batch operations outside of a transaction (timanovsky)
|
19
|
+
* Fix handling of rollback errors (timanovsky)
|
20
|
+
* Remove setting of project/dataset_id in query partition (toots)
|
21
|
+
|
3
22
|
### 0.8.0 / 2016-04-28
|
4
23
|
|
5
24
|
#### Major changes
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# Copyright 2016 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
|
16
|
+
module Gcloud
|
17
|
+
module Datastore
|
18
|
+
##
|
19
|
+
# # Commit
|
20
|
+
#
|
21
|
+
# Object yielded from `commit` methods to allow multiple changes to be made
|
22
|
+
# in a single commit.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# dataset.commit do |c|
|
26
|
+
# c.save task1, task2
|
27
|
+
# c.delete entity1, entity2
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# See {Gcloud::Datastore::Dataset#commit} and
|
31
|
+
# {Gcloud::Datastore::Transaction#commit}.
|
32
|
+
#
|
33
|
+
class Commit
|
34
|
+
##
|
35
|
+
# @private Create a new Commit object.
|
36
|
+
def initialize
|
37
|
+
@shared_entities = []
|
38
|
+
@shared_auto_ids = []
|
39
|
+
@shared_deletes = []
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Saves entities to the Datastore.
|
44
|
+
#
|
45
|
+
# @param [Entity] entities One or more Entity objects to save.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# gcloud = Gcloud.new
|
49
|
+
# dataset = gcloud.datastore
|
50
|
+
# dataset.commit do |c|
|
51
|
+
# c.save task1, task2
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
def save *entities
|
55
|
+
entities.each do |entity|
|
56
|
+
shared_auto_ids << entity if entity.key.incomplete?
|
57
|
+
shared_entities << entity
|
58
|
+
end
|
59
|
+
# Do not save yet
|
60
|
+
entities
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Remove entities from the Datastore.
|
65
|
+
#
|
66
|
+
# @param [Entity, Key] entities_or_keys One or more Entity or Key
|
67
|
+
# objects to remove.
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# gcloud = Gcloud.new
|
71
|
+
# dataset = gcloud.datastore
|
72
|
+
# dataset.commit do |c|
|
73
|
+
# c.delete entity1, entity2
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
def delete *entities_or_keys
|
77
|
+
keys = entities_or_keys.map do |e_or_k|
|
78
|
+
e_or_k.respond_to?(:key) ? e_or_k.key : e_or_k
|
79
|
+
end
|
80
|
+
keys.each { |k| shared_deletes << k }
|
81
|
+
# Do not delete yet
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
# @private Mutation object to be committed.
|
86
|
+
def mutation
|
87
|
+
Proto.new_mutation.tap do |m|
|
88
|
+
m.insert_auto_id = shared_auto_ids.map(&:to_proto)
|
89
|
+
m.upsert = shared_upserts.map(&:to_proto)
|
90
|
+
m.delete = shared_deletes.map(&:to_proto)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# @private Entities that need key ids assigned.
|
95
|
+
def auto_id_entities
|
96
|
+
shared_auto_ids
|
97
|
+
end
|
98
|
+
|
99
|
+
# @private All entities saved in the commit.
|
100
|
+
def entities
|
101
|
+
shared_entities
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
##
|
107
|
+
# @private List of Entity objects to be saved.
|
108
|
+
def shared_entities
|
109
|
+
@shared_entities
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# @private List of Entity objects that need auto_ids
|
114
|
+
def shared_auto_ids
|
115
|
+
@shared_auto_ids
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# @private List of Entity objects to be saved.
|
120
|
+
def shared_upserts
|
121
|
+
shared_entities - shared_auto_ids
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# @private List of Key objects to be deleted.
|
126
|
+
def shared_deletes
|
127
|
+
@shared_deletes
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -63,19 +63,33 @@ module Gcloud
|
|
63
63
|
|
64
64
|
##
|
65
65
|
# Look up entities by keys.
|
66
|
-
def lookup *keys
|
67
|
-
lookup = Proto::LookupRequest.new
|
68
|
-
|
66
|
+
def lookup *keys, consistency: nil, transaction: nil
|
67
|
+
lookup = Proto::LookupRequest.new key: keys
|
68
|
+
if consistency == :eventual
|
69
|
+
lookup.read_options = Proto::ReadOptions.new(read_consistency: 2)
|
70
|
+
elsif consistency == :strong
|
71
|
+
lookup.read_options = Proto::ReadOptions.new(read_consistency: 1)
|
72
|
+
elsif transaction
|
73
|
+
lookup.read_options = Proto::ReadOptions.new(
|
74
|
+
transaction: transaction)
|
75
|
+
end
|
69
76
|
|
70
77
|
Proto::LookupResponse.decode rpc("lookup", lookup)
|
71
78
|
end
|
72
79
|
|
73
80
|
# Query for entities.
|
74
|
-
def run_query query, partition = nil
|
81
|
+
def run_query query, partition = nil, consistency: nil, transaction: nil
|
75
82
|
run_query = Proto::RunQueryRequest.new.tap do |rq|
|
76
83
|
rq.query = query
|
77
84
|
rq.partition_id = partition if partition
|
78
|
-
|
85
|
+
end
|
86
|
+
if consistency == :eventual
|
87
|
+
run_query.read_options = Proto::ReadOptions.new(read_consistency: 2)
|
88
|
+
elsif consistency == :strong
|
89
|
+
run_query.read_options = Proto::ReadOptions.new(read_consistency: 1)
|
90
|
+
elsif transaction
|
91
|
+
run_query.read_options = Proto::ReadOptions.new(
|
92
|
+
transaction: transaction)
|
79
93
|
end
|
80
94
|
|
81
95
|
Proto::RunQueryResponse.decode rpc("runQuery", run_query)
|
@@ -16,6 +16,7 @@
|
|
16
16
|
require "gcloud/gce"
|
17
17
|
require "gcloud/datastore/connection"
|
18
18
|
require "gcloud/datastore/credentials"
|
19
|
+
require "gcloud/datastore/commit"
|
19
20
|
require "gcloud/datastore/entity"
|
20
21
|
require "gcloud/datastore/key"
|
21
22
|
require "gcloud/datastore/query"
|
@@ -114,8 +115,7 @@ module Gcloud
|
|
114
115
|
##
|
115
116
|
# Persist one or more entities to the Datastore.
|
116
117
|
#
|
117
|
-
# @param [Entity] entities One or more entity objects to be saved
|
118
|
-
# `id` or `name` set.
|
118
|
+
# @param [Entity] entities One or more entity objects to be saved.
|
119
119
|
#
|
120
120
|
# @return [Array<Gcloud::Datastore::Entity>]
|
121
121
|
#
|
@@ -123,11 +123,51 @@ module Gcloud
|
|
123
123
|
# dataset.save task1, task2
|
124
124
|
#
|
125
125
|
def save *entities
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
126
|
+
commit { |c| c.save(*entities) }
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Remove entities from the Datastore.
|
131
|
+
#
|
132
|
+
# @param [Entity, Key] entities_or_keys One or more Entity or Key objects
|
133
|
+
# to remove.
|
134
|
+
#
|
135
|
+
# @return [Boolean] Returns `true` if successful
|
136
|
+
#
|
137
|
+
# @example
|
138
|
+
# gcloud = Gcloud.new
|
139
|
+
# dataset = gcloud.datastore
|
140
|
+
# dataset.delete entity1, entity2
|
141
|
+
#
|
142
|
+
def delete *entities_or_keys
|
143
|
+
commit { |c| c.delete(*entities_or_keys) }
|
144
|
+
true
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Make multiple changes in a single commit.
|
149
|
+
#
|
150
|
+
# @yield [commit] a block for making changes
|
151
|
+
# @yieldparam [Commit] commit The object that changes are made on
|
152
|
+
#
|
153
|
+
# @return [Array<Gcloud::Datastore::Entity>] The entities that were
|
154
|
+
# persisted.
|
155
|
+
#
|
156
|
+
# @example
|
157
|
+
# dataset.commit do |c|
|
158
|
+
# c.save task1, task2
|
159
|
+
# c.delete entity1, entity2
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
def commit
|
163
|
+
return unless block_given?
|
164
|
+
c = Commit.new
|
165
|
+
yield c
|
166
|
+
response = connection.commit c.mutation
|
167
|
+
auto_id_assign_ids c.auto_id_entities,
|
168
|
+
response.mutation_result.insert_auto_id_key
|
130
169
|
# Make sure all entity keys are frozen so all show as persisted
|
170
|
+
entities = c.entities
|
131
171
|
entities.each { |e| e.key.freeze unless e.persisted? }
|
132
172
|
entities
|
133
173
|
end
|
@@ -138,6 +178,14 @@ module Gcloud
|
|
138
178
|
# @param [Key, String] key_or_kind A Key object or `kind` string value.
|
139
179
|
# @param [Integer, String, nil] id_or_name The Key's `id` or `name` value
|
140
180
|
# if a `kind` was provided in the first parameter.
|
181
|
+
# @param [Symbol] consistency The non-transactional read consistency to
|
182
|
+
# use. Cannot be set to `:strong` for global queries. Accepted values
|
183
|
+
# are `:eventual` and `:strong`.
|
184
|
+
#
|
185
|
+
# The default consistency depends on the type of lookup used. See
|
186
|
+
# [Eventual Consistency in Google Cloud
|
187
|
+
# Datastore](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/#h.tf76fya5nqk8)
|
188
|
+
# for more information.
|
141
189
|
#
|
142
190
|
# @return [Gcloud::Datastore::Entity, nil]
|
143
191
|
#
|
@@ -148,12 +196,12 @@ module Gcloud
|
|
148
196
|
# @example Finding an entity with a `kind` and `id`/`name`:
|
149
197
|
# task = dataset.find "Task", 123456
|
150
198
|
#
|
151
|
-
def find key_or_kind, id_or_name = nil
|
199
|
+
def find key_or_kind, id_or_name = nil, consistency: nil
|
152
200
|
key = key_or_kind
|
153
201
|
unless key.is_a? Gcloud::Datastore::Key
|
154
202
|
key = Key.new key_or_kind, id_or_name
|
155
203
|
end
|
156
|
-
find_all(key).first
|
204
|
+
find_all(key, consistency: consistency).first
|
157
205
|
end
|
158
206
|
alias_method :get, :find
|
159
207
|
|
@@ -161,6 +209,14 @@ module Gcloud
|
|
161
209
|
# Retrieve the entities for the provided keys.
|
162
210
|
#
|
163
211
|
# @param [Key] keys One or more Key objects to find records for.
|
212
|
+
# @param [Symbol] consistency The non-transactional read consistency to
|
213
|
+
# use. Cannot be set to `:strong` for global queries. Accepted values
|
214
|
+
# are `:eventual` and `:strong`.
|
215
|
+
#
|
216
|
+
# The default consistency depends on the type of lookup used. See
|
217
|
+
# [Eventual Consistency in Google Cloud
|
218
|
+
# Datastore](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/#h.tf76fya5nqk8)
|
219
|
+
# for more information.
|
164
220
|
#
|
165
221
|
# @return [Gcloud::Datastore::Dataset::LookupResults]
|
166
222
|
#
|
@@ -171,8 +227,10 @@ module Gcloud
|
|
171
227
|
# key2 = dataset.key "Task", 987654
|
172
228
|
# tasks = dataset.find_all key1, key2
|
173
229
|
#
|
174
|
-
def find_all *keys
|
175
|
-
|
230
|
+
def find_all *keys, consistency: nil
|
231
|
+
check_consistency! consistency
|
232
|
+
response = connection.lookup(*keys.map(&:to_proto),
|
233
|
+
consistency: consistency)
|
176
234
|
entities = to_gcloud_entities response.found
|
177
235
|
deferred = to_gcloud_keys response.deferred
|
178
236
|
missing = to_gcloud_entities response.missing
|
@@ -180,35 +238,19 @@ module Gcloud
|
|
180
238
|
end
|
181
239
|
alias_method :lookup, :find_all
|
182
240
|
|
183
|
-
##
|
184
|
-
# Remove entities from the Datastore.
|
185
|
-
#
|
186
|
-
# @param [Entity, Key] entities_or_keys One or more Entity or Key objects
|
187
|
-
# to remove.
|
188
|
-
#
|
189
|
-
# @return [Boolean] Returns `true` if successful
|
190
|
-
#
|
191
|
-
# @example
|
192
|
-
# gcloud = Gcloud.new
|
193
|
-
# dataset = gcloud.datastore
|
194
|
-
# dataset.delete entity1, entity2
|
195
|
-
#
|
196
|
-
def delete *entities_or_keys
|
197
|
-
keys = entities_or_keys.map do |e_or_k|
|
198
|
-
e_or_k.respond_to?(:key) ? e_or_k.key.to_proto : e_or_k.to_proto
|
199
|
-
end
|
200
|
-
mutation = Proto.new_mutation.tap do |m|
|
201
|
-
m.delete = keys
|
202
|
-
end
|
203
|
-
connection.commit mutation
|
204
|
-
true
|
205
|
-
end
|
206
|
-
|
207
241
|
##
|
208
242
|
# Retrieve entities specified by a Query.
|
209
243
|
#
|
210
244
|
# @param [Query] query The Query object with the search criteria.
|
211
245
|
# @param [String] namespace The namespace the query is to run within.
|
246
|
+
# @param [Symbol] consistency The non-transactional read consistency to
|
247
|
+
# use. Cannot be set to `:strong` for global queries. Accepted values
|
248
|
+
# are `:eventual` and `:strong`.
|
249
|
+
#
|
250
|
+
# The default consistency depends on the type of query used. See
|
251
|
+
# [Eventual Consistency in Google Cloud
|
252
|
+
# Datastore](https://cloud.google.com/datastore/docs/articles/balancing-strong-and-eventual-consistency-with-google-cloud-datastore/#h.tf76fya5nqk8)
|
253
|
+
# for more information.
|
212
254
|
#
|
213
255
|
# @return [Gcloud::Datastore::Dataset::QueryResults]
|
214
256
|
#
|
@@ -222,9 +264,11 @@ module Gcloud
|
|
222
264
|
# where("completed", "=", true)
|
223
265
|
# tasks = dataset.run query, namespace: "ns~todo-project"
|
224
266
|
#
|
225
|
-
def run query, namespace: nil
|
267
|
+
def run query, namespace: nil, consistency: nil
|
226
268
|
partition = optional_partition_id namespace
|
227
|
-
|
269
|
+
check_consistency! consistency
|
270
|
+
response = connection.run_query query.to_proto, partition,
|
271
|
+
consistency: consistency
|
228
272
|
entities = to_gcloud_entities response.batch.entity_result
|
229
273
|
cursor = Proto.encode_cursor response.batch.end_cursor
|
230
274
|
more_results = Proto.to_more_results_string response.batch.more_results
|
@@ -284,8 +328,14 @@ module Gcloud
|
|
284
328
|
yield tx
|
285
329
|
tx.commit
|
286
330
|
rescue => e
|
287
|
-
|
288
|
-
|
331
|
+
begin
|
332
|
+
tx.rollback
|
333
|
+
rescue => re
|
334
|
+
msg = "Transaction failed to commit and rollback."
|
335
|
+
raise TransactionError.new(msg, commit_error: e, rollback_error: re)
|
336
|
+
end
|
337
|
+
raise TransactionError.new("Transaction failed to commit.",
|
338
|
+
commit_error: e)
|
289
339
|
end
|
290
340
|
end
|
291
341
|
|
@@ -411,45 +461,28 @@ module Gcloud
|
|
411
461
|
end
|
412
462
|
end
|
413
463
|
|
414
|
-
##
|
415
|
-
# @private Save a key to be given an ID when comitted.
|
416
|
-
def auto_id_register entity
|
417
|
-
@_auto_id_entities ||= []
|
418
|
-
@_auto_id_entities << entity
|
419
|
-
end
|
420
|
-
|
421
464
|
##
|
422
465
|
# @private Update saved keys with new IDs post-commit.
|
423
|
-
def auto_id_assign_ids auto_ids
|
424
|
-
@_auto_id_entities ||= []
|
466
|
+
def auto_id_assign_ids entities, auto_ids
|
425
467
|
Array(auto_ids).each_with_index do |key, index|
|
426
|
-
entity =
|
468
|
+
entity = entities[index]
|
427
469
|
entity.key = Key.from_proto key
|
428
470
|
end
|
429
|
-
@_auto_id_entities = []
|
430
|
-
end
|
431
|
-
|
432
|
-
##
|
433
|
-
# @private Add entities to a Mutation, and register they key to be
|
434
|
-
# updated with an auto ID if needed.
|
435
|
-
def save_entities_to_mutation entities, mutation
|
436
|
-
entities.each do |entity|
|
437
|
-
if entity.key.id.nil? && entity.key.name.nil?
|
438
|
-
mutation.insert_auto_id << entity.to_proto
|
439
|
-
auto_id_register entity
|
440
|
-
else
|
441
|
-
mutation.upsert << entity.to_proto
|
442
|
-
end
|
443
|
-
end
|
444
471
|
end
|
445
472
|
|
446
473
|
def optional_partition_id namespace = nil
|
447
474
|
return nil if namespace.nil?
|
448
475
|
Proto::PartitionId.new.tap do |p|
|
449
476
|
p.namespace = namespace
|
450
|
-
p.dataset_id = project
|
451
477
|
end
|
452
478
|
end
|
479
|
+
|
480
|
+
def check_consistency! consistency
|
481
|
+
fail(ArgumentError,
|
482
|
+
format("Consistency must be :eventual or :strong, not %s.",
|
483
|
+
consistency.inspect)
|
484
|
+
) unless [:eventual, :strong, nil].include? consistency
|
485
|
+
end
|
453
486
|
end
|
454
487
|
end
|
455
488
|
end
|
@@ -46,6 +46,9 @@ module Gcloud
|
|
46
46
|
##
|
47
47
|
# Retrieve a property value by providing the name.
|
48
48
|
#
|
49
|
+
# Property values are converted from the Datastore value type
|
50
|
+
# automatically. Blob properties are returned as StringIO objects.
|
51
|
+
#
|
49
52
|
# @param [String, Symbol] prop_name The name of the property.
|
50
53
|
#
|
51
54
|
# @return [Object, nil] Returns `nil` if the property doesn't exist
|
@@ -66,6 +69,14 @@ module Gcloud
|
|
66
69
|
# user = dataset.find "User", "heidi@example.com"
|
67
70
|
# user[:name] #=> "Heidi Henderson"
|
68
71
|
#
|
72
|
+
# @example Getting a blob value returns a StringIO object:
|
73
|
+
# require "gcloud"
|
74
|
+
#
|
75
|
+
# gcloud = Gcloud.new
|
76
|
+
# dataset = gcloud.datastore
|
77
|
+
# user = dataset.find "User", "heidi@example.com"
|
78
|
+
# user["avatar"] #=> StringIO("\x89PNG\r\n\x1A...")
|
79
|
+
#
|
69
80
|
def [] prop_name
|
70
81
|
@properties[prop_name]
|
71
82
|
end
|
@@ -73,6 +84,12 @@ module Gcloud
|
|
73
84
|
##
|
74
85
|
# Set a property value by name.
|
75
86
|
#
|
87
|
+
# Property values are converted to use the proper Datastore value type
|
88
|
+
# automatically. Use an IO-compatible object (File, StringIO, Tempfile) to
|
89
|
+
# indicate the property value should be stored as a Datastore `blob`.
|
90
|
+
# IO-compatible objects are converted to StringIO objects when they are
|
91
|
+
# set.
|
92
|
+
#
|
76
93
|
# @param [String, Symbol] prop_name The name of the property.
|
77
94
|
# @param [Object] prop_value The value of the property.
|
78
95
|
#
|
@@ -92,6 +109,15 @@ module Gcloud
|
|
92
109
|
# user = dataset.find "User", "heidi@example.com"
|
93
110
|
# user[:name] = "Heidi H. Henderson"
|
94
111
|
#
|
112
|
+
# @example Setting a blob value using an IO:
|
113
|
+
# require "gcloud"
|
114
|
+
#
|
115
|
+
# gcloud = Gcloud.new
|
116
|
+
# dataset = gcloud.datastore
|
117
|
+
# user = dataset.find "User", "heidi@example.com"
|
118
|
+
# user["avatar"] = File.open "/avatars/heidi.png"
|
119
|
+
# user["avatar"] #=> StringIO("\x89PNG\r\n\x1A...")
|
120
|
+
#
|
95
121
|
def []= prop_name, prop_value
|
96
122
|
@properties[prop_name] = prop_value
|
97
123
|
end
|
@@ -66,12 +66,18 @@ module Gcloud
|
|
66
66
|
class TransactionError < Gcloud::Datastore::Error
|
67
67
|
##
|
68
68
|
# An error that occurred within the transaction. (optional)
|
69
|
-
attr_reader :
|
69
|
+
attr_reader :commit_error
|
70
|
+
alias_method :inner, :commit_error # backwards compatibility
|
71
|
+
|
72
|
+
##
|
73
|
+
# An error that occurred within the transaction. (optional)
|
74
|
+
attr_reader :rollback_error
|
70
75
|
|
71
76
|
# @private
|
72
|
-
def initialize message,
|
77
|
+
def initialize message, commit_error: nil, rollback_error: nil
|
73
78
|
super(message)
|
74
|
-
@
|
79
|
+
@commit_error = commit_error
|
80
|
+
@rollback_error = rollback_error
|
75
81
|
end
|
76
82
|
end
|
77
83
|
end
|
@@ -13,6 +13,8 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
|
16
|
+
require "stringio"
|
17
|
+
|
16
18
|
module Gcloud
|
17
19
|
module Datastore
|
18
20
|
##
|
@@ -86,8 +88,7 @@ module Gcloud
|
|
86
88
|
# Ensures the value is a type that can be persisted,
|
87
89
|
# otherwise a PropertyError is raised.
|
88
90
|
def ensure_value_type value
|
89
|
-
if
|
90
|
-
Gcloud::Datastore::Key === value ||
|
91
|
+
if Gcloud::Datastore::Key === value ||
|
91
92
|
Gcloud::Datastore::Entity === value ||
|
92
93
|
NilClass === value ||
|
93
94
|
TrueClass === value ||
|
@@ -97,6 +98,12 @@ module Gcloud
|
|
97
98
|
String === value ||
|
98
99
|
Array === value
|
99
100
|
return value
|
101
|
+
elsif value.respond_to?(:to_time)
|
102
|
+
return value
|
103
|
+
elsif value.respond_to?(:read) && value.respond_to?(:rewind)
|
104
|
+
# Always convert an IO object to a StringIO when storing.
|
105
|
+
value.rewind
|
106
|
+
return StringIO.new(value.read.force_encoding("ASCII-8BIT"))
|
100
107
|
elsif defined?(BigDecimal) && BigDecimal === value
|
101
108
|
return value
|
102
109
|
end
|
@@ -15,6 +15,7 @@
|
|
15
15
|
|
16
16
|
require "gcloud/proto/datastore_v1.pb"
|
17
17
|
require "gcloud/datastore/errors"
|
18
|
+
require "stringio"
|
18
19
|
|
19
20
|
module Gcloud
|
20
21
|
module Datastore
|
@@ -53,6 +54,8 @@ module Gcloud
|
|
53
54
|
return Array(proto_value.list_value).map do |item|
|
54
55
|
from_proto_value item
|
55
56
|
end
|
57
|
+
elsif !proto_value.blob_value.nil?
|
58
|
+
return StringIO.new(proto_value.blob_value.force_encoding("ASCII-8BIT"))
|
56
59
|
else
|
57
60
|
nil
|
58
61
|
end
|
@@ -60,9 +63,7 @@ module Gcloud
|
|
60
63
|
|
61
64
|
def self.to_proto_value value
|
62
65
|
v = Gcloud::Datastore::Proto::Value.new
|
63
|
-
if
|
64
|
-
v.timestamp_microseconds_value = self.microseconds_from_time value
|
65
|
-
elsif Gcloud::Datastore::Key === value
|
66
|
+
if Gcloud::Datastore::Key === value
|
66
67
|
v.key_value = value.to_proto
|
67
68
|
elsif Gcloud::Datastore::Entity === value
|
68
69
|
v.entity_value = value.to_proto
|
@@ -82,6 +83,11 @@ module Gcloud
|
|
82
83
|
v.string_value = value
|
83
84
|
elsif Array === value
|
84
85
|
v.list_value = value.map { |item| to_proto_value item }
|
86
|
+
elsif value.respond_to?(:to_time)
|
87
|
+
v.timestamp_microseconds_value = self.microseconds_from_time value.to_time
|
88
|
+
elsif value.respond_to?(:read) && value.respond_to?(:rewind)
|
89
|
+
value.rewind
|
90
|
+
v.blob_value = value.read.force_encoding("ASCII-8BIT")
|
85
91
|
else
|
86
92
|
fail PropertyError, "A property of type #{value.class} is not supported."
|
87
93
|
end
|
@@ -44,8 +44,7 @@ module Gcloud
|
|
44
44
|
# end
|
45
45
|
#
|
46
46
|
def save *entities
|
47
|
-
|
48
|
-
@_saved_entities += entities
|
47
|
+
@commit.save(*entities)
|
49
48
|
# Do not save or assign auto_ids yet
|
50
49
|
entities
|
51
50
|
end
|
@@ -61,16 +60,94 @@ module Gcloud
|
|
61
60
|
# end
|
62
61
|
#
|
63
62
|
def delete *entities_or_keys
|
64
|
-
|
65
|
-
e_or_k.respond_to?(:key) ? e_or_k.key.to_proto : e_or_k.to_proto
|
66
|
-
end
|
67
|
-
shared_mutation.tap do |m|
|
68
|
-
m.delete = keys
|
69
|
-
end
|
63
|
+
@commit.delete(*entities_or_keys)
|
70
64
|
# Do not delete yet
|
71
65
|
true
|
72
66
|
end
|
73
67
|
|
68
|
+
##
|
69
|
+
# Retrieve an entity by providing key information. The lookup is run
|
70
|
+
# within the transaction.
|
71
|
+
#
|
72
|
+
# @param [Key, String] key_or_kind A Key object or `kind` string value.
|
73
|
+
#
|
74
|
+
# @return [Gcloud::Datastore::Entity, nil]
|
75
|
+
#
|
76
|
+
# @example Finding an entity with a key:
|
77
|
+
# key = dataset.key "Task", 123456
|
78
|
+
# task = dataset.find key
|
79
|
+
#
|
80
|
+
# @example Finding an entity with a `kind` and `id`/`name`:
|
81
|
+
# task = dataset.find "Task", 123456
|
82
|
+
#
|
83
|
+
def find key_or_kind, id_or_name = nil
|
84
|
+
key = key_or_kind
|
85
|
+
unless key.is_a? Gcloud::Datastore::Key
|
86
|
+
key = Key.new key_or_kind, id_or_name
|
87
|
+
end
|
88
|
+
find_all(key).first
|
89
|
+
end
|
90
|
+
alias_method :get, :find
|
91
|
+
|
92
|
+
##
|
93
|
+
# Retrieve the entities for the provided keys. The lookup is run within
|
94
|
+
# the transaction.
|
95
|
+
#
|
96
|
+
# @param [Key] keys One or more Key objects to find records for.
|
97
|
+
#
|
98
|
+
# @return [Gcloud::Datastore::Dataset::LookupResults]
|
99
|
+
#
|
100
|
+
# @example
|
101
|
+
# gcloud = Gcloud.new
|
102
|
+
# dataset = gcloud.datastore
|
103
|
+
# key1 = dataset.key "Task", 123456
|
104
|
+
# key2 = dataset.key "Task", 987654
|
105
|
+
# tasks = dataset.find_all key1, key2
|
106
|
+
#
|
107
|
+
def find_all *keys
|
108
|
+
response = connection.lookup(*keys.map(&:to_proto),
|
109
|
+
transaction: @id)
|
110
|
+
entities = to_gcloud_entities response.found
|
111
|
+
deferred = to_gcloud_keys response.deferred
|
112
|
+
missing = to_gcloud_entities response.missing
|
113
|
+
LookupResults.new entities, deferred, missing
|
114
|
+
end
|
115
|
+
alias_method :lookup, :find_all
|
116
|
+
|
117
|
+
##
|
118
|
+
# Retrieve entities specified by a Query. The query is run within the
|
119
|
+
# transaction.
|
120
|
+
#
|
121
|
+
# @param [Query] query The Query object with the search criteria.
|
122
|
+
# @param [String] namespace The namespace the query is to run within.
|
123
|
+
#
|
124
|
+
# @return [Gcloud::Datastore::Dataset::QueryResults]
|
125
|
+
#
|
126
|
+
# @example
|
127
|
+
# query = dataset.query("Task").
|
128
|
+
# where("completed", "=", true)
|
129
|
+
# dataset.transaction do |tx|
|
130
|
+
# tasks = tx.run query
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# @example Run the query within a namespace with the `namespace` option:
|
134
|
+
# query = Gcloud::Datastore::Query.new.kind("Task").
|
135
|
+
# where("completed", "=", true)
|
136
|
+
# dataset.transaction do |tx|
|
137
|
+
# tasks = tx.run query, namespace: "ns~todo-project"
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
def run query, namespace: nil
|
141
|
+
partition = optional_partition_id namespace
|
142
|
+
response = connection.run_query query.to_proto, partition,
|
143
|
+
transaction: @id
|
144
|
+
entities = to_gcloud_entities response.batch.entity_result
|
145
|
+
cursor = Proto.encode_cursor response.batch.end_cursor
|
146
|
+
more_results = Proto.to_more_results_string response.batch.more_results
|
147
|
+
QueryResults.new entities, cursor, more_results
|
148
|
+
end
|
149
|
+
alias_method :run_query, :run
|
150
|
+
|
74
151
|
##
|
75
152
|
# Begins a transaction.
|
76
153
|
# This method is run when a new Transaction is created.
|
@@ -84,20 +161,84 @@ module Gcloud
|
|
84
161
|
|
85
162
|
##
|
86
163
|
# Commits a transaction.
|
164
|
+
#
|
165
|
+
# @yield [commit] an optional block for making changes
|
166
|
+
# @yieldparam [Commit] commit The object that changes are made on
|
167
|
+
#
|
168
|
+
# @example
|
169
|
+
# require "gcloud"
|
170
|
+
#
|
171
|
+
# gcloud = Gcloud.new
|
172
|
+
# dataset = gcloud.datastore
|
173
|
+
#
|
174
|
+
# user = dataset.entity "User", "heidi" do |u|
|
175
|
+
# u["name"] = "Heidi Henderson"
|
176
|
+
# u["email"] = "heidi@example.net"
|
177
|
+
# end
|
178
|
+
#
|
179
|
+
# tx = dataset.transaction
|
180
|
+
# begin
|
181
|
+
# if tx.find(user.key).nil?
|
182
|
+
# tx.save user
|
183
|
+
# end
|
184
|
+
# tx.commit
|
185
|
+
# rescue
|
186
|
+
# tx.rollback
|
187
|
+
# end
|
188
|
+
#
|
189
|
+
# @example Commit can be passed a block, same as {Dataset#commit}:
|
190
|
+
# require "gcloud"
|
191
|
+
#
|
192
|
+
# gcloud = Gcloud.new
|
193
|
+
# dataset = gcloud.datastore
|
194
|
+
#
|
195
|
+
# tx = dataset.transaction
|
196
|
+
# begin
|
197
|
+
# tx.commit do |c|
|
198
|
+
# c.save task1, task2
|
199
|
+
# c.delete entity1, entity2
|
200
|
+
# end
|
201
|
+
# rescue
|
202
|
+
# tx.rollback
|
203
|
+
# end
|
204
|
+
#
|
87
205
|
def commit
|
88
206
|
if @id.nil?
|
89
207
|
fail TransactionError, "Cannot commit when not in a transaction."
|
90
208
|
end
|
91
209
|
|
92
|
-
|
93
|
-
|
210
|
+
yield @commit if block_given?
|
211
|
+
response = connection.commit @commit.mutation, @id
|
212
|
+
auto_id_assign_ids @commit.auto_id_entities,
|
213
|
+
response.mutation_result.insert_auto_id_key
|
94
214
|
# Make sure all entity keys are frozen so all show as persisted
|
95
|
-
@
|
215
|
+
@commit.entities.each { |e| e.key.freeze unless e.persisted? }
|
96
216
|
true
|
97
217
|
end
|
98
218
|
|
99
219
|
##
|
100
220
|
# Rolls a transaction back.
|
221
|
+
#
|
222
|
+
# @example
|
223
|
+
# require "gcloud"
|
224
|
+
#
|
225
|
+
# gcloud = Gcloud.new
|
226
|
+
# dataset = gcloud.datastore
|
227
|
+
#
|
228
|
+
# user = dataset.entity "User", "heidi" do |u|
|
229
|
+
# u["name"] = "Heidi Henderson"
|
230
|
+
# u["email"] = "heidi@example.net"
|
231
|
+
# end
|
232
|
+
#
|
233
|
+
# tx = dataset.transaction
|
234
|
+
# begin
|
235
|
+
# if tx.find(user.key).nil?
|
236
|
+
# tx.save user
|
237
|
+
# end
|
238
|
+
# tx.commit
|
239
|
+
# rescue
|
240
|
+
# tx.rollback
|
241
|
+
# end
|
101
242
|
def rollback
|
102
243
|
if @id.nil?
|
103
244
|
fail TransactionError, "Cannot rollback when not in a transaction."
|
@@ -111,19 +252,8 @@ module Gcloud
|
|
111
252
|
# Reset the transaction.
|
112
253
|
# {Transaction#start} must be called afterwards.
|
113
254
|
def reset!
|
114
|
-
@shared_mutation = nil
|
115
255
|
@id = nil
|
116
|
-
@
|
117
|
-
@_saved_entities = []
|
118
|
-
end
|
119
|
-
|
120
|
-
protected
|
121
|
-
|
122
|
-
##
|
123
|
-
# @private Mutation to be shared across save, delete, and commit calls.
|
124
|
-
# This enables updates to happen when commit is called.
|
125
|
-
def shared_mutation
|
126
|
-
@shared_mutation ||= Proto.new_mutation
|
256
|
+
@commit = Commit.new
|
127
257
|
end
|
128
258
|
end
|
129
259
|
end
|
data/lib/gcloud/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gcloud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Silvano Luciani
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-04
|
13
|
+
date: 2016-05-04 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: grpc
|
@@ -290,6 +290,7 @@ files:
|
|
290
290
|
- lib/gcloud/bigquery/view.rb
|
291
291
|
- lib/gcloud/credentials.rb
|
292
292
|
- lib/gcloud/datastore.rb
|
293
|
+
- lib/gcloud/datastore/commit.rb
|
293
294
|
- lib/gcloud/datastore/connection.rb
|
294
295
|
- lib/gcloud/datastore/credentials.rb
|
295
296
|
- lib/gcloud/datastore/dataset.rb
|