nexus_mods 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac9b76be57a989b2cd00cb120912dc8360fc926e76589769b725879f582e7d82
4
- data.tar.gz: 61342738bb1678bdbbbf350bf645b90210c55c8b53a63bdbb9cc755ca6f146bd
3
+ metadata.gz: 0d5efb3b98f3d8b66565eff56b7b6360fc99bae69a256423e6eae391eb3cd676
4
+ data.tar.gz: 7091d96c8a79ec591e805bcc09958199187b5f0afd73de85b5da314c2e24381a
5
5
  SHA512:
6
- metadata.gz: 8ad0436dd7917b94ebbedaa26e7f943a92520b0336413730e273108849b7f2659e026954ad1a68570e99c27ad6654a6c970be208ec500078a4490e65930ce896
7
- data.tar.gz: 67a49c5e42db887253894726eed4b7ff3adde7f6da0f31d123ce6587117abb99d80a26e9a0db69bb541368fec4c6561b3b0a9564a8fe5ac27a412b0fb3c0173e
6
+ metadata.gz: 1485e5270d8fcffb7a2ebfec15848f0f2af3a2575beef6b2d1c4746af682bd13142d15f140adae1c9e7c15d731d85d63d01ec135f28600e73d58249bc130990e
7
+ data.tar.gz: eccb9f4312f63a8c8fc459592c4c0c618378f59a69a261512981e890c0bfffa8c2044bafbcd16da9ec9c8d3b980a53a2e5322a89c5eece959240cafb7ac05e19
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # [v2.3.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.2.0...v2.3.0) (2023-04-12 15:08:22)
2
+
3
+ ### Features
4
+
5
+ * [[Feature] Add a way to set cache timestamps manually](https://github.com/Muriel-Salvan/nexus_mods/commit/7e5ac1d16d1d31f4aa33ebb9a748310e4572a06f)
6
+
7
+ # [v2.2.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.1.0...v2.2.0) (2023-04-12 12:56:31)
8
+
9
+ ### Features
10
+
11
+ * [[Feature] Add possibility to retrieve the cache timestamp of API resources](https://github.com/Muriel-Salvan/nexus_mods/commit/ba67323a32472deb1f063a51683ff9282ef96981)
12
+
1
13
  # [v2.1.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.0.1...v2.1.0) (2023-04-11 18:24:04)
2
14
 
3
15
  ### Features
@@ -60,6 +60,37 @@ class NexusMods
60
60
  cached_api(path, parameters:, verb:)
61
61
  end
62
62
 
63
+ # Get the timestamp of the cached data linked to a given API call
64
+ #
65
+ # Parameters::
66
+ # * *path* (String): API path to contact (from v1/ and without .json)
67
+ # * *parameters* (Hash<Symbol,Object>): Optional parameters to add to the path [default: {}]
68
+ # * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
69
+ # Result::
70
+ # * Time or nil: The refresh time of the data, or nil if not part of the cache
71
+ def api_cache_timestamp(path, parameters: {}, verb: :get)
72
+ key = ApiClient.cache_key(path, parameters:, verb:)
73
+ return unless Cacheable.cache_adapter.exist?(key)
74
+
75
+ str_time = Cacheable.cache_adapter.context.dig(key, 'invalidate_time')
76
+ str_time.nil? ? nil : Time.parse(str_time)
77
+ end
78
+
79
+ # Set the timestamp of the cached data linked to a given API call.
80
+ # This should be used only to update the cache timestamp of a resource we know is still up-to-date without fetching the resource for real again.
81
+ #
82
+ # Parameters::
83
+ # * *path* (String): API path to contact (from v1/ and without .json)
84
+ # * *parameters* (Hash<Symbol,Object>): Optional parameters to add to the path [default: {}]
85
+ # * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
86
+ # * *cache_timestamp* (Time): The cache timestamp to set for this resource
87
+ def set_api_cache_timestamp(path, cache_timestamp:, parameters: {}, verb: :get)
88
+ key = ApiClient.cache_key(path, parameters:, verb:)
89
+ Cacheable.cache_adapter.context[key] = {} unless Cacheable.cache_adapter.context.key?(key)
90
+ Cacheable.cache_adapter.context[key]['invalidate_time'] = cache_timestamp.utc.strftime('%FT%T.%9NUTC')
91
+ save_api_cache
92
+ end
93
+
63
94
  # Send an HTTP request to the API and get back the HTTP response
64
95
  #
65
96
  # Parameters::
@@ -99,6 +130,18 @@ class NexusMods
99
130
  # ApiClient: The API client to be used by the cacheable adapter (singleton pattern)
100
131
  attr_accessor :api_client
101
132
 
133
+ # Get the cache key to be used for a given API query
134
+ #
135
+ # Parameters::
136
+ # * *path* (String): API path to contact (from v1/ and without .json)
137
+ # * *parameters* (Hash<Symbol,Object>): Optional parameters to add to the path [default: {}]
138
+ # * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
139
+ # Result::
140
+ # * String: The corresponding cache key
141
+ def cache_key(path, parameters:, verb:)
142
+ "#{verb}/#{path}#{parameters.empty? ? '' : "/#{parameters.map { |param, value| "#{param}=#{value}" }.sort.join('/')}"}"
143
+ end
144
+
102
145
  end
103
146
 
104
147
  @api_client = nil
@@ -142,9 +185,7 @@ class NexusMods
142
185
  cacheable_api(
143
186
  :cached_api,
144
187
  key_format: proc do |_target, _method_name, method_args, method_kwargs|
145
- "#{method_kwargs[:verb]}/#{method_args.first}#{
146
- method_kwargs[:parameters].empty? ? '' : "/#{method_kwargs[:parameters].map { |param, value| "#{param}=#{value}" }.sort.join('/')}"
147
- }"
188
+ cache_key(method_args.first, parameters: method_kwargs[:parameters], verb: method_kwargs[:verb])
148
189
  end,
149
190
  expiry_from_key: proc do |key|
150
191
  # Example of keys:
@@ -45,7 +45,7 @@ class NexusMods
45
45
  expiry_cache[key].nil? || (Time.now.utc - Time.parse(context['invalidate_time']).utc > expiry_cache[key])
46
46
  end,
47
47
  update_context_after_fetch: proc do |_key, _value, _options, context|
48
- context['invalidate_time'] = Time.now.utc.strftime('%FT%TUTC')
48
+ context['invalidate_time'] = Time.now.utc.strftime('%FT%T.%9NUTC')
49
49
  end
50
50
  }
51
51
  )
@@ -13,6 +13,8 @@ module Cacheable
13
13
  # * The context information is JSON serializable.
14
14
  class PersistentJsonAdapter < MemoryAdapter
15
15
 
16
+ attr_reader :context
17
+
16
18
  # Fetch a key with the givien cache options
17
19
  #
18
20
  # Parameters::
@@ -87,10 +89,6 @@ module Cacheable
87
89
  @context = loaded_content['context']
88
90
  end
89
91
 
90
- private
91
-
92
- attr_reader :context
93
-
94
92
  end
95
93
 
96
94
  end
@@ -1,5 +1,5 @@
1
1
  class NexusMods
2
2
 
3
- VERSION = '2.1.0'
3
+ VERSION = '2.3.0'
4
4
 
5
5
  end
data/lib/nexus_mods.rb CHANGED
@@ -143,6 +143,23 @@ class NexusMods
143
143
  end
144
144
  end
145
145
 
146
+ # Get the cached timestamp of the list of games
147
+ #
148
+ # Result::
149
+ # * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
150
+ def games_cache_timestamp
151
+ @api_client.api_cache_timestamp('games')
152
+ end
153
+
154
+ # Set the cached timestamp of the list of games.
155
+ # This should be used only to update the cache timestamp of a resource we know is still up-to-date without fetching the resource for real again.
156
+ #
157
+ # Parameters::
158
+ # * *cache_timestamp* (Time): The cache timestamp to set for this resource
159
+ def set_games_cache_timestamp(cache_timestamp:)
160
+ @api_client.set_api_cache_timestamp('games', cache_timestamp:)
161
+ end
162
+
146
163
  # Get information about a mod
147
164
  #
148
165
  # Parameters::
@@ -183,6 +200,28 @@ class NexusMods
183
200
  )
184
201
  end
185
202
 
203
+ # Get the cached timestamp of a mod information
204
+ #
205
+ # Parameters::
206
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
207
+ # * *mod_id* (Integer): The mod ID [default: @mod_id]
208
+ # Result::
209
+ # * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
210
+ def mod_cache_timestamp(game_domain_name: @game_domain_name, mod_id: @mod_id)
211
+ @api_client.api_cache_timestamp("games/#{game_domain_name}/mods/#{mod_id}")
212
+ end
213
+
214
+ # Set the cached timestamp of a mod information.
215
+ # This should be used only to update the cache timestamp of a resource we know is still up-to-date without fetching the resource for real again.
216
+ #
217
+ # Parameters::
218
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
219
+ # * *mod_id* (Integer): The mod ID [default: @mod_id]
220
+ # * *cache_timestamp* (Time): The cache timestamp to set for this resource
221
+ def set_mod_cache_timestamp(cache_timestamp:, game_domain_name: @game_domain_name, mod_id: @mod_id)
222
+ @api_client.set_api_cache_timestamp("games/#{game_domain_name}/mods/#{mod_id}", cache_timestamp:)
223
+ end
224
+
186
225
  # Get files belonging to a mod
187
226
  #
188
227
  # Parameters::
@@ -214,6 +253,28 @@ class NexusMods
214
253
  end
215
254
  end
216
255
 
256
+ # Get the cached timestamp of a mod files information
257
+ #
258
+ # Parameters::
259
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
260
+ # * *mod_id* (Integer): The mod ID [default: @mod_id]
261
+ # Result::
262
+ # * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
263
+ def mod_files_cache_timestamp(game_domain_name: @game_domain_name, mod_id: @mod_id)
264
+ @api_client.api_cache_timestamp("games/#{game_domain_name}/mods/#{mod_id}/files")
265
+ end
266
+
267
+ # Set the cached timestamp of a mod files information.
268
+ # This should be used only to update the cache timestamp of a resource we know is still up-to-date without fetching the resource for real again.
269
+ #
270
+ # Parameters::
271
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
272
+ # * *mod_id* (Integer): The mod ID [default: @mod_id]
273
+ # * *cache_timestamp* (Time): The cache timestamp to set for this resource
274
+ def set_mod_files_cache_timestamp(cache_timestamp:, game_domain_name: @game_domain_name, mod_id: @mod_id)
275
+ @api_client.set_api_cache_timestamp("games/#{game_domain_name}/mods/#{mod_id}/files", cache_timestamp:)
276
+ end
277
+
217
278
  # Get a list of updated mod ids since a given time
218
279
  #
219
280
  # Parameters::
@@ -227,6 +288,58 @@ class NexusMods
227
288
  # Result::
228
289
  # * Array<ModUpdates>: Mod's updates information
229
290
  def updated_mods(game_domain_name: @game_domain_name, since: :one_day, clear_cache: false)
291
+ @api_client.api("games/#{game_domain_name}/mods/updated", parameters: period_to_url_params(since), clear_cache:).map do |updated_mod_json|
292
+ Api::ModUpdates.new(
293
+ mod_id: updated_mod_json['mod_id'],
294
+ latest_file_update: Time.at(updated_mod_json['latest_file_update']).utc,
295
+ latest_mod_activity: Time.at(updated_mod_json['latest_mod_activity']).utc
296
+ )
297
+ end
298
+ end
299
+
300
+ # Get the cached timestamp of updated mod ids
301
+ #
302
+ # Parameters::
303
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
304
+ # * *since* (Symbol): The time from which we look for updated mods [default: :one_day]
305
+ # Possible values are:
306
+ # * *one_day*: Since 1 day
307
+ # * *one_week*: Since 1 week
308
+ # * *one_month*: Since 1 month
309
+ # Result::
310
+ # * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
311
+ def updated_mods_cache_timestamp(game_domain_name: @game_domain_name, since: :one_day)
312
+ @api_client.api_cache_timestamp("games/#{game_domain_name}/mods/updated", parameters: period_to_url_params(since))
313
+ end
314
+
315
+ # Set the cached timestamp of updated mod ids.
316
+ # This should be used only to update the cache timestamp of a resource we know is still up-to-date without fetching the resource for real again.
317
+ #
318
+ # Parameters::
319
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
320
+ # * *since* (Symbol): The time from which we look for updated mods [default: :one_day]
321
+ # Possible values are:
322
+ # * *one_day*: Since 1 day
323
+ # * *one_week*: Since 1 week
324
+ # * *one_month*: Since 1 month
325
+ # * *cache_timestamp* (Time): The cache timestamp to set for this resource
326
+ def set_updated_mods_cache_timestamp(cache_timestamp:, game_domain_name: @game_domain_name, since: :one_day)
327
+ @api_client.set_api_cache_timestamp("games/#{game_domain_name}/mods/updated", parameters: period_to_url_params(since), cache_timestamp:)
328
+ end
329
+
330
+ private
331
+
332
+ # Get the URL parameters from the required period
333
+ #
334
+ # Parameters::
335
+ # * *since* (Symbol): The time from which we look for updated mods
336
+ # Possible values are:
337
+ # * *one_day*: Since 1 day
338
+ # * *one_week*: Since 1 week
339
+ # * *one_month*: Since 1 month
340
+ # Result::
341
+ # * Hash<Symbol,Object>: Corresponding URL parameters
342
+ def period_to_url_params(since)
230
343
  nexus_mods_period = {
231
344
  one_day: '1d',
232
345
  one_week: '1w',
@@ -234,13 +347,7 @@ class NexusMods
234
347
  }[since]
235
348
  raise "Unknown time stamp: #{since}" if nexus_mods_period.nil?
236
349
 
237
- @api_client.api("games/#{game_domain_name}/mods/updated", parameters: { period: nexus_mods_period }, clear_cache:).map do |updated_mod_json|
238
- Api::ModUpdates.new(
239
- mod_id: updated_mod_json['mod_id'],
240
- latest_file_update: Time.at(updated_mod_json['latest_file_update']).utc,
241
- latest_mod_activity: Time.at(updated_mod_json['latest_mod_activity']).utc
242
- )
243
- end
350
+ { period: nexus_mods_period }
244
351
  end
245
352
 
246
353
  end
@@ -5,7 +5,7 @@ module NexusModsTest
5
5
  module Games
6
6
 
7
7
  # Test game with id 100
8
- def json_game100
8
+ def self.json_game100
9
9
  {
10
10
  'id' => 100,
11
11
  'name' => 'Morrowind',
@@ -35,8 +35,12 @@ module NexusModsTest
35
35
  }
36
36
  end
37
37
 
38
+ def json_game100
39
+ Games.json_game100
40
+ end
41
+
38
42
  # Test game with id 101
39
- def json_game101
43
+ def self.json_game101
40
44
  {
41
45
  'id' => 101,
42
46
  'name' => 'Oblivion',
@@ -66,6 +70,10 @@ module NexusModsTest
66
70
  }
67
71
  end
68
72
 
73
+ def json_game101
74
+ Games.json_game101
75
+ end
76
+
69
77
  # Expect a game to be the test game of id 100
70
78
  #
71
79
  # Parameters::
@@ -5,7 +5,7 @@ module NexusModsTest
5
5
  module ModFiles
6
6
 
7
7
  # Test mod file with id 2472
8
- def json_mod_file2472
8
+ def self.json_mod_file2472
9
9
  {
10
10
  'id' => [
11
11
  2472,
@@ -32,8 +32,12 @@ module NexusModsTest
32
32
  }
33
33
  end
34
34
 
35
+ def json_mod_file2472
36
+ ModFiles.json_mod_file2472
37
+ end
38
+
35
39
  # Test mod file with id 2487
36
- def json_mod_file2487
40
+ def self.json_mod_file2487
37
41
  {
38
42
  'id' => [
39
43
  2487,
@@ -60,6 +64,10 @@ module NexusModsTest
60
64
  }
61
65
  end
62
66
 
67
+ def json_mod_file2487
68
+ ModFiles.json_mod_file2487
69
+ end
70
+
63
71
  # Expect a mod's file to be the example one with id 2472
64
72
  #
65
73
  # Parameters::
@@ -5,7 +5,7 @@ module NexusModsTest
5
5
  module ModUpdates
6
6
 
7
7
  # Test mod updates with id 2014
8
- def json_mod_updates2014
8
+ def self.json_mod_updates2014
9
9
  {
10
10
  'mod_id' => 2014,
11
11
  'latest_file_update' => 1_655_813_855,
@@ -13,8 +13,12 @@ module NexusModsTest
13
13
  }
14
14
  end
15
15
 
16
+ def json_mod_updates2014
17
+ ModUpdates.json_mod_updates2014
18
+ end
19
+
16
20
  # Test mod updates with id 100
17
- def json_mod_updates100
21
+ def self.json_mod_updates100
18
22
  {
19
23
  'mod_id' => 100,
20
24
  'latest_file_update' => 1_681_143_964,
@@ -22,6 +26,10 @@ module NexusModsTest
22
26
  }
23
27
  end
24
28
 
29
+ def json_mod_updates100
30
+ ModUpdates.json_mod_updates100
31
+ end
32
+
25
33
  # Expect a mod's updates to be the example one with id 2014
26
34
  #
27
35
  # Parameters::
@@ -5,7 +5,7 @@ module NexusModsTest
5
5
  module Mods
6
6
 
7
7
  # Example of JSON object returned by the API for a mod information, having all possible fields
8
- def json_complete_mod
8
+ def self.json_complete_mod
9
9
  {
10
10
  'name' => 'ApachiiSkyHair SSE',
11
11
  'summary' => 'New Female and Male Hairstyles for Humans, Elves and Orcs. Converted hair from Sims2 and Sims3.<br />Standalone version.',
@@ -44,8 +44,12 @@ module NexusModsTest
44
44
  }
45
45
  end
46
46
 
47
+ def json_complete_mod
48
+ Mods.json_complete_mod
49
+ end
50
+
47
51
  # Example of JSON object returned by the API for a mod information, having minimum fields
48
- def json_partial_mod
52
+ def self.json_partial_mod
49
53
  {
50
54
  'mod_downloads' => 13_634_545,
51
55
  'mod_unique_downloads' => 4_052_221,
@@ -75,6 +79,10 @@ module NexusModsTest
75
79
  }
76
80
  end
77
81
 
82
+ def json_partial_mod
83
+ Mods.json_partial_mod
84
+ end
85
+
78
86
  # Expect a mod to be the example complete one
79
87
  #
80
88
  # Parameters::
@@ -10,163 +10,291 @@ describe NexusMods do
10
10
  nexus_mods.api_limits
11
11
  end
12
12
 
13
- it 'caches games queries' do
14
- expect_validate_user
15
- expect_http_call_to(
16
- path: '/v1/games.json',
17
- json: [
18
- json_game100,
19
- json_game101
13
+ {
14
+ 'games' => {
15
+ expected_api_path: '/v1/games.json',
16
+ mocked_api_json: [
17
+ NexusModsTest::Factories::Games.json_game100,
18
+ NexusModsTest::Factories::Games.json_game101
19
+ ],
20
+ query: proc { |nm| nm.games },
21
+ query_without_cache: proc { |nm| nm.games(clear_cache: true) },
22
+ get_cache_timestamp: proc { |nm| nm.games_cache_timestamp },
23
+ set_cache_timestamp: proc { |nm, ts| nm.set_games_cache_timestamp(cache_timestamp: ts) },
24
+ expiry_cache_param: :games
25
+ },
26
+ 'mods' => {
27
+ expected_api_path: '/v1/games/skyrimspecialedition/mods/2014.json',
28
+ mocked_api_json: NexusModsTest::Factories::Mods.json_complete_mod,
29
+ query: proc { |nm| nm.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
30
+ query_without_cache: proc { |nm| nm.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014, clear_cache: true) },
31
+ get_cache_timestamp: proc { |nm| nm.mod_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
32
+ set_cache_timestamp: proc { |nm, ts| nm.set_mod_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014, cache_timestamp: ts) },
33
+ expiry_cache_param: :mod
34
+ },
35
+ 'mod files' => {
36
+ expected_api_path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
37
+ mocked_api_json: {
38
+ files: [
39
+ NexusModsTest::Factories::ModFiles.json_mod_file2472,
40
+ NexusModsTest::Factories::ModFiles.json_mod_file2487
41
+ ]
42
+ },
43
+ query: proc { |nm| nm.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
44
+ query_without_cache: proc { |nm| nm.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014, clear_cache: true) },
45
+ get_cache_timestamp: proc { |nm| nm.mod_files_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
46
+ set_cache_timestamp: proc { |nm, ts| nm.set_mod_files_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014, cache_timestamp: ts) },
47
+ expiry_cache_param: :mod_files
48
+ }
49
+ }.merge(
50
+ {
51
+ 'last day' => {
52
+ since: :one_day,
53
+ expected_url_params: 'period=1d'
54
+ },
55
+ 'last week' => {
56
+ since: :one_week,
57
+ expected_url_params: 'period=1w'
58
+ },
59
+ 'last month' => {
60
+ since: :one_month,
61
+ expected_url_params: 'period=1m'
62
+ }
63
+ }.to_h do |since, since_config|
64
+ [
65
+ "mod updates since #{since}",
66
+ {
67
+ expected_api_path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
68
+ mocked_api_json: [
69
+ NexusModsTest::Factories::ModUpdates.json_mod_updates2014,
70
+ NexusModsTest::Factories::ModUpdates.json_mod_updates100
71
+ ],
72
+ query: proc { |nm| nm.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since]) },
73
+ query_without_cache: proc { |nm| nm.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since], clear_cache: true) },
74
+ get_cache_timestamp: proc { |nm| nm.updated_mods_cache_timestamp(game_domain_name: 'skyrimspecialedition', since: since_config[:since]) },
75
+ set_cache_timestamp: proc { |nm, ts| nm.set_updated_mods_cache_timestamp(game_domain_name: 'skyrimspecialedition', since: since_config[:since], cache_timestamp: ts) }
76
+ }
20
77
  ]
21
- )
22
- games = nexus_mods.games
23
- expect(nexus_mods.games).to eq games
24
- end
78
+ end
79
+ ).each do |resource, resource_config|
25
80
 
26
- it 'does not cache games queries if asked' do
27
- expect_validate_user
28
- expect_http_call_to(
29
- path: '/v1/games.json',
30
- json: [
31
- json_game100,
32
- json_game101
33
- ],
34
- times: 2
35
- )
36
- games = nexus_mods.games
37
- expect(nexus_mods.games(clear_cache: true)).to eq games
38
- end
81
+ context "when testing #{resource}" do
39
82
 
40
- it 'caches mod queries' do
41
- expect_validate_user
42
- expect_http_call_to(
43
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
44
- json: json_complete_mod
45
- )
46
- mod = nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
47
- expect(nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq mod
48
- end
83
+ it 'caches API queries' do
84
+ expect_validate_user
85
+ expect_http_call_to(
86
+ path: resource_config[:expected_api_path],
87
+ json: resource_config[:mocked_api_json]
88
+ )
89
+ resource = resource_config[:query].call(nexus_mods)
90
+ expect(resource_config[:query].call(nexus_mods)).to eq resource
91
+ end
49
92
 
50
- it 'does not cache mod queries if asked' do
51
- expect_validate_user
52
- expect_http_call_to(
53
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
54
- json: json_complete_mod,
55
- times: 2
56
- )
57
- mod = nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
58
- expect(nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014, clear_cache: true)).to eq mod
59
- end
93
+ it 'does not cache API queries if asked' do
94
+ expect_validate_user
95
+ expect_http_call_to(
96
+ path: resource_config[:expected_api_path],
97
+ json: resource_config[:mocked_api_json],
98
+ times: 2
99
+ )
100
+ resource = resource_config[:query].call(nexus_mods)
101
+ expect(resource_config[:query_without_cache].call(nexus_mods)).to eq resource
102
+ end
60
103
 
61
- it 'caches mod files queries' do
62
- expect_validate_user
63
- expect_http_call_to(
64
- path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
65
- json: { files: [json_mod_file2472, json_mod_file2487] }
66
- )
67
- mod_files = nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
68
- expect(nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq mod_files
69
- end
104
+ if resource_config[:expiry_cache_param]
70
105
 
71
- it 'does not cache mod files queries if asked' do
72
- expect_validate_user
73
- expect_http_call_to(
74
- path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
75
- json: { files: [json_mod_file2472, json_mod_file2487] },
76
- times: 2
77
- )
78
- mod_files = nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
79
- expect(nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014, clear_cache: true)).to eq mod_files
80
- end
106
+ it 'expires API queries cache' do
107
+ expect_validate_user
108
+ expect_http_call_to(
109
+ path: resource_config[:expected_api_path],
110
+ json: resource_config[:mocked_api_json],
111
+ times: 2
112
+ )
113
+ nexus_mods_instance = nexus_mods(api_cache_expiry: { resource_config[:expiry_cache_param] => 1 })
114
+ resource = resource_config[:query].call(nexus_mods_instance)
115
+ sleep 2
116
+ expect(resource_config[:query].call(nexus_mods_instance)).to eq resource
117
+ end
81
118
 
82
- {
83
- 'last day' => {
84
- since: :one_day,
85
- expected_url_params: 'period=1d'
86
- },
87
- 'last week' => {
88
- since: :one_week,
89
- expected_url_params: 'period=1w'
90
- },
91
- 'last month' => {
92
- since: :one_month,
93
- expected_url_params: 'period=1m'
94
- }
95
- }.each do |since, since_config|
119
+ end
96
120
 
97
- context "when testing updated months since #{since}" do
121
+ it 'stores no timestamp of the data stored in the API cache before fetching data' do
122
+ expect_validate_user
123
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_nil
124
+ end
98
125
 
99
- it 'caches mod updates queries' do
126
+ it 'retrieves the timestamp of the data stored in the API cache' do
100
127
  expect_validate_user
101
128
  expect_http_call_to(
102
- path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
103
- json: [
104
- json_mod_updates2014,
105
- json_mod_updates100
106
- ]
129
+ path: resource_config[:expected_api_path],
130
+ json: resource_config[:mocked_api_json]
107
131
  )
108
- mod_updates = nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since])
109
- expect(nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since])).to eq mod_updates
132
+ before = Time.now
133
+ resource_config[:query].call(nexus_mods)
134
+ after = Time.now
135
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_between(before, after)
110
136
  end
111
137
 
112
- it 'does not cache mod updates queries if asked' do
138
+ it 'changes manually the timestamp of the data stored in the API cache' do
113
139
  expect_validate_user
114
140
  expect_http_call_to(
115
- path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
116
- json: [
117
- json_mod_updates2014,
118
- json_mod_updates100
119
- ],
141
+ path: resource_config[:expected_api_path],
142
+ json: resource_config[:mocked_api_json]
143
+ )
144
+ resource_config[:query].call(nexus_mods)
145
+ new_cache_timestamp = Time.parse('2023-01-12 11:22:33 UTC')
146
+ resource_config[:set_cache_timestamp].call(nexus_mods, new_cache_timestamp)
147
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to eq new_cache_timestamp
148
+ end
149
+
150
+ it 'retrieves the timestamp of the games data stored in the cache even after cache is used' do
151
+ expect_validate_user
152
+ expect_http_call_to(
153
+ path: resource_config[:expected_api_path],
154
+ json: resource_config[:mocked_api_json]
155
+ )
156
+ before = Time.now
157
+ resource_config[:query].call(nexus_mods)
158
+ after = Time.now
159
+ resource_config[:query].call(nexus_mods)
160
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_between(before, after)
161
+ end
162
+
163
+ it 'retrieves the timestamp of the games data stored in the cache even after cache is persisted' do
164
+ with_api_cache_file do |api_cache_file|
165
+ expect_validate_user(times: 2)
166
+ expect_http_call_to(
167
+ path: resource_config[:expected_api_path],
168
+ json: resource_config[:mocked_api_json]
169
+ )
170
+ before = Time.now
171
+ resource_config[:query].call(nexus_mods(api_cache_file:))
172
+ after = Time.now
173
+ reset_nexus_mods
174
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods(api_cache_file:))).to be_between(before, after)
175
+ end
176
+ end
177
+
178
+ it 'persists the cache timestamp that has been changed manually' do
179
+ with_api_cache_file do |api_cache_file|
180
+ expect_validate_user(times: 2)
181
+ expect_http_call_to(
182
+ path: resource_config[:expected_api_path],
183
+ json: resource_config[:mocked_api_json]
184
+ )
185
+ resource_config[:query].call(nexus_mods(api_cache_file:))
186
+ new_cache_timestamp = Time.parse('2023-01-12 11:22:33 UTC')
187
+ resource_config[:set_cache_timestamp].call(nexus_mods, new_cache_timestamp)
188
+ reset_nexus_mods
189
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods(api_cache_file:))).to eq new_cache_timestamp
190
+ end
191
+ end
192
+
193
+ it 'updates the timestamp of the data stored in the API cache by forcing an API query' do
194
+ expect_validate_user
195
+ expect_http_call_to(
196
+ path: resource_config[:expected_api_path],
197
+ json: resource_config[:mocked_api_json],
120
198
  times: 2
121
199
  )
122
- mod_updates = nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since])
123
- expect(nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since], clear_cache: true)).to eq mod_updates
200
+ resource_config[:query].call(nexus_mods)
201
+ sleep 1
202
+ before = Time.now
203
+ resource_config[:query_without_cache].call(nexus_mods)
204
+ after = Time.now
205
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_between(before, after)
124
206
  end
125
207
 
126
- end
208
+ it 'updates the timestamp of the data stored in the API cache by updating manually the cache timestamp' do
209
+ expect_validate_user
210
+ expect_http_call_to(
211
+ path: resource_config[:expected_api_path],
212
+ json: resource_config[:mocked_api_json],
213
+ times: 2
214
+ )
215
+ resource_config[:query].call(nexus_mods)
216
+ resource_config[:set_cache_timestamp].call(nexus_mods, Time.now.utc - (365 * 24 * 60 * 60))
217
+ before = Time.now
218
+ resource_config[:query].call(nexus_mods)
219
+ after = Time.now
220
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_between(before, after)
221
+ end
127
222
 
128
- end
223
+ context 'when testing cache persistence in files' do
129
224
 
130
- it 'expires games queries cache' do
131
- expect_validate_user
132
- expect_http_call_to(
133
- path: '/v1/games.json',
134
- json: [
135
- json_game100,
136
- json_game101
137
- ],
138
- times: 2
139
- )
140
- nexus_mods_instance = nexus_mods(api_cache_expiry: { games: 1 })
141
- games = nexus_mods_instance.games
142
- sleep 2
143
- expect(nexus_mods_instance.games).to eq games
144
- end
225
+ it 'persists API cache in a file' do
226
+ with_api_cache_file do |api_cache_file|
227
+ expect_validate_user
228
+ expect_http_call_to(
229
+ path: resource_config[:expected_api_path],
230
+ json: resource_config[:mocked_api_json]
231
+ )
232
+ resource_config[:query].call(nexus_mods(api_cache_file:))
233
+ expect(File.exist?(api_cache_file)).to be true
234
+ expect(File.size(api_cache_file)).to be > 0
235
+ end
236
+ end
145
237
 
146
- it 'expires mod queries cache' do
147
- expect_validate_user
148
- expect_http_call_to(
149
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
150
- json: json_complete_mod,
151
- times: 2
152
- )
153
- nexus_mods_instance = nexus_mods(api_cache_expiry: { mod: 1 })
154
- mod = nexus_mods_instance.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
155
- sleep 2
156
- expect(nexus_mods_instance.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq mod
157
- end
238
+ it 'uses API cache from a file' do
239
+ with_api_cache_file do |api_cache_file|
240
+ expect_validate_user(times: 2)
241
+ expect_http_call_to(
242
+ path: resource_config[:expected_api_path],
243
+ json: resource_config[:mocked_api_json]
244
+ )
245
+ # Generate the cache first
246
+ resource = resource_config[:query].call(nexus_mods(api_cache_file:))
247
+ # Force a new instance of NexusMods API to run
248
+ reset_nexus_mods
249
+ expect(resource_config[:query].call(nexus_mods(api_cache_file:))).to eq resource
250
+ end
251
+ end
252
+
253
+ if resource_config[:expiry_cache_param]
254
+
255
+ it 'uses API cache from a file, taking expiry time into account' do
256
+ with_api_cache_file do |api_cache_file|
257
+ expect_validate_user(times: 2)
258
+ expect_http_call_to(
259
+ path: resource_config[:expected_api_path],
260
+ json: resource_config[:mocked_api_json],
261
+ times: 2
262
+ )
263
+ # Generate the cache first
264
+ resource = resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 1 }))
265
+ # Force a new instance of NexusMods API to run
266
+ reset_nexus_mods
267
+ sleep 2
268
+ # As the expiry time is 1 second, then the cache should still be invalidated
269
+ expect(resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 1 }))).to eq resource
270
+ end
271
+ end
272
+
273
+ it 'uses API cache from a file, taking expiry time of the new process into account' do
274
+ with_api_cache_file do |api_cache_file|
275
+ expect_validate_user(times: 2)
276
+ expect_http_call_to(
277
+ path: resource_config[:expected_api_path],
278
+ json: resource_config[:mocked_api_json],
279
+ times: 2
280
+ )
281
+ # Generate the cache first
282
+ resource = resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 10 }))
283
+ # Force a new instance of NexusMods API to run
284
+ reset_nexus_mods
285
+ sleep 2
286
+ # Even if the expiry time was 10 seconds while fetching the resource,
287
+ # if we decide it has to be 1 second now then it has to be invalidated.
288
+ expect(resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 1 }))).to eq resource
289
+ end
290
+ end
291
+
292
+ end
293
+
294
+ end
295
+
296
+ end
158
297
 
159
- it 'expires mod files queries cache' do
160
- expect_validate_user
161
- expect_http_call_to(
162
- path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
163
- json: { files: [json_mod_file2472, json_mod_file2487] },
164
- times: 2
165
- )
166
- nexus_mods_instance = nexus_mods(api_cache_expiry: { mod_files: 1 })
167
- mod_files = nexus_mods_instance.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
168
- sleep 2
169
- expect(nexus_mods_instance.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq mod_files
170
298
  end
171
299
 
172
300
  it 'only clears the cache of the wanted resource' do
@@ -190,83 +318,6 @@ describe NexusMods do
190
318
 
191
319
  context 'with file persistence' do
192
320
 
193
- it 'persists API cache in a file' do
194
- with_api_cache_file do |api_cache_file|
195
- expect_validate_user
196
- expect_http_call_to(
197
- path: '/v1/games.json',
198
- json: [
199
- json_game100,
200
- json_game101
201
- ]
202
- )
203
- nexus_mods(api_cache_file:).games
204
- expect(File.exist?(api_cache_file)).to be true
205
- expect(File.size(api_cache_file)).to be > 0
206
- end
207
- end
208
-
209
- it 'uses API cache from a file' do
210
- with_api_cache_file do |api_cache_file|
211
- expect_validate_user(times: 2)
212
- expect_http_call_to(
213
- path: '/v1/games.json',
214
- json: [
215
- json_game100,
216
- json_game101
217
- ]
218
- )
219
- # Generate the cache first
220
- games = nexus_mods(api_cache_file:).games
221
- # Force a new instance of NexusMods API to run
222
- reset_nexus_mods
223
- expect(nexus_mods(api_cache_file:).games).to eq games
224
- end
225
- end
226
-
227
- it 'uses API cache from a file, taking expiry time into account' do
228
- with_api_cache_file do |api_cache_file|
229
- expect_validate_user(times: 2)
230
- expect_http_call_to(
231
- path: '/v1/games.json',
232
- json: [
233
- json_game100,
234
- json_game101
235
- ],
236
- times: 2
237
- )
238
- # Generate the cache first
239
- games = nexus_mods(api_cache_file:, api_cache_expiry: { games: 1 }).games
240
- # Force a new instance of NexusMods API to run
241
- reset_nexus_mods
242
- sleep 2
243
- # As the expiry time is 1 second, then the cache should still be invalidated
244
- expect(nexus_mods(api_cache_file:, api_cache_expiry: { games: 1 }).games).to eq games
245
- end
246
- end
247
-
248
- it 'uses API cache from a file, taking expiry time of the new process into account' do
249
- with_api_cache_file do |api_cache_file|
250
- expect_validate_user(times: 2)
251
- expect_http_call_to(
252
- path: '/v1/games.json',
253
- json: [
254
- json_game100,
255
- json_game101
256
- ],
257
- times: 2
258
- )
259
- # Generate the cache first
260
- games = nexus_mods(api_cache_file:, api_cache_expiry: { games: 10 }).games
261
- # Force a new instance of NexusMods API to run
262
- reset_nexus_mods
263
- sleep 2
264
- # Even if the expiry time was 10 seconds while fetching the resource,
265
- # if we decide it has to be 1 second now then it has to be invalidated.
266
- expect(nexus_mods(api_cache_file:, api_cache_expiry: { games: 1 }).games).to eq games
267
- end
268
- end
269
-
270
321
  it 'completes the API cache from a file' do
271
322
  with_api_cache_file do |api_cache_file|
272
323
  expect_validate_user(times: 3)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nexus_mods
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Muriel Salvan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-11 00:00:00.000000000 Z
11
+ date: 2023-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday