active_remote-cached 1.1.1 → 1.3.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +85 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +20 -17
- data/Appraisals +27 -0
- data/README.md +164 -1
- data/Rakefile +3 -8
- data/active_remote-cached.gemspec +29 -6
- data/lib/active_remote/cached/argument_keys.rb +61 -27
- data/lib/active_remote/cached/cache.rb +32 -6
- data/lib/active_remote/cached/version.rb +1 -1
- data/lib/active_remote/cached.rb +129 -69
- data/spec/active_remote/cached/argument_keys_spec.rb +124 -0
- data/spec/active_remote/cached/cache_spec.rb +171 -5
- data/spec/active_remote/cached_delete_methods_spec.rb +51 -4
- data/spec/active_remote/cached_exist_methods_spec.rb +75 -4
- data/spec/active_remote/cached_find_methods_spec.rb +8 -10
- data/spec/active_remote/cached_search_methods_spec.rb +61 -12
- data/spec/active_remote/cached_spec.rb +390 -0
- data/spec/gemspec_spec.rb +29 -0
- data/spec/spec_helper.rb +1 -0
- metadata +49 -23
data/lib/active_remote/cached.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'active_support'
|
|
2
4
|
require 'active_support/cache'
|
|
3
5
|
require 'active_support/concern'
|
|
@@ -19,23 +21,60 @@ module ActiveRemote
|
|
|
19
21
|
RUBY_AND_ACTIVE_SUPPORT_VERSION = "#{RUBY_ENGINE_VERSION}:#{ActiveSupport::VERSION::STRING}".freeze
|
|
20
22
|
|
|
21
23
|
def self.cache(cache_provider = nil)
|
|
22
|
-
|
|
24
|
+
if cache_provider
|
|
25
|
+
nested_caching = @cache_provider&.nested_caching?
|
|
26
|
+
@cache_provider = ::ActiveRemote::Cached::Cache.new(cache_provider)
|
|
27
|
+
# A new Cache starts with nested caching off, so carry the setting over.
|
|
28
|
+
@cache_provider.enable_nested_caching! if nested_caching
|
|
29
|
+
end
|
|
23
30
|
|
|
24
31
|
@cache_provider
|
|
25
32
|
end
|
|
26
33
|
|
|
34
|
+
# Merges the given options into the current options, and returns the
|
|
35
|
+
# result. A later call adds to the options, so an app initializer keeps the
|
|
36
|
+
# :expires_in and :race_condition_ttl that the Rails railtie set before it.
|
|
27
37
|
def self.default_options(options = nil)
|
|
28
|
-
@default_options = options if options
|
|
38
|
+
@default_options = default_options.merge(options) if options
|
|
29
39
|
|
|
30
40
|
@default_options || {}
|
|
31
41
|
end
|
|
32
42
|
|
|
43
|
+
# Replaces the current options with the given options, and returns them.
|
|
44
|
+
# Pass an empty hash to clear the options.
|
|
45
|
+
def self.default_options_overwrite(options)
|
|
46
|
+
@default_options = options.dup
|
|
47
|
+
end
|
|
48
|
+
|
|
33
49
|
module ClassMethods
|
|
34
50
|
def cached_methods
|
|
35
51
|
@cached_methods ||= []
|
|
36
52
|
@cached_methods
|
|
37
53
|
end
|
|
38
54
|
|
|
55
|
+
# The options each cached finder was declared with, keyed by method name.
|
|
56
|
+
# The generated methods read this at call time. Interpolating the options
|
|
57
|
+
# into the generated source instead would require every value to have a
|
|
58
|
+
# literal form, and a value such as 5.minutes does not.
|
|
59
|
+
def cached_finder_options
|
|
60
|
+
@cached_finder_options ||= {}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# A subclass inherits the finder methods its parent defined, but not the
|
|
64
|
+
# parent registry, so the lookup walks up the superclass chain.
|
|
65
|
+
def _cached_finder_options_for(method_name)
|
|
66
|
+
klass = self
|
|
67
|
+
|
|
68
|
+
while klass.respond_to?(:cached_finder_options)
|
|
69
|
+
options = klass.cached_finder_options[method_name]
|
|
70
|
+
return options if options
|
|
71
|
+
|
|
72
|
+
klass = klass.superclass
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
{}
|
|
76
|
+
end
|
|
77
|
+
|
|
39
78
|
def cached_finders_for(*cached_finder_keys)
|
|
40
79
|
options = cached_finder_keys.extract_options!
|
|
41
80
|
|
|
@@ -103,37 +142,38 @@ module ActiveRemote
|
|
|
103
142
|
"#{::Regexp.last_match(1)}#{params.sort.join('_and_')}#{::Regexp.last_match(3)}".to_sym
|
|
104
143
|
end
|
|
105
144
|
|
|
106
|
-
# rubocop:disable Metrics/AbcSize
|
|
107
145
|
def _args_in_sorted_order(m, args)
|
|
108
146
|
regex = /cached_(?:delete|exist_search|search|exist_find|find)_by_([0-9a-zA-Z_]*)(!|\?)?/
|
|
109
147
|
|
|
110
|
-
|
|
148
|
+
called_match = m.match(regex)
|
|
149
|
+
sorted_match = _method_missing_name(m).match(regex)
|
|
111
150
|
|
|
112
|
-
|
|
113
|
-
match_2 = method_name.match(regex)
|
|
151
|
+
return args unless called_match[1] && sorted_match[1]
|
|
114
152
|
|
|
115
|
-
|
|
153
|
+
called_field_names = called_match[1].split('_and_')
|
|
154
|
+
sorted_field_names = sorted_match[1].split('_and_')
|
|
116
155
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
args_names_in_order = match_2[1].split('_and_')
|
|
156
|
+
_reorder_args(m, args, called_field_names, sorted_field_names)
|
|
157
|
+
end
|
|
120
158
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
159
|
+
def _reorder_args(m, args, called_field_names, sorted_field_names)
|
|
160
|
+
if args.size < called_field_names.size
|
|
161
|
+
raise ::ArgumentError,
|
|
162
|
+
"wrong number of arguments to #{m} (given #{args.size}, expected #{called_field_names.size})"
|
|
163
|
+
end
|
|
125
164
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
end
|
|
165
|
+
args_in_order = sorted_field_names.map do |field_name|
|
|
166
|
+
index = called_field_names.index(field_name)
|
|
167
|
+
raise ::ArgumentError, "#{m} has no argument named #{field_name}" if index.nil?
|
|
130
168
|
|
|
131
|
-
|
|
132
|
-
else
|
|
133
|
-
args
|
|
169
|
+
args[index]
|
|
134
170
|
end
|
|
171
|
+
|
|
172
|
+
# Add the options hash if the caller passed one.
|
|
173
|
+
args_in_order << args.last if args.size > called_field_names.size
|
|
174
|
+
|
|
175
|
+
args_in_order
|
|
135
176
|
end
|
|
136
|
-
# rubocop:enable Metrics/AbcSize
|
|
137
177
|
|
|
138
178
|
# rubocop:disable Metrics/AbcSize
|
|
139
179
|
def _create_cached_finder_for(cached_finder_key, options = {})
|
|
@@ -192,25 +232,41 @@ module ActiveRemote
|
|
|
192
232
|
"cached_search_by_#{arguments.sort.join('_and_')}"
|
|
193
233
|
end
|
|
194
234
|
|
|
195
|
-
def
|
|
235
|
+
def _expanded_search_args(method_arguments)
|
|
236
|
+
method_arguments.map { |method_argument| ":#{method_argument} => #{method_argument}," }.join
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# Returns the source text that builds the cache key for a generated
|
|
240
|
+
# method. The key names each field, so that two finders that take the
|
|
241
|
+
# same values do not share one cache entry.
|
|
242
|
+
def _expanded_cache_key_args(method_arguments)
|
|
243
|
+
sorted_arguments = method_arguments.sort
|
|
244
|
+
field_names = sorted_arguments.map { |argument| ":#{argument}" }.join(',')
|
|
245
|
+
|
|
246
|
+
'::ActiveRemote::Cached::ArgumentKeys.for_fields(' \
|
|
247
|
+
"[#{field_names}], [#{sorted_arguments.join(',')}], __active_remote_cached_options" \
|
|
248
|
+
').cache_key'
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def _define_cached_delete_method(method_name, *method_arguments, cached_finder_options_hash)
|
|
196
252
|
method_arguments.flatten!
|
|
197
253
|
expanded_method_args = method_arguments.join(',')
|
|
198
|
-
|
|
199
|
-
cached_methods << method_name
|
|
254
|
+
expanded_cache_key_args = _expanded_cache_key_args(method_arguments)
|
|
200
255
|
|
|
201
256
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
202
257
|
# def self.cached_delete_by_user_guid(user_guid, options = {})
|
|
203
258
|
# ::ActiveRemote::Cached.cache.delete([name, user_guid])
|
|
204
259
|
# end
|
|
205
260
|
def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options = {})
|
|
206
|
-
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(#{
|
|
261
|
+
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(_cached_finder_options_for('#{method_name}')).merge(__active_remote_cached_options)
|
|
207
262
|
namespace = __active_remote_cached_options.delete(:namespace)
|
|
263
|
+
argument_cache_key = #{expanded_cache_key_args}
|
|
208
264
|
find_cache_key = [
|
|
209
265
|
RUBY_AND_ACTIVE_SUPPORT_VERSION,
|
|
210
266
|
namespace,
|
|
211
267
|
name,
|
|
212
268
|
"#find",
|
|
213
|
-
|
|
269
|
+
argument_cache_key
|
|
214
270
|
].compact
|
|
215
271
|
|
|
216
272
|
search_cache_key = [
|
|
@@ -218,35 +274,36 @@ module ActiveRemote
|
|
|
218
274
|
namespace,
|
|
219
275
|
name,
|
|
220
276
|
"#search",
|
|
221
|
-
|
|
277
|
+
argument_cache_key
|
|
222
278
|
].compact
|
|
223
279
|
|
|
224
280
|
::ActiveRemote::Cached.cache.delete(find_cache_key)
|
|
225
281
|
::ActiveRemote::Cached.cache.delete(search_cache_key)
|
|
226
282
|
end
|
|
227
283
|
RUBY
|
|
284
|
+
|
|
285
|
+
cached_finder_options[method_name] = cached_finder_options_hash
|
|
286
|
+
cached_methods << method_name
|
|
228
287
|
end
|
|
229
288
|
|
|
230
|
-
def _define_cached_exist_find_method(method_name, *method_arguments,
|
|
289
|
+
def _define_cached_exist_find_method(method_name, *method_arguments, cached_finder_options_hash)
|
|
231
290
|
method_arguments.flatten!
|
|
232
291
|
expanded_method_args = method_arguments.join(',')
|
|
233
|
-
|
|
234
|
-
cached_methods << method_name
|
|
235
|
-
cached_methods << "#{method_name}?"
|
|
292
|
+
expanded_cache_key_args = _expanded_cache_key_args(method_arguments)
|
|
236
293
|
|
|
237
294
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
238
295
|
# def self.cached_exist_find_by_user_guid(user_guid, options = {})
|
|
239
296
|
# ::ActiveRemote::Cached.cache.exist?([name, user_guid])
|
|
240
297
|
# end
|
|
241
298
|
def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options = {})
|
|
242
|
-
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(#{
|
|
299
|
+
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(_cached_finder_options_for('#{method_name}')).merge(__active_remote_cached_options)
|
|
243
300
|
namespace = __active_remote_cached_options.delete(:namespace)
|
|
244
301
|
cache_key = [
|
|
245
302
|
RUBY_AND_ACTIVE_SUPPORT_VERSION,
|
|
246
303
|
namespace,
|
|
247
304
|
name,
|
|
248
305
|
"#find",
|
|
249
|
-
|
|
306
|
+
#{expanded_cache_key_args}
|
|
250
307
|
].compact
|
|
251
308
|
|
|
252
309
|
::ActiveRemote::Cached.cache.exist?(cache_key)
|
|
@@ -254,28 +311,30 @@ module ActiveRemote
|
|
|
254
311
|
RUBY
|
|
255
312
|
|
|
256
313
|
singleton_class.send(:alias_method, "#{method_name}?", method_name)
|
|
314
|
+
|
|
315
|
+
cached_finder_options[method_name] = cached_finder_options_hash
|
|
316
|
+
cached_methods << method_name
|
|
317
|
+
cached_methods << "#{method_name}?"
|
|
257
318
|
end
|
|
258
319
|
|
|
259
|
-
def _define_cached_exist_search_method(method_name, *method_arguments,
|
|
320
|
+
def _define_cached_exist_search_method(method_name, *method_arguments, cached_finder_options_hash)
|
|
260
321
|
method_arguments.flatten!
|
|
261
322
|
expanded_method_args = method_arguments.join(',')
|
|
262
|
-
|
|
263
|
-
cached_methods << method_name
|
|
264
|
-
cached_methods << "#{method_name}?"
|
|
323
|
+
expanded_cache_key_args = _expanded_cache_key_args(method_arguments)
|
|
265
324
|
|
|
266
325
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
267
326
|
# def self.cached_exist_search_by_user_guid(user_guid, options = {})
|
|
268
327
|
# ::ActiveRemote::Cached.cache.exist?([namespace, name, "#search", user_guid])
|
|
269
328
|
# end
|
|
270
329
|
def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options = {})
|
|
271
|
-
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(#{
|
|
330
|
+
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(_cached_finder_options_for('#{method_name}')).merge(__active_remote_cached_options)
|
|
272
331
|
namespace = __active_remote_cached_options.delete(:namespace)
|
|
273
332
|
cache_key = [
|
|
274
333
|
RUBY_AND_ACTIVE_SUPPORT_VERSION,
|
|
275
334
|
namespace,
|
|
276
335
|
name,
|
|
277
336
|
"#search",
|
|
278
|
-
|
|
337
|
+
#{expanded_cache_key_args}
|
|
279
338
|
].compact
|
|
280
339
|
|
|
281
340
|
::ActiveRemote::Cached.cache.exist?(cache_key)
|
|
@@ -283,18 +342,18 @@ module ActiveRemote
|
|
|
283
342
|
RUBY
|
|
284
343
|
|
|
285
344
|
singleton_class.send(:alias_method, "#{method_name}?", method_name)
|
|
345
|
+
|
|
346
|
+
cached_finder_options[method_name] = cached_finder_options_hash
|
|
347
|
+
cached_methods << method_name
|
|
348
|
+
cached_methods << "#{method_name}?"
|
|
286
349
|
end
|
|
287
350
|
|
|
288
|
-
def _define_cached_find_method(method_name, *method_arguments,
|
|
351
|
+
def _define_cached_find_method(method_name, *method_arguments, cached_finder_options_hash)
|
|
289
352
|
method_arguments.flatten!
|
|
290
353
|
expanded_method_args = method_arguments.join(',')
|
|
291
|
-
|
|
292
|
-
cached_methods << method_name
|
|
354
|
+
expanded_cache_key_args = _expanded_cache_key_args(method_arguments)
|
|
293
355
|
|
|
294
|
-
expanded_search_args =
|
|
295
|
-
method_arguments.each do |method_argument|
|
|
296
|
-
expanded_search_args << ":#{method_argument} => #{method_argument},"
|
|
297
|
-
end
|
|
356
|
+
expanded_search_args = _expanded_search_args(method_arguments)
|
|
298
357
|
|
|
299
358
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
300
359
|
# def self.cached_find_by_user_guid(user_guid, options = {})
|
|
@@ -309,14 +368,14 @@ module ActiveRemote
|
|
|
309
368
|
# of the result object is maintained for requests/responses
|
|
310
369
|
#
|
|
311
370
|
def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options = {})
|
|
312
|
-
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(#{
|
|
371
|
+
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(_cached_finder_options_for('#{method_name}')).merge(__active_remote_cached_options)
|
|
313
372
|
namespace = __active_remote_cached_options.delete(:namespace)
|
|
314
373
|
cache_key = [
|
|
315
374
|
RUBY_AND_ACTIVE_SUPPORT_VERSION,
|
|
316
375
|
namespace,
|
|
317
376
|
name,
|
|
318
377
|
"#find",
|
|
319
|
-
|
|
378
|
+
#{expanded_cache_key_args}
|
|
320
379
|
].compact
|
|
321
380
|
|
|
322
381
|
::ActiveRemote::Cached.cache.fetch(cache_key, __active_remote_cached_options) do
|
|
@@ -328,18 +387,17 @@ module ActiveRemote
|
|
|
328
387
|
end
|
|
329
388
|
end
|
|
330
389
|
RUBY
|
|
390
|
+
|
|
391
|
+
cached_finder_options[method_name] = cached_finder_options_hash
|
|
392
|
+
cached_methods << method_name
|
|
331
393
|
end
|
|
332
394
|
|
|
333
|
-
def _define_cached_search_method(method_name, *method_arguments,
|
|
395
|
+
def _define_cached_search_method(method_name, *method_arguments, cached_finder_options_hash)
|
|
334
396
|
method_arguments.flatten!
|
|
335
397
|
expanded_method_args = method_arguments.join(',')
|
|
336
|
-
|
|
337
|
-
cached_methods << method_name
|
|
398
|
+
expanded_cache_key_args = _expanded_cache_key_args(method_arguments)
|
|
338
399
|
|
|
339
|
-
expanded_search_args =
|
|
340
|
-
method_arguments.each do |method_argument|
|
|
341
|
-
expanded_search_args << ":#{method_argument} => #{method_argument},"
|
|
342
|
-
end
|
|
400
|
+
expanded_search_args = _expanded_search_args(method_arguments)
|
|
343
401
|
|
|
344
402
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
345
403
|
# def self.cached_search_by_user_guid(user_guid, options = {})
|
|
@@ -358,14 +416,14 @@ module ActiveRemote
|
|
|
358
416
|
# of the result object is maintained for requests/responses
|
|
359
417
|
#
|
|
360
418
|
def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options = {})
|
|
361
|
-
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(#{
|
|
419
|
+
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(_cached_finder_options_for('#{method_name}')).merge(__active_remote_cached_options)
|
|
362
420
|
namespace = __active_remote_cached_options.delete(:namespace)
|
|
363
421
|
cache_key = [
|
|
364
422
|
RUBY_AND_ACTIVE_SUPPORT_VERSION,
|
|
365
423
|
namespace,
|
|
366
424
|
name,
|
|
367
425
|
"#search",
|
|
368
|
-
|
|
426
|
+
#{expanded_cache_key_args}
|
|
369
427
|
].compact
|
|
370
428
|
|
|
371
429
|
::ActiveRemote::Cached.cache.fetch(cache_key, __active_remote_cached_options) do
|
|
@@ -377,18 +435,17 @@ module ActiveRemote
|
|
|
377
435
|
end
|
|
378
436
|
end
|
|
379
437
|
RUBY
|
|
438
|
+
|
|
439
|
+
cached_finder_options[method_name] = cached_finder_options_hash
|
|
440
|
+
cached_methods << method_name
|
|
380
441
|
end
|
|
381
442
|
|
|
382
|
-
def _define_cached_search_bang_method(method_name, *method_arguments,
|
|
443
|
+
def _define_cached_search_bang_method(method_name, *method_arguments, cached_finder_options_hash)
|
|
383
444
|
method_arguments.flatten!
|
|
384
445
|
expanded_method_args = method_arguments.join(',')
|
|
385
|
-
|
|
386
|
-
cached_methods << method_name
|
|
446
|
+
expanded_cache_key_args = _expanded_cache_key_args(method_arguments)
|
|
387
447
|
|
|
388
|
-
expanded_search_args =
|
|
389
|
-
method_arguments.each do |method_argument|
|
|
390
|
-
expanded_search_args << ":#{method_argument} => #{method_argument},"
|
|
391
|
-
end
|
|
448
|
+
expanded_search_args = _expanded_search_args(method_arguments)
|
|
392
449
|
|
|
393
450
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
394
451
|
# def self.cached_search_by_user_guid!(user_guid, options = {})
|
|
@@ -403,7 +460,7 @@ module ActiveRemote
|
|
|
403
460
|
# results = self.search(:user_guid => user_guid)
|
|
404
461
|
# end
|
|
405
462
|
#
|
|
406
|
-
# raise ::ActiveRemote::RemoteRecordNotFound
|
|
463
|
+
# raise ::ActiveRemote::RemoteRecordNotFound, self if results.nil? || results.first.nil?
|
|
407
464
|
# results
|
|
408
465
|
# end
|
|
409
466
|
# end
|
|
@@ -412,14 +469,14 @@ module ActiveRemote
|
|
|
412
469
|
# of the result object is maintained for requests/responses
|
|
413
470
|
#
|
|
414
471
|
def self.#{method_name}(#{expanded_method_args}, __active_remote_cached_options = {})
|
|
415
|
-
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(#{
|
|
472
|
+
__active_remote_cached_options = ::ActiveRemote::Cached.default_options.merge(_cached_finder_options_for('#{method_name}')).merge(__active_remote_cached_options)
|
|
416
473
|
namespace = __active_remote_cached_options.delete(:namespace)
|
|
417
474
|
cache_key = [
|
|
418
475
|
RUBY_AND_ACTIVE_SUPPORT_VERSION,
|
|
419
476
|
namespace,
|
|
420
477
|
name,
|
|
421
478
|
"#search",
|
|
422
|
-
|
|
479
|
+
#{expanded_cache_key_args}
|
|
423
480
|
].compact
|
|
424
481
|
|
|
425
482
|
::ActiveRemote::Cached.cache.fetch(cache_key, __active_remote_cached_options) do
|
|
@@ -431,11 +488,14 @@ module ActiveRemote
|
|
|
431
488
|
results = self.search(#{expanded_search_args})
|
|
432
489
|
end
|
|
433
490
|
|
|
434
|
-
raise ::ActiveRemote::RemoteRecordNotFound
|
|
491
|
+
raise ::ActiveRemote::RemoteRecordNotFound, self if results.nil? || results.first.nil?
|
|
435
492
|
results
|
|
436
493
|
end
|
|
437
494
|
end
|
|
438
495
|
RUBY
|
|
496
|
+
|
|
497
|
+
cached_finder_options[method_name] = cached_finder_options_hash
|
|
498
|
+
cached_methods << method_name
|
|
439
499
|
end
|
|
440
500
|
end
|
|
441
501
|
|
|
@@ -24,4 +24,128 @@ describe ::ActiveRemote::Cached::ArgumentKeys do
|
|
|
24
24
|
options = { :active_remote_cached_replace_characters => true }
|
|
25
25
|
expect(::ActiveRemote::Cached::ArgumentKeys.new('hello {}', options).cache_key).to eq('helloSPLBRB')
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
it 'replaces a tab when :active_remote_cached_replace_characters' do
|
|
29
|
+
options = { :active_remote_cached_replace_characters => true }
|
|
30
|
+
expect(::ActiveRemote::Cached::ArgumentKeys.new("a\tb", options).cache_key).to eq('aTBb')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'replaces a newline when :active_remote_cached_replace_characters' do
|
|
34
|
+
options = { :active_remote_cached_replace_characters => true }
|
|
35
|
+
expect(::ActiveRemote::Cached::ArgumentKeys.new("a\nb", options).cache_key).to eq('aNLb')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# REMOVE_CHARACTERS uses [[:space:]], so both options must cover the same
|
|
39
|
+
# whitespace. A raw tab or newline in a key breaks the Memcached protocol.
|
|
40
|
+
it 'leaves no whitespace in the key under either option' do
|
|
41
|
+
argument = "a b\tc\nd\re\ff\vg"
|
|
42
|
+
|
|
43
|
+
removed = ::ActiveRemote::Cached::ArgumentKeys.new(
|
|
44
|
+
argument, :active_remote_cached_remove_characters => true
|
|
45
|
+
).cache_key
|
|
46
|
+
replaced = ::ActiveRemote::Cached::ArgumentKeys.new(
|
|
47
|
+
argument, :active_remote_cached_replace_characters => true
|
|
48
|
+
).cache_key
|
|
49
|
+
|
|
50
|
+
expect(removed).not_to match(/[[:space:]]/)
|
|
51
|
+
expect(replaced).not_to match(/[[:space:]]/)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'joins multiple arguments into one key' do
|
|
55
|
+
expect(::ActiveRemote::Cached::ArgumentKeys.new('hello', 'world', {}).cache_key).to eq('helloworld')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'removes nil arguments from the key' do
|
|
59
|
+
expect(::ActiveRemote::Cached::ArgumentKeys.new('hello', nil, 'world', {}).cache_key).to eq('helloworld')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'flattens nested array arguments into the key' do
|
|
63
|
+
expect(::ActiveRemote::Cached::ArgumentKeys.new(['hello', ['world']], {}).cache_key).to eq('helloworld')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'returns an empty key when no arguments are given' do
|
|
67
|
+
expect(::ActiveRemote::Cached::ArgumentKeys.new({}).cache_key).to eq('')
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'returns the cache key from #to_s' do
|
|
71
|
+
expect(::ActiveRemote::Cached::ArgumentKeys.new('hello', {}).to_s).to eq('hello')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'removes characters when both the remove and the replace option are given' do
|
|
75
|
+
options = {
|
|
76
|
+
:active_remote_cached_remove_characters => true,
|
|
77
|
+
:active_remote_cached_replace_characters => true
|
|
78
|
+
}
|
|
79
|
+
expect(::ActiveRemote::Cached::ArgumentKeys.new('hello {}', options).cache_key).to eq('hello')
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe '.for_fields' do
|
|
83
|
+
it 'names each field in the key' do
|
|
84
|
+
argument_keys = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[alpha beta], %w[x y], {})
|
|
85
|
+
|
|
86
|
+
expect(argument_keys.cache_key).to eq('alpha.x/beta.y')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'gives two finders with the same values two different keys' do
|
|
90
|
+
alpha_beta = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[alpha beta], %w[x y], {})
|
|
91
|
+
gamma_delta = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[gamma delta], %w[x y], {})
|
|
92
|
+
|
|
93
|
+
expect(alpha_beta.cache_key).not_to eq(gamma_delta.cache_key)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'gives a single field a different key than two fields with the same characters' do
|
|
97
|
+
one_field = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[guid], %w[xy], {})
|
|
98
|
+
two_fields = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[alpha beta], %w[x y], {})
|
|
99
|
+
|
|
100
|
+
expect(one_field.cache_key).not_to eq(two_fields.cache_key)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'joins an array value with a comma' do
|
|
104
|
+
argument_keys = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[guid], [%w[a b]], {})
|
|
105
|
+
|
|
106
|
+
expect(argument_keys.cache_key).to eq('guid.a.b')
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it 'removes a nil inside an array value' do
|
|
110
|
+
argument_keys = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[guid], [['a', nil, 'b']], {})
|
|
111
|
+
|
|
112
|
+
expect(argument_keys.cache_key).to eq('guid.a.b')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'gives an empty value for a nil argument' do
|
|
116
|
+
argument_keys = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[alpha beta], ['x', nil], {})
|
|
117
|
+
|
|
118
|
+
expect(argument_keys.cache_key).to eq('alpha.x/beta.')
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'escapes a separator inside a value' do
|
|
122
|
+
argument_keys = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[guid], ['a/b.c%d'], {})
|
|
123
|
+
|
|
124
|
+
expect(argument_keys.cache_key).to eq('guid.a%2Fb%2Ec%25d')
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'keeps the separators when the remove option is given' do
|
|
128
|
+
options = { :active_remote_cached_remove_characters => true }
|
|
129
|
+
argument_keys = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[alpha beta], ['hello {}', 'y'], options)
|
|
130
|
+
|
|
131
|
+
expect(argument_keys.cache_key).to eq('alpha.hello/beta.y')
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'keeps the separators when the replace option is given' do
|
|
135
|
+
options = { :active_remote_cached_replace_characters => true }
|
|
136
|
+
argument_keys = ::ActiveRemote::Cached::ArgumentKeys.for_fields(%i[alpha beta], ['hello {}', 'y'], options)
|
|
137
|
+
|
|
138
|
+
expect(argument_keys.cache_key).to eq('alpha.helloSPLBRB/beta.y')
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# #cache_key calls gsub! on an instance variable, so a second call must not
|
|
143
|
+
# replace the characters of the first result again.
|
|
144
|
+
it 'returns the same key when :active_remote_cached_replace_characters is called twice' do
|
|
145
|
+
options = { :active_remote_cached_replace_characters => true }
|
|
146
|
+
argument_keys = ::ActiveRemote::Cached::ArgumentKeys.new('hello {}', options)
|
|
147
|
+
|
|
148
|
+
expect(argument_keys.cache_key).to eq('helloSPLBRB')
|
|
149
|
+
expect(argument_keys.cache_key).to eq('helloSPLBRB')
|
|
150
|
+
end
|
|
27
151
|
end
|