nexus_mods 2.3.0 → 2.5.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: 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