gcloud 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +26 -0
  3. data/OVERVIEW.md +10 -8
  4. data/lib/gcloud.rb +12 -13
  5. data/lib/gcloud/bigquery/dataset/list.rb +2 -4
  6. data/lib/gcloud/bigquery/job/list.rb +3 -5
  7. data/lib/gcloud/bigquery/table/list.rb +3 -5
  8. data/lib/gcloud/datastore.rb +326 -97
  9. data/lib/gcloud/datastore/commit.rb +73 -56
  10. data/lib/gcloud/datastore/credentials.rb +1 -12
  11. data/lib/gcloud/datastore/cursor.rb +76 -0
  12. data/lib/gcloud/datastore/dataset.rb +337 -134
  13. data/lib/gcloud/datastore/dataset/lookup_results.rb +12 -12
  14. data/lib/gcloud/datastore/dataset/query_results.rb +117 -27
  15. data/lib/gcloud/datastore/entity.rb +159 -93
  16. data/lib/gcloud/datastore/errors.rb +0 -21
  17. data/lib/gcloud/datastore/gql_query.rb +211 -0
  18. data/lib/gcloud/datastore/grpc_utils.rb +131 -0
  19. data/lib/gcloud/datastore/key.rb +74 -65
  20. data/lib/gcloud/datastore/properties.rb +14 -1
  21. data/lib/gcloud/datastore/query.rb +188 -52
  22. data/lib/gcloud/datastore/service.rb +161 -0
  23. data/lib/gcloud/datastore/transaction.rb +175 -60
  24. data/lib/gcloud/dns/change/list.rb +2 -4
  25. data/lib/gcloud/dns/record/list.rb +2 -4
  26. data/lib/gcloud/dns/zone/list.rb +2 -4
  27. data/lib/gcloud/grpc_utils.rb +11 -0
  28. data/lib/gcloud/logging/entry.rb +6 -17
  29. data/lib/gcloud/logging/entry/list.rb +8 -9
  30. data/lib/gcloud/logging/metric/list.rb +4 -5
  31. data/lib/gcloud/logging/resource.rb +1 -12
  32. data/lib/gcloud/logging/resource_descriptor.rb +9 -12
  33. data/lib/gcloud/logging/resource_descriptor/list.rb +4 -5
  34. data/lib/gcloud/logging/sink/list.rb +4 -5
  35. data/lib/gcloud/pubsub/message.rb +1 -3
  36. data/lib/gcloud/pubsub/subscription.rb +5 -7
  37. data/lib/gcloud/pubsub/topic.rb +1 -3
  38. data/lib/gcloud/resource_manager/project/list.rb +2 -4
  39. data/lib/gcloud/translate/api.rb +5 -3
  40. data/lib/gcloud/translate/connection.rb +4 -4
  41. data/lib/gcloud/version.rb +1 -1
  42. metadata +9 -20
  43. data/lib/gcloud/datastore/connection.rb +0 -203
  44. data/lib/gcloud/datastore/proto.rb +0 -266
  45. data/lib/gcloud/proto/datastore_v1.pb.rb +0 -377
  46. data/lib/google/protobuf/any.rb +0 -17
  47. data/lib/google/protobuf/api.rb +0 -31
  48. data/lib/google/protobuf/duration.rb +0 -17
  49. data/lib/google/protobuf/empty.rb +0 -15
  50. data/lib/google/protobuf/field_mask.rb +0 -16
  51. data/lib/google/protobuf/source_context.rb +0 -16
  52. data/lib/google/protobuf/struct.rb +0 -35
  53. data/lib/google/protobuf/timestamp.rb +0 -17
  54. data/lib/google/protobuf/type.rb +0 -79
  55. data/lib/google/protobuf/wrappers.rb +0 -48
@@ -28,20 +28,20 @@ module Gcloud
28
28
  # Many common Array methods will return a new Array instance.
29
29
  #
30
30
  # @example
31
- # entities = dataset.find_all key1, key2, key3
32
- # entities.size #=> 3
33
- # entities.deferred #=> []
34
- # entities.missing #=> []
31
+ # tasks = datastore.find_all task_key1, task_key2, task_key3
32
+ # tasks.size #=> 3
33
+ # tasks.deferred #=> []
34
+ # tasks.missing #=> []
35
35
  #
36
36
  # @example Caution, many Array methods will return a new Array instance:
37
- # entities = dataset.find_all key1, key2, key3
38
- # entities.size #=> 3
39
- # entities.deferred #=> []
40
- # entities.missing #=> []
41
- # names = entities.map { |e| e["name"] }
42
- # names.size #=> 3
43
- # names.deferred #=> NoMethodError
44
- # names.missing #=> NoMethodError
37
+ # tasks = datastore.find_all task_key1, task_key2, task_key3
38
+ # tasks.size #=> 3
39
+ # tasks.deferred #=> []
40
+ # tasks.missing #=> []
41
+ # descriptions = tasks.map { |task| task["description"] }
42
+ # descriptions.size #=> 3
43
+ # descriptions.deferred #=> NoMethodError
44
+ # descriptions.missing #=> NoMethodError
45
45
  #
46
46
  class LookupResults < DelegateClass(::Array)
47
47
  ##
@@ -28,21 +28,23 @@ module Gcloud
28
28
  # Many common Array methods will return a new Array instance.
29
29
  #
30
30
  # @example
31
- # entities = dataset.run query
32
- # entities.size #=> 3
33
- # entities.cursor #=> "c3VwZXJhd2Vzb21lIQ"
31
+ # tasks = datastore.run query
32
+ # tasks.size #=> 3
33
+ # tasks.cursor #=> Gcloud::Datastore::Cursor(c3VwZXJhd2Vzb21lIQ)
34
34
  #
35
35
  # @example Caution, many Array methods will return a new Array instance:
36
- # entities = dataset.run query
37
- # entities.size #=> 3
38
- # entities.end_cursor #=> "c3VwZXJhd2Vzb21lIQ"
39
- # names = entities.map { |e| e.name }
40
- # names.size #=> 3
41
- # names.cursor #=> NoMethodError
36
+ # tasks = datastore.run query
37
+ # tasks.size #=> 3
38
+ # tasks.end_cursor #=> Gcloud::Datastore::Cursor(c3VwZXJhd2Vzb21lIQ)
39
+ # descriptions = tasks.map { |task| task["description"] }
40
+ # descriptions.size #=> 3
41
+ # descriptions.cursor #=> NoMethodError
42
42
  #
43
43
  class QueryResults < DelegateClass(::Array)
44
44
  ##
45
45
  # The end_cursor of the QueryResults.
46
+ #
47
+ # @return [Gcloud::Datastore::Cursor]
46
48
  attr_reader :end_cursor
47
49
  alias_method :cursor, :end_cursor
48
50
 
@@ -51,41 +53,129 @@ module Gcloud
51
53
  #
52
54
  # Expected values are:
53
55
  #
54
- # "MORE_RESULTS_AFTER_LIMIT":
55
- # "NOT_FINISHED":
56
- # "NO_MORE_RESULTS":
56
+ # * `:NOT_FINISHED`
57
+ # * `:MORE_RESULTS_AFTER_LIMIT`
58
+ # * `:MORE_RESULTS_AFTER_CURSOR`
59
+ # * `:NO_MORE_RESULTS`
57
60
  attr_reader :more_results
58
61
 
59
62
  ##
60
- # Convenience method for determining id the more_results value
61
- # is "NOT_FINISHED"
63
+ # @private
64
+ attr_accessor :service, :namespace, :query
65
+
66
+ ##
67
+ # @private
68
+ attr_writer :end_cursor, :more_results
69
+
70
+ ##
71
+ # Convenience method for determining if the `more_results` value
72
+ # is `:NOT_FINISHED`
62
73
  def not_finished?
63
- more_results == Proto.to_more_results_string(
64
- Proto::QueryResultBatch::MoreResultsType::NOT_FINISHED)
74
+ more_results == :NOT_FINISHED
65
75
  end
66
76
 
67
77
  ##
68
- # Convenience method for determining id the more_results value
69
- # is "MORE_RESULTS_AFTER_LIMIT"
78
+ # Convenience method for determining if the `more_results` value
79
+ # is `:MORE_RESULTS_AFTER_LIMIT`
70
80
  def more_after_limit?
71
- more_results == Proto.to_more_results_string(
72
- Proto::QueryResultBatch::MoreResultsType::MORE_RESULTS_AFTER_LIMIT)
81
+ more_results == :MORE_RESULTS_AFTER_LIMIT
82
+ end
83
+
84
+ ##
85
+ # Convenience method for determining if the `more_results` value
86
+ # is `:MORE_RESULTS_AFTER_CURSOR`
87
+ def more_after_cursor?
88
+ more_results == :MORE_RESULTS_AFTER_CURSOR
73
89
  end
74
90
 
75
91
  ##
76
- # Convenience method for determining id the more_results value
77
- # is "NO_MORE_RESULTS"
92
+ # Convenience method for determining if the `more_results` value
93
+ # is `:NO_MORE_RESULTS`
78
94
  def no_more?
79
- more_results == Proto.to_more_results_string(
80
- Proto::QueryResultBatch::MoreResultsType::NO_MORE_RESULTS)
95
+ more_results == :NO_MORE_RESULTS
81
96
  end
82
97
 
83
98
  ##
84
99
  # Create a new QueryResults with an array of values.
85
- def initialize arr = [], end_cursor = nil, more_results = nil
100
+ def initialize arr = []
86
101
  super arr
87
- @end_cursor = end_cursor
88
- @more_results = more_results
102
+ end
103
+
104
+ ##
105
+ # Whether there are more results available.
106
+ def next?
107
+ !no_more?
108
+ end
109
+
110
+ ##
111
+ # Retrieve the next page of results.
112
+ def next
113
+ return nil unless next?
114
+ return nil if end_cursor.nil?
115
+ ensure_service!
116
+ query.start_cursor = cursor.to_grpc # should always be a Cursor...
117
+ query_res = service.run_query query, namespace
118
+ self.class.from_grpc query_res, service, namespace, query
119
+ rescue GRPC::BadStatus => e
120
+ raise Gcloud::Error.from_error(e)
121
+ end
122
+
123
+ ##
124
+ # Retrieves all log entries by repeatedly loading {#next} until
125
+ # {#next?} returns `false`. Returns the list instance for method
126
+ # chaining.
127
+ #
128
+ # This method may make several API calls until all log entries are
129
+ # retrieved. Be sure to use as narrow a search criteria as possible.
130
+ # Please use with caution.
131
+ #
132
+ # @example
133
+ # require "gcloud"
134
+ #
135
+ # gcloud = Gcloud.new
136
+ # logging = gcloud.logging
137
+ # hour_ago = (Time.now - 60*60).utc.strftime('%FT%TZ')
138
+ # recent_errors = "timestamp >= \"#{hour_ago}\" severity >= ERROR"
139
+ # entries = logging.entries(filter: recent_errors).all
140
+ #
141
+ def all
142
+ while next?
143
+ next_records = self.next
144
+ push(*next_records)
145
+ self.end_cursor = next_records.end_cursor
146
+ self.more_results = next_records.more_results
147
+ self.service = next_records.service
148
+ self.namespace = next_records.namespace
149
+ self.query = next_records.query
150
+ end
151
+ self
152
+ end
153
+
154
+ ##
155
+ # @private New Dataset::QueryResults from a
156
+ # Google::Dataset::V1beta3::RunQueryResponse object.
157
+ def self.from_grpc query_res, service, namespace, query
158
+ entities = Array(query_res.batch.entity_results).map do |result|
159
+ # TODO: Make this return an EntityResult with cursor...
160
+ Entity.from_grpc result.entity
161
+ end
162
+ new(entities).tap do |qr|
163
+ qr.end_cursor = Cursor.from_grpc query_res.batch.end_cursor
164
+ qr.more_results = query_res.batch.more_results
165
+ qr.service = service
166
+ qr.namespace = namespace
167
+ qr.query = query_res.query || query
168
+ end
169
+ end
170
+
171
+ protected
172
+
173
+ ##
174
+ # @private Raise an error unless an active connection to the service is
175
+ # available.
176
+ def ensure_service!
177
+ msg = "Must have active connection to datastore service to get next"
178
+ fail msg if @service.nil? || @query.nil?
89
179
  end
90
180
  end
91
181
  end
@@ -15,7 +15,6 @@
15
15
 
16
16
  require "gcloud/datastore/key"
17
17
  require "gcloud/datastore/properties"
18
- require "gcloud/datastore/proto"
19
18
 
20
19
  module Gcloud
21
20
  module Datastore
@@ -25,10 +24,35 @@ module Gcloud
25
24
  # Entity represents a Datastore record.
26
25
  # Every Entity has a {Key}, and a list of properties.
27
26
  #
28
- # @example
29
- # entity = Gcloud::Datastore::Entity.new
30
- # entity.key = Gcloud::Datastore::Key.new "User", "heidi@example.com"
31
- # entity["name"] = "Heidi Henderson"
27
+ # Entities in Datastore form a hierarchically structured space similar to
28
+ # the directory structure of a file system. When you create an entity, you
29
+ # can optionally designate another entity as its parent; the new entity is a
30
+ # child of the parent entity.
31
+ #
32
+ # @see https://cloud.google.com/datastore/docs/concepts/entities Entities,
33
+ # Properties, and Keys
34
+ #
35
+ # @example Create a new entity using a block:
36
+ # task = datastore.entity "Task", "sampleTask" do |t|
37
+ # t["type"] = "Personal"
38
+ # t["created"] = Time.now
39
+ # t["done"] = false
40
+ # t["priority"] = 4
41
+ # t["percent_complete"] = 10.0
42
+ # t["description"] = "Learn Cloud Datastore"
43
+ # end
44
+ #
45
+ # @example Create a new entity belonging to an existing parent entity:
46
+ # task_key = datastore.key "Task", "sampleTask"
47
+ # task_key.parent = datastore.key "TaskList", "default"
48
+ #
49
+ # task = Gcloud::Datastore::Entity.new
50
+ # task.key = task_key
51
+ #
52
+ # task["type"] = "Personal"
53
+ # task["done"] = false
54
+ # task["priority"] = 4
55
+ # task["description"] = "Learn Cloud Datastore"
32
56
  #
33
57
  class Entity
34
58
  ##
@@ -48,6 +72,8 @@ module Gcloud
48
72
  #
49
73
  # Property values are converted from the Datastore value type
50
74
  # automatically. Blob properties are returned as StringIO objects.
75
+ # Location properties are returned as a Hash with `:longitude` and
76
+ # `:latitude` keys.
51
77
  #
52
78
  # @param [String, Symbol] prop_name The name of the property.
53
79
  #
@@ -57,28 +83,45 @@ module Gcloud
57
83
  # require "gcloud"
58
84
  #
59
85
  # gcloud = Gcloud.new
60
- # dataset = gcloud.datastore
61
- # user = dataset.find "User", "heidi@example.com"
62
- # user["name"] #=> "Heidi Henderson"
86
+ # datastore = gcloud.datastore
87
+ # task = datastore.find "Task", "sampleTask"
88
+ # task["description"] #=> "Learn Cloud Datastore"
63
89
  #
64
90
  # @example Or with a symbol name:
65
91
  # require "gcloud"
66
92
  #
67
93
  # gcloud = Gcloud.new
68
- # dataset = gcloud.datastore
69
- # user = dataset.find "User", "heidi@example.com"
70
- # user[:name] #=> "Heidi Henderson"
94
+ # datastore = gcloud.datastore
95
+ # task = datastore.find "Task", "sampleTask"
96
+ # task[:description] #=> "Learn Cloud Datastore"
97
+ #
98
+ # @example Getting a blob value returns a StringIO object:
99
+ # require "gcloud"
100
+ #
101
+ # gcloud = Gcloud.new
102
+ # datastore = gcloud.datastore
103
+ # user = datastore.find "User", "alice"
104
+ # user["avatar"] #=> StringIO("\x89PNG\r\n\x1A...")
105
+ #
106
+ # @example Getting a geo point value returns a Hash:
107
+ # require "gcloud"
108
+ #
109
+ # gcloud = Gcloud.new
110
+ # datastore = gcloud.datastore
111
+ # user = datastore.find "User", "alice"
112
+ # user["location"] #=> { longitude: -122.0862462,
113
+ # # latitude: 37.4220041 }
71
114
  #
72
115
  # @example Getting a blob value returns a StringIO object:
73
116
  # require "gcloud"
74
117
  #
75
118
  # gcloud = Gcloud.new
76
- # dataset = gcloud.datastore
77
- # user = dataset.find "User", "heidi@example.com"
119
+ # datastore = gcloud.datastore
120
+ # user = datastore.find "User", "alice"
78
121
  # user["avatar"] #=> StringIO("\x89PNG\r\n\x1A...")
79
122
  #
80
123
  def [] prop_name
81
- @properties[prop_name]
124
+ properties[prop_name]
82
125
  end
83
126
 
84
127
  ##
@@ -88,7 +131,8 @@ module Gcloud
88
131
  # automatically. Use an IO-compatible object (File, StringIO, Tempfile) to
89
132
  # indicate the property value should be stored as a Datastore `blob`.
90
133
  # IO-compatible objects are converted to StringIO objects when they are
91
- # set.
134
+ # set. Use a Hash with `:longitude` and `:latitude` keys to indicate the
135
+ # property value should be stored as a Geo Point/LatLng.
92
136
  #
93
137
  # @param [String, Symbol] prop_name The name of the property.
94
138
  # @param [Object] prop_value The value of the property.
@@ -97,29 +141,48 @@ module Gcloud
97
141
  # require "gcloud"
98
142
  #
99
143
  # gcloud = Gcloud.new
100
- # dataset = gcloud.datastore
101
- # user = dataset.find "User", "heidi@example.com"
102
- # user["name"] = "Heidi H. Henderson"
144
+ # datastore = gcloud.datastore
145
+ # task = datastore.find "Task", "sampleTask"
146
+ # task["description"] = "Learn Cloud Datastore"
147
+ # task["tags"] = ["fun", "programming"]
103
148
  #
104
149
  # @example Or with a symbol name:
105
150
  # require "gcloud"
106
151
  #
107
152
  # gcloud = Gcloud.new
108
- # dataset = gcloud.datastore
109
- # user = dataset.find "User", "heidi@example.com"
110
- # user[:name] = "Heidi H. Henderson"
153
+ # datastore = gcloud.datastore
154
+ # task = datastore.find "Task", "sampleTask"
155
+ # task[:description] = "Learn Cloud Datastore"
156
+ # task[:tags] = ["fun", "programming"]
111
157
  #
112
158
  # @example Setting a blob value using an IO:
113
159
  # require "gcloud"
114
160
  #
115
161
  # gcloud = Gcloud.new
116
- # dataset = gcloud.datastore
117
- # user = dataset.find "User", "heidi@example.com"
118
- # user["avatar"] = File.open "/avatars/heidi.png"
162
+ # datastore = gcloud.datastore
163
+ # user = datastore.find "User", "alice"
164
+ # user["avatar"] = File.open "/avatars/alice.png"
165
+ # user["avatar"] #=> StringIO("\x89PNG\r\n\x1A...")
166
+ #
167
+ # @example Setting a geo point value using a Hash:
168
+ # require "gcloud"
169
+ #
170
+ # gcloud = Gcloud.new
171
+ # datastore = gcloud.datastore
172
+ # user = datastore.find "User", "alice"
173
+ # user["location"] = { longitude: -122.0862462, latitude: 37.4220041 }
174
+ #
175
+ # @example Setting a blob value using an IO:
176
+ # require "gcloud"
177
+ #
178
+ # gcloud = Gcloud.new
179
+ # datastore = gcloud.datastore
180
+ # user = datastore.find "User", "alice"
181
+ # user["avatar"] = File.open "/avatars/alice.png"
119
182
  # user["avatar"] #=> StringIO("\x89PNG\r\n\x1A...")
120
183
  #
121
184
  def []= prop_name, prop_value
122
- @properties[prop_name] = prop_value
185
+ properties[prop_name] = prop_value
123
186
  end
124
187
 
125
188
  ##
@@ -129,52 +192,52 @@ module Gcloud
129
192
  # @return [Gcloud::Datastore::Properties]
130
193
  #
131
194
  # @example
132
- # entity.properties[:name] = "Heidi H. Henderson"
133
- # entity.properties["name"] #=> "Heidi H. Henderson"
195
+ # task.properties[:description] = "Learn Cloud Datastore"
196
+ # task.properties["description"] #=> "Learn Cloud Datastore"
134
197
  #
135
- # entity.properties.each do |name, value|
198
+ # task.properties.each do |name, value|
136
199
  # puts "property #{name} has a value of #{value}"
137
200
  # end
138
201
  #
139
202
  # @example A property's existence can be determined by calling `exist?`:
140
- # entity.properties.exist? :name #=> true
141
- # entity.properties.exist? "name" #=> true
142
- # entity.properties.exist? :expiration #=> false
203
+ # task.properties.exist? :description #=> true
204
+ # task.properties.exist? "description" #=> true
205
+ # task.properties.exist? :expiration #=> false
143
206
  #
144
207
  # @example A property can be removed from the entity:
145
- # entity.properties.delete :name
146
- # entity.save
208
+ # task.properties.delete :description
209
+ # task.save
147
210
  #
148
211
  # @example The properties can be converted to a hash:
149
- # prop_hash = entity.properties.to_h
212
+ # prop_hash = task.properties.to_h
150
213
  #
151
214
  attr_reader :properties
152
215
 
153
216
  ##
154
- # Sets the Key that identifies the entity.
217
+ # Sets the {Gcloud::Datastore::Key} that identifies the entity.
155
218
  #
156
219
  # Once the entity is saved, the key is frozen and immutable. Trying to set
157
220
  # a key when immutable will raise a `RuntimeError`.
158
221
  #
159
- # @example The Key can be set before the entity is saved:
222
+ # @example The key can be set before the entity is saved:
160
223
  # require "gcloud"
161
224
  #
162
225
  # gcloud = Gcloud.new
163
- # dataset = gcloud.datastore
164
- # entity = Gcloud::Datastore::Entity.new
165
- # entity.key = Gcloud::Datastore::Key.new "User"
166
- # dataset.save entity
226
+ # datastore = gcloud.datastore
227
+ # task = Gcloud::Datastore::Entity.new
228
+ # task.key = datastore.key "Task"
229
+ # datastore.save task
167
230
  #
168
231
  # @example Once the entity is saved, the key is frozen and immutable:
169
232
  # require "gcloud"
170
233
  #
171
234
  # gcloud = Gcloud.new
172
- # dataset = gcloud.datastore
173
- # entity = dataset.find "User", "heidi@example.com"
174
- # entity.persisted? #=> true
175
- # entity.key = Gcloud::Datastore::Key.new "User" #=> RuntimeError
176
- # entity.key.frozen? #=> true
177
- # entity.key.id = 9876543221 #=> RuntimeError
235
+ # datastore = gcloud.datastore
236
+ # task = datastore.find "Task", "sampleTask"
237
+ # task.persisted? #=> true
238
+ # task.key = datastore.key "Task" #=> RuntimeError
239
+ # task.key.frozen? #=> true
240
+ # task.key.id = 9876543221 #=> RuntimeError
178
241
  #
179
242
  def key= new_key
180
243
  fail "This entity's key is immutable." if persisted?
@@ -188,13 +251,13 @@ module Gcloud
188
251
  # require "gcloud"
189
252
  #
190
253
  # gcloud = Gcloud.new
191
- # dataset = gcloud.datastore
254
+ # datastore = gcloud.datastore
192
255
  #
193
- # new_entity = Gcloud::Datastore::Entity.new
194
- # new_entity.persisted? #=> false
256
+ # task = Gcloud::Datastore::Entity.new
257
+ # task.persisted? #=> false
195
258
  #
196
- # found_entity = dataset.find "User", "heidi@example.com"
197
- # found_entity.persisted? #=> true
259
+ # task = datastore.find "Task", "sampleTask"
260
+ # task.persisted? #=> true
198
261
  #
199
262
  def persisted?
200
263
  @key && @key.frozen?
@@ -217,14 +280,14 @@ module Gcloud
217
280
  # Unindexed properties
218
281
  #
219
282
  # @example Single property values will return a single flag setting:
220
- # entity["age"] = 21
221
- # entity.exclude_from_indexes? "age" #=> false
283
+ # task["priority"] = 4
284
+ # task.exclude_from_indexes? "priority" #=> false
222
285
  #
223
286
  # @example A multi-valued property will return an array of flag settings:
224
- # entity["tags"] = ["ruby", "code"]
225
- # entity.exclude_from_indexes! "tags", [true, false]
287
+ # task["tags"] = ["fun", "programming"]
288
+ # task.exclude_from_indexes! "tags", [true, false]
226
289
  #
227
- # entity.exclude_from_indexes? "tags" #=> [true, false]
290
+ # task.exclude_from_indexes? "tags" #=> [true, false]
228
291
  #
229
292
  def exclude_from_indexes? name
230
293
  value = self[name]
@@ -260,21 +323,21 @@ module Gcloud
260
323
  # Unindexed properties
261
324
  #
262
325
  # @example
263
- # entity["age"] = 21
264
- # entity.exclude_from_indexes! "age", true
326
+ # entity["priority"] = 4
327
+ # entity.exclude_from_indexes! "priority", true
265
328
  #
266
329
  # @example Multi-valued properties can be given multiple exclude flags:
267
- # entity["tags"] = ["ruby", "code"]
330
+ # entity["tags"] = ["fun", "programming"]
268
331
  # entity.exclude_from_indexes! "tags", [true, false]
269
332
  #
270
333
  # @example Or, a single flag can be applied to all values in a property:
271
- # entity["tags"] = ["ruby", "code"]
334
+ # entity["tags"] = ["fun", "programming"]
272
335
  # entity.exclude_from_indexes! "tags", true
273
336
  #
274
337
  # @example Flags can also be set with a block:
275
- # entity["age"] = 21
276
- # entity.exclude_from_indexes! "age" do |age|
277
- # age > 18
338
+ # entity["priority"] = 4
339
+ # entity.exclude_from_indexes! "priority" do |priority|
340
+ # priority > 4
278
341
  # end
279
342
  #
280
343
  def exclude_from_indexes! name, flag = nil, &block
@@ -288,30 +351,34 @@ module Gcloud
288
351
  end
289
352
 
290
353
  ##
291
- # @private Convert the Entity to a protocol buffer object.
292
- def to_proto
293
- entity = Proto::Entity.new.tap do |e|
294
- e.key = @key.to_proto
295
- e.property = Proto.to_proto_properties @properties.to_h
296
- end
297
- update_properties_indexed! entity
298
- entity
354
+ # @private Convert the Entity to a Google::Datastore::V1beta3::Entity
355
+ # object.
356
+ def to_grpc
357
+ grpc = Google::Datastore::V1beta3::Entity.new(
358
+ key: @key.to_grpc,
359
+ properties: @properties.to_grpc
360
+ )
361
+ update_properties_indexed! grpc.properties
362
+ grpc
299
363
  end
300
364
 
301
365
  ##
302
- # @private Create a new Entity from a protocol buffer object.
303
- def self.from_proto proto
366
+ # @private Create a new Entity from a Google::Datastore::V1beta3::Key
367
+ # object.
368
+ def self.from_grpc grpc
304
369
  entity = Entity.new
305
- entity.key = Key.from_proto proto.key
306
- Array(proto.property).each do |p|
307
- entity[p.name] = Proto.from_proto_value p.value
308
- end
309
- entity.send :update_exclude_indexes!, proto
370
+ entity.key = Key.from_grpc grpc.key
371
+ entity.send :properties=, Properties.from_grpc(grpc.properties)
372
+ entity.send :update_exclude_indexes!, grpc.properties
310
373
  entity
311
374
  end
312
375
 
313
376
  protected
314
377
 
378
+ ##
379
+ # @private Allow friendly objects to set Properties object.
380
+ attr_writer :properties
381
+
315
382
  # rubocop:disable all
316
383
  # Disabled rubocop because this is intentionally complex.
317
384
 
@@ -342,31 +409,30 @@ module Gcloud
342
409
 
343
410
  ##
344
411
  # @private Update the exclude data after a new object is created.
345
- def update_exclude_indexes! entity
412
+ def update_exclude_indexes! grpc_map
346
413
  @_exclude_indexes = {}
347
- Array(entity.property).each do |property|
348
- @_exclude_indexes[property.name] = !property.value.indexed
349
- unless property.value.list_value.nil?
350
- exclude = Array(property.value.list_value).map{|v| !v.indexed}
351
- @_exclude_indexes[property.name] = exclude
414
+ grpc_map.each do |name, value|
415
+ next if value.nil?
416
+ @_exclude_indexes[name] = value.exclude_from_indexes
417
+ unless value.array_value.nil?
418
+ exclude = value.array_value.values.map &:exclude_from_indexes
419
+ @_exclude_indexes[name] = exclude
352
420
  end
353
421
  end
354
422
  end
355
423
 
356
424
  ##
357
425
  # @private Update the indexed values before the object is saved.
358
- def update_properties_indexed! entity
359
- Array(entity.property).each do |property|
360
- excluded = exclude_from_indexes? property.name
426
+ def update_properties_indexed! grpc_map
427
+ grpc_map.each do |name, value|
428
+ next if value.nil?
429
+ excluded = exclude_from_indexes? name
361
430
  if excluded.is_a? Array
362
- # Lists must not set indexed, or this error will happen:
363
- # "A Value containing a list_value cannot specify indexed."
364
- property.value.indexed = nil
365
- property.value.list_value.each_with_index do |value, index|
366
- value.indexed = !excluded[index]
431
+ value.array_value.values.each_with_index do |v, i|
432
+ v.exclude_from_indexes = excluded[i]
367
433
  end
368
434
  else
369
- property.value.indexed = !excluded
435
+ value.exclude_from_indexes = excluded
370
436
  end
371
437
  end
372
438
  end