gcloud 0.8.0 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|