nexus_mods 2.3.0 → 2.5.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: 0d5efb3b98f3d8b66565eff56b7b6360fc99bae69a256423e6eae391eb3cd676
4
- data.tar.gz: 7091d96c8a79ec591e805bcc09958199187b5f0afd73de85b5da314c2e24381a
3
+ metadata.gz: 6f6c2d6e72c966955e6153a3381458fba4f56c36deac811d125edc962046b5a0
4
+ data.tar.gz: d0a2ec92da860dbb53b608e1de22762b3562bac1d4fe1333066f868612983747
5
5
  SHA512:
6
- metadata.gz: 1485e5270d8fcffb7a2ebfec15848f0f2af3a2575beef6b2d1c4746af682bd13142d15f140adae1c9e7c15d731d85d63d01ec135f28600e73d58249bc130990e
7
- data.tar.gz: eccb9f4312f63a8c8fc459592c4c0c618378f59a69a261512981e890c0bfffa8c2044bafbcd16da9ec9c8d3b980a53a2e5322a89c5eece959240cafb7ac05e19
6
+ metadata.gz: d159a8824c0c6b723ec7f3361f1df52162cebf3d4f91833e072ceeb94089fb84d102d46d597d0ee24e92c6983e56ddcd87a95a6a4ffbb282331a7f3aae94cc84
7
+ data.tar.gz: 9dda81c7f0f593190547ed55aeff36a3040610d13ee59f7c5ff6ffed7df1d95d806685d9cf77e090e3a2410a24a516a32617b9fc047d59cb30f149607022aaba
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # [v2.5.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.4.0...v2.5.0) (2023-04-12 19:15:44)
2
+
3
+ ### Features
4
+
5
+ * [[Feature] Add links between resources](https://github.com/Muriel-Salvan/nexus_mods/commit/84c474b75f3ee1e4ac0c8bbda0d2ad25022f1f07)
6
+
7
+ # [v2.4.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.3.0...v2.4.0) (2023-04-12 17:23:29)
8
+
9
+ ### Features
10
+
11
+ * [[Feature] Add options and methods for mod and mod_files to query for updates to check data freshness](https://github.com/Muriel-Salvan/nexus_mods/commit/72a933a6ac22faae601fbdc547cc9e0ffa8908fc)
12
+
1
13
  # [v2.3.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v2.2.0...v2.3.0) (2023-04-12 15:08:22)
2
14
 
3
15
  ### Features
@@ -1,9 +1,11 @@
1
+ require 'nexus_mods/api/resource'
2
+
1
3
  class NexusMods
2
4
 
3
5
  module Api
4
6
 
5
7
  # Object giving the NexusMods API limits
6
- class ApiLimits
8
+ class ApiLimits < Resource
7
9
 
8
10
  attr_reader(
9
11
  *%i[
@@ -19,6 +21,7 @@ class NexusMods
19
21
  # Constructor
20
22
  #
21
23
  # Parameters::
24
+ # * *nexus_mods* (NexusMods): The NexusMods API instance that the resource can use to query for other resources
22
25
  # * *daily_limit* (Integer): The daily limit
23
26
  # * *daily_remaining* (Integer): The daily remaining
24
27
  # * *daily_reset* (Integer): The daily reset time
@@ -26,6 +29,7 @@ class NexusMods
26
29
  # * *hourly_remaining* (Integer): The hourly remaining
27
30
  # * *hourly_reset* (Integer): The hourly reset time
28
31
  def initialize(
32
+ nexus_mods:,
29
33
  daily_limit:,
30
34
  daily_remaining:,
31
35
  daily_reset:,
@@ -33,6 +37,7 @@ class NexusMods
33
37
  hourly_remaining:,
34
38
  hourly_reset:
35
39
  )
40
+ super(nexus_mods:)
36
41
  @daily_limit = daily_limit
37
42
  @daily_remaining = daily_remaining
38
43
  @daily_reset = daily_reset
@@ -1,9 +1,11 @@
1
+ require 'nexus_mods/api/resource'
2
+
1
3
  class NexusMods
2
4
 
3
5
  module Api
4
6
 
5
7
  # Categories defined for a game in NexusMods
6
- class Category
8
+ class Category < Resource
7
9
 
8
10
  attr_reader(
9
11
  *%i[
@@ -21,14 +23,17 @@ class NexusMods
21
23
  # Constructor
22
24
  #
23
25
  # Parameters::
26
+ # * *nexus_mods* (NexusMods): The NexusMods API instance that the resource can use to query for other resources
24
27
  # *id* (Integer): The category id
25
28
  # *name* (String): The category id
26
29
  # *parent_category* (Category or nil): The parent category, or nil if none [default: nil]
27
30
  def initialize(
31
+ nexus_mods:,
28
32
  id:,
29
33
  name:,
30
34
  parent_category: nil
31
35
  )
36
+ super(nexus_mods:)
32
37
  @id = id
33
38
  @name = name
34
39
  @parent_category = parent_category
@@ -1,3 +1,5 @@
1
+ require 'nexus_mods/api/resource'
2
+
1
3
  class NexusMods
2
4
 
3
5
  module Api
@@ -5,7 +7,7 @@ class NexusMods
5
7
  # A NexusMods game.
6
8
  # Attributes info can be taken from there:
7
9
  # * https://github.com/Nexus-Mods/node-nexus-api/blob/master/docs/interfaces/_types_.igameinfo.md
8
- class Game
10
+ class Game < Resource
9
11
 
10
12
  attr_reader(
11
13
  *%i[
@@ -29,6 +31,7 @@ class NexusMods
29
31
  # Constructor
30
32
  #
31
33
  # Parameters::
34
+ # * *nexus_mods* (NexusMods): The NexusMods API instance that the resource can use to query for other resources
32
35
  # * *id* (Integer): The game's id
33
36
  # * *name* (String): The game's name
34
37
  # * *forum_url* (String): The game's forum's URL
@@ -44,6 +47,7 @@ class NexusMods
44
47
  # * *mods_count* (Integer): The game's mods' count [default: 0]
45
48
  # * *categories* (Array<Category>): The list of game's categories [default: []]
46
49
  def initialize(
50
+ nexus_mods:,
47
51
  id:,
48
52
  name:,
49
53
  forum_url:,
@@ -59,6 +63,7 @@ class NexusMods
59
63
  mods_count: 0,
60
64
  categories: []
61
65
  )
66
+ super(nexus_mods:)
62
67
  @id = id
63
68
  @name = name
64
69
  @forum_url = forum_url
@@ -1,3 +1,5 @@
1
+ require 'nexus_mods/api/resource'
2
+
1
3
  class NexusMods
2
4
 
3
5
  module Api
@@ -5,7 +7,7 @@ class NexusMods
5
7
  # A NexusMods mod.
6
8
  # Attributes info can be taken from there:
7
9
  # * https://github.com/Nexus-Mods/node-nexus-api/blob/master/docs/interfaces/_types_.imodinfo.md
8
- class Mod
10
+ class Mod < Resource
9
11
 
10
12
  attr_reader(
11
13
  *%i[
@@ -36,6 +38,7 @@ class NexusMods
36
38
  # Constructor
37
39
  #
38
40
  # Parameters::
41
+ # * *nexus_mods* (NexusMods): The NexusMods API instance that the resource can use to query for other resources
39
42
  # * *uid* (Integer): The mod's uid
40
43
  # * *mod_id* (Integer): The mod's id
41
44
  # * *game_id* (Integer): The mod's game id
@@ -58,6 +61,7 @@ class NexusMods
58
61
  # * *unique_downloads_count* (Integer): The mod's unique downloads' count [default: 0]
59
62
  # * *endorsements_count* (Integer): The mod's endorsements' count [default: 0]
60
63
  def initialize(
64
+ nexus_mods:,
61
65
  uid:,
62
66
  mod_id:,
63
67
  game_id:,
@@ -80,6 +84,7 @@ class NexusMods
80
84
  unique_downloads_count: 0,
81
85
  endorsements_count: 0
82
86
  )
87
+ super(nexus_mods:)
83
88
  @uid = uid
84
89
  @mod_id = mod_id
85
90
  @game_id = game_id
@@ -134,6 +139,14 @@ class NexusMods
134
139
  @endorsements_count == other.endorsements_count
135
140
  end
136
141
 
142
+ # Get associated files information
143
+ #
144
+ # Result::
145
+ # * Array<ModFile>: The list of mod files information
146
+ def files
147
+ @nexus_mods.mod_files(game_domain_name: domain_name, mod_id:)
148
+ end
149
+
137
150
  end
138
151
 
139
152
  end
@@ -1,3 +1,5 @@
1
+ require 'nexus_mods/api/resource'
2
+
1
3
  class NexusMods
2
4
 
3
5
  module Api
@@ -5,10 +7,12 @@ class NexusMods
5
7
  # A NexusMods file.
6
8
  # Attributes info can be taken from there:
7
9
  # * https://github.com/Nexus-Mods/node-nexus-api/blob/master/docs/interfaces/_types_.ifileinfo.md
8
- class ModFile
10
+ class ModFile < Resource
9
11
 
10
12
  attr_reader(
11
13
  *%i[
14
+ game_domain_name
15
+ mod_id
12
16
  ids
13
17
  uid
14
18
  id
@@ -43,6 +47,9 @@ class NexusMods
43
47
  # Constructor
44
48
  #
45
49
  # Parameters::
50
+ # * *nexus_mods* (NexusMods): The NexusMods API instance that the resource can use to query for other resources
51
+ # * *game_domain_name* (String): The game this file belongs to
52
+ # * *mod_id* (Integer): The mod id this file belongs to
46
53
  # * *ids* (Array<Integer>): The file's list of IDs
47
54
  # * *uid* (Integer): The file's UID
48
55
  # * *id* (Integer): The file's main ID
@@ -60,6 +67,9 @@ class NexusMods
60
67
  # * *changelog_html* (String): The file's change log in HTML
61
68
  # * *content_preview_url* (String): URL to a JSON that gives info on the file's content
62
69
  def initialize(
70
+ nexus_mods:,
71
+ game_domain_name:,
72
+ mod_id:,
63
73
  ids:,
64
74
  uid:,
65
75
  id:,
@@ -77,6 +87,9 @@ class NexusMods
77
87
  changelog_html:,
78
88
  content_preview_url:
79
89
  )
90
+ super(nexus_mods:)
91
+ @game_domain_name = game_domain_name
92
+ @mod_id = mod_id
80
93
  @ids = ids
81
94
  @uid = uid
82
95
  @id = id
@@ -105,6 +118,8 @@ class NexusMods
105
118
  # * Boolean: Are objects equal?
106
119
  def ==(other)
107
120
  other.is_a?(ModFile) &&
121
+ @game_domain_name == other.game_domain_name &&
122
+ @mod_id == other.mod_id &&
108
123
  @ids == other.ids &&
109
124
  @uid == other.uid &&
110
125
  @id == other.id &&
@@ -123,6 +138,14 @@ class NexusMods
123
138
  @content_preview_url == other.content_preview_url
124
139
  end
125
140
 
141
+ # Get associated mod information
142
+ #
143
+ # Result::
144
+ # * Mod: The corresponding mod
145
+ def mod
146
+ @nexus_mods.mod(game_domain_name:, mod_id:)
147
+ end
148
+
126
149
  end
127
150
 
128
151
  end
@@ -1,12 +1,15 @@
1
+ require 'nexus_mods/api/resource'
2
+
1
3
  class NexusMods
2
4
 
3
5
  module Api
4
6
 
5
7
  # A NexusMods mod updates.
6
- class ModUpdates
8
+ class ModUpdates < Resource
7
9
 
8
10
  attr_reader(
9
11
  *%i[
12
+ game_domain_name
10
13
  mod_id
11
14
  latest_file_update
12
15
  latest_mod_activity
@@ -16,14 +19,20 @@ class NexusMods
16
19
  # Constructor
17
20
  #
18
21
  # Parameters::
22
+ # * *nexus_mods* (NexusMods): The NexusMods API instance that the resource can use to query for other resources
23
+ # * *game_domain_name* (String): The game this file belongs to
19
24
  # * *mod_id* (Integer): The mod's id
20
25
  # * *latest_file_update* (Time): The mod's latest file update
21
26
  # * *latest_mod_activity* (Time): The mod's latest activity
22
27
  def initialize(
28
+ nexus_mods:,
29
+ game_domain_name:,
23
30
  mod_id:,
24
31
  latest_file_update:,
25
32
  latest_mod_activity:
26
33
  )
34
+ super(nexus_mods:)
35
+ @game_domain_name = game_domain_name
27
36
  @mod_id = mod_id
28
37
  @latest_file_update = latest_file_update
29
38
  @latest_mod_activity = latest_mod_activity
@@ -37,11 +46,28 @@ class NexusMods
37
46
  # * Boolean: Are objects equal?
38
47
  def ==(other)
39
48
  other.is_a?(ModUpdates) &&
49
+ @game_domain_name == game_domain_name &&
40
50
  @mod_id == other.mod_id &&
41
51
  @latest_file_update == other.latest_file_update &&
42
52
  @latest_mod_activity == other.latest_mod_activity
43
53
  end
44
54
 
55
+ # Get associated mod information
56
+ #
57
+ # Result::
58
+ # * Mod: The corresponding mod
59
+ def mod
60
+ @nexus_mods.mod(game_domain_name:, mod_id:)
61
+ end
62
+
63
+ # Get associated mod files information
64
+ #
65
+ # Result::
66
+ # * Array<ModFile>: The corresponding mod files
67
+ def mod_files
68
+ @nexus_mods.mod_files(game_domain_name:, mod_id:)
69
+ end
70
+
45
71
  end
46
72
 
47
73
  end
@@ -0,0 +1,20 @@
1
+ class NexusMods
2
+
3
+ module Api
4
+
5
+ # Base class for any API resource
6
+ class Resource
7
+
8
+ # Constructor
9
+ #
10
+ # Parameters::
11
+ # * *nexus_mods* (NexusMods): The NexusMods API instance that the resource can use to query for other resources
12
+ def initialize(nexus_mods:)
13
+ @nexus_mods = nexus_mods
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -1,10 +1,12 @@
1
+ require 'nexus_mods/api/resource'
2
+
1
3
  class NexusMods
2
4
 
3
5
  module Api
4
6
 
5
7
  # A user on NExusMods.
6
8
  # Mainly used for uploaders information.
7
- class User
9
+ class User < Resource
8
10
 
9
11
  attr_reader(
10
12
  *%i[
@@ -18,16 +20,19 @@ class NexusMods
18
20
  # Constructor
19
21
  #
20
22
  # Parameters::
23
+ # * *nexus_mods* (NexusMods): The NexusMods API instance that the resource can use to query for other resources
21
24
  # * *member_id* (Integer): The user's member id
22
25
  # * *member_group_id* (Integer): The user's member group id
23
26
  # * *name* (String): The user's name
24
27
  # * *profile_url* (String): The user's profile URL
25
28
  def initialize(
29
+ nexus_mods:,
26
30
  member_id:,
27
31
  member_group_id:,
28
32
  name:,
29
33
  profile_url:
30
34
  )
35
+ super(nexus_mods:)
31
36
  @member_id = member_id
32
37
  @member_group_id = member_group_id
33
38
  @name = name
@@ -1,5 +1,5 @@
1
1
  class NexusMods
2
2
 
3
- VERSION = '2.3.0'
3
+ VERSION = '2.5.0'
4
4
 
5
5
  end
data/lib/nexus_mods.rb CHANGED
@@ -88,6 +88,7 @@ class NexusMods
88
88
  def api_limits
89
89
  api_limits_headers = @api_client.http('users/validate').headers
90
90
  Api::ApiLimits.new(
91
+ nexus_mods: self,
91
92
  daily_limit: Integer(api_limits_headers['x-rl-daily-limit']),
92
93
  daily_remaining: Integer(api_limits_headers['x-rl-daily-remaining']),
93
94
  daily_reset: Time.parse(api_limits_headers['x-rl-daily-reset']).utc,
@@ -113,6 +114,7 @@ class NexusMods
113
114
  category_id,
114
115
  [
115
116
  Api::Category.new(
117
+ nexus_mods: self,
116
118
  id: category_id,
117
119
  name: category_json['name']
118
120
  ),
@@ -125,6 +127,7 @@ class NexusMods
125
127
  category.parent_category = categories[parent_category_id]&.first if parent_category_id
126
128
  end
127
129
  Api::Game.new(
130
+ nexus_mods: self,
128
131
  id: game_json['id'],
129
132
  name: game_json['name'],
130
133
  forum_url: game_json['forum_url'],
@@ -166,11 +169,16 @@ class NexusMods
166
169
  # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
167
170
  # * *mod_id* (Integer): The mod ID [default: @mod_id]
168
171
  # * *clear_cache* (Boolean): Should we clear the API cache for this resource? [default: false]
172
+ # * *check_updates* (Boolean): Should we check updates?
173
+ # If yes then an extra call to updated_mods may be done to check for updates before retrieving the mod information.
174
+ # In case the mod was previously retrieved and may be in an old cache, then using this will optimize the calls to NexusMods API to the minimum.
169
175
  # Result::
170
176
  # * Mod: Mod information
171
- def mod(game_domain_name: @game_domain_name, mod_id: @mod_id, clear_cache: false)
177
+ def mod(game_domain_name: @game_domain_name, mod_id: @mod_id, clear_cache: false, check_updates: false)
178
+ mod_cache_up_to_date?(game_domain_name:, mod_id:) if check_updates
172
179
  mod_json = @api_client.api("games/#{game_domain_name}/mods/#{mod_id}", clear_cache:)
173
180
  Api::Mod.new(
181
+ nexus_mods: self,
174
182
  uid: mod_json['uid'],
175
183
  mod_id: mod_json['mod_id'],
176
184
  game_id: mod_json['game_id'],
@@ -185,6 +193,7 @@ class NexusMods
185
193
  status: mod_json['status'],
186
194
  available: mod_json['available'],
187
195
  uploader: Api::User.new(
196
+ nexus_mods: self,
188
197
  member_id: mod_json['user']['member_id'],
189
198
  member_group_id: mod_json['user']['member_group_id'],
190
199
  name: mod_json['user']['name'],
@@ -228,11 +237,18 @@ class NexusMods
228
237
  # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
229
238
  # * *mod_id* (Integer): The mod ID [default: @mod_id]
230
239
  # * *clear_cache* (Boolean): Should we clear the API cache for this resource? [default: false]
240
+ # * *check_updates* (Boolean): Should we check updates?
241
+ # If yes then an extra call to updated_mods may be done to check for updates before retrieving the mod information.
242
+ # In case the mod files were previously retrieved and may be in an old cache, then using this will optimize the calls to NexusMods API to the minimum.
231
243
  # Result::
232
244
  # * Array<ModFile>: List of mod's files
233
- def mod_files(game_domain_name: @game_domain_name, mod_id: @mod_id, clear_cache: false)
245
+ def mod_files(game_domain_name: @game_domain_name, mod_id: @mod_id, clear_cache: false, check_updates: false)
246
+ mod_files_cache_up_to_date?(game_domain_name:, mod_id:) if check_updates
234
247
  @api_client.api("games/#{game_domain_name}/mods/#{mod_id}/files", clear_cache:)['files'].map do |file_json|
235
248
  Api::ModFile.new(
249
+ nexus_mods: self,
250
+ game_domain_name:,
251
+ mod_id:,
236
252
  ids: file_json['id'],
237
253
  uid: file_json['uid'],
238
254
  id: file_json['file_id'],
@@ -290,6 +306,8 @@ class NexusMods
290
306
  def updated_mods(game_domain_name: @game_domain_name, since: :one_day, clear_cache: false)
291
307
  @api_client.api("games/#{game_domain_name}/mods/updated", parameters: period_to_url_params(since), clear_cache:).map do |updated_mod_json|
292
308
  Api::ModUpdates.new(
309
+ nexus_mods: self,
310
+ game_domain_name:,
293
311
  mod_id: updated_mod_json['mod_id'],
294
312
  latest_file_update: Time.at(updated_mod_json['latest_file_update']).utc,
295
313
  latest_mod_activity: Time.at(updated_mod_json['latest_mod_activity']).utc
@@ -327,6 +345,80 @@ class NexusMods
327
345
  @api_client.set_api_cache_timestamp("games/#{game_domain_name}/mods/updated", parameters: period_to_url_params(since), cache_timestamp:)
328
346
  end
329
347
 
348
+ # Does a given mod id have fresh information in our cache?
349
+ # This may fire queries to the updated mods API to get info from NexusMods about the latest updated mods.
350
+ # If we know the mod is up-to-date, then its mod information cache timestamp will be set to the time when we checked for updates if it was greater than the cache date.
351
+ #
352
+ # Here is the algorithm:
353
+ # If it is not in the cache, then it is not up-to-date.
354
+ # Otherwise, the API allows us to know if it has been updated up to 1 month in the past.
355
+ # Therefore if the current cache timestamp is older than 1 month, assume that it has to be updated.
356
+ # Otherwise query the API to know the latest updated mods since 1 month:
357
+ # * If the mod ID is not there, then it is up-to-date.
358
+ # * If the mod ID is there, then check if our cache timestamp is older than the last update timestamp from NexusMods.
359
+ #
360
+ # Parameters::
361
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
362
+ # * *mod_id* (Integer): The mod ID [default: @mod_id]
363
+ # Result::
364
+ # * Boolean: Is the mod cache up-to-date?
365
+ def mod_cache_up_to_date?(game_domain_name: @game_domain_name, mod_id: @mod_id)
366
+ existing_cache_timestamp = mod_cache_timestamp(game_domain_name:, mod_id:)
367
+ mod_up_to_date =
368
+ if existing_cache_timestamp.nil? || existing_cache_timestamp < Time.now - (30 * 24 * 60 * 60)
369
+ # It's not in the cache
370
+ # or it's older than 1 month
371
+ false
372
+ else
373
+ found_mod_updates = updated_mods(game_domain_name:, since: :one_month).find { |mod_updates| mod_updates.mod_id == mod_id }
374
+ # true if it has not been updated on NexusMods since 1 month
375
+ # or our cache timestamp is more recent
376
+ found_mod_updates.nil? || found_mod_updates.latest_mod_activity < existing_cache_timestamp
377
+ end
378
+ if mod_up_to_date
379
+ update_time = updated_mods_cache_timestamp(game_domain_name:, since: :one_month)
380
+ set_mod_cache_timestamp(cache_timestamp: update_time, game_domain_name:, mod_id:) if update_time > existing_cache_timestamp
381
+ end
382
+ mod_up_to_date
383
+ end
384
+
385
+ # Does a given mod id have fresh files information in our cache?
386
+ # This may fire queries to the updated mods API to get info from NexusMods about the latest updated mods.
387
+ # If we know the mod is up-to-date, then its mod information cache timestamp will be set to the time when we checked for updates if it was greater than the cache date.
388
+ #
389
+ # Here is the algorithm:
390
+ # If it is not in the cache, then it is not up-to-date.
391
+ # Otherwise, the API allows us to know if it has been updated up to 1 month in the past.
392
+ # Therefore if the current cache timestamp is older than 1 month, assume that it has to be updated.
393
+ # Otherwise query the API to know the latest updated mods since 1 month:
394
+ # * If the mod ID is not there, then it is up-to-date.
395
+ # * If the mod ID is there, then check if our cache timestamp is older than the last update timestamp from NexusMods.
396
+ #
397
+ # Parameters::
398
+ # * *game_domain_name* (String): Game domain name to query by default [default: @game_domain_name]
399
+ # * *mod_id* (Integer): The mod ID [default: @mod_id]
400
+ # Result::
401
+ # * Boolean: Is the mod cache up-to-date?
402
+ def mod_files_cache_up_to_date?(game_domain_name: @game_domain_name, mod_id: @mod_id)
403
+ existing_cache_timestamp = mod_files_cache_timestamp(game_domain_name:, mod_id:)
404
+ mod_up_to_date =
405
+ if existing_cache_timestamp.nil? || existing_cache_timestamp < Time.now - (30 * 24 * 60 * 60)
406
+ # It's not in the cache
407
+ # or it's older than 1 month
408
+ false
409
+ else
410
+ found_mod_updates = updated_mods(game_domain_name:, since: :one_month).find { |mod_updates| mod_updates.mod_id == mod_id }
411
+ # true if it has not been updated on NexusMods since 1 month
412
+ # or our cache timestamp is more recent
413
+ found_mod_updates.nil? || found_mod_updates.latest_file_update < existing_cache_timestamp
414
+ end
415
+ if mod_up_to_date
416
+ update_time = updated_mods_cache_timestamp(game_domain_name:, since: :one_month)
417
+ set_mod_files_cache_timestamp(cache_timestamp: update_time, game_domain_name:, mod_id:) if update_time > existing_cache_timestamp
418
+ end
419
+ mod_up_to_date
420
+ end
421
+
330
422
  private
331
423
 
332
424
  # Get the URL parameters from the required period
@@ -25,60 +25,55 @@ describe NexusMods::Api::ModFile do
25
25
  expect_mod_file_to_be2487(sorted_mod_files[1])
26
26
  end
27
27
 
28
- it 'returns a mod files list' do
29
- expect_http_call_to(
30
- path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
31
- json: { files: json_example_mod_files }
32
- )
33
- expect_mod_files_to_be_example(nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014))
34
- end
28
+ context 'when testing a mod with 2 files' do
35
29
 
36
- it 'returns the default mod files list' do
37
- expect_http_call_to(
38
- path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
39
- json: { files: json_example_mod_files }
40
- )
41
- expect_mod_files_to_be_example(nexus_mods(mod_id: 2014).mod_files(game_domain_name: 'skyrimspecialedition'))
42
- end
30
+ before do
31
+ expect_http_call_to(
32
+ path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
33
+ json: { 'files' => json_example_mod_files }
34
+ )
35
+ end
43
36
 
44
- it 'returns mod files list for the default game' do
45
- expect_http_call_to(
46
- path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
47
- json: { files: json_example_mod_files }
48
- )
49
- expect_mod_files_to_be_example(nexus_mods(game_domain_name: 'skyrimspecialedition').mod_files(mod_id: 2014))
50
- end
37
+ it 'returns a mod files list' do
38
+ expect_mod_files_to_be_example(nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014))
39
+ end
51
40
 
52
- it 'returns mod files list for the default game set using accessor' do
53
- expect_http_call_to(
54
- path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
55
- json: { files: json_example_mod_files }
56
- )
57
- nexus_mods.game_domain_name = 'skyrimspecialedition'
58
- expect_mod_files_to_be_example(nexus_mods.mod_files(mod_id: 2014))
59
- end
41
+ it 'returns the default mod files list' do
42
+ expect_mod_files_to_be_example(nexus_mods(mod_id: 2014).mod_files(game_domain_name: 'skyrimspecialedition'))
43
+ end
60
44
 
61
- it 'returns mod files list for the default game and mod' do
62
- expect_http_call_to(
63
- path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
64
- json: { files: json_example_mod_files }
65
- )
66
- expect_mod_files_to_be_example(nexus_mods(game_domain_name: 'skyrimspecialedition', mod_id: 2014).mod_files)
67
- end
45
+ it 'returns mod files list for the default game' do
46
+ expect_mod_files_to_be_example(nexus_mods(game_domain_name: 'skyrimspecialedition').mod_files(mod_id: 2014))
47
+ end
48
+
49
+ it 'returns mod files list for the default game set using accessor' do
50
+ nexus_mods.game_domain_name = 'skyrimspecialedition'
51
+ expect_mod_files_to_be_example(nexus_mods.mod_files(mod_id: 2014))
52
+ end
53
+
54
+ it 'returns mod files list for the default game and mod' do
55
+ expect_mod_files_to_be_example(nexus_mods(game_domain_name: 'skyrimspecialedition', mod_id: 2014).mod_files)
56
+ end
57
+
58
+ it 'returns mod files list for the default game and mod using accessor' do
59
+ nexus_mods.mod_id = 2014
60
+ expect_mod_files_to_be_example(nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition'))
61
+ end
62
+
63
+ it 'returns the mod associated to the mod file' do
64
+ expect_http_call_to(
65
+ path: '/v1/games/skyrimspecialedition/mods/2014.json',
66
+ json: json_complete_mod
67
+ )
68
+ expect_mod_to_be_complete(nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014).first.mod)
69
+ end
68
70
 
69
- it 'returns mod files list for the default game and mod using accessor' do
70
- expect_http_call_to(
71
- path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
72
- json: { files: json_example_mod_files }
73
- )
74
- nexus_mods.mod_id = 2014
75
- expect_mod_files_to_be_example(nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition'))
76
71
  end
77
72
 
78
73
  it 'compares objects for equality' do
79
74
  expect_http_call_to(
80
75
  path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
81
- json: { files: [json_mod_file2472] }
76
+ json: { 'files' => [json_mod_file2472] }
82
77
  )
83
78
  mod_file1 = nexus_mods(game_domain_name: 'skyrimspecialedition', mod_id: 2014).mod_files.first
84
79
  mod_file2 = nexus_mods(game_domain_name: 'skyrimspecialedition', mod_id: 2014).mod_files.first
@@ -96,11 +91,12 @@ describe NexusMods::Api::ModFile do
96
91
  archived: 7,
97
92
  unknown: 100
98
93
  }.each do |category, category_id|
94
+
99
95
  it "accepts mod files having category #{category}" do
100
96
  expect_http_call_to(
101
97
  path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
102
98
  json: {
103
- files: [
99
+ 'files' => [
104
100
  {
105
101
  'id' => [
106
102
  2472,
@@ -132,6 +128,197 @@ describe NexusMods::Api::ModFile do
132
128
  expect(mod_file.category).to eq category
133
129
  expect(mod_file.category_id).to eq category_id
134
130
  end
131
+
132
+ end
133
+
134
+ context 'when checking cache data freshness' do
135
+
136
+ it 'returns that mod files never retrieved are not up-to-date' do
137
+ expect(nexus_mods.mod_files_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be false
138
+ end
139
+
140
+ context 'when retrieving mod files previously' do
141
+
142
+ before do
143
+ expect_http_call_to(
144
+ path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
145
+ json: { 'files' => [json_mod_file2472] }
146
+ )
147
+ nexus_mods.mod_files(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
148
+ end
149
+
150
+ context 'when retrieved 40 days ago' do
151
+
152
+ let(:forty_days_ago) { Time.now - (40 * 24 * 60 * 60) }
153
+
154
+ before do
155
+ nexus_mods.set_mod_files_cache_timestamp(cache_timestamp: forty_days_ago, game_domain_name: 'skyrimspecialedition', mod_id: 2014)
156
+ end
157
+
158
+ it 'returns that mod files are not up-to-date' do
159
+ expect(nexus_mods.mod_files_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be false
160
+ expect(nexus_mods.mod_files_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq forty_days_ago
161
+ end
162
+
163
+ end
164
+
165
+ context 'when retrieved 2 days ago' do
166
+
167
+ let(:two_days_ago) { Time.now - (2 * 24 * 60 * 60) }
168
+
169
+ before do
170
+ nexus_mods.set_mod_files_cache_timestamp(cache_timestamp: two_days_ago, game_domain_name: 'skyrimspecialedition', mod_id: 2014)
171
+ end
172
+
173
+ it 'returns that mod files are up-to-date after checking updated mods and not finding it, and updates its cache timestamp to the update time' do
174
+ expect_http_call_to(
175
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
176
+ json: []
177
+ )
178
+ expect(nexus_mods.mod_files_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be true
179
+ expect(nexus_mods.mod_files_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq(
180
+ nexus_mods.updated_mods_cache_timestamp(game_domain_name: 'skyrimspecialedition', since: :one_month)
181
+ )
182
+ end
183
+
184
+ it 'returns that mod files are up-to-date after checking updated mods and finding that cache is more recent, and updates its cache timestamp to the update time' do
185
+ expect_http_call_to(
186
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
187
+ json: [
188
+ {
189
+ 'mod_id' => 2014,
190
+ # Mock that mod was updated 3 days ago
191
+ 'latest_file_update' => Integer((Time.now - (3 * 24 * 60 * 60)).strftime('%s')),
192
+ 'latest_mod_activity' => 1
193
+ }
194
+ ]
195
+ )
196
+ expect(nexus_mods.mod_files_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be true
197
+ expect(nexus_mods.mod_files_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq(
198
+ nexus_mods.updated_mods_cache_timestamp(game_domain_name: 'skyrimspecialedition', since: :one_month)
199
+ )
200
+ end
201
+
202
+ it 'returns that mod files are not up-to-date after checking updated mods and finding that cache is less recent' do
203
+ expect_http_call_to(
204
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
205
+ json: [
206
+ {
207
+ 'mod_id' => 2014,
208
+ # Mock that mod was updated yesterday
209
+ 'latest_file_update' => Integer((Time.now - (24 * 60 * 60)).strftime('%s')),
210
+ 'latest_mod_activity' => 1
211
+ }
212
+ ]
213
+ )
214
+ expect(nexus_mods.mod_files_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be false
215
+ expect(nexus_mods.mod_files_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq two_days_ago
216
+ end
217
+
218
+ end
219
+
220
+ context 'when retrieved 3 minutes ago with an updated mods query 4 minutes ago in the cache' do
221
+
222
+ let(:three_minutes_ago) { Time.now - (3 * 60) }
223
+ let(:four_minutes_ago) { Time.now - (4 * 60) }
224
+
225
+ before do
226
+ expect_http_call_to(
227
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
228
+ json: [
229
+ {
230
+ 'mod_id' => 2014,
231
+ # Mock that mod was updated 3 days ago
232
+ 'latest_file_update' => Integer((Time.now - (3 * 24 * 60 * 60)).strftime('%s')),
233
+ 'latest_mod_activity' => 1
234
+ }
235
+ ]
236
+ )
237
+ nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: :one_month)
238
+ nexus_mods.set_mod_files_cache_timestamp(cache_timestamp: three_minutes_ago, game_domain_name: 'skyrimspecialedition', mod_id: 2014)
239
+ nexus_mods.set_updated_mods_cache_timestamp(cache_timestamp: four_minutes_ago, game_domain_name: 'skyrimspecialedition', since: :one_month)
240
+ end
241
+
242
+ it 'returns that mod files are up-to-date but doesn\'t change its mod cache timestamp' do
243
+ expect(nexus_mods.mod_files_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be true
244
+ expect(nexus_mods.mod_files_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq three_minutes_ago
245
+ expect(nexus_mods.updated_mods_cache_timestamp(game_domain_name: 'skyrimspecialedition', since: :one_month)).to eq four_minutes_ago
246
+ end
247
+
248
+ end
249
+
250
+ end
251
+
252
+ end
253
+
254
+ context 'when checking for updates' do
255
+
256
+ before do
257
+ nexus_mods(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
258
+ end
259
+
260
+ it 'does not check for updates if mod files have not been retrieved before' do
261
+ expect_http_call_to(
262
+ path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
263
+ json: { 'files' => [json_mod_file2472] }
264
+ )
265
+ expect_mod_file_to_be2472(nexus_mods.mod_files(check_updates: true).first)
266
+ end
267
+
268
+ it 'does not check for updates if mod files have been retrieved more than 1 month ago, and re-query the mod' do
269
+ expect_http_call_to(
270
+ path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
271
+ json: { 'files' => [json_mod_file2472] },
272
+ times: 2
273
+ )
274
+ nexus_mods.mod_files
275
+ nexus_mods.set_mod_files_cache_timestamp(cache_timestamp: Time.now - (40 * 24 * 60 * 60), game_domain_name: 'skyrimspecialedition', mod_id: 2014)
276
+ expect_mod_file_to_be2472(nexus_mods.mod_files(check_updates: true).first)
277
+ end
278
+
279
+ it 'checks for updates when mod has been retrieved less than 1 month ago and does nothing if its date is less recent than the cache' do
280
+ expect_http_call_to(
281
+ path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
282
+ json: { 'files' => [json_mod_file2472] }
283
+ )
284
+ expect_http_call_to(
285
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
286
+ json: [
287
+ {
288
+ 'mod_id' => 2014,
289
+ # Mock that mod was updated 25 days ago
290
+ 'latest_file_update' => Integer((Time.now - (25 * 24 * 60 * 60)).strftime('%s')),
291
+ 'latest_mod_activity' => 1
292
+ }
293
+ ]
294
+ )
295
+ nexus_mods.mod_files
296
+ nexus_mods.set_mod_files_cache_timestamp(cache_timestamp: Time.now - (20 * 24 * 60 * 60), game_domain_name: 'skyrimspecialedition', mod_id: 2014)
297
+ expect_mod_file_to_be2472(nexus_mods.mod_files(check_updates: true).first)
298
+ end
299
+
300
+ it 'checks for updates when mod has been retrieved less than 1 month ago and re-query the mod if its date is more recent than the cache' do
301
+ expect_http_call_to(
302
+ path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
303
+ json: { 'files' => [json_mod_file2472] },
304
+ times: 2
305
+ )
306
+ expect_http_call_to(
307
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
308
+ json: [
309
+ {
310
+ 'mod_id' => 2014,
311
+ # Mock that mod was updated 15 days ago
312
+ 'latest_file_update' => Integer((Time.now - (15 * 24 * 60 * 60)).strftime('%s')),
313
+ 'latest_mod_activity' => 1
314
+ }
315
+ ]
316
+ )
317
+ nexus_mods.mod_files
318
+ nexus_mods.set_mod_files_cache_timestamp(cache_timestamp: Time.now - (20 * 24 * 60 * 60), game_domain_name: 'skyrimspecialedition', mod_id: 2014)
319
+ expect_mod_file_to_be2472(nexus_mods.mod_files(check_updates: true).first)
320
+ end
321
+
135
322
  end
136
323
 
137
324
  end
@@ -6,73 +6,261 @@ describe NexusMods::Api::Mod do
6
6
  expect_validate_user
7
7
  end
8
8
 
9
- it 'returns a mod complete information' do
10
- expect_http_call_to(
11
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
12
- json: json_complete_mod
13
- )
14
- expect_mod_to_be_complete(nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014))
15
- end
9
+ context 'when accessing a partial mod' do
16
10
 
17
- it 'returns a mod partial information' do
18
- expect_http_call_to(
19
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
20
- json: json_partial_mod
21
- )
22
- expect_mod_to_be_partial(nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014))
23
- end
11
+ before do
12
+ expect_http_call_to(
13
+ path: '/v1/games/skyrimspecialedition/mods/2014.json',
14
+ json: json_partial_mod
15
+ )
16
+ end
24
17
 
25
- it 'returns the default mod information' do
26
- expect_http_call_to(
27
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
28
- json: json_complete_mod
29
- )
30
- expect_mod_to_be_complete(nexus_mods(mod_id: 2014).mod(game_domain_name: 'skyrimspecialedition'))
31
- end
18
+ it 'returns the mod information' do
19
+ expect_mod_to_be_partial(nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014))
20
+ end
32
21
 
33
- it 'returns mod information for the default game' do
34
- expect_http_call_to(
35
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
36
- json: json_complete_mod
37
- )
38
- expect_mod_to_be_complete(nexus_mods(game_domain_name: 'skyrimspecialedition').mod(mod_id: 2014))
39
22
  end
40
23
 
41
- it 'returns mod information for the default game set using accessor' do
42
- expect_http_call_to(
43
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
44
- json: json_complete_mod
45
- )
46
- nexus_mods.game_domain_name = 'skyrimspecialedition'
47
- expect_mod_to_be_complete(nexus_mods.mod(mod_id: 2014))
48
- end
24
+ context 'when accessing a complete mod' do
25
+
26
+ before do
27
+ expect_http_call_to(
28
+ path: '/v1/games/skyrimspecialedition/mods/2014.json',
29
+ json: json_complete_mod
30
+ )
31
+ end
32
+
33
+ it 'returns the mod information' do
34
+ expect_mod_to_be_complete(nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014))
35
+ end
36
+
37
+ it 'returns the default mod information' do
38
+ expect_mod_to_be_complete(nexus_mods(mod_id: 2014).mod(game_domain_name: 'skyrimspecialedition'))
39
+ end
40
+
41
+ it 'returns mod information for the default game' do
42
+ expect_mod_to_be_complete(nexus_mods(game_domain_name: 'skyrimspecialedition').mod(mod_id: 2014))
43
+ end
44
+
45
+ it 'returns mod information for the default game set using accessor' do
46
+ nexus_mods.game_domain_name = 'skyrimspecialedition'
47
+ expect_mod_to_be_complete(nexus_mods.mod(mod_id: 2014))
48
+ end
49
+
50
+ it 'returns mod information for the default game and mod' do
51
+ expect_mod_to_be_complete(nexus_mods(game_domain_name: 'skyrimspecialedition', mod_id: 2014).mod)
52
+ end
53
+
54
+ it 'returns mod information for the default game and mod set using accessor' do
55
+ nexus_mods.mod_id = 2014
56
+ expect_mod_to_be_complete(nexus_mods.mod(game_domain_name: 'skyrimspecialedition'))
57
+ end
58
+
59
+ it 'compares objects for equality' do
60
+ mod1 = nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
61
+ mod2 = nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
62
+ expect(mod1.object_id).not_to eq mod2.object_id
63
+ expect(mod1).to eq mod2
64
+ end
65
+
66
+ it 'returns the mod files associated to the mod' do
67
+ expect_http_call_to(
68
+ path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
69
+ json: { 'files' => [json_mod_file2472] }
70
+ )
71
+ expect_mod_file_to_be2472(nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014).files.first)
72
+ end
49
73
 
50
- it 'returns mod information for the default game and mod' do
51
- expect_http_call_to(
52
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
53
- json: json_complete_mod
54
- )
55
- expect_mod_to_be_complete(nexus_mods(game_domain_name: 'skyrimspecialedition', mod_id: 2014).mod)
56
74
  end
57
75
 
58
- it 'returns mod information for the default game and mod set using accessor' do
59
- expect_http_call_to(
60
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
61
- json: json_complete_mod
62
- )
63
- nexus_mods.mod_id = 2014
64
- expect_mod_to_be_complete(nexus_mods.mod(game_domain_name: 'skyrimspecialedition'))
76
+ context 'when checking cache data freshness' do
77
+
78
+ it 'returns that a mod never retrieved is not up-to-date' do
79
+ expect(nexus_mods.mod_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be false
80
+ end
81
+
82
+ context 'when retrieving a mod previously' do
83
+
84
+ before do
85
+ expect_http_call_to(
86
+ path: '/v1/games/skyrimspecialedition/mods/2014.json',
87
+ json: json_complete_mod
88
+ )
89
+ nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
90
+ end
91
+
92
+ context 'when retrieved 40 days ago' do
93
+
94
+ let(:forty_days_ago) { Time.now - (40 * 24 * 60 * 60) }
95
+
96
+ before do
97
+ nexus_mods.set_mod_cache_timestamp(cache_timestamp: forty_days_ago, game_domain_name: 'skyrimspecialedition', mod_id: 2014)
98
+ end
99
+
100
+ it 'returns that the mod is not up-to-date' do
101
+ expect(nexus_mods.mod_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be false
102
+ expect(nexus_mods.mod_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq forty_days_ago
103
+ end
104
+
105
+ end
106
+
107
+ context 'when retrieved 2 days ago' do
108
+
109
+ let(:two_days_ago) { Time.now - (2 * 24 * 60 * 60) }
110
+
111
+ before do
112
+ nexus_mods.set_mod_cache_timestamp(cache_timestamp: two_days_ago, game_domain_name: 'skyrimspecialedition', mod_id: 2014)
113
+ end
114
+
115
+ it 'returns that the mod is up-to-date after checking updated mods and not finding it, and updates its cache timestamp to the update time' do
116
+ expect_http_call_to(
117
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
118
+ json: []
119
+ )
120
+ expect(nexus_mods.mod_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be true
121
+ expect(nexus_mods.mod_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq(
122
+ nexus_mods.updated_mods_cache_timestamp(game_domain_name: 'skyrimspecialedition', since: :one_month)
123
+ )
124
+ end
125
+
126
+ it 'returns that the mod is up-to-date after checking updated mods and finding that cache is more recent, and updates its cache timestamp to the update time' do
127
+ expect_http_call_to(
128
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
129
+ json: [
130
+ {
131
+ 'mod_id' => 2014,
132
+ # Mock that mod was updated 3 days ago
133
+ 'latest_file_update' => 1,
134
+ 'latest_mod_activity' => Integer((Time.now - (3 * 24 * 60 * 60)).strftime('%s'))
135
+ }
136
+ ]
137
+ )
138
+ expect(nexus_mods.mod_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be true
139
+ expect(nexus_mods.mod_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq(
140
+ nexus_mods.updated_mods_cache_timestamp(game_domain_name: 'skyrimspecialedition', since: :one_month)
141
+ )
142
+ end
143
+
144
+ it 'returns that the mod is not up-to-date after checking updated mods and finding that cache is less recent' do
145
+ expect_http_call_to(
146
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
147
+ json: [
148
+ {
149
+ 'mod_id' => 2014,
150
+ # Mock that mod was updated yesterday
151
+ 'latest_file_update' => 1,
152
+ 'latest_mod_activity' => Integer((Time.now - (24 * 60 * 60)).strftime('%s'))
153
+ }
154
+ ]
155
+ )
156
+ expect(nexus_mods.mod_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be false
157
+ expect(nexus_mods.mod_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq two_days_ago
158
+ end
159
+
160
+ end
161
+
162
+ context 'when retrieved 3 minutes ago with an updated mods query 4 minutes ago in the cache' do
163
+
164
+ let(:three_minutes_ago) { Time.now - (3 * 60) }
165
+ let(:four_minutes_ago) { Time.now - (4 * 60) }
166
+
167
+ before do
168
+ expect_http_call_to(
169
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
170
+ json: [
171
+ {
172
+ 'mod_id' => 2014,
173
+ # Mock that mod was updated 3 days ago
174
+ 'latest_file_update' => 1,
175
+ 'latest_mod_activity' => Integer((Time.now - (3 * 24 * 60 * 60)).strftime('%s'))
176
+ }
177
+ ]
178
+ )
179
+ nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: :one_month)
180
+ nexus_mods.set_mod_cache_timestamp(cache_timestamp: three_minutes_ago, game_domain_name: 'skyrimspecialedition', mod_id: 2014)
181
+ nexus_mods.set_updated_mods_cache_timestamp(cache_timestamp: four_minutes_ago, game_domain_name: 'skyrimspecialedition', since: :one_month)
182
+ end
183
+
184
+ it 'returns that the mod is up-to-date but doesn\'t change its mod cache timestamp' do
185
+ expect(nexus_mods.mod_cache_up_to_date?(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to be true
186
+ expect(nexus_mods.mod_cache_timestamp(game_domain_name: 'skyrimspecialedition', mod_id: 2014)).to eq three_minutes_ago
187
+ expect(nexus_mods.updated_mods_cache_timestamp(game_domain_name: 'skyrimspecialedition', since: :one_month)).to eq four_minutes_ago
188
+ end
189
+
190
+ end
191
+
192
+ end
193
+
65
194
  end
66
195
 
67
- it 'compares objects for equality' do
68
- expect_http_call_to(
69
- path: '/v1/games/skyrimspecialedition/mods/2014.json',
70
- json: json_complete_mod
71
- )
72
- mod1 = nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
73
- mod2 = nexus_mods.mod(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
74
- expect(mod1.object_id).not_to eq mod2.object_id
75
- expect(mod1).to eq mod2
196
+ context 'when checking for updates' do
197
+
198
+ before do
199
+ nexus_mods(game_domain_name: 'skyrimspecialedition', mod_id: 2014)
200
+ end
201
+
202
+ it 'does not check for updates if mod has not been retrieved before' do
203
+ expect_http_call_to(
204
+ path: '/v1/games/skyrimspecialedition/mods/2014.json',
205
+ json: json_complete_mod
206
+ )
207
+ expect_mod_to_be_complete(nexus_mods.mod(check_updates: true))
208
+ end
209
+
210
+ it 'does not check for updates if mod has been retrieved more than 1 month ago, and re-query the mod' do
211
+ expect_http_call_to(
212
+ path: '/v1/games/skyrimspecialedition/mods/2014.json',
213
+ json: json_complete_mod,
214
+ times: 2
215
+ )
216
+ nexus_mods.mod
217
+ nexus_mods.set_mod_cache_timestamp(cache_timestamp: Time.now - (40 * 24 * 60 * 60), game_domain_name: 'skyrimspecialedition', mod_id: 2014)
218
+ expect_mod_to_be_complete(nexus_mods.mod(check_updates: true))
219
+ end
220
+
221
+ it 'checks for updates when mod has been retrieved less than 1 month ago and does nothing if its date is less recent than the cache' do
222
+ expect_http_call_to(
223
+ path: '/v1/games/skyrimspecialedition/mods/2014.json',
224
+ json: json_complete_mod
225
+ )
226
+ expect_http_call_to(
227
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
228
+ json: [
229
+ {
230
+ 'mod_id' => 2014,
231
+ # Mock that mod was updated 25 days ago
232
+ 'latest_file_update' => 1,
233
+ 'latest_mod_activity' => Integer((Time.now - (25 * 24 * 60 * 60)).strftime('%s'))
234
+ }
235
+ ]
236
+ )
237
+ nexus_mods.mod
238
+ nexus_mods.set_mod_cache_timestamp(cache_timestamp: Time.now - (20 * 24 * 60 * 60), game_domain_name: 'skyrimspecialedition', mod_id: 2014)
239
+ expect_mod_to_be_complete(nexus_mods.mod(check_updates: true))
240
+ end
241
+
242
+ it 'checks for updates when mod has been retrieved less than 1 month ago and re-query the mod if its date is more recent than the cache' do
243
+ expect_http_call_to(
244
+ path: '/v1/games/skyrimspecialedition/mods/2014.json',
245
+ json: json_complete_mod,
246
+ times: 2
247
+ )
248
+ expect_http_call_to(
249
+ path: '/v1/games/skyrimspecialedition/mods/updated.json?period=1m',
250
+ json: [
251
+ {
252
+ 'mod_id' => 2014,
253
+ # Mock that mod was updated 15 days ago
254
+ 'latest_file_update' => 1,
255
+ 'latest_mod_activity' => Integer((Time.now - (15 * 24 * 60 * 60)).strftime('%s'))
256
+ }
257
+ ]
258
+ )
259
+ nexus_mods.mod
260
+ nexus_mods.set_mod_cache_timestamp(cache_timestamp: Time.now - (20 * 24 * 60 * 60), game_domain_name: 'skyrimspecialedition', mod_id: 2014)
261
+ expect_mod_to_be_complete(nexus_mods.mod(check_updates: true))
262
+ end
263
+
76
264
  end
77
265
 
78
266
  end
@@ -78,6 +78,34 @@ describe NexusMods::Api::ModUpdates do
78
78
  expect(mod_updates1).to eq mod_updates2
79
79
  end
80
80
 
81
+ it 'returns the mod associated to the mod updates' do
82
+ expect_http_call_to(
83
+ path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
84
+ json: [
85
+ json_mod_updates2014
86
+ ]
87
+ )
88
+ expect_http_call_to(
89
+ path: '/v1/games/skyrimspecialedition/mods/2014.json',
90
+ json: json_complete_mod
91
+ )
92
+ expect_mod_to_be_complete(nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since]).first.mod)
93
+ end
94
+
95
+ it 'returns the mod files associated to the mod updates' do
96
+ expect_http_call_to(
97
+ path: "/v1/games/skyrimspecialedition/mods/updated.json?#{since_config[:expected_url_params]}",
98
+ json: [
99
+ json_mod_updates2014
100
+ ]
101
+ )
102
+ expect_http_call_to(
103
+ path: '/v1/games/skyrimspecialedition/mods/2014/files.json',
104
+ json: { 'files' => [json_mod_file2472] }
105
+ )
106
+ expect_mod_file_to_be2472(nexus_mods.updated_mods(game_domain_name: 'skyrimspecialedition', since: since_config[:since]).first.mod_files.first)
107
+ end
108
+
81
109
  end
82
110
 
83
111
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nexus_mods
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Muriel Salvan
@@ -138,6 +138,7 @@ files:
138
138
  - lib/nexus_mods/api/mod.rb
139
139
  - lib/nexus_mods/api/mod_file.rb
140
140
  - lib/nexus_mods/api/mod_updates.rb
141
+ - lib/nexus_mods/api/resource.rb
141
142
  - lib/nexus_mods/api/user.rb
142
143
  - lib/nexus_mods/api_client.rb
143
144
  - lib/nexus_mods/cacheable_api.rb