active_remote-cached 1.1.1 → 1.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.
@@ -0,0 +1,331 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ # 5.minutes below. The library does not require this itself.
6
+ require 'active_support/core_ext/numeric/time'
7
+
8
+ class ConfigurationClass
9
+ include ::ActiveRemote::Cached
10
+
11
+ def self.find(*)
12
+ :find_result
13
+ end
14
+
15
+ def self.search(*)
16
+ [:search_result]
17
+ end
18
+
19
+ cached_finders_for :guid
20
+ cached_finders_for %i[alpha beta]
21
+
22
+ # Declared twice on purpose, to prove cached_methods does not repeat itself.
23
+ cached_finders_for :guid
24
+ end
25
+
26
+ class AliasFinderClass
27
+ include ::ActiveRemote::Cached
28
+
29
+ def self.find(*)
30
+ :find_result
31
+ end
32
+
33
+ def self.search(*)
34
+ [:search_result]
35
+ end
36
+
37
+ cached_finders :guid
38
+ end
39
+
40
+ class ChildFinderClass < ConfigurationClass; end
41
+
42
+ class DurationOptionClass
43
+ include ::ActiveRemote::Cached
44
+
45
+ def self.find(*)
46
+ :find_result
47
+ end
48
+
49
+ def self.search(*)
50
+ [:search_result]
51
+ end
52
+
53
+ # 5.minutes has no literal form. The options must not be interpolated into
54
+ # the generated source.
55
+ cached_finders_for :guid, :expires_in => 5.minutes
56
+ end
57
+
58
+ class DurationChildClass < DurationOptionClass; end
59
+
60
+ describe ::ActiveRemote::Cached do
61
+ let(:versioned_prefix) { ::ActiveRemote::Cached::RUBY_AND_ACTIVE_SUPPORT_VERSION }
62
+
63
+ before do
64
+ ::ActiveRemote::Cached.cache(HashCache.new)
65
+ end
66
+
67
+ after do
68
+ ::ActiveRemote::Cached.default_options({})
69
+ end
70
+
71
+ describe '.cache' do
72
+ it 'wraps the cache provider in a cache' do
73
+ expect(::ActiveRemote::Cached.cache).to be_a(::ActiveRemote::Cached::Cache)
74
+ end
75
+
76
+ it 'returns the current cache when no cache provider is given' do
77
+ cache = ::ActiveRemote::Cached.cache
78
+
79
+ expect(::ActiveRemote::Cached.cache).to be(cache)
80
+ end
81
+ end
82
+
83
+ describe '.default_options' do
84
+ it 'returns an empty hash when no options are set' do
85
+ expect(::ActiveRemote::Cached.default_options).to eq({})
86
+ end
87
+
88
+ it 'returns the options that were set' do
89
+ ::ActiveRemote::Cached.default_options(:expires_in => 100)
90
+
91
+ expect(::ActiveRemote::Cached.default_options).to eq(:expires_in => 100)
92
+ end
93
+
94
+ # Passing nil reads the options. Callers must pass an empty hash to clear.
95
+ it 'keeps the current options when nil is given' do
96
+ ::ActiveRemote::Cached.default_options(:expires_in => 100)
97
+
98
+ expect(::ActiveRemote::Cached.default_options(nil)).to eq(:expires_in => 100)
99
+ end
100
+
101
+ it 'clears the current options when an empty hash is given' do
102
+ ::ActiveRemote::Cached.default_options(:expires_in => 100)
103
+
104
+ expect(::ActiveRemote::Cached.default_options({})).to eq({})
105
+ end
106
+ end
107
+
108
+ describe 'RUBY_AND_ACTIVE_SUPPORT_VERSION' do
109
+ it 'prefixes every cache key' do
110
+ expect(::ActiveRemote::Cached.cache).to receive(:fetch).with(
111
+ [versioned_prefix, ConfigurationClass.name, '#find', 'guid.guid'], {}
112
+ ).and_return(:find_result)
113
+
114
+ ConfigurationClass.cached_find_by_guid(:guid)
115
+ end
116
+ end
117
+
118
+ describe '.cached_find' do
119
+ it 'calls find with the given arguments' do
120
+ expect(ConfigurationClass).to receive(:find).and_return(:hello)
121
+
122
+ expect(ConfigurationClass.cached_find(:guid => :guid)).to eq(:hello)
123
+ end
124
+
125
+ it 'sorts the argument keys before it builds the cache key' do
126
+ expect(ConfigurationClass).to receive(:cached_find_by_alpha_and_beta).with('A', 'B', {})
127
+
128
+ ConfigurationClass.cached_find(:beta => 'B', :alpha => 'A')
129
+ end
130
+
131
+ it 'yields to the block instead of calling find' do
132
+ expect(ConfigurationClass).not_to receive(:find)
133
+
134
+ expect(ConfigurationClass.cached_find(:guid => :guid) { :block_result }).to eq(:block_result)
135
+ end
136
+
137
+ it 'passes the options through to the fetch call' do
138
+ expect(::ActiveRemote::Cached.cache).to receive(:fetch).with(
139
+ [versioned_prefix, ConfigurationClass.name, '#find', 'guid.guid'], { :expires_in => 200 }
140
+ ).and_return(:hello)
141
+
142
+ expect(ConfigurationClass.cached_find({ :guid => :guid }, :expires_in => 200)).to eq(:hello)
143
+ end
144
+ end
145
+
146
+ describe '.cached_search' do
147
+ it 'calls search with the given arguments' do
148
+ expect(ConfigurationClass).to receive(:search).and_return([:hello])
149
+
150
+ expect(ConfigurationClass.cached_search(:guid => :guid)).to eq([:hello])
151
+ end
152
+
153
+ it 'sorts the argument keys before it builds the cache key' do
154
+ expect(ConfigurationClass).to receive(:cached_search_by_alpha_and_beta).with('A', 'B', {})
155
+
156
+ ConfigurationClass.cached_search(:beta => 'B', :alpha => 'A')
157
+ end
158
+ end
159
+
160
+ describe '.cached_methods' do
161
+ it 'does not repeat a method when the same finder is declared twice' do
162
+ expect(ConfigurationClass.cached_methods).to eq(ConfigurationClass.cached_methods.uniq)
163
+ end
164
+ end
165
+
166
+ describe '.cached_finders' do
167
+ it 'creates the same methods as cached_finders_for' do
168
+ expect(AliasFinderClass).to respond_to(:cached_find_by_guid)
169
+ expect(AliasFinderClass).to respond_to(:cached_search_by_guid)
170
+ end
171
+ end
172
+
173
+ describe '.respond_to_missing?' do
174
+ it 'is true for a finder that was defined' do
175
+ expect(ConfigurationClass.respond_to?(:cached_find_by_alpha_and_beta)).to eq(true)
176
+ end
177
+
178
+ it 'is true for a finder whose arguments are in a different order' do
179
+ expect(ConfigurationClass.respond_to?(:cached_find_by_beta_and_alpha)).to eq(true)
180
+ end
181
+
182
+ it 'is false for a finder that was not defined' do
183
+ expect(ConfigurationClass.respond_to?(:cached_find_by_missing)).to eq(false)
184
+ end
185
+
186
+ it 'returns a method object for a finder in a different argument order' do
187
+ expect(ConfigurationClass.method(:cached_find_by_beta_and_alpha)).to be_a(::Method)
188
+ end
189
+ end
190
+
191
+ describe '.method_missing' do
192
+ it 'raises NoMethodError for a method that is not a finder' do
193
+ expect { ConfigurationClass.not_a_finder }.to raise_error(::NoMethodError)
194
+ end
195
+
196
+ it 'raises NoMethodError for a finder that was not defined' do
197
+ expect { ConfigurationClass.cached_find_by_missing(:value) }.to raise_error(::NoMethodError)
198
+ end
199
+ end
200
+
201
+ describe '._args_in_sorted_order' do
202
+ it 'reorders the arguments to match the defined method' do
203
+ expect(ConfigurationClass).to receive(:cached_find_by_alpha_and_beta).with('A', 'B')
204
+
205
+ ConfigurationClass.cached_find_by_beta_and_alpha('B', 'A')
206
+ end
207
+
208
+ it 'passes the options hash through as the last argument' do
209
+ expect(ConfigurationClass).to receive(:cached_find_by_alpha_and_beta).with('A', 'B', { :expires_in => 10 })
210
+
211
+ ConfigurationClass.cached_find_by_beta_and_alpha('B', 'A', { :expires_in => 10 })
212
+ end
213
+
214
+ # A missing argument used to become nil, and the finder then cached a
215
+ # record under a key built from that nil.
216
+ it 'raises ArgumentError when an argument is missing' do
217
+ expect do
218
+ ConfigurationClass.cached_find_by_beta_and_alpha('B')
219
+ end.to raise_error(::ArgumentError, /given 1, expected 2/)
220
+ end
221
+ end
222
+
223
+ describe '._method_missing_name' do
224
+ it 'returns nil when the method name is not a finder' do
225
+ expect(ConfigurationClass._method_missing_name(:not_a_finder)).to be_nil
226
+ end
227
+
228
+ it 'sorts the argument names in the method name' do
229
+ expect(ConfigurationClass._method_missing_name(:cached_find_by_beta_and_alpha)).to eq(
230
+ :cached_find_by_alpha_and_beta
231
+ )
232
+ end
233
+
234
+ # The regex has no anchors, so it matches a finder name inside a longer
235
+ # name. This records the current behavior. See the note in the README.
236
+ it 'matches a finder name that is a suffix of a longer method name' do
237
+ expect(ConfigurationClass._method_missing_name(:not_cached_find_by_guid)).to eq(
238
+ :cached_find_by_guid
239
+ )
240
+ end
241
+ end
242
+
243
+ describe 'a finder whose definition fails' do
244
+ let(:broken_class) do
245
+ Class.new do
246
+ include ::ActiveRemote::Cached
247
+
248
+ def self.find(*)
249
+ :find_result
250
+ end
251
+
252
+ def self.search(*)
253
+ [:search_result]
254
+ end
255
+ end
256
+ end
257
+
258
+ # cached_methods used to be written before class_eval ran, so a failure
259
+ # registered a name with no method behind it. method_missing then
260
+ # dispatched to that same missing name and recursed until the stack ran out.
261
+ it 'registers no method name' do
262
+ expect { broken_class.cached_finders_for :'bad-name' }.to raise_error(::SyntaxError)
263
+
264
+ expect(broken_class.cached_methods).to eq([])
265
+ end
266
+
267
+ it 'raises NoMethodError rather than recursing' do
268
+ begin
269
+ broken_class.cached_finders_for :'bad-name'
270
+ rescue ::SyntaxError # rubocop:disable Lint/SuppressedException
271
+ end
272
+
273
+ expect { broken_class.cached_delete_by_guid('x') }.to raise_error(::NoMethodError)
274
+ end
275
+ end
276
+
277
+ describe 'a finder declared with an option that has no literal form' do
278
+ it 'defines the finder methods' do
279
+ expect(DurationOptionClass).to respond_to(:cached_find_by_guid)
280
+ expect(DurationOptionClass).to respond_to(:cached_search_by_guid)
281
+ end
282
+
283
+ it 'passes the option through to the fetch call' do
284
+ expect(::ActiveRemote::Cached.cache).to receive(:fetch).with(
285
+ [versioned_prefix, DurationOptionClass.name, '#find', 'guid.guid'], { :expires_in => 5.minutes }
286
+ ).and_return(:hello)
287
+
288
+ expect(DurationOptionClass.cached_find_by_guid(:guid)).to eq(:hello)
289
+ end
290
+
291
+ it 'lets a local option override the declared option' do
292
+ expect(::ActiveRemote::Cached.cache).to receive(:fetch).with(
293
+ [versioned_prefix, DurationOptionClass.name, '#find', 'guid.guid'], { :expires_in => 200 }
294
+ ).and_return(:hello)
295
+
296
+ expect(DurationOptionClass.cached_find_by_guid(:guid, :expires_in => 200)).to eq(:hello)
297
+ end
298
+
299
+ it 'applies the declared option in a subclass' do
300
+ expect(::ActiveRemote::Cached.cache).to receive(:fetch).with(
301
+ [versioned_prefix, DurationChildClass.name, '#find', 'guid.guid'], { :expires_in => 5.minutes }
302
+ ).and_return(:hello)
303
+
304
+ expect(DurationChildClass.cached_find_by_guid(:guid)).to eq(:hello)
305
+ end
306
+ end
307
+
308
+ describe 'a subclass of a class with cached finders' do
309
+ it 'responds to the finders the parent defined' do
310
+ expect(ChildFinderClass).to respond_to(:cached_find_by_alpha_and_beta)
311
+ end
312
+
313
+ it 'calls the finders the parent defined' do
314
+ expect(ChildFinderClass).to receive(:find).and_return(:hello)
315
+
316
+ expect(ChildFinderClass.cached_find_by_alpha_and_beta('A', 'B')).to eq(:hello)
317
+ end
318
+
319
+ it 'has an empty cached_methods list of its own' do
320
+ expect(ChildFinderClass.cached_methods).to eq([])
321
+ end
322
+
323
+ # method_missing reads cached_methods, which is empty on the subclass, so
324
+ # the subclass cannot reorder the arguments the way the parent can.
325
+ it 'does not accept the finder arguments in a different order' do
326
+ expect(ConfigurationClass.cached_find_by_beta_and_alpha('B', 'A')).to eq(:find_result)
327
+
328
+ expect { ChildFinderClass.cached_find_by_beta_and_alpha('B', 'A') }.to raise_error(::NoMethodError)
329
+ end
330
+ end
331
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'ostruct'
3
4
  require 'rubygems'
4
5
  require 'bundler'
5
6
  Bundler.require(:default, :development, :test)
metadata CHANGED
@@ -1,15 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_remote-cached
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Dewitt
8
8
  - MXDevExperience
9
- autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2024-06-10 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: active_remote
@@ -31,9 +30,23 @@ dependencies:
31
30
  requirements:
32
31
  - - ">="
33
32
  - !ruby/object:Gem::Version
34
- version: '0'
33
+ version: '6.1'
35
34
  type: :runtime
36
35
  prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '6.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: appraisal
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
37
50
  version_requirements: !ruby/object:Gem::Requirement
38
51
  requirements:
39
52
  - - ">="
@@ -67,6 +80,20 @@ dependencies:
67
80
  - - ">="
68
81
  - !ruby/object:Gem::Version
69
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: ostruct
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
70
97
  - !ruby/object:Gem::Dependency
71
98
  name: pry
72
99
  requirement: !ruby/object:Gem::Requirement
@@ -113,16 +140,16 @@ dependencies:
113
140
  name: rubocop
114
141
  requirement: !ruby/object:Gem::Requirement
115
142
  requirements:
116
- - - ">="
143
+ - - "~>"
117
144
  - !ruby/object:Gem::Version
118
- version: '0'
145
+ version: '1.80'
119
146
  type: :development
120
147
  prerelease: false
121
148
  version_requirements: !ruby/object:Gem::Requirement
122
149
  requirements:
123
- - - ">="
150
+ - - "~>"
124
151
  - !ruby/object:Gem::Version
125
- version: '0'
152
+ version: '1.80'
126
153
  description: ' Provides "cached" finders and a DSL to enumerate which finders should
127
154
  have cached versions '
128
155
  email:
@@ -132,8 +159,10 @@ executables: []
132
159
  extensions: []
133
160
  extra_rdoc_files: []
134
161
  files:
162
+ - ".github/workflows/ci.yml"
135
163
  - ".gitignore"
136
164
  - ".rubocop.yml"
165
+ - Appraisals
137
166
  - Gemfile
138
167
  - LICENSE.txt
139
168
  - README.md
@@ -151,11 +180,15 @@ files:
151
180
  - spec/active_remote/cached_exist_methods_spec.rb
152
181
  - spec/active_remote/cached_find_methods_spec.rb
153
182
  - spec/active_remote/cached_search_methods_spec.rb
183
+ - spec/active_remote/cached_spec.rb
154
184
  - spec/spec_helper.rb
155
- homepage: ''
156
- licenses: []
157
- metadata: {}
158
- post_install_message:
185
+ homepage: https://github.com/mxenabled/active_remote-cached
186
+ licenses:
187
+ - MIT
188
+ metadata:
189
+ homepage_uri: https://github.com/mxenabled/active_remote-cached
190
+ source_code_uri: https://github.com/mxenabled/active_remote-cached
191
+ rubygems_mfa_required: 'true'
159
192
  rdoc_options: []
160
193
  require_paths:
161
194
  - lib
@@ -163,23 +196,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
163
196
  requirements:
164
197
  - - ">="
165
198
  - !ruby/object:Gem::Version
166
- version: '2.6'
199
+ version: '3.1'
167
200
  required_rubygems_version: !ruby/object:Gem::Requirement
168
201
  requirements:
169
202
  - - ">="
170
203
  - !ruby/object:Gem::Version
171
204
  version: '0'
172
205
  requirements: []
173
- rubygems_version: 3.1.6
174
- signing_key:
206
+ rubygems_version: 3.6.9
175
207
  specification_version: 4
176
208
  summary: Provides a configuration for caching mechanisms and finders on ActiveRemote
177
209
  models
178
- test_files:
179
- - spec/active_remote/cached/argument_keys_spec.rb
180
- - spec/active_remote/cached/cache_spec.rb
181
- - spec/active_remote/cached_delete_methods_spec.rb
182
- - spec/active_remote/cached_exist_methods_spec.rb
183
- - spec/active_remote/cached_find_methods_spec.rb
184
- - spec/active_remote/cached_search_methods_spec.rb
185
- - spec/spec_helper.rb
210
+ test_files: []