momento 0.2.0 → 0.5.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 (89) 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 +105 -0
  6. data/CONTRIBUTING.md +5 -6
  7. data/Gemfile +1 -6
  8. data/Gemfile.lock +34 -28
  9. data/README.md +64 -44
  10. data/README.template.md +30 -25
  11. data/examples/.gitignore +1 -0
  12. data/examples/Gemfile +1 -1
  13. data/examples/README.md +5 -6
  14. data/examples/compact.rb +13 -9
  15. data/examples/example.rb +24 -8
  16. data/examples/file.rb +7 -6
  17. data/lib/README-generating-pb.txt +1 -1
  18. data/lib/momento/auth/credential_provider.rb +78 -0
  19. data/lib/momento/cache_client.rb +478 -0
  20. data/lib/momento/collection_ttl.rb +79 -0
  21. data/lib/momento/config/configuration.rb +42 -0
  22. data/lib/momento/config/configurations.rb +24 -0
  23. data/lib/momento/config/transport/grpc_configuration.rb +35 -0
  24. data/lib/momento/config/transport/static_transport_strategy.rb +12 -0
  25. data/lib/momento/config/transport/transport_strategy.rb +16 -0
  26. data/lib/momento/error/types.rb +22 -0
  27. data/lib/momento/generated/README.md +16 -0
  28. data/lib/momento/generated/auth_pb.rb +52 -0
  29. data/lib/momento/generated/auth_services_pb.rb +27 -0
  30. data/lib/momento/generated/cacheclient_pb.rb +203 -0
  31. data/lib/momento/generated/cacheclient_services_pb.rb +90 -0
  32. data/lib/momento/generated/cacheping_pb.rb +38 -0
  33. data/lib/momento/generated/cacheping_services_pb.rb +23 -0
  34. data/lib/momento/generated/cachepubsub_pb.rb +48 -0
  35. data/lib/momento/generated/cachepubsub_services_pb.rb +56 -0
  36. data/lib/momento/generated/common_pb.rb +44 -0
  37. data/lib/momento/generated/controlclient_pb.rb +72 -0
  38. data/lib/momento/generated/controlclient_services_pb.rb +35 -0
  39. data/lib/momento/generated/extensions_pb.rb +35 -0
  40. data/lib/momento/generated/generate_protos.sh +47 -0
  41. data/lib/momento/generated/leaderboard_pb.rb +56 -0
  42. data/lib/momento/generated/leaderboard_services_pb.rb +57 -0
  43. data/lib/momento/generated/permissionmessages_pb.rb +48 -0
  44. data/lib/momento/generated/token_pb.rb +43 -0
  45. data/lib/momento/generated/token_services_pb.rb +23 -0
  46. data/lib/momento/generated/webhook_pb.rb +49 -0
  47. data/lib/momento/generated/webhook_services_pb.rb +32 -0
  48. data/lib/momento/response/control/create_cache_response.rb +61 -0
  49. data/lib/momento/{delete_cache_response_builder.rb → response/control/delete_cache_response.rb} +24 -3
  50. data/lib/momento/response/control/list_caches_response.rb +80 -0
  51. data/lib/momento/{delete_response_builder.rb → response/delete_response.rb} +24 -2
  52. data/lib/momento/{get_response.rb → response/get_response.rb} +39 -9
  53. data/lib/momento/{response.rb → response/response.rb} +11 -14
  54. data/lib/momento/response/set_response.rb +59 -0
  55. data/lib/momento/response/sort_order.rb +11 -0
  56. data/lib/momento/response/sorted_set/sorted_set_fetch_response.rb +107 -0
  57. data/lib/momento/response/sorted_set/sorted_set_put_element_response.rb +44 -0
  58. data/lib/momento/response/sorted_set/sorted_set_put_elements_response.rb +44 -0
  59. data/lib/momento/version.rb +1 -1
  60. data/lib/momento.rb +6 -1
  61. data/momento.gemspec +5 -3
  62. data/release-please-config.json +1 -1
  63. data/sig/momento/auth/credential_provider.rbs +11 -0
  64. data/sig/momento/cache_client.rbs +11 -0
  65. data/sig/momento/collection_ttl.rbs +22 -0
  66. data/sig/momento/config/configuration.rbs +13 -0
  67. data/sig/momento/config/configurations.rbs +9 -0
  68. data/sig/momento/config/transport/grpc_configuration.rbs +9 -0
  69. data/sig/momento/config/transport/transport_strategy.rbs +7 -0
  70. data/sig/momento/list_caches_response.rbs +7 -0
  71. data/sig/momento/sorted_set_fetch_response.rbs +13 -0
  72. data/sig/momento/sorted_set_put_element_response.rbs +5 -0
  73. data/sig/momento/sorted_set_put_elements_response.rbs +5 -0
  74. metadata +101 -40
  75. data/lib/momento/cacheclient_pb.rb +0 -334
  76. data/lib/momento/cacheclient_services_pb.rb +0 -44
  77. data/lib/momento/controlclient_pb.rb +0 -73
  78. data/lib/momento/controlclient_services_pb.rb +0 -31
  79. data/lib/momento/create_cache_response.rb +0 -37
  80. data/lib/momento/create_cache_response_builder.rb +0 -27
  81. data/lib/momento/delete_cache_response.rb +0 -24
  82. data/lib/momento/delete_response.rb +0 -24
  83. data/lib/momento/get_response_builder.rb +0 -37
  84. data/lib/momento/list_caches_response.rb +0 -77
  85. data/lib/momento/list_caches_response_builder.rb +0 -25
  86. data/lib/momento/set_response.rb +0 -39
  87. data/lib/momento/set_response_builder.rb +0 -25
  88. data/lib/momento/simple_cache_client.rb +0 -336
  89. /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