google-cloud-datastore 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,43 @@
1
+ # Copyright 2014 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
+ require "google/cloud/errors"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Datastore
21
+ ##
22
+ # # KeyError
23
+ #
24
+ # Raised when a key is not correct.
25
+ class KeyError < Google::Cloud::Error
26
+ end
27
+
28
+ ##
29
+ # # PropertyError
30
+ #
31
+ # Raised when a property is not correct.
32
+ class PropertyError < Google::Cloud::Error
33
+ end
34
+
35
+ ##
36
+ # # TransactionError
37
+ #
38
+ # General error for Transaction problems.
39
+ class TransactionError < Google::Cloud::Error
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,216 @@
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
+ require "google/cloud/datastore/entity"
17
+ require "google/cloud/datastore/key"
18
+
19
+ module Google
20
+ module Cloud
21
+ module Datastore
22
+ ##
23
+ # # GqlQuery
24
+ #
25
+ # Represents a GQL query.
26
+ #
27
+ # GQL is a SQL-like language for retrieving entities or keys from
28
+ # Datastore.
29
+ #
30
+ # @see https://cloud.google.com/datastore/docs/apis/gql/gql_reference GQL
31
+ # Reference
32
+ #
33
+ # @example
34
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
35
+ # gql_query.query_string = "SELECT * FROM Task ORDER BY created ASC"
36
+ # tasks = datastore.run gql_query
37
+ #
38
+ class GqlQuery
39
+ ##
40
+ # Returns a new GqlQuery instance.
41
+ #
42
+ # @example
43
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
44
+ #
45
+ def initialize
46
+ @grpc = Google::Datastore::V1::GqlQuery.new
47
+ end
48
+
49
+ ##
50
+ # The GQL query string for the query. The string may contain named or
51
+ # positional argument binding sites that start with `@`. Corresponding
52
+ # binding values should be set with {#named_bindings=} or
53
+ # {#positional_bindings=}.
54
+ #
55
+ # @return [String] a GQL statement
56
+ #
57
+ def query_string
58
+ gql = @grpc.query_string.dup
59
+ gql.freeze
60
+ gql
61
+ end
62
+
63
+ ##
64
+ # Sets the GQL query string for the query. The string may contain named
65
+ # or positional argument binding sites that start with `@`.
66
+ # Corresponding binding values should be set with {#named_bindings=} or
67
+ # {#positional_bindings=}.
68
+ #
69
+ # See the [GQL
70
+ # Reference](https://cloud.google.com/datastore/docs/apis/gql/gql_reference).
71
+ #
72
+ # @param [String] new_query_string a valid GQL statement
73
+ #
74
+ # @example
75
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
76
+ # gql_query.query_string = "SELECT * FROM Task " \
77
+ # "WHERE done = @done " \
78
+ # "AND priority = @priority"
79
+ # gql_query.named_bindings = {done: false, priority: 4}
80
+ #
81
+ def query_string= new_query_string
82
+ @grpc.query_string = new_query_string.to_s
83
+ end
84
+
85
+ ##
86
+ # Whether the query may contain literal values. When false, the query
87
+ # string must not contain any literals and instead must bind all values
88
+ # using {#named_bindings=} or {#positional_bindings=}.
89
+ #
90
+ # @return [Boolean] `true` if the query may contain literal values
91
+ #
92
+ def allow_literals
93
+ @grpc.allow_literals
94
+ end
95
+
96
+ ##
97
+ # Sets whether the query may contain literal values. When false, the
98
+ # query string must not contain any literals and instead must bind all
99
+ # values using {#named_bindings=} or {#positional_bindings=}.
100
+ #
101
+ # @param [Boolean] new_allow_literals `true` if the query may contain
102
+ # literal values
103
+ #
104
+ # @example
105
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
106
+ # gql_query.query_string = "SELECT * FROM Task " \
107
+ # "WHERE completed = false AND priority = 4"
108
+ # gql_query.allow_literals = true
109
+ #
110
+ def allow_literals= new_allow_literals
111
+ @grpc.allow_literals = new_allow_literals
112
+ end
113
+
114
+ ##
115
+ # The named binding values for a query that contains named argument
116
+ # binding sites that start with `@`.
117
+ #
118
+ # @return [Hash] a frozen hash that maps the binding site names in the
119
+ # query string to valid GQL arguments
120
+ #
121
+ def named_bindings
122
+ bindings = Hash[@grpc.named_bindings.map do |name, gql_query_param|
123
+ if gql_query_param.parameter_type == :cursor
124
+ [name, Cursor.from_grpc(gql_query_param.cursor)]
125
+ else
126
+ [name, Core::GRPCUtils.from_value(gql_query_param.value)]
127
+ end
128
+ end]
129
+ bindings.freeze
130
+ bindings
131
+ end
132
+
133
+ ##
134
+ # Sets named binding values for a query that contains named argument
135
+ # binding sites that start with `@`.
136
+ #
137
+ # @param [Hash] new_named_bindings a hash that maps the binding site
138
+ # names in the query string to valid GQL arguments
139
+ #
140
+ # @example
141
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
142
+ # gql_query.query_string = "SELECT * FROM Task " \
143
+ # "WHERE done = @done " \
144
+ # "AND priority = @priority"
145
+ # gql_query.named_bindings = {done: false, priority: 4}
146
+ #
147
+ def named_bindings= new_named_bindings
148
+ @grpc.named_bindings.clear
149
+ new_named_bindings.map do |name, value|
150
+ if value.is_a? Google::Cloud::Datastore::Cursor
151
+ @grpc.named_bindings[name.to_s] = \
152
+ Google::Datastore::V1::GqlQueryParameter.new(
153
+ cursor: value.to_grpc)
154
+ else
155
+ @grpc.named_bindings[name.to_s] = \
156
+ Google::Datastore::V1::GqlQueryParameter.new(
157
+ value: Core::GRPCUtils.to_value(value))
158
+ end
159
+ end
160
+ end
161
+
162
+ ##
163
+ # The binding values for a query that contains numbered argument binding
164
+ # sites that start with `@`.
165
+ #
166
+ # @return [Array] a frozen array containing the query arguments in the
167
+ # order of the numbered binding sites in the query string
168
+ #
169
+ def positional_bindings
170
+ bindings = @grpc.positional_bindings.map do |gql_query_param|
171
+ if gql_query_param.parameter_type == :cursor
172
+ Cursor.from_grpc gql_query_param.cursor
173
+ else
174
+ Core::GRPCUtils.from_value gql_query_param.value
175
+ end
176
+ end
177
+ bindings.freeze
178
+ bindings
179
+ end
180
+
181
+ ##
182
+ # Sets the binding values for a query that contains numbered argument
183
+ # binding sites that start with `@`.
184
+ #
185
+ # @param [Array] new_positional_bindings query arguments in the order
186
+ # of the numbered binding sites in the query string
187
+ #
188
+ # @example
189
+ # gql_query = Google::Cloud::Datastore::GqlQuery.new
190
+ # gql_query.query_string = "SELECT * FROM Task" \
191
+ # "WHERE completed = @1 AND priority = @2"
192
+ # gql_query.positional_bindings = [false, 4]
193
+ #
194
+ def positional_bindings= new_positional_bindings
195
+ @grpc.positional_bindings.clear
196
+ new_positional_bindings.map do |value|
197
+ if value.is_a? Google::Cloud::Datastore::Cursor
198
+ @grpc.positional_bindings << \
199
+ Google::Datastore::V1::GqlQueryParameter.new(
200
+ cursor: value.to_grpc)
201
+ else
202
+ @grpc.positional_bindings << \
203
+ Google::Datastore::V1::GqlQueryParameter.new(
204
+ value: Core::GRPCUtils.to_value(value))
205
+ end
206
+ end
207
+ end
208
+
209
+ # @private
210
+ def to_grpc
211
+ @grpc
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,140 @@
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
+ require "google/cloud/core/grpc_utils"
17
+ require "google/cloud/datastore/errors"
18
+ require "stringio"
19
+ require "base64"
20
+
21
+ module Google
22
+ module Cloud
23
+ module Core
24
+ ##
25
+ # @private Conversion to/from Datastore GRPC objects.
26
+ # This file adds Datastore methods to Core::GRPCUtils.
27
+ module GRPCUtils
28
+ # rubocop:disable all
29
+
30
+ PROP_FILTER_OPS = { "<" => :LESS_THAN,
31
+ "lt" => :LESS_THAN,
32
+ "<=" => :LESS_THAN_OR_EQUAL,
33
+ "lte" => :LESS_THAN_OR_EQUAL,
34
+ ">" => :GREATER_THAN,
35
+ "gt" => :GREATER_THAN,
36
+ ">=" => :GREATER_THAN_OR_EQUAL,
37
+ "gte" => :GREATER_THAN_OR_EQUAL,
38
+ "=" => :EQUAL,
39
+ "eq" => :EQUAL,
40
+ "eql" => :EQUAL,
41
+ "~" => :HAS_ANCESTOR,
42
+ "~>" => :HAS_ANCESTOR,
43
+ "ancestor" => :HAS_ANCESTOR,
44
+ "has_ancestor" => :HAS_ANCESTOR,
45
+ "has ancestor" => :HAS_ANCESTOR }
46
+
47
+ ##
48
+ # Get a property filter operator from op
49
+ def self.to_prop_filter_op op
50
+ PROP_FILTER_OPS[op.to_s.downcase] || :EQUAL
51
+ end
52
+
53
+ ##
54
+ # Gets an object from a Google::Datastore::V1::Value.
55
+ def self.from_value grpc_value
56
+ if grpc_value.value_type == :null_value
57
+ return nil
58
+ elsif grpc_value.value_type == :key_value
59
+ return Google::Cloud::Datastore::Key.from_grpc(grpc_value.key_value)
60
+ elsif grpc_value.value_type == :entity_value
61
+ return Google::Cloud::Datastore::Entity.from_grpc(
62
+ grpc_value.entity_value)
63
+ elsif grpc_value.value_type == :boolean_value
64
+ return grpc_value.boolean_value
65
+ elsif grpc_value.value_type == :double_value
66
+ return grpc_value.double_value
67
+ elsif grpc_value.value_type == :integer_value
68
+ return grpc_value.integer_value
69
+ elsif grpc_value.value_type == :string_value
70
+ return grpc_value.string_value
71
+ elsif grpc_value.value_type == :array_value
72
+ return Array(grpc_value.array_value.values).map { |v| from_value v }
73
+ elsif grpc_value.value_type == :timestamp_value
74
+ return Time.at grpc_value.timestamp_value.seconds,
75
+ grpc_value.timestamp_value.nanos/1000.0
76
+ elsif grpc_value.value_type == :geo_point_value
77
+ return grpc_value.geo_point_value.to_hash
78
+ elsif grpc_value.value_type == :blob_value
79
+ return StringIO.new(
80
+ grpc_value.blob_value.dup.force_encoding("ASCII-8BIT"))
81
+ else
82
+ nil
83
+ end
84
+ end
85
+
86
+ ##
87
+ # Stores an object into a Google::Datastore::V1::Value.
88
+ def self.to_value value
89
+ v = Google::Datastore::V1::Value.new
90
+ if NilClass === value
91
+ v.null_value = :NULL_VALUE
92
+ elsif TrueClass === value
93
+ v.boolean_value = true
94
+ elsif FalseClass === value
95
+ v.boolean_value = false
96
+ elsif Integer === value
97
+ v.integer_value = value
98
+ elsif Float === value
99
+ v.double_value = value
100
+ elsif defined?(BigDecimal) && BigDecimal === value
101
+ v.double_value = value
102
+ elsif Google::Cloud::Datastore::Key === value
103
+ v.key_value = value.to_grpc
104
+ elsif Google::Cloud::Datastore::Entity === value
105
+ value.key = nil # Embedded entities can't have keys
106
+ v.entity_value = value.to_grpc
107
+ elsif String === value
108
+ v.string_value = value
109
+ elsif Array === value
110
+ v.array_value = Google::Datastore::V1::ArrayValue.new(
111
+ values: value.map { |val| to_value val }
112
+ )
113
+ elsif value.respond_to? :to_time
114
+ v.timestamp_value = Google::Protobuf::Timestamp.new(
115
+ seconds: value.to_time.to_i, nanos: value.to_time.nsec)
116
+ elsif value.respond_to?(:to_hash) &&
117
+ value.keys.sort == [:latitude, :longitude]
118
+ v.geo_point_value = Google::Type::LatLng.new(value)
119
+ elsif value.respond_to?(:read) && value.respond_to?(:rewind)
120
+ value.rewind
121
+ v.blob_value = value.read.force_encoding("ASCII-8BIT")
122
+ else
123
+ fail Google::Cloud::Datastore::PropertyError,
124
+ "A property of type #{value.class} is not supported."
125
+ end
126
+ v
127
+ end
128
+
129
+ def self.encode_bytes bytes
130
+ Base64.strict_encode64(bytes.to_s).encode("ASCII-8BIT")
131
+ end
132
+
133
+ def self.decode_bytes bytes
134
+ Base64.decode64(bytes.to_s).force_encoding Encoding::ASCII_8BIT
135
+ end
136
+ # rubocop:enable all
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,289 @@
1
+ # Copyright 2014 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 Google
17
+ module Cloud
18
+ module Datastore
19
+ ##
20
+ # # Key
21
+ #
22
+ # Every Datastore record has an identifying key, which includes the
23
+ # record's entity kind and a unique identifier. The identifier may be
24
+ # either a key name string, assigned explicitly by the application, or an
25
+ # integer numeric ID, assigned automatically by Datastore.
26
+ #
27
+ # @example
28
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
29
+ #
30
+ class Key
31
+ ##
32
+ # The kind of the Key.
33
+ #
34
+ # @return [String]
35
+ #
36
+ # @example
37
+ # key = Google::Cloud::Datastore::Key.new "TaskList"
38
+ # key.kind #=> "TaskList"
39
+ # key.kind = "Task"
40
+ #
41
+ attr_accessor :kind
42
+
43
+ ##
44
+ # The project of the Key.
45
+ #
46
+ # @return [String]
47
+ #
48
+ # @example
49
+ # require "google/cloud"
50
+ #
51
+ # gcloud = Google::Cloud.new "my-todo-project",
52
+ # "/path/to/keyfile.json"
53
+ #
54
+ # datastore = gcloud.datastore
55
+ # task = datastore.find "Task", "sampleTask"
56
+ # task.key.project #=> "my-todo-project"
57
+ #
58
+ attr_accessor :project
59
+ alias_method :dataset_id, :project
60
+ alias_method :dataset_id=, :project=
61
+
62
+ ##
63
+ # The namespace of the Key.
64
+ #
65
+ # @return [String, nil]
66
+ #
67
+ # @example
68
+ # require "google/cloud"
69
+ #
70
+ # gcloud = Google::Cloud.new "my-todo-project",
71
+ # "/path/to/keyfile.json"
72
+ #
73
+ # datastore = gcloud.datastore
74
+ # task = datastore.find "Task", "sampleTask"
75
+ # task.key.namespace #=> "ns~todo-project"
76
+ #
77
+ attr_accessor :namespace
78
+
79
+ ##
80
+ # Create a new Key instance.
81
+ #
82
+ # @param [String] kind The kind of the Key. This is optional.
83
+ # @param [Integer, String] id_or_name The id or name of the Key. This is
84
+ # optional.
85
+ #
86
+ # @return [Google::Cloud::Datastore::Dataset::Key]
87
+ #
88
+ # @example
89
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
90
+ #
91
+ def initialize kind = nil, id_or_name = nil
92
+ @kind = kind
93
+ if id_or_name.is_a? Integer
94
+ @id = id_or_name
95
+ else
96
+ @name = id_or_name
97
+ end
98
+ end
99
+
100
+ ##
101
+ # @private Set the id of the Key.
102
+ # If a name is already present it will be removed.
103
+ #
104
+ # @return [Integer, nil]
105
+ #
106
+ # @example
107
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
108
+ # task_key.id #=> nil
109
+ # task_key.name #=> "sampleTask"
110
+ # task_key.id = 654321
111
+ # task_key.id #=> 654321
112
+ # task_key.name #=> nil
113
+ #
114
+ def id= new_id
115
+ @name = nil if new_id
116
+ @id = new_id
117
+ end
118
+
119
+ ##
120
+ # The id of the Key.
121
+ #
122
+ # @return [Integer, nil]
123
+ #
124
+ # @example
125
+ # task_key = Google::Cloud::Datastore::Key.new "Task", 123456
126
+ # task_key.id #=> 123456
127
+ #
128
+ attr_reader :id
129
+
130
+ ##
131
+ # @private Set the name of the Key.
132
+ # If an id is already present it will be removed.
133
+ #
134
+ # @return [String, nil]
135
+ #
136
+ # @example
137
+ # task_key = Google::Cloud::Datastore::Key.new "Task", 123456
138
+ # task_key.id #=> 123456
139
+ # task_key.name #=> nil
140
+ # task_key.name = "sampleTask"
141
+ # task_key.id #=> nil
142
+ # task_key.name #=> "sampleTask"
143
+ #
144
+ def name= new_name
145
+ @id = nil if new_name
146
+ @name = new_name
147
+ end
148
+
149
+ ##
150
+ # The name of the Key.
151
+ #
152
+ # @return [String, nil]
153
+ #
154
+ # @example
155
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
156
+ # task_key.name #=> "sampleTask"
157
+ #
158
+ attr_reader :name
159
+
160
+ ##
161
+ # Set the parent of the Key.
162
+ #
163
+ # @return [Key, nil]
164
+ #
165
+ # @example
166
+ # parent_key = Google::Cloud::Datastore::Key.new "TaskList", "default"
167
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
168
+ # task_key.parent = parent_key
169
+ #
170
+ # @example With multiple levels:
171
+ # user_key = Google::Cloud::Datastore::Key.new "User", "alice"
172
+ # list_key = Google::Cloud::Datastore::Key.new "TaskList", "default"
173
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
174
+ # list_key.parent = user_key
175
+ # task_key.parent = list_key
176
+ #
177
+ def parent= new_parent
178
+ # store key if given an entity
179
+ new_parent = new_parent.key if new_parent.respond_to? :key
180
+ @parent = new_parent
181
+ end
182
+
183
+ ##
184
+ # The parent of the Key.
185
+ #
186
+ # @return [Key, nil]
187
+ #
188
+ # @example
189
+ # require "google/cloud"
190
+ #
191
+ # gcloud = Google::Cloud.new
192
+ # datastore = gcloud.datastore
193
+ #
194
+ # task_list = datastore.find "TaskList", "default"
195
+ # query = datastore.query("Task").
196
+ # ancestor(task_list)
197
+ # lists = datastore.run query
198
+ # lists.first.key.parent #=> Key("TaskList", "default")
199
+ #
200
+ attr_reader :parent
201
+
202
+ ##
203
+ # Represent the Key's path (including parent) as an array of arrays.
204
+ # Each inner array contains two values, the kind and the id or name.
205
+ # If neither an id or name exist then nil will be returned.
206
+ #
207
+ # @return [Array<Array<(String, String)>>]
208
+ #
209
+ # @example
210
+ # parent_key = Google::Cloud::Datastore::Key.new "TaskList", "default"
211
+ # task_key = Google::Cloud::Datastore::Key.new "Task", "sampleTask"
212
+ # task_key.parent = parent_key
213
+ # task_key.path #=> [["TaskList", "default"], ["Task", "sampleTask"]]
214
+ #
215
+ def path
216
+ new_path = parent ? parent.path : []
217
+ new_path << [kind, (id || name)]
218
+ end
219
+
220
+ ##
221
+ # Determine if the key is complete.
222
+ # A complete key has either an id or a name.
223
+ #
224
+ # Inverse of {#incomplete?}
225
+ def complete?
226
+ !incomplete?
227
+ end
228
+
229
+ ##
230
+ # Determine if the key is incomplete.
231
+ # An incomplete key has neither an id nor a name.
232
+ #
233
+ # Inverse of {#complete?}
234
+ def incomplete?
235
+ kind.nil? || (id.nil? && (name.nil? || name.empty?))
236
+ end
237
+
238
+ ##
239
+ # The number of bytes the Key will take to serialize during API calls.
240
+ def serialized_size
241
+ to_grpc.to_proto.length
242
+ end
243
+
244
+ ##
245
+ # @private Convert the Key to a Google::Datastore::V1::Key object.
246
+ def to_grpc
247
+ grpc_path = path.map do |pe_kind, pe_id_or_name|
248
+ path_args = { kind: pe_kind }
249
+ if pe_id_or_name.is_a? Integer
250
+ path_args[:id] = pe_id_or_name
251
+ elsif pe_id_or_name.is_a? String
252
+ path_args[:name] = pe_id_or_name unless pe_id_or_name.empty?
253
+ end
254
+ Google::Datastore::V1::Key::PathElement.new(path_args)
255
+ end
256
+ grpc = Google::Datastore::V1::Key.new(path: grpc_path)
257
+ if project || namespace
258
+ grpc.partition_id = Google::Datastore::V1::PartitionId.new(
259
+ project_id: project.to_s, namespace_id: namespace.to_s)
260
+ end
261
+ grpc
262
+ end
263
+
264
+ ##
265
+ # @private Create a new Key from a Google::Datastore::V1::Key
266
+ # object.
267
+ def self.from_grpc grpc
268
+ return nil if grpc.nil?
269
+ key_grpc = grpc.dup
270
+ key = Key.new
271
+ path_grpc = key_grpc.path.pop
272
+ if path_grpc
273
+ id_or_name =
274
+ (path_grpc.id_type == :id ? path_grpc.id : path_grpc.name)
275
+ key = Key.new path_grpc.kind, id_or_name
276
+ end
277
+ if key_grpc.partition_id
278
+ key.project = key_grpc.partition_id.project_id
279
+ key.namespace = key_grpc.partition_id.namespace_id
280
+ end
281
+ key.parent = Key.from_grpc(key_grpc) if key_grpc.path.count > 0
282
+ # Freeze the key to make it immutable.
283
+ key.freeze
284
+ key
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end