nexus_mods 2.1.0 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac9b76be57a989b2cd00cb120912dc8360fc926e76589769b725879f582e7d82
4
- data.tar.gz: 61342738bb1678bdbbbf350bf645b90210c55c8b53a63bdbb9cc755ca6f146bd
3
+ metadata.gz: a25aa2568a5be1e1a8592196344fde1ca68bcf814b67a51cf6921f7ed64c0f70
4
+ data.tar.gz: cdb50c9b1394af620109ea82439a3254b96ad223286ae1f797fcbc37c32a1a58
5
5
  SHA512:
6
- metadata.gz: 8ad0436dd7917b94ebbedaa26e7f943a92520b0336413730e273108849b7f2659e026954ad1a68570e99c27ad6654a6c970be208ec500078a4490e65930ce896
7
- data.tar.gz: 67a49c5e42db887253894726eed4b7ff3adde7f6da0f31d123ce6587117abb99d80a26e9a0db69bb541368fec4c6561b3b0a9564a8fe5ac27a412b0fb3c0173e
6
+ metadata.gz: b7f49167041ee67f3855c0a474cdba90a744660f424356943516016628f1e65f37154c7c6b596eca2b7ac2430eae4b0726db6a45d3685b750bdf7a33e8674d2e
7
+ data.tar.gz: ce904391c82e93f029a41a47d3e7e391af8dac23625ac38eda01b2066ea9261ae3ff7bbea2aed23fbbba9b124846be80640bdbbf94ef98291b1b020824ec74df
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # [v2.2.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.1.0...v2.2.0) (2023-04-12 12:56:31)
2
+
3
+ ### Features
4
+
5
+ * [[Feature] Add possibility to retrieve the cache timestamp of API resources](https://github.com/Muriel-Salvan/nexus_mods/commit/ba67323a32472deb1f063a51683ff9282ef96981)
6
+
1
7
  # [v2.1.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.0.1...v2.1.0) (2023-04-11 18:24:04)
2
8
 
3
9
  ### Features
@@ -60,6 +60,22 @@ 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
+
63
79
  # Send an HTTP request to the API and get back the HTTP response
64
80
  #
65
81
  # Parameters::
@@ -99,6 +115,18 @@ class NexusMods
99
115
  # ApiClient: The API client to be used by the cacheable adapter (singleton pattern)
100
116
  attr_accessor :api_client
101
117
 
118
+ # Get the cache key to be used for a given API query
119
+ #
120
+ # Parameters::
121
+ # * *path* (String): API path to contact (from v1/ and without .json)
122
+ # * *parameters* (Hash<Symbol,Object>): Optional parameters to add to the path [default: {}]
123
+ # * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
124
+ # Result::
125
+ # * String: The corresponding cache key
126
+ def cache_key(path, parameters:, verb:)
127
+ "#{verb}/#{path}#{parameters.empty? ? '' : "/#{parameters.map { |param, value| "#{param}=#{value}" }.sort.join('/')}"}"
128
+ end
129
+
102
130
  end
103
131
 
104
132
  @api_client = nil
@@ -142,9 +170,7 @@ class NexusMods
142
170
  cacheable_api(
143
171
  :cached_api,
144
172
  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
- }"
173
+ cache_key(method_args.first, parameters: method_kwargs[:parameters], verb: method_kwargs[:verb])
148
174
  end,
149
175
  expiry_from_key: proc do |key|
150
176
  # 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.2.0'
4
4
 
5
5
  end
data/lib/nexus_mods.rb CHANGED
@@ -143,6 +143,14 @@ 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
+
146
154
  # Get information about a mod
147
155
  #
148
156
  # Parameters::
@@ -183,6 +191,17 @@ class NexusMods
183
191
  )
184
192
  end
185
193
 
194
+ # Get the cached timestamp of a mod information
195
+ #
196
+ # Parameters::
197
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
198
+ # * *mod_id* (Integer): The mod ID [default: @mod_id]
199
+ # Result::
200
+ # * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
201
+ def mod_cache_timestamp(game_domain_name: @game_domain_name, mod_id: @mod_id)
202
+ @api_client.api_cache_timestamp("games/#{game_domain_name}/mods/#{mod_id}")
203
+ end
204
+
186
205
  # Get files belonging to a mod
187
206
  #
188
207
  # Parameters::
@@ -214,6 +233,17 @@ class NexusMods
214
233
  end
215
234
  end
216
235
 
236
+ # Get the cached timestamp of a mod files information
237
+ #
238
+ # Parameters::
239
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
240
+ # * *mod_id* (Integer): The mod ID [default: @mod_id]
241
+ # Result::
242
+ # * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
243
+ def mod_files_cache_timestamp(game_domain_name: @game_domain_name, mod_id: @mod_id)
244
+ @api_client.api_cache_timestamp("games/#{game_domain_name}/mods/#{mod_id}/files")
245
+ end
246
+
217
247
  # Get a list of updated mod ids since a given time
218
248
  #
219
249
  # Parameters::
@@ -227,6 +257,43 @@ class NexusMods
227
257
  # Result::
228
258
  # * Array<ModUpdates>: Mod's updates information
229
259
  def updated_mods(game_domain_name: @game_domain_name, since: :one_day, clear_cache: false)
260
+ @api_client.api("games/#{game_domain_name}/mods/updated", parameters: period_to_url_params(since), clear_cache:).map do |updated_mod_json|
261
+ Api::ModUpdates.new(
262
+ mod_id: updated_mod_json['mod_id'],
263
+ latest_file_update: Time.at(updated_mod_json['latest_file_update']).utc,
264
+ latest_mod_activity: Time.at(updated_mod_json['latest_mod_activity']).utc
265
+ )
266
+ end
267
+ end
268
+
269
+ # Get the cached timestamp of a mod files information
270
+ #
271
+ # Parameters::
272
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
273
+ # * *since* (Symbol): The time from which we look for updated mods [default: :one_day]
274
+ # Possible values are:
275
+ # * *one_day*: Since 1 day
276
+ # * *one_week*: Since 1 week
277
+ # * *one_month*: Since 1 month
278
+ # Result::
279
+ # * Time or nil: Freshness time of the data in the API cache, or nil if not present in the cache
280
+ def updated_mods_cache_timestamp(game_domain_name: @game_domain_name, since: :one_day)
281
+ @api_client.api_cache_timestamp("games/#{game_domain_name}/mods/updated", parameters: period_to_url_params(since))
282
+ end
283
+
284
+ private
285
+
286
+ # Get the URL parameters from the required period
287
+ #
288
+ # Parameters::
289
+ # * *since* (Symbol): The time from which we look for updated mods
290
+ # Possible values are:
291
+ # * *one_day*: Since 1 day
292
+ # * *one_week*: Since 1 week
293
+ # * *one_month*: Since 1 month
294
+ # Result::
295
+ # * Hash<Symbol,Object>: Corresponding URL parameters
296
+ def period_to_url_params(since)
230
297
  nexus_mods_period = {
231
298
  one_day: '1d',
232
299
  one_week: '1w',
@@ -234,13 +301,7 @@ class NexusMods
234
301
  }[since]
235
302
  raise "Unknown time stamp: #{since}" if nexus_mods_period.nil?
236
303
 
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
304
+ { period: nexus_mods_period }
244
305
  end
245
306
 
246
307
  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,245 @@ 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
+ expiry_cache_param: :games
24
+ },
25
+ 'mods' => {
26
+ expected_api_path: '/v1/games/skyrimspecialedition/mods/2014.json',
27
+ mocked_api_json: NexusModsTest::Factories::Mods.json_complete_mod,
28
+ query: proc { |nm| nm.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
29
+ query_without_cache: proc { |nm| nm.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014, clear_cache: true) },
30
+ get_cache_timestamp: proc { |nm| nm.mod_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
31
+ expiry_cache_param: :mod
32
+ },
33
+ 'mod files' => {
34
+ expected_api_path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
35
+ mocked_api_json: {
36
+ files: [
37
+ NexusModsTest::Factories::ModFiles.json_mod_file2472,
38
+ NexusModsTest::Factories::ModFiles.json_mod_file2487
39
+ ]
40
+ },
41
+ query: proc { |nm| nm.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
42
+ query_without_cache: proc { |nm| nm.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014, clear_cache: true) },
43
+ get_cache_timestamp: proc { |nm| nm.mod_files_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014) },
44
+ expiry_cache_param: :mod_files
45
+ }
46
+ }.merge(
47
+ {
48
+ 'last day' => {
49
+ since: :one_day,
50
+ expected_url_params: 'period=1d'
51
+ },
52
+ 'last week' => {
53
+ since: :one_week,
54
+ expected_url_params: 'period=1w'
55
+ },
56
+ 'last month' => {
57
+ since: :one_month,
58
+ expected_url_params: 'period=1m'
59
+ }
60
+ }.to_h do |since, since_config|
61
+ [
62
+ "mod updates since #{since}",
63
+ {
64
+ expected_api_path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
65
+ mocked_api_json: [
66
+ NexusModsTest::Factories::ModUpdates.json_mod_updates2014,
67
+ NexusModsTest::Factories::ModUpdates.json_mod_updates100
68
+ ],
69
+ query: proc { |nm| nm.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since]) },
70
+ query_without_cache: proc { |nm| nm.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since], clear_cache: true) },
71
+ get_cache_timestamp: proc { |nm| nm.updated_mods_cache_timestamp(game_domain_name: 'skyrimspecialedition', since: since_config[:since]) }
72
+ }
20
73
  ]
21
- )
22
- games = nexus_mods.games
23
- expect(nexus_mods.games).to eq games
24
- end
74
+ end
75
+ ).each do |resource, resource_config|
25
76
 
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
77
+ context "when testing #{resource}" do
39
78
 
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
79
+ it 'caches API queries' do
80
+ expect_validate_user
81
+ expect_http_call_to(
82
+ path: resource_config[:expected_api_path],
83
+ json: resource_config[:mocked_api_json]
84
+ )
85
+ resource = resource_config[:query].call(nexus_mods)
86
+ expect(resource_config[:query].call(nexus_mods)).to eq resource
87
+ end
49
88
 
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
89
+ it 'does not cache API queries if asked' do
90
+ expect_validate_user
91
+ expect_http_call_to(
92
+ path: resource_config[:expected_api_path],
93
+ json: resource_config[:mocked_api_json],
94
+ times: 2
95
+ )
96
+ resource = resource_config[:query].call(nexus_mods)
97
+ expect(resource_config[:query_without_cache].call(nexus_mods)).to eq resource
98
+ end
60
99
 
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
100
+ if resource_config[:expiry_cache_param]
70
101
 
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
102
+ it 'expires API queries cache' do
103
+ expect_validate_user
104
+ expect_http_call_to(
105
+ path: resource_config[:expected_api_path],
106
+ json: resource_config[:mocked_api_json],
107
+ times: 2
108
+ )
109
+ nexus_mods_instance = nexus_mods(api_cache_expiry: { resource_config[:expiry_cache_param] => 1 })
110
+ resource = resource_config[:query].call(nexus_mods_instance)
111
+ sleep 2
112
+ expect(resource_config[:query].call(nexus_mods_instance)).to eq resource
113
+ end
81
114
 
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|
115
+ end
96
116
 
97
- context "when testing updated months since #{since}" do
117
+ it 'stores no timestamp of the data stored in the API cache before fetching data' do
118
+ expect_validate_user
119
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_nil
120
+ end
98
121
 
99
- it 'caches mod updates queries' do
122
+ it 'retrieves the timestamp of the data stored in the API cache' do
100
123
  expect_validate_user
101
124
  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
- ]
125
+ path: resource_config[:expected_api_path],
126
+ json: resource_config[:mocked_api_json]
107
127
  )
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
128
+ before = Time.now
129
+ resource_config[:query].call(nexus_mods)
130
+ after = Time.now
131
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_between(before, after)
110
132
  end
111
133
 
112
- it 'does not cache mod updates queries if asked' do
134
+ it 'retrieves the timestamp of the games data stored in the cache even after cache is used' do
113
135
  expect_validate_user
114
136
  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
- ],
137
+ path: resource_config[:expected_api_path],
138
+ json: resource_config[:mocked_api_json]
139
+ )
140
+ before = Time.now
141
+ resource_config[:query].call(nexus_mods)
142
+ after = Time.now
143
+ resource_config[:query].call(nexus_mods)
144
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_between(before, after)
145
+ end
146
+
147
+ it 'retrieves the timestamp of the games data stored in the cache even after cache is persisted' do
148
+ with_api_cache_file do |api_cache_file|
149
+ expect_validate_user(times: 2)
150
+ expect_http_call_to(
151
+ path: resource_config[:expected_api_path],
152
+ json: resource_config[:mocked_api_json]
153
+ )
154
+ before = Time.now
155
+ resource_config[:query].call(nexus_mods(api_cache_file:))
156
+ after = Time.now
157
+ reset_nexus_mods
158
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods(api_cache_file:))).to be_between(before, after)
159
+ end
160
+ end
161
+
162
+ it 'updates the timestamp of the data stored in the API cache' do
163
+ expect_validate_user
164
+ expect_http_call_to(
165
+ path: resource_config[:expected_api_path],
166
+ json: resource_config[:mocked_api_json],
120
167
  times: 2
121
168
  )
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
169
+ resource_config[:query].call(nexus_mods)
170
+ sleep 1
171
+ before = Time.now
172
+ resource_config[:query_without_cache].call(nexus_mods)
173
+ after = Time.now
174
+ expect(resource_config[:get_cache_timestamp].call(nexus_mods)).to be_between(before, after)
124
175
  end
125
176
 
126
- end
177
+ context 'when testing cache persistence in files' do
127
178
 
128
- end
179
+ it 'persists API cache in a file' do
180
+ with_api_cache_file do |api_cache_file|
181
+ expect_validate_user
182
+ expect_http_call_to(
183
+ path: resource_config[:expected_api_path],
184
+ json: resource_config[:mocked_api_json]
185
+ )
186
+ resource_config[:query].call(nexus_mods(api_cache_file:))
187
+ expect(File.exist?(api_cache_file)).to be true
188
+ expect(File.size(api_cache_file)).to be > 0
189
+ end
190
+ end
129
191
 
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
192
+ it 'uses API cache from a file' do
193
+ with_api_cache_file do |api_cache_file|
194
+ expect_validate_user(times: 2)
195
+ expect_http_call_to(
196
+ path: resource_config[:expected_api_path],
197
+ json: resource_config[:mocked_api_json]
198
+ )
199
+ # Generate the cache first
200
+ resource = resource_config[:query].call(nexus_mods(api_cache_file:))
201
+ # Force a new instance of NexusMods API to run
202
+ reset_nexus_mods
203
+ expect(resource_config[:query].call(nexus_mods(api_cache_file:))).to eq resource
204
+ end
205
+ end
145
206
 
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
207
+ if resource_config[:expiry_cache_param]
208
+
209
+ it 'uses API cache from a file, taking expiry time into account' do
210
+ with_api_cache_file do |api_cache_file|
211
+ expect_validate_user(times: 2)
212
+ expect_http_call_to(
213
+ path: resource_config[:expected_api_path],
214
+ json: resource_config[:mocked_api_json],
215
+ times: 2
216
+ )
217
+ # Generate the cache first
218
+ resource = resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 1 }))
219
+ # Force a new instance of NexusMods API to run
220
+ reset_nexus_mods
221
+ sleep 2
222
+ # As the expiry time is 1 second, then the cache should still be invalidated
223
+ expect(resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 1 }))).to eq resource
224
+ end
225
+ end
226
+
227
+ it 'uses API cache from a file, taking expiry time of the new process 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: resource_config[:expected_api_path],
232
+ json: resource_config[:mocked_api_json],
233
+ times: 2
234
+ )
235
+ # Generate the cache first
236
+ resource = resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 10 }))
237
+ # Force a new instance of NexusMods API to run
238
+ reset_nexus_mods
239
+ sleep 2
240
+ # Even if the expiry time was 10 seconds while fetching the resource,
241
+ # if we decide it has to be 1 second now then it has to be invalidated.
242
+ expect(resource_config[:query].call(nexus_mods(api_cache_file:, api_cache_expiry: { resource_config[:expiry_cache_param] => 1 }))).to eq resource
243
+ end
244
+ end
245
+
246
+ end
247
+
248
+ end
249
+
250
+ end
158
251
 
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
252
  end
171
253
 
172
254
  it 'only clears the cache of the wanted resource' do
@@ -190,83 +272,6 @@ describe NexusMods do
190
272
 
191
273
  context 'with file persistence' do
192
274
 
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
275
  it 'completes the API cache from a file' do
271
276
  with_api_cache_file do |api_cache_file|
272
277
  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.2.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