nexus_mods 2.1.0 → 2.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 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