momento 0.2.0 → 0.4.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/.rubocop.yml +14 -1
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +91 -0
  6. data/CONTRIBUTING.md +5 -6
  7. data/Gemfile +1 -6
  8. data/Gemfile.lock +30 -24
  9. data/README.md +53 -43
  10. data/README.template.md +30 -25
  11. data/examples/Gemfile +1 -1
  12. data/examples/README.md +3 -3
  13. data/examples/compact.rb +12 -8
  14. data/examples/example.rb +13 -7
  15. data/examples/file.rb +6 -5
  16. data/lib/README-generating-pb.txt +1 -1
  17. data/lib/momento/auth/credential_provider.rb +78 -0
  18. data/lib/momento/cache_client.rb +457 -0
  19. data/lib/momento/collection_ttl.rb +79 -0
  20. data/lib/momento/config/configuration.rb +16 -0
  21. data/lib/momento/config/configurations.rb +17 -0
  22. data/lib/momento/config/transport/grpc_configuration.rb +25 -0
  23. data/lib/momento/config/transport/static_transport_strategy.rb +12 -0
  24. data/lib/momento/config/transport/transport_strategy.rb +16 -0
  25. data/lib/momento/error/types.rb +15 -0
  26. data/lib/momento/generated/README.md +16 -0
  27. data/lib/momento/generated/auth_pb.rb +52 -0
  28. data/lib/momento/generated/auth_services_pb.rb +27 -0
  29. data/lib/momento/generated/cacheclient_pb.rb +203 -0
  30. data/lib/momento/generated/cacheclient_services_pb.rb +90 -0
  31. data/lib/momento/generated/cacheping_pb.rb +38 -0
  32. data/lib/momento/generated/cacheping_services_pb.rb +23 -0
  33. data/lib/momento/generated/cachepubsub_pb.rb +48 -0
  34. data/lib/momento/generated/cachepubsub_services_pb.rb +56 -0
  35. data/lib/momento/generated/common_pb.rb +44 -0
  36. data/lib/momento/generated/controlclient_pb.rb +72 -0
  37. data/lib/momento/generated/controlclient_services_pb.rb +35 -0
  38. data/lib/momento/generated/extensions_pb.rb +35 -0
  39. data/lib/momento/generated/generate_protos.sh +47 -0
  40. data/lib/momento/generated/leaderboard_pb.rb +56 -0
  41. data/lib/momento/generated/leaderboard_services_pb.rb +57 -0
  42. data/lib/momento/generated/permissionmessages_pb.rb +48 -0
  43. data/lib/momento/generated/token_pb.rb +43 -0
  44. data/lib/momento/generated/token_services_pb.rb +23 -0
  45. data/lib/momento/generated/webhook_pb.rb +49 -0
  46. data/lib/momento/generated/webhook_services_pb.rb +32 -0
  47. data/lib/momento/response/control/create_cache_response.rb +61 -0
  48. data/lib/momento/{delete_cache_response_builder.rb → response/control/delete_cache_response.rb} +24 -3
  49. data/lib/momento/response/control/list_caches_response.rb +80 -0
  50. data/lib/momento/{delete_response_builder.rb → response/delete_response.rb} +24 -2
  51. data/lib/momento/{get_response.rb → response/get_response.rb} +39 -9
  52. data/lib/momento/{response.rb → response/response.rb} +11 -14
  53. data/lib/momento/response/set_response.rb +59 -0
  54. data/lib/momento/response/sort_order.rb +11 -0
  55. data/lib/momento/response/sorted_set/sorted_set_fetch_response.rb +107 -0
  56. data/lib/momento/response/sorted_set/sorted_set_put_element_response.rb +44 -0
  57. data/lib/momento/response/sorted_set/sorted_set_put_elements_response.rb +44 -0
  58. data/lib/momento/version.rb +1 -1
  59. data/lib/momento.rb +6 -1
  60. data/momento.gemspec +5 -3
  61. data/release-please-config.json +1 -1
  62. data/sig/momento/auth/credential_provider.rbs +11 -0
  63. data/sig/momento/cache_client.rbs +12 -0
  64. data/sig/momento/collection_ttl.rbs +22 -0
  65. data/sig/momento/config/configuration.rbs +9 -0
  66. data/sig/momento/config/configurations.rbs +9 -0
  67. data/sig/momento/config/transport/grpc_configuration.rbs +7 -0
  68. data/sig/momento/config/transport/transport_strategy.rbs +7 -0
  69. data/sig/momento/list_caches_response.rbs +7 -0
  70. data/sig/momento/sorted_set_fetch_response.rbs +13 -0
  71. data/sig/momento/sorted_set_put_element_response.rbs +5 -0
  72. data/sig/momento/sorted_set_put_elements_response.rbs +5 -0
  73. metadata +101 -40
  74. data/lib/momento/cacheclient_pb.rb +0 -334
  75. data/lib/momento/cacheclient_services_pb.rb +0 -44
  76. data/lib/momento/controlclient_pb.rb +0 -73
  77. data/lib/momento/controlclient_services_pb.rb +0 -31
  78. data/lib/momento/create_cache_response.rb +0 -37
  79. data/lib/momento/create_cache_response_builder.rb +0 -27
  80. data/lib/momento/delete_cache_response.rb +0 -24
  81. data/lib/momento/delete_response.rb +0 -24
  82. data/lib/momento/get_response_builder.rb +0 -37
  83. data/lib/momento/list_caches_response.rb +0 -77
  84. data/lib/momento/list_caches_response_builder.rb +0 -25
  85. data/lib/momento/set_response.rb +0 -39
  86. data/lib/momento/set_response_builder.rb +0 -25
  87. data/lib/momento/simple_cache_client.rb +0 -336
  88. /data/lib/momento/{response_builder.rb → response/response_builder.rb} +0 -0
@@ -1,77 +0,0 @@
1
- require_relative 'response/error'
2
-
3
- module Momento
4
- # A response from listing the caches.
5
- #
6
- # Each response is a single page of caches, there may be additional pages.
7
- # Use Momento::SimpleCacheClient#caches to efficiently get the whole list.
8
- class ListCachesResponse < Response
9
- # Did it get a page of caches?
10
- # @return [Boolean]
11
- def success?
12
- false
13
- end
14
-
15
- # The names of the caches in this page.
16
- # @return [Array,nil]
17
- def cache_names
18
- nil
19
- end
20
-
21
- # A token to fetch the next page.
22
- # The last page will have a blank token.
23
- # @return [String,nil]
24
- def next_token
25
- nil
26
- end
27
-
28
- # @!method to_s
29
- # Displays the response and the list of cache names.
30
- # The list of cache names will be truncated.
31
- # @return [String]
32
-
33
- # @private
34
- class Success < ListCachesResponse
35
- CACHE_NAMES_TO_DISPLAY = 5
36
-
37
- # rubocop:disable Lint/MissingSuper
38
- def initialize(grpc_response:)
39
- @grpc_response = grpc_response
40
- end
41
- # rubocop:enable Lint/MissingSuper
42
-
43
- def success?
44
- true
45
- end
46
-
47
- def cache_names
48
- @grpc_response.cache.map(&:cache_name)
49
- end
50
-
51
- def next_token
52
- @grpc_response.next_token
53
- end
54
-
55
- def to_s
56
- "#{super}: #{display_cache_names}"
57
- end
58
-
59
- private
60
-
61
- def display_cache_names
62
- display = cache_names.first(CACHE_NAMES_TO_DISPLAY).join(", ")
63
-
64
- if cache_names.size > CACHE_NAMES_TO_DISPLAY
65
- "#{display}, ..."
66
- else
67
- display
68
- end
69
- end
70
- end
71
-
72
- # @private
73
- class Error < ListCachesResponse
74
- include ::Momento::Response::Error
75
- end
76
- end
77
- end
@@ -1,25 +0,0 @@
1
- require 'grpc'
2
- require_relative 'controlclient_pb'
3
-
4
- module Momento
5
- # @private
6
- class ListCachesResponseBuilder < ResponseBuilder
7
- # Build a Momento::ListCachesResponse from a block of code
8
- # which returns a Momento::ControlClient::ListCachesResponse..
9
- #
10
- # @return [Momento::ListCachesResponse]
11
- # @raise [StandardError] when the exception is not recognized.
12
- # @raise [TypeError] when the response is not recognized.
13
- def from_block
14
- response = yield
15
- rescue GRPC::BadStatus => e
16
- ListCachesResponse::Error.new(
17
- exception: e, context: context
18
- )
19
- else
20
- raise TypeError unless response.is_a?(::Momento::ControlClient::ListCachesResponse)
21
-
22
- return ListCachesResponse::Success.new(grpc_response: response)
23
- end
24
- end
25
- end
@@ -1,39 +0,0 @@
1
- require_relative 'response/error'
2
-
3
- module Momento
4
- # A response from setting a key.
5
- class SetResponse < Response
6
- # Was the key/value pair added to the cache?
7
- # @return [Boolean]
8
- def success?
9
- false
10
- end
11
-
12
- # @private
13
- class Success < SetResponse
14
- attr_accessor :key, :value
15
-
16
- # rubocop:disable Lint/MissingSuper
17
- def initialize(key:, value:)
18
- @key = key
19
- @value = value
20
-
21
- return
22
- end
23
- # rubocop:enable Lint/MissingSuper
24
-
25
- def success?
26
- true
27
- end
28
-
29
- def to_s
30
- "#{super}: '#{display_string(key)}' = '#{display_string(value)}'"
31
- end
32
- end
33
-
34
- # @private
35
- class Error < SetResponse
36
- include Momento::Response::Error
37
- end
38
- end
39
- end
@@ -1,25 +0,0 @@
1
- require 'grpc'
2
- require_relative 'cacheclient_pb'
3
-
4
- module Momento
5
- # Builds SetResponses
6
- #
7
- # @private
8
- class SetResponseBuilder < ResponseBuilder
9
- # Build a Momento::SetResponse from a block of code
10
- # which returns a Momento::CacheClient::SetResponse..
11
- #
12
- # @return [Momento::SetResponse]
13
- # @raise [StandardError] when the exception is not recognized.
14
- # @raise [TypeError] when the response is not recognized.
15
- def from_block
16
- response = yield
17
- rescue *RESCUED_EXCEPTIONS => e
18
- SetResponse::Error.new(exception: e, context: context)
19
- else
20
- raise TypeError unless response.is_a?(::Momento::CacheClient::SetResponse)
21
-
22
- SetResponse::Success.new(key: context[:key], value: context[:value])
23
- end
24
- end
25
- end
@@ -1,336 +0,0 @@
1
- require 'jwt'
2
- require_relative 'cacheclient_services_pb'
3
- require_relative 'controlclient_services_pb'
4
- require_relative 'response'
5
- require_relative 'ttl'
6
- require_relative 'exceptions'
7
-
8
- module Momento
9
- # rubocop:disable Metrics/ClassLength
10
-
11
- # A simple client for Momento.
12
- #
13
- # SimpleCacheClient does not use exceptions to report errors.
14
- # Instead it returns an error response. Please see {file:README.md#label-Error+Handling}.
15
- #
16
- # @example
17
- # token = ...your Momento JWT...
18
- # client = Momento::SimpleCacheClient.new(
19
- # auth_token: token,
20
- # # cached items will be deleted after 100 seconds
21
- # default_ttl: 100
22
- # )
23
- #
24
- # response = client.create_cache("my_cache")
25
- # if response.success?
26
- # puts "my_cache was created"
27
- # elsif response.already_exists?
28
- # puts "my_cache already exists"
29
- # elsif response.error?
30
- # raise response.error
31
- # end
32
- #
33
- # # set will only return success or error,
34
- # # we only need to check for error
35
- # response = client.set("my_cache", "key", "value")
36
- # raise response.error if response.error?
37
- #
38
- # response = client.get("my_cache", "key")
39
- # if response.hit?
40
- # puts "We got #{response.value_string}"
41
- # elsif response.miss?
42
- # puts "It's not in the cache"
43
- # elsif response.error?
44
- # raise response.error
45
- # end
46
- #
47
- # @see Momento::Response
48
- class SimpleCacheClient
49
- # This gem's version.
50
- VERSION = Momento::VERSION
51
- CACHE_CLIENT_STUB_CLASS = CacheClient::Scs::Stub
52
- CONTROL_CLIENT_STUB_CLASS = ControlClient::ScsControl::Stub
53
- private_constant :CACHE_CLIENT_STUB_CLASS, :CONTROL_CLIENT_STUB_CLASS
54
-
55
- # @return [Numeric] how long items should remain in the cache, in seconds.
56
- attr_accessor :default_ttl
57
-
58
- # @param auth_token [String] the JWT for your Momento account
59
- # @param default_ttl [Numeric] time-to-live, in seconds
60
- # @raise [ArgumentError] if the default_ttl or auth_token is invalid
61
- def initialize(auth_token:, default_ttl:)
62
- @auth_token = auth_token
63
- @default_ttl = Momento::Ttl.to_ttl(default_ttl)
64
- load_endpoints_from_token
65
- end
66
-
67
- # Get a value in a cache.
68
- #
69
- # The value can be retrieved as either bytes or a string.
70
- # @example
71
- # response = client.get("my_cache", "key")
72
- # if response.hit?
73
- # puts "We got #{response.value_string}"
74
- # elsif response.miss?
75
- # puts "It's not in the cache"
76
- # elsif response.error?
77
- # raise response.error
78
- # end
79
- #
80
- # @see Momento::GetResponse
81
- # @param cache_name [String]
82
- # @param key [String] must only contain ASCII characters
83
- # @return [Momento::GetResponse]
84
- # @raise [TypeError] when the cache_name or key is not a String
85
- def get(cache_name, key)
86
- builder = GetResponseBuilder.new(
87
- context: { cache_name: cache_name, key: key }
88
- )
89
-
90
- return builder.from_block do
91
- validate_cache_name(cache_name)
92
-
93
- cache_stub.get(
94
- CacheClient::GetRequest.new(cache_key: to_bytes(key)),
95
- metadata: { cache: cache_name }
96
- )
97
- end
98
- end
99
-
100
- # Set a value in a cache.
101
- #
102
- # If ttl is not set, it will use the default_ttl.
103
- # @example
104
- # response = client.set("my_cache", "key", "value")
105
- # raise response.error if response.error?
106
- #
107
- # @see Momento::SetResponse
108
- # @param cache_name [String]
109
- # @param key [String] must only contain ASCII characters
110
- # @param value [String] the value to cache
111
- # @param ttl [Numeric] time-to-live, in seconds.
112
- # @raise [ArgumentError] if the ttl is invalid
113
- # @return [Momento::SetResponse]
114
- # @raise [TypeError] when the cache_name, key, or value is not a String
115
- def set(cache_name, key, value, ttl: default_ttl)
116
- ttl = Momento::Ttl.to_ttl(ttl)
117
-
118
- builder = SetResponseBuilder.new(
119
- context: { cache_name: cache_name, key: key, value: value, ttl: ttl }
120
- )
121
-
122
- return builder.from_block do
123
- validate_cache_name(cache_name)
124
-
125
- req = CacheClient::SetRequest.new(
126
- cache_key: to_bytes(key),
127
- cache_body: to_bytes(value),
128
- ttl_milliseconds: ttl.milliseconds
129
- )
130
-
131
- cache_stub.set(req, metadata: { cache: cache_name })
132
- end
133
- end
134
-
135
- # Delete a key in a cache.
136
- #
137
- # If the key does not exist, delete will still succeed.
138
- # @example
139
- # response = client.delete("my_cache", "key")
140
- # raise response.error if response.error?
141
- #
142
- # @see Momento::DeleteResponse
143
- # @param cache_name [String]
144
- # @param key [String] must only contain ASCII characters
145
- # @return [Momento::DeleteResponse]
146
- # @raise [TypeError] when the cache_name or key is not a String
147
- def delete(cache_name, key)
148
- builder = DeleteResponseBuilder.new(
149
- context: { cache_name: cache_name, key: key }
150
- )
151
-
152
- return builder.from_block do
153
- validate_cache_name(cache_name)
154
-
155
- cache_stub.delete(
156
- CacheClient::DeleteRequest.new(cache_key: to_bytes(key)),
157
- metadata: { cache: cache_name }
158
- )
159
- end
160
- end
161
-
162
- # Create a new Momento cache.
163
- # @example
164
- # response = client.create_cache("my_cache")
165
- # if response.success?
166
- # puts "my_cache was created"
167
- # elsif response.already_exists?
168
- # puts "my_cache already exists"
169
- # elsif response.error?
170
- # raise response.error
171
- # end
172
- #
173
- # @see Momento::CreateCacheResponse
174
- # @param cache_name [String] the name of the cache to create.
175
- # @return [Momento::CreateCacheResponse] the response from Momento.
176
- # @raise [TypeError] when the cache_name is not a String
177
- def create_cache(cache_name)
178
- builder = CreateCacheResponseBuilder.new(
179
- context: { cache_name: cache_name }
180
- )
181
-
182
- return builder.from_block do
183
- validate_cache_name(cache_name)
184
-
185
- control_stub.create_cache(
186
- ControlClient::CreateCacheRequest.new(cache_name: cache_name)
187
- )
188
- end
189
- end
190
-
191
- # Delete an existing Momento cache.
192
- #
193
- # @example
194
- # response = client.delete_cache("my_cache")
195
- # raise response.error if response.error?
196
- #
197
- # @see Momento::DeleteCacheResponse
198
- # @param cache_name [String] the name of the cache to delete.
199
- # @return [Momento::DeleteCacheResponse] the response from Momento.
200
- # @raise [TypeError] when the cache_name is not a String
201
- def delete_cache(cache_name)
202
- builder = DeleteCacheResponseBuilder.new(
203
- context: { cache_name: cache_name }
204
- )
205
-
206
- return builder.from_block do
207
- validate_cache_name(cache_name)
208
-
209
- control_stub.delete_cache(
210
- ControlClient::DeleteCacheRequest.new(cache_name: cache_name)
211
- )
212
- end
213
- end
214
-
215
- # List a page of your caches.
216
- #
217
- # This is a low-level method. You probably want to use {#caches} instead.
218
- #
219
- # The next_token indicates which page to fetch.
220
- # If nil or "" it will fetch the first page. Default is to fetch the first page.
221
- #
222
- # @see #caches
223
- # @see Momento::ListCachesResponse
224
- # @note Consider using `caches` instead.
225
- # @param next_token [String, nil] the token of the page to request
226
- # @return [Momento::ListCachesResponse]
227
- def list_caches(next_token: "")
228
- builder = ListCachesResponseBuilder.new(
229
- context: { next_token: next_token }
230
- )
231
- return builder.from_block do
232
- control_stub.list_caches(
233
- ControlClient::ListCachesRequest.new(next_token: next_token)
234
- )
235
- end
236
- end
237
-
238
- # Lists the names of all your caches, as a lazy Enumerator.
239
- # @example
240
- # cache_names = client.caches
241
- # cache_names.each { |name| puts name }
242
- #
243
- # @note Unlike other methods, this will raise if there is a problem
244
- # with the client or service.
245
- # @return [Enumerator::Lazy<String>] the cache names
246
- # @raise [Momento::Error] when there is an error listing caches.
247
- def caches
248
- Enumerator.new do |yielder|
249
- next_token = ""
250
-
251
- loop do
252
- response = list_caches(next_token: next_token)
253
- raise response.error if response.is_a? Momento::Response::Error
254
-
255
- response.cache_names.each do |name|
256
- yielder << name
257
- end
258
-
259
- break if response.next_token == ''
260
-
261
- next_token = response.next_token
262
- end
263
- end.lazy
264
- end
265
-
266
- private
267
-
268
- def cache_stub
269
- @cache_stub ||= CACHE_CLIENT_STUB_CLASS.new(@cache_endpoint, combined_credentials)
270
- end
271
-
272
- def control_stub
273
- @control_stub ||= CONTROL_CLIENT_STUB_CLASS.new(@control_endpoint, combined_credentials)
274
- end
275
-
276
- def combined_credentials
277
- @combined_credentials ||= make_combined_credentials
278
- end
279
-
280
- def load_endpoints_from_token
281
- claim = JWT.decode(@auth_token, nil, false).first
282
-
283
- @control_endpoint = claim["cp"]
284
- @cache_endpoint = claim["c"]
285
- rescue JWT::DecodeError
286
- raise ArgumentError, "Invalid Momento auth token."
287
- end
288
-
289
- def make_combined_credentials
290
- # :nocov:
291
- auth_proc = proc do
292
- { authorization: @auth_token, agent: "ruby:#{VERSION}" }
293
- end
294
- # :nocov:
295
-
296
- call_creds = GRPC::Core::CallCredentials.new(auth_proc)
297
-
298
- return GRPC::Core::ChannelCredentials.new.compose(call_creds)
299
- end
300
-
301
- # Ruby uses String for bytes. GRPC wants a String encoded as ASCII.
302
- # GRPC will re-encode a String, but treats it as characters; GRPC will
303
- # raise if you pass a String with non-ASCII characters.
304
- # So we do the re-encoding ourselves in a way that treats the String as
305
- # bytes and will not raise. The data is not changed.
306
- #
307
- # A duplicate String is returned, but since Ruby is copy-on-write it
308
- # does not copy the data.
309
- #
310
- # @param string [String] the string to make safe for GRPC bytes
311
- # @return [String] a duplicate safe to use as GRPC bytes
312
- # @raise [TypeError] when the string is not a String
313
- def to_bytes(string)
314
- raise TypeError, "expected a String, got a #{string.class}" unless string.is_a?(String)
315
-
316
- # dup in case the value is frozen and to avoid changing the value's encoding
317
- # for the caller.
318
- return string.dup.force_encoding(Encoding::ASCII_8BIT)
319
- end
320
-
321
- # This is not a complete validation of the cache name, just
322
- # issues that might cause an exception in the client. Let the server
323
- # handle the rest of the validation.
324
- #
325
- # @param name [String] the cache name to validate
326
- # @raise [TypeError] when the name is not a String
327
- # @raise [Momento::CacheNameError] when the name is not ASCII
328
- def validate_cache_name(name)
329
- raise TypeError, "Cache name must be a String, got a #{name.class}" unless name.is_a?(String)
330
- raise Momento::CacheNameError, "Cache name must be ASCII, got '#{name}'" if name.match?(/[^[:ascii:]]/)
331
-
332
- return
333
- end
334
- end
335
- # rubocop:enable Metrics/ClassLength
336
- end