momento 0.1.0 → 0.2.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +3 -0
  3. data/.rubocop.yml +12 -0
  4. data/.yardopts +2 -0
  5. data/CHANGELOG.md +28 -0
  6. data/CONTRIBUTING.md +1 -1
  7. data/Gemfile.lock +7 -3
  8. data/README.md +131 -36
  9. data/README.template.md +88 -0
  10. data/examples/.gitignore +1 -0
  11. data/examples/Gemfile +5 -0
  12. data/examples/README.md +34 -0
  13. data/examples/compact.rb +43 -0
  14. data/examples/example.rb +66 -0
  15. data/examples/file.rb +57 -0
  16. data/lib/momento/cacheclient_pb.rb +2 -0
  17. data/lib/momento/cacheclient_services_pb.rb +2 -0
  18. data/lib/momento/controlclient_pb.rb +2 -0
  19. data/lib/momento/controlclient_services_pb.rb +2 -0
  20. data/lib/momento/create_cache_response.rb +9 -24
  21. data/lib/momento/create_cache_response_builder.rb +27 -0
  22. data/lib/momento/delete_cache_response.rb +6 -21
  23. data/lib/momento/delete_cache_response_builder.rb +25 -0
  24. data/lib/momento/delete_response.rb +6 -15
  25. data/lib/momento/delete_response_builder.rb +23 -0
  26. data/lib/momento/error/grpc_details.rb +38 -0
  27. data/lib/momento/error/transport_details.rb +20 -0
  28. data/lib/momento/error/types.rb +232 -0
  29. data/lib/momento/error.rb +54 -0
  30. data/lib/momento/error_builder.rb +50 -0
  31. data/lib/momento/exceptions.rb +7 -0
  32. data/lib/momento/get_response.rb +37 -40
  33. data/lib/momento/get_response_builder.rb +37 -0
  34. data/lib/momento/list_caches_response.rb +45 -21
  35. data/lib/momento/list_caches_response_builder.rb +25 -0
  36. data/lib/momento/response/error.rb +10 -3
  37. data/lib/momento/response.rb +54 -1
  38. data/lib/momento/response_builder.rb +18 -0
  39. data/lib/momento/set_response.rb +21 -21
  40. data/lib/momento/set_response_builder.rb +25 -0
  41. data/lib/momento/simple_cache_client.rb +163 -31
  42. data/lib/momento/ttl.rb +48 -0
  43. data/lib/momento/version.rb +2 -1
  44. data/momento.gemspec +1 -0
  45. data/release-please-config.json +15 -0
  46. metadata +44 -6
  47. data/examples/basic.rb +0 -45
@@ -1,50 +1,95 @@
1
1
  require 'jwt'
2
- require 'momento/cacheclient_services_pb'
3
- require 'momento/controlclient_services_pb'
4
- require 'momento/response'
2
+ require_relative 'cacheclient_services_pb'
3
+ require_relative 'controlclient_services_pb'
4
+ require_relative 'response'
5
+ require_relative 'ttl'
6
+ require_relative 'exceptions'
5
7
 
6
8
  module Momento
9
+ # rubocop:disable Metrics/ClassLength
10
+
7
11
  # A simple client for Momento.
8
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
+ #
9
16
  # @example
17
+ # token = ...your Momento JWT...
10
18
  # client = Momento::SimpleCacheClient.new(
11
- # auth_token: jwt,
12
- # default_ttl: 10_000
19
+ # auth_token: token,
20
+ # # cached items will be deleted after 100 seconds
21
+ # default_ttl: 100
13
22
  # )
14
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
+ #
15
38
  # response = client.get("my_cache", "key")
16
39
  # if response.hit?
17
- # puts "We got #{response}"
40
+ # puts "We got #{response.value_string}"
18
41
  # elsif response.miss?
19
42
  # puts "It's not in the cache"
20
43
  # elsif response.error?
21
- # puts "The front fell off."
44
+ # raise response.error
22
45
  # end
46
+ #
47
+ # @see Momento::Response
23
48
  class SimpleCacheClient
49
+ # This gem's version.
24
50
  VERSION = Momento::VERSION
25
51
  CACHE_CLIENT_STUB_CLASS = CacheClient::Scs::Stub
26
52
  CONTROL_CLIENT_STUB_CLASS = ControlClient::ScsControl::Stub
53
+ private_constant :CACHE_CLIENT_STUB_CLASS, :CONTROL_CLIENT_STUB_CLASS
27
54
 
28
- # The default time to live, in milliseconds.
55
+ # @return [Numeric] how long items should remain in the cache, in seconds.
29
56
  attr_accessor :default_ttl
30
57
 
31
58
  # @param auth_token [String] the JWT for your Momento account
32
- # @param default_ttl [Integer]
59
+ # @param default_ttl [Numeric] time-to-live, in seconds
60
+ # @raise [ArgumentError] if the default_ttl or auth_token is invalid
33
61
  def initialize(auth_token:, default_ttl:)
34
62
  @auth_token = auth_token
35
- @default_ttl = default_ttl
63
+ @default_ttl = Momento::Ttl.to_ttl(default_ttl)
36
64
  load_endpoints_from_token
37
65
  end
38
66
 
39
67
  # Get a value in a cache.
40
68
  #
41
- # Momento only stores bytes; the returned value will be encoded as ASCII-8BIT.
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
42
79
  #
80
+ # @see Momento::GetResponse
43
81
  # @param cache_name [String]
44
82
  # @param key [String] must only contain ASCII characters
45
83
  # @return [Momento::GetResponse]
84
+ # @raise [TypeError] when the cache_name or key is not a String
46
85
  def get(cache_name, key)
47
- return GetResponse.from_block do
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
+
48
93
  cache_stub.get(
49
94
  CacheClient::GetRequest.new(cache_key: to_bytes(key)),
50
95
  metadata: { cache: cache_name }
@@ -55,18 +100,32 @@ module Momento
55
100
  # Set a value in a cache.
56
101
  #
57
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?
58
106
  #
107
+ # @see Momento::SetResponse
59
108
  # @param cache_name [String]
60
109
  # @param key [String] must only contain ASCII characters
61
110
  # @param value [String] the value to cache
62
- # @param ttl [Integer] time to live, in milliseconds.
111
+ # @param ttl [Numeric] time-to-live, in seconds.
112
+ # @raise [ArgumentError] if the ttl is invalid
63
113
  # @return [Momento::SetResponse]
114
+ # @raise [TypeError] when the cache_name, key, or value is not a String
64
115
  def set(cache_name, key, value, ttl: default_ttl)
65
- return SetResponse.from_block do
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
+
66
125
  req = CacheClient::SetRequest.new(
67
126
  cache_key: to_bytes(key),
68
127
  cache_body: to_bytes(value),
69
- ttl_milliseconds: ttl
128
+ ttl_milliseconds: ttl.milliseconds
70
129
  )
71
130
 
72
131
  cache_stub.set(req, metadata: { cache: cache_name })
@@ -75,11 +134,24 @@ module Momento
75
134
 
76
135
  # Delete a key in a cache.
77
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
78
143
  # @param cache_name [String]
79
144
  # @param key [String] must only contain ASCII characters
80
145
  # @return [Momento::DeleteResponse]
146
+ # @raise [TypeError] when the cache_name or key is not a String
81
147
  def delete(cache_name, key)
82
- return DeleteResponse.from_block do
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
+
83
155
  cache_stub.delete(
84
156
  CacheClient::DeleteRequest.new(cache_key: to_bytes(key)),
85
157
  metadata: { cache: cache_name }
@@ -88,56 +160,97 @@ module Momento
88
160
  end
89
161
 
90
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
91
172
  #
92
- # @param name [String] the name of the cache to create.
173
+ # @see Momento::CreateCacheResponse
174
+ # @param cache_name [String] the name of the cache to create.
93
175
  # @return [Momento::CreateCacheResponse] the response from Momento.
94
- def create_cache(name)
95
- return CreateCacheResponse.from_block do
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
+
96
185
  control_stub.create_cache(
97
- ControlClient::CreateCacheRequest.new(cache_name: name)
186
+ ControlClient::CreateCacheRequest.new(cache_name: cache_name)
98
187
  )
99
188
  end
100
189
  end
101
190
 
102
191
  # Delete an existing Momento cache.
103
192
  #
104
- # @param name [String] the name of the cache to delete.
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.
105
199
  # @return [Momento::DeleteCacheResponse] the response from Momento.
106
- def delete_cache(name)
107
- return DeleteCacheResponse.from_block do
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
+
108
209
  control_stub.delete_cache(
109
- ControlClient::DeleteCacheRequest.new(cache_name: name)
210
+ ControlClient::DeleteCacheRequest.new(cache_name: cache_name)
110
211
  )
111
212
  end
112
213
  end
113
214
 
114
215
  # List a page of your caches.
115
216
  #
217
+ # This is a low-level method. You probably want to use {#caches} instead.
218
+ #
116
219
  # The next_token indicates which page to fetch.
117
220
  # If nil or "" it will fetch the first page. Default is to fetch the first page.
118
221
  #
119
- # @params next_token [String, nil] the token of the page to request
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
120
226
  # @return [Momento::ListCachesResponse]
121
227
  def list_caches(next_token: "")
122
- return ListCachesResponse.from_block do
228
+ builder = ListCachesResponseBuilder.new(
229
+ context: { next_token: next_token }
230
+ )
231
+ return builder.from_block do
123
232
  control_stub.list_caches(
124
233
  ControlClient::ListCachesRequest.new(next_token: next_token)
125
234
  )
126
235
  end
127
236
  end
128
237
 
129
- # Lists the names of all your caches.
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 }
130
242
  #
243
+ # @note Unlike other methods, this will raise if there is a problem
244
+ # with the client or service.
131
245
  # @return [Enumerator::Lazy<String>] the cache names
132
- # @raise [GRPC::BadStatus]
133
- # rubocop:disable Metrics/MethodLength
246
+ # @raise [Momento::Error] when there is an error listing caches.
134
247
  def caches
135
248
  Enumerator.new do |yielder|
136
249
  next_token = ""
137
250
 
138
251
  loop do
139
252
  response = list_caches(next_token: next_token)
140
- raise response.grpc_exception if response.is_a? Momento::Response::Error
253
+ raise response.error if response.is_a? Momento::Response::Error
141
254
 
142
255
  response.cache_names.each do |name|
143
256
  yielder << name
@@ -149,7 +262,6 @@ module Momento
149
262
  end
150
263
  end.lazy
151
264
  end
152
- # rubocop:enable Metrics/MethodLength
153
265
 
154
266
  private
155
267
 
@@ -170,6 +282,8 @@ module Momento
170
282
 
171
283
  @control_endpoint = claim["cp"]
172
284
  @cache_endpoint = claim["c"]
285
+ rescue JWT::DecodeError
286
+ raise ArgumentError, "Invalid Momento auth token."
173
287
  end
174
288
 
175
289
  def make_combined_credentials
@@ -195,10 +309,28 @@ module Momento
195
309
  #
196
310
  # @param string [String] the string to make safe for GRPC bytes
197
311
  # @return [String] a duplicate safe to use as GRPC bytes
312
+ # @raise [TypeError] when the string is not a String
198
313
  def to_bytes(string)
314
+ raise TypeError, "expected a String, got a #{string.class}" unless string.is_a?(String)
315
+
199
316
  # dup in case the value is frozen and to avoid changing the value's encoding
200
317
  # for the caller.
201
318
  return string.dup.force_encoding(Encoding::ASCII_8BIT)
202
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
203
334
  end
335
+ # rubocop:enable Metrics/ClassLength
204
336
  end
@@ -0,0 +1,48 @@
1
+ module Momento
2
+ # Validates and represents a time-to-live.
3
+ #
4
+ # @private
5
+ class Ttl
6
+ class << self
7
+ # Create a Momento::Ttl object.
8
+ #
9
+ # If given a Momento::Ttl it will return it.
10
+ # This means you don't have to check if it's already a Momento::Ttl
11
+ #
12
+ # @example
13
+ # ttl = Momento::Ttl.to_ttl(ttl)
14
+ # @param ttl [Numeric,Momento::Ttl] the TTL in seconds, or an existing Ttl object
15
+ # @return [Momento::Ttl]
16
+ def to_ttl(ttl)
17
+ return ttl if ttl.is_a?(self)
18
+
19
+ raise ArgumentError, "ttl '#{ttl}' is not Numeric" unless ttl.is_a?(Numeric)
20
+ raise ArgumentError, "ttl #{ttl} is less than 0" if ttl.negative?
21
+
22
+ new(ttl)
23
+ end
24
+ end
25
+
26
+ # @return [Numeric] the TTL in seconds
27
+ attr_reader :seconds
28
+
29
+ def initialize(ttl)
30
+ @seconds = ttl
31
+ end
32
+
33
+ # @return [Numeric] the TTL in milliseconds
34
+ def milliseconds
35
+ @seconds * 1_000
36
+ end
37
+
38
+ def ==(other)
39
+ return false unless other.is_a?(Momento::Ttl)
40
+
41
+ milliseconds == other.milliseconds
42
+ end
43
+
44
+ def to_s
45
+ "#{seconds} seconds"
46
+ end
47
+ end
48
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Momento
4
- VERSION = "0.1.0"
4
+ # This gem's version.
5
+ VERSION = "0.2.0"
5
6
  end
data/momento.gemspec CHANGED
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency 'rubocop-rake', '~> 0.6.0'
39
39
  spec.add_development_dependency 'rubocop-rspec', '~> 2.15'
40
40
  spec.add_development_dependency 'simplecov', '~> 0.21'
41
+ spec.add_development_dependency 'yard', '~> 0.9'
41
42
 
42
43
  spec.add_dependency "grpc", '~> 1'
43
44
  spec.add_dependency 'jwt', '~> 2'
@@ -0,0 +1,15 @@
1
+ {
2
+ "packages": {
3
+ ".": {
4
+ "changelog-path": "CHANGELOG.md",
5
+ "release-type": "ruby",
6
+ "bump-minor-pre-major": true,
7
+ "bump-patch-for-minor-pre-major": false,
8
+ "draft": false,
9
+ "prerelease": false,
10
+ "package-name": "momento",
11
+ "bootstrap-sha": "ddb1cf2711fc1c7a29c0b48fe7f3aa12527c528c"
12
+ }
13
+ },
14
+ "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
15
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: momento
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Momento
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-11-21 00:00:00.000000000 Z
11
+ date: 2022-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0.21'
139
+ - !ruby/object:Gem::Dependency
140
+ name: yard
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.9'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.9'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: grpc
141
155
  requirement: !ruby/object:Gem::Requirement
@@ -172,16 +186,25 @@ extensions: []
172
186
  extra_rdoc_files: []
173
187
  files:
174
188
  - ".editorconfig"
189
+ - ".release-please-manifest.json"
175
190
  - ".rspec"
176
191
  - ".rubocop.yml"
177
192
  - ".ruby-version"
193
+ - ".yardopts"
194
+ - CHANGELOG.md
178
195
  - CONTRIBUTING.md
179
196
  - Gemfile
180
197
  - Gemfile.lock
181
198
  - LICENSE.txt
182
199
  - README.md
200
+ - README.template.md
183
201
  - Rakefile
184
- - examples/basic.rb
202
+ - examples/.gitignore
203
+ - examples/Gemfile
204
+ - examples/README.md
205
+ - examples/compact.rb
206
+ - examples/example.rb
207
+ - examples/file.rb
185
208
  - lib/README-generating-pb.txt
186
209
  - lib/momento.rb
187
210
  - lib/momento/cacheclient_pb.rb
@@ -189,16 +212,31 @@ files:
189
212
  - lib/momento/controlclient_pb.rb
190
213
  - lib/momento/controlclient_services_pb.rb
191
214
  - lib/momento/create_cache_response.rb
215
+ - lib/momento/create_cache_response_builder.rb
192
216
  - lib/momento/delete_cache_response.rb
217
+ - lib/momento/delete_cache_response_builder.rb
193
218
  - lib/momento/delete_response.rb
219
+ - lib/momento/delete_response_builder.rb
220
+ - lib/momento/error.rb
221
+ - lib/momento/error/grpc_details.rb
222
+ - lib/momento/error/transport_details.rb
223
+ - lib/momento/error/types.rb
224
+ - lib/momento/error_builder.rb
225
+ - lib/momento/exceptions.rb
194
226
  - lib/momento/get_response.rb
227
+ - lib/momento/get_response_builder.rb
195
228
  - lib/momento/list_caches_response.rb
229
+ - lib/momento/list_caches_response_builder.rb
196
230
  - lib/momento/response.rb
197
231
  - lib/momento/response/error.rb
232
+ - lib/momento/response_builder.rb
198
233
  - lib/momento/set_response.rb
234
+ - lib/momento/set_response_builder.rb
199
235
  - lib/momento/simple_cache_client.rb
236
+ - lib/momento/ttl.rb
200
237
  - lib/momento/version.rb
201
238
  - momento.gemspec
239
+ - release-please-config.json
202
240
  - sig/momento/client.rbs
203
241
  homepage: https://github.com/momentohq/client-sdk-ruby
204
242
  licenses:
@@ -207,7 +245,7 @@ metadata:
207
245
  homepage_uri: https://github.com/momentohq/client-sdk-ruby
208
246
  rubygems_mfa_required: 'true'
209
247
  source_code_uri: https://github.com/momentohq/client-sdk-ruby
210
- post_install_message:
248
+ post_install_message:
211
249
  rdoc_options: []
212
250
  require_paths:
213
251
  - lib
@@ -223,7 +261,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
223
261
  version: '0'
224
262
  requirements: []
225
263
  rubygems_version: 3.3.7
226
- signing_key:
264
+ signing_key:
227
265
  specification_version: 4
228
266
  summary: Client for Momento Serverless Cache
229
267
  test_files: []
data/examples/basic.rb DELETED
@@ -1,45 +0,0 @@
1
- require 'momento'
2
-
3
- # Get your Momento token from an environment variable.
4
- token = ENV.fetch('MOMENTO_AUTH_TOKEN')
5
-
6
- # Cached items will be deleted after 30 seconds.
7
- ttl = 30_000
8
-
9
- # Instantiate a Momento client.
10
- client = Momento::SimpleCacheClient.new(
11
- auth_token: token,
12
- default_ttl: ttl
13
- )
14
-
15
- # Create a cache named "test_cache" to play with.
16
- response = client.create_cache("test_cache")
17
- if response.success? || response.already_exists?
18
- puts "Created the cache, or it already exists."
19
- elsif response.error?
20
- raise "Couldn't create a cache: #{response}"
21
- else
22
- raise
23
- end
24
-
25
- # Put an item in the cache.
26
- response = client.set("test_cache", "key", "You cached something!")
27
- if response.success?
28
- puts "Set an item in the cache."
29
- elsif response.error?
30
- raise "Couldn't set an item in the cache: #{response}"
31
- else
32
- raise
33
- end
34
-
35
- # And get it back.
36
- response = client.get("test_cache", "key")
37
- if response.hit?
38
- puts "Cache returned: #{response.value}"
39
- elsif response.miss?
40
- puts "The item wasn't found in the cache."
41
- elsif response.error?
42
- raise "Couldn't get an item from the cache: #{response}"
43
- else
44
- raise
45
- end