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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6490bd894c40f7806bb126cb70ade888ab5121499b86ff2cc959ee663dd223d
4
- data.tar.gz: d9fdeb53c88c27ce9503e328a261ddb31a0facbbe0b6c35c304628c6e9580790
3
+ metadata.gz: c2325f79c99fb7304d16a0e4b12682229d8187ae30f6d0321796a4d3d5e587fb
4
+ data.tar.gz: 1cce0b8b4a82f8c903be9d6b7441d739d3b5dffa375c699444e8f2e87af4f2be
5
5
  SHA512:
6
- metadata.gz: 7c8f9576edd38dab0076f24ad1d396973c9d1ab8542ff4eef548d9b49758f85b6da465465b10b1e53dbb2db6d8312f31e9b88649916f654e7b8c1a12092aaadb
7
- data.tar.gz: 1b2d1e915594af39c9bd793a8f216fc2796024e2729a2b5596c35b546e04c8e2bb96dce77aa35e11f5d7a98fba1652c79f77f01648a363cfdf9b93ec40ee7602
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][link-author]
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
- @http_cache = http_cache_file.nil? ? nil : FileCache.new(http_cache_file)
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
@@ -1,5 +1,5 @@
1
1
  class NexusMods
2
2
 
3
- VERSION = '1.1.0'
3
+ VERSION = '2.0.0'
4
4
 
5
5
  end
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: 1.1.0
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-10 00:00:00.000000000 Z
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