nexus_mods 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +12 -1
- data/examples/api_caching.rb +41 -0
- data/examples/api_limits.rb +12 -0
- data/examples/games.rb +8 -0
- data/examples/log_debug.rb +10 -0
- data/examples/mods.rb +24 -0
- data/lib/nexus_mods/api_client.rb +1 -14
- data/lib/nexus_mods/version.rb +1 -1
- data/lib/nexus_mods.rb +0 -3
- data/spec/nexus_mods_test/helpers.rb +1 -9
- metadata +12 -17
- data/lib/nexus_mods/file_cache.rb +0 -71
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2325f79c99fb7304d16a0e4b12682229d8187ae30f6d0321796a4d3d5e587fb
|
4
|
+
data.tar.gz: 1cce0b8b4a82f8c903be9d6b7441d739d3b5dffa375c699444e8f2e87af4f2be
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d868fa12c0e79385a7377e08ff5c4d4b0ccab2b3c78bedfc99cc9761c7a85c98073260c1409534b2ba8ccebdf74e10f03ddb7dd619901a05c809514798818d9
|
7
|
+
data.tar.gz: 45b7c418a3c0aeeac13b3b6e5d7a2d095513cc96e78e30f9900bee7852ff17389a512afe347fe345b33cd97ab099831cd020ee09c1aab70241f576c980447f5b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
# [v2.0.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v1.1.1...v2.0.0) (2023-04-11 10:15:58)
|
2
|
+
|
3
|
+
### Breaking changes
|
4
|
+
|
5
|
+
* [[Breaking] Remove http cache file and useless support for etags as the API does not cache at http level](https://github.com/Muriel-Salvan/nexus_mods/commit/e157524e17ef0ed1a7013f14b15eaa35bb309592)
|
6
|
+
|
7
|
+
# [v1.1.1](https://github.com/Muriel-Salvan/nexus_mods/compare/v1.1.0...v1.1.1) (2023-04-10 19:39:28)
|
8
|
+
|
9
|
+
### Patches
|
10
|
+
|
11
|
+
* [Improved documentation and added examples](https://github.com/Muriel-Salvan/nexus_mods/commit/0eb3fd00e0d1ad34db04a30ab2043a49371dc64f)
|
12
|
+
|
1
13
|
# [v1.1.0](https://github.com/Muriel-Salvan/nexus_mods/compare/v1.0.0...v1.1.0) (2023-04-10 19:19:16)
|
2
14
|
|
3
15
|
### Features
|
data/README.md
CHANGED
@@ -2,6 +2,17 @@
|
|
2
2
|
|
3
3
|
Simple Ruby API letting you handle [NexusMods](https://www.nexusmods.com/) REST API.
|
4
4
|
|
5
|
+
## Main features
|
6
|
+
|
7
|
+
* Get the API **limits**.
|
8
|
+
* Get the **games** information.
|
9
|
+
* Get individual **mods** and **mod files** information.
|
10
|
+
* Configurable **caching** with expiry times to save API calls to nexusmods.com.
|
11
|
+
* All served in an object-oriented **API in full Ruby**.
|
12
|
+
|
13
|
+
See the [examples](examples) for more details on how to use it.
|
14
|
+
Those examples expect that you set a valid NexusMods API key in the `NEXUS_MODS_API_KEY` environment variable.
|
15
|
+
|
5
16
|
## Install
|
6
17
|
|
7
18
|
Via gem
|
@@ -54,7 +65,7 @@ Any contribution is welcome:
|
|
54
65
|
|
55
66
|
## Credits
|
56
67
|
|
57
|
-
- [Muriel Salvan]
|
68
|
+
- [Muriel Salvan](https://x-aeon.com/muriel)
|
58
69
|
|
59
70
|
## License
|
60
71
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'nexus_mods'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# Make sure a previous file for caching was not here before
|
5
|
+
test_api_cache_file = 'nexus_mods_test_api_cache.json'
|
6
|
+
FileUtils.rm_f test_api_cache_file
|
7
|
+
|
8
|
+
nexus_mods = NexusMods.new(
|
9
|
+
api_key: ENV.fetch('NEXUS_MODS_API_KEY'),
|
10
|
+
api_cache_file: test_api_cache_file
|
11
|
+
)
|
12
|
+
|
13
|
+
initial_remaining = nexus_mods.api_limits.daily_remaining
|
14
|
+
puts "Before fetching anything, daily API remaining is #{initial_remaining}"
|
15
|
+
|
16
|
+
puts 'Fetch the list of games (without using cache)...'
|
17
|
+
puts "Fetched #{nexus_mods.games.size} games."
|
18
|
+
|
19
|
+
puts "After fetching those games, daily API remaining is #{nexus_mods.api_limits.daily_remaining}"
|
20
|
+
|
21
|
+
puts 'Now we fetch again the list of games (this should use the cache)...'
|
22
|
+
puts "Fetched #{nexus_mods.games.size} games."
|
23
|
+
|
24
|
+
puts "After fetching those games a second time, daily API remaining is #{nexus_mods.api_limits.daily_remaining}"
|
25
|
+
|
26
|
+
puts 'Now we close the current NexusMods instance ad re-instantiate a new one from scratch using the same API cache file'
|
27
|
+
|
28
|
+
new_nexus_mods = NexusMods.new(
|
29
|
+
api_key: ENV.fetch('NEXUS_MODS_API_KEY'),
|
30
|
+
api_cache_file: test_api_cache_file
|
31
|
+
)
|
32
|
+
|
33
|
+
puts "Before fetching anything from the new instance, daily API remaining is #{new_nexus_mods.api_limits.daily_remaining}"
|
34
|
+
|
35
|
+
puts 'Now we fetch the list of games from the new instance (this should use the cache that was stored in the file)...'
|
36
|
+
puts "Fetched #{new_nexus_mods.games.size} games."
|
37
|
+
|
38
|
+
puts "After fetching those games from the new instance, daily API remaining is #{new_nexus_mods.api_limits.daily_remaining}"
|
39
|
+
|
40
|
+
puts
|
41
|
+
puts "As a conclusion, we used 2 instances of NexusMods that have fetched games 3 times, and it consumed #{initial_remaining - new_nexus_mods.api_limits.daily_remaining} real API call."
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'nexus_mods'
|
2
|
+
|
3
|
+
api_limits = NexusMods.new(api_key: ENV.fetch('NEXUS_MODS_API_KEY')).api_limits
|
4
|
+
puts <<~EO_OUTPUT
|
5
|
+
API limits:
|
6
|
+
daily_limit: #{api_limits.daily_limit}
|
7
|
+
daily_remaining: #{api_limits.daily_remaining}
|
8
|
+
daily_reset: #{api_limits.daily_reset}
|
9
|
+
hourly_limit: #{api_limits.hourly_limit}
|
10
|
+
hourly_remaining: #{api_limits.hourly_remaining}
|
11
|
+
hourly_reset: #{api_limits.hourly_reset}
|
12
|
+
EO_OUTPUT
|
data/examples/games.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'nexus_mods'
|
2
|
+
|
3
|
+
games = NexusMods.new(api_key: ENV.fetch('NEXUS_MODS_API_KEY')).games
|
4
|
+
puts "Found a total of #{games.size} games."
|
5
|
+
puts 'Here is the top 10 by number of downloads:'
|
6
|
+
games.sort_by { |game| -game.downloads_count }[0..9].each do |game|
|
7
|
+
puts "* #{game.name} (#{game.mods_count} mods, #{game.downloads_count} downloads)"
|
8
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'nexus_mods'
|
2
|
+
|
3
|
+
puts 'Example of fetching a mod with log debug activated, and without using the cache (so that we always see the query):'
|
4
|
+
puts
|
5
|
+
NexusMods.new(
|
6
|
+
api_key: ENV.fetch('NEXUS_MODS_API_KEY'),
|
7
|
+
log_level: :debug,
|
8
|
+
game_domain_name: 'skyrimspecialedition',
|
9
|
+
mod_id: 42_521
|
10
|
+
).mod(clear_cache: true)
|
data/examples/mods.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'nexus_mods'
|
2
|
+
|
3
|
+
nexus_mods = NexusMods.new(
|
4
|
+
api_key: ENV.fetch('NEXUS_MODS_API_KEY'),
|
5
|
+
game_domain_name: 'skyrimspecialedition'
|
6
|
+
)
|
7
|
+
some_mod_ids = [
|
8
|
+
266,
|
9
|
+
2_347,
|
10
|
+
17_230,
|
11
|
+
42_521
|
12
|
+
]
|
13
|
+
puts 'Here are some details about a few mods for Skyrim Special Edition:'
|
14
|
+
puts
|
15
|
+
some_mod_ids.each do |mod_id|
|
16
|
+
mod = nexus_mods.mod(mod_id:)
|
17
|
+
mod_files = nexus_mods.mod_files(mod_id:)
|
18
|
+
puts <<~EO_OUTPUT
|
19
|
+
===== #{mod.name} (v#{mod.version}) by #{mod.uploader.name} (#{mod.downloads_count} downloads)
|
20
|
+
#{mod.summary}
|
21
|
+
* Last 5 files: #{mod_files.reverse[0..4].map(&:file_name).join(', ')}
|
22
|
+
|
23
|
+
EO_OUTPUT
|
24
|
+
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'faraday'
|
3
|
-
require 'faraday-http-cache'
|
4
|
-
require 'nexus_mods/file_cache'
|
5
3
|
require 'nexus_mods/cacheable_api'
|
6
4
|
|
7
5
|
class NexusMods
|
@@ -23,7 +21,6 @@ class NexusMods
|
|
23
21
|
#
|
24
22
|
# Parameters::
|
25
23
|
# * *api_key* (String or nil): The API key to be used, or nil for another authentication [default: nil]
|
26
|
-
# * *http_cache_file* (String): File used to store the HTTP cache, or nil for no cache [default: "#{Dir.tmpdir}/nexus_mods_http_cache.json"]
|
27
24
|
# * *api_cache_expiry* (Hash<Symbol,Integer>): Expiry times in seconds, per expiry key. Possible keys are:
|
28
25
|
# * *games*: Expiry associated to queries on games [default: 1 day]
|
29
26
|
# * *mod*: Expiry associated to queries on mod [default: 1 day]
|
@@ -32,7 +29,6 @@ class NexusMods
|
|
32
29
|
# * *logger* (Logger): The logger to be used for log messages [default: Logger.new(STDOUT)]
|
33
30
|
def initialize(
|
34
31
|
api_key: nil,
|
35
|
-
http_cache_file: "#{Dir.tmpdir}/nexus_mods_http_cache.json",
|
36
32
|
api_cache_expiry: DEFAULT_API_CACHE_EXPIRY,
|
37
33
|
api_cache_file: "#{Dir.tmpdir}/nexus_mods_api_cache.json",
|
38
34
|
logger: Logger.new($stdout)
|
@@ -43,16 +39,7 @@ class NexusMods
|
|
43
39
|
ApiClient.api_client = self
|
44
40
|
@logger = logger
|
45
41
|
# Initialize our HTTP client
|
46
|
-
@
|
47
|
-
@http_client = Faraday.new do |builder|
|
48
|
-
# Indicate that the cache is not shared, meaning that private resources (depending on the session) can be cached as we consider only 1 user is using it for a given file cache.
|
49
|
-
# Use Marshal serializer as some URLs can't get decoded correctly due to UTF-8 issues
|
50
|
-
builder.use :http_cache,
|
51
|
-
store: @http_cache,
|
52
|
-
shared_cache: false,
|
53
|
-
serializer: Marshal
|
54
|
-
builder.adapter Faraday.default_adapter
|
55
|
-
end
|
42
|
+
@http_client = Faraday.new
|
56
43
|
Cacheable.cache_adapter = :persistent_json
|
57
44
|
load_api_cache
|
58
45
|
end
|
data/lib/nexus_mods/version.rb
CHANGED
data/lib/nexus_mods.rb
CHANGED
@@ -39,7 +39,6 @@ class NexusMods
|
|
39
39
|
# * *game_domain_name* (String): Game domain name to query by default [default: 'skyrimspecialedition']
|
40
40
|
# * *mod_id* (Integer): Mod to query by default [default: 1]
|
41
41
|
# * *file_id* (Integer): File to query by default [default: 1]
|
42
|
-
# * *http_cache_file* (String): File used to store the HTTP cache, or nil for no cache [default: "#{Dir.tmpdir}/nexus_mods_http_cache.json"]
|
43
42
|
# * *api_cache_expiry* (Hash<Symbol,Integer>): Expiry times in seconds, per expiry key. Possible keys are:
|
44
43
|
# * *games*: Expiry associated to queries on games [default: 1 day]
|
45
44
|
# * *mod*: Expiry associated to queries on mod [default: 1 day]
|
@@ -52,7 +51,6 @@ class NexusMods
|
|
52
51
|
game_domain_name: 'skyrimspecialedition',
|
53
52
|
mod_id: 1,
|
54
53
|
file_id: 1,
|
55
|
-
http_cache_file: "#{Dir.tmpdir}/nexus_mods_http_cache.json",
|
56
54
|
api_cache_expiry: {},
|
57
55
|
api_cache_file: "#{Dir.tmpdir}/nexus_mods_api_cache.json",
|
58
56
|
logger: Logger.new($stdout),
|
@@ -66,7 +64,6 @@ class NexusMods
|
|
66
64
|
@premium = false
|
67
65
|
@api_client = ApiClient.new(
|
68
66
|
api_key:,
|
69
|
-
http_cache_file:,
|
70
67
|
api_cache_expiry:,
|
71
68
|
api_cache_file:,
|
72
69
|
logger:
|
@@ -56,7 +56,6 @@ module NexusModsTest
|
|
56
56
|
if @nexus_mods.nil?
|
57
57
|
args[:api_key] = MOCKED_API_KEY unless args.key?(:api_key)
|
58
58
|
# By default running tests should not persistent cache files
|
59
|
-
args[:http_cache_file] = nil unless args.key?(:http_cache_file)
|
60
59
|
args[:api_cache_file] = nil unless args.key?(:api_cache_file)
|
61
60
|
# Redirect any log into a string so that they don't pollute the tests output and they could be asserted.
|
62
61
|
@nexus_mods_logger = StringIO.new
|
@@ -115,11 +114,6 @@ module NexusModsTest
|
|
115
114
|
'User-Agent' => "nexus_mods (#{RUBY_PLATFORM}) Ruby/#{RUBY_VERSION}",
|
116
115
|
'apikey' => api_key
|
117
116
|
}
|
118
|
-
if @expected_returned_etags.include? mocked_etag
|
119
|
-
expected_request_headers['If-None-Match'] = mocked_etag
|
120
|
-
else
|
121
|
-
@expected_returned_etags << mocked_etag
|
122
|
-
end
|
123
117
|
@expected_stubs << [
|
124
118
|
stub_request(http_method, "https://#{host}#{path}").with(headers: expected_request_headers).to_return(
|
125
119
|
status: [code, message],
|
@@ -190,9 +184,6 @@ RSpec.configure do |config|
|
|
190
184
|
@nexus_mods = nil
|
191
185
|
# Reload the ApiClient as it stores caches at class level
|
192
186
|
NexusMods::ApiClient.clear_cacheable_expiry_caches
|
193
|
-
# Keep a list of the etags we should have returned, so that we know when queries should contain them
|
194
|
-
# Array<String>
|
195
|
-
@expected_returned_etags = []
|
196
187
|
# List of expected stubs and the number of times they were supposed to mock
|
197
188
|
# Array< [ WebMock::RequestStub, Integer ] >
|
198
189
|
@expected_stubs = []
|
@@ -205,6 +196,7 @@ RSpec.configure do |config|
|
|
205
196
|
config.around do |example|
|
206
197
|
example.call
|
207
198
|
ensure
|
199
|
+
# This would dump the logs in case of debug mode
|
208
200
|
reset_nexus_mods
|
209
201
|
end
|
210
202
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nexus_mods
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Muriel Salvan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-04-
|
11
|
+
date: 2023-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2.7'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: faraday-http-cache
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '2.4'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '2.4'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: cacheable
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -131,10 +117,20 @@ extra_rdoc_files:
|
|
131
117
|
- CHANGELOG.md
|
132
118
|
- LICENSE.md
|
133
119
|
- README.md
|
120
|
+
- examples/api_caching.rb
|
121
|
+
- examples/api_limits.rb
|
122
|
+
- examples/games.rb
|
123
|
+
- examples/log_debug.rb
|
124
|
+
- examples/mods.rb
|
134
125
|
files:
|
135
126
|
- CHANGELOG.md
|
136
127
|
- LICENSE.md
|
137
128
|
- README.md
|
129
|
+
- examples/api_caching.rb
|
130
|
+
- examples/api_limits.rb
|
131
|
+
- examples/games.rb
|
132
|
+
- examples/log_debug.rb
|
133
|
+
- examples/mods.rb
|
138
134
|
- lib/nexus_mods.rb
|
139
135
|
- lib/nexus_mods/api/api_limits.rb
|
140
136
|
- lib/nexus_mods/api/category.rb
|
@@ -147,7 +143,6 @@ files:
|
|
147
143
|
- lib/nexus_mods/cacheable_with_expiry.rb
|
148
144
|
- lib/nexus_mods/core_extensions/cacheable/cache_adapters/persistent_json_adapter.rb
|
149
145
|
- lib/nexus_mods/core_extensions/cacheable/method_generator.rb
|
150
|
-
- lib/nexus_mods/file_cache.rb
|
151
146
|
- lib/nexus_mods/version.rb
|
152
147
|
- spec/nexus_mods_test/factories/games.rb
|
153
148
|
- spec/nexus_mods_test/factories/mod_files.rb
|
@@ -1,71 +0,0 @@
|
|
1
|
-
class NexusMods
|
2
|
-
|
3
|
-
# Simple key/value file cache
|
4
|
-
class FileCache
|
5
|
-
|
6
|
-
# Constructor
|
7
|
-
#
|
8
|
-
# Parameters::
|
9
|
-
# * *file* (String): File to use as a cache
|
10
|
-
def initialize(file)
|
11
|
-
@file = file
|
12
|
-
@cache_content = File.exist?(file) ? JSON.parse(File.read(file)) : {}
|
13
|
-
end
|
14
|
-
|
15
|
-
# Dump the cache in file
|
16
|
-
def dump
|
17
|
-
File.write(@file, @cache_content.to_json)
|
18
|
-
end
|
19
|
-
|
20
|
-
# Get the cache content as a Hash
|
21
|
-
#
|
22
|
-
# Result::
|
23
|
-
# * Hash<String, Object>: Cache content
|
24
|
-
def to_h
|
25
|
-
@cache_content
|
26
|
-
end
|
27
|
-
|
28
|
-
# Is a given key present in the cache?
|
29
|
-
#
|
30
|
-
# Parameters::
|
31
|
-
# * *key* (String): The key
|
32
|
-
# Result::
|
33
|
-
# * Boolean: Is a given key present in the cache?
|
34
|
-
def key?(key)
|
35
|
-
@cache_content.key?(key)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Read a key from the cache
|
39
|
-
#
|
40
|
-
# Parameters:
|
41
|
-
# * *key* (String): The cache key
|
42
|
-
# Result::
|
43
|
-
# * Object or nil: JSON-serializable object storing the value, or nil in case of cache-miss
|
44
|
-
def read(key)
|
45
|
-
@cache_content.key?(key) ? @cache_content[key] : nil
|
46
|
-
end
|
47
|
-
|
48
|
-
alias [] read
|
49
|
-
|
50
|
-
# Write a key/value in the cache
|
51
|
-
#
|
52
|
-
# Parameters:
|
53
|
-
# * *key* (String): The key
|
54
|
-
# * *value* (Object): JSON-serializable object storing the value
|
55
|
-
def write(key, value)
|
56
|
-
@cache_content[key] = value
|
57
|
-
end
|
58
|
-
|
59
|
-
alias []= write
|
60
|
-
|
61
|
-
# Delete a key in the cache
|
62
|
-
#
|
63
|
-
# Parameters:
|
64
|
-
# * *key* (String): The key
|
65
|
-
def delete(key)
|
66
|
-
@cache_content.delete(key)
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
70
|
-
|
71
|
-
end
|