nexus_mods 0.1.1 → 0.3.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 +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/nexus_mods/api/api_limits.rb +64 -0
- data/lib/nexus_mods/api/category.rb +54 -0
- data/lib/nexus_mods/api/game.rb +106 -0
- data/lib/nexus_mods/api/mod.rb +141 -0
- data/lib/nexus_mods/api/mod_file.rb +116 -0
- data/lib/nexus_mods/api/user.rb +55 -0
- data/lib/nexus_mods/api_client.rb +182 -0
- data/lib/nexus_mods/cacheable_api.rb +52 -0
- data/lib/nexus_mods/cacheable_with_expiry.rb +70 -0
- data/lib/nexus_mods/core_extensions/cacheable/cache_adapters/persistent_json_adapter.rb +62 -0
- data/lib/nexus_mods/core_extensions/cacheable/method_generator.rb +62 -0
- data/lib/nexus_mods/file_cache.rb +71 -0
- data/lib/nexus_mods/version.rb +1 -1
- data/lib/nexus_mods.rb +32 -86
- data/spec/nexus_mods_test/factories/games.rb +135 -0
- data/spec/nexus_mods_test/factories/mod_files.rb +113 -0
- data/spec/nexus_mods_test/factories/mods.rb +144 -0
- data/spec/nexus_mods_test/helpers.rb +39 -14
- data/spec/nexus_mods_test/scenarios/nexus_mods/{api_limits_spec.rb → api/api_limits_spec.rb} +10 -3
- data/spec/nexus_mods_test/scenarios/nexus_mods/api/game_spec.rb +93 -0
- data/spec/nexus_mods_test/scenarios/nexus_mods/api/mod_file_spec.rb +73 -0
- data/spec/nexus_mods_test/scenarios/nexus_mods/api/mod_spec.rb +62 -0
- data/spec/nexus_mods_test/scenarios/nexus_mods_caching_spec.rb +88 -0
- metadata +37 -13
- data/lib/nexus_mods/api_limits.rb +0 -44
- data/lib/nexus_mods/category.rb +0 -37
- data/lib/nexus_mods/game.rb +0 -78
- data/lib/nexus_mods/mod.rb +0 -106
- data/lib/nexus_mods/mod_file.rb +0 -86
- data/lib/nexus_mods/user.rb +0 -37
- data/spec/nexus_mods_test/scenarios/nexus_mods/game_spec.rb +0 -180
- data/spec/nexus_mods_test/scenarios/nexus_mods/mod_file_spec.rb +0 -140
- data/spec/nexus_mods_test/scenarios/nexus_mods/mod_spec.rb +0 -185
    
        data/lib/nexus_mods.rb
    CHANGED
    
    | @@ -1,14 +1,13 @@ | |
| 1 | 
            -
            require 'addressable/uri'
         | 
| 2 1 | 
             
            require 'json'
         | 
| 3 2 | 
             
            require 'time'
         | 
| 4 3 | 
             
            require 'tmpdir'
         | 
| 5 | 
            -
            require ' | 
| 6 | 
            -
            require 'nexus_mods/api_limits'
         | 
| 7 | 
            -
            require 'nexus_mods/category'
         | 
| 8 | 
            -
            require 'nexus_mods/game'
         | 
| 9 | 
            -
            require 'nexus_mods/user'
         | 
| 10 | 
            -
            require 'nexus_mods/mod'
         | 
| 11 | 
            -
            require 'nexus_mods/mod_file'
         | 
| 4 | 
            +
            require 'nexus_mods/api_client'
         | 
| 5 | 
            +
            require 'nexus_mods/api/api_limits'
         | 
| 6 | 
            +
            require 'nexus_mods/api/category'
         | 
| 7 | 
            +
            require 'nexus_mods/api/game'
         | 
| 8 | 
            +
            require 'nexus_mods/api/user'
         | 
| 9 | 
            +
            require 'nexus_mods/api/mod'
         | 
| 10 | 
            +
            require 'nexus_mods/api/mod_file'
         | 
| 12 11 |  | 
| 13 12 | 
             
            # Ruby API to access NexusMods REST API
         | 
| 14 13 | 
             
            class NexusMods
         | 
| @@ -40,27 +39,36 @@ class NexusMods | |
| 40 39 | 
             
              # * *game_domain_name* (String): Game domain name to query by default [default: 'skyrimspecialedition']
         | 
| 41 40 | 
             
              # * *mod_id* (Integer): Mod to query by default [default: 1]
         | 
| 42 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 | 
            +
              # * *api_cache_expiry* (Hash<Symbol,Integer>): Expiry times in seconds, per expiry key. Possible keys are:
         | 
| 44 | 
            +
              #   * *games*: Expiry associated to queries on games [default: 1 day]
         | 
| 45 | 
            +
              #   * *mod*: Expiry associated to queries on mod [default: 1 day]
         | 
| 46 | 
            +
              #   * *mod_files*: Expiry associated to queries on mod files [default: 1 day]
         | 
| 43 47 | 
             
              # * *logger* (Logger): The logger to be used for log messages [default: Logger.new(STDOUT)]
         | 
| 44 48 | 
             
              def initialize(
         | 
| 45 49 | 
             
                api_key: nil,
         | 
| 46 50 | 
             
                game_domain_name: 'skyrimspecialedition',
         | 
| 47 51 | 
             
                mod_id: 1,
         | 
| 48 52 | 
             
                file_id: 1,
         | 
| 53 | 
            +
                http_cache_file: "#{Dir.tmpdir}/nexus_mods_http_cache.json",
         | 
| 54 | 
            +
                api_cache_expiry: {},
         | 
| 49 55 | 
             
                logger: Logger.new($stdout)
         | 
| 50 56 | 
             
              )
         | 
| 51 | 
            -
                @api_key = api_key
         | 
| 52 57 | 
             
                @game_domain_name = game_domain_name
         | 
| 53 58 | 
             
                @mod_id = mod_id
         | 
| 54 59 | 
             
                @file_id = file_id
         | 
| 55 60 | 
             
                @logger = logger
         | 
| 56 61 | 
             
                @premium = false
         | 
| 57 | 
            -
                 | 
| 58 | 
            -
             | 
| 59 | 
            -
                   | 
| 60 | 
            -
             | 
| 62 | 
            +
                @api_client = ApiClient.new(
         | 
| 63 | 
            +
                  api_key:,
         | 
| 64 | 
            +
                  http_cache_file:,
         | 
| 65 | 
            +
                  api_cache_expiry:,
         | 
| 66 | 
            +
                  logger:
         | 
| 67 | 
            +
                )
         | 
| 68 | 
            +
             | 
| 61 69 | 
             
                # Check that the key is correct and know if the user is premium
         | 
| 62 70 | 
             
                begin
         | 
| 63 | 
            -
                  @premium = api('users/validate')['is_premium?']
         | 
| 71 | 
            +
                  @premium = @api_client.api('users/validate')['is_premium?']
         | 
| 64 72 | 
             
                rescue LimitsExceededError
         | 
| 65 73 | 
             
                  raise
         | 
| 66 74 | 
             
                rescue ApiError
         | 
| @@ -74,8 +82,8 @@ class NexusMods | |
| 74 82 | 
             
              # Result::
         | 
| 75 83 | 
             
              # * ApiLimits: API calls limits
         | 
| 76 84 | 
             
              def api_limits
         | 
| 77 | 
            -
                api_limits_headers = http('users/validate').headers
         | 
| 78 | 
            -
                ApiLimits.new(
         | 
| 85 | 
            +
                api_limits_headers = @api_client.http('users/validate').headers
         | 
| 86 | 
            +
                Api::ApiLimits.new(
         | 
| 79 87 | 
             
                  daily_limit: Integer(api_limits_headers['x-rl-daily-limit']),
         | 
| 80 88 | 
             
                  daily_remaining: Integer(api_limits_headers['x-rl-daily-remaining']),
         | 
| 81 89 | 
             
                  daily_reset: Time.parse(api_limits_headers['x-rl-daily-reset']).utc,
         | 
| @@ -90,7 +98,7 @@ class NexusMods | |
| 90 98 | 
             
              # Result::
         | 
| 91 99 | 
             
              # * Array<Game>: List of games
         | 
| 92 100 | 
             
              def games
         | 
| 93 | 
            -
                api('games').map do |game_json|
         | 
| 101 | 
            +
                @api_client.api('games').map do |game_json|
         | 
| 94 102 | 
             
                  # First create categories tree
         | 
| 95 103 | 
             
                  # Hash<Integer, [Category, Integer]>: Category and its parent category id, per category id
         | 
| 96 104 | 
             
                  categories = game_json['categories'].to_h do |category_json|
         | 
| @@ -98,7 +106,7 @@ class NexusMods | |
| 98 106 | 
             
                    [
         | 
| 99 107 | 
             
                      category_id,
         | 
| 100 108 | 
             
                      [
         | 
| 101 | 
            -
                        Category.new(
         | 
| 109 | 
            +
                        Api::Category.new(
         | 
| 102 110 | 
             
                          id: category_id,
         | 
| 103 111 | 
             
                          name: category_json['name']
         | 
| 104 112 | 
             
                        ),
         | 
| @@ -110,7 +118,7 @@ class NexusMods | |
| 110 118 | 
             
                    # Ignore missing parent categories: this situation happens.
         | 
| 111 119 | 
             
                    category.parent_category = categories[parent_category_id]&.first if parent_category_id
         | 
| 112 120 | 
             
                  end
         | 
| 113 | 
            -
                  Game.new(
         | 
| 121 | 
            +
                  Api::Game.new(
         | 
| 114 122 | 
             
                    id: game_json['id'],
         | 
| 115 123 | 
             
                    name: game_json['name'],
         | 
| 116 124 | 
             
                    forum_url: game_json['forum_url'],
         | 
| @@ -137,8 +145,8 @@ class NexusMods | |
| 137 145 | 
             
              # Result::
         | 
| 138 146 | 
             
              # * Mod: Mod information
         | 
| 139 147 | 
             
              def mod(game_domain_name: @game_domain_name, mod_id: @mod_id)
         | 
| 140 | 
            -
                mod_json = api "games/#{game_domain_name}/mods/#{mod_id}"
         | 
| 141 | 
            -
                Mod.new(
         | 
| 148 | 
            +
                mod_json = @api_client.api "games/#{game_domain_name}/mods/#{mod_id}"
         | 
| 149 | 
            +
                Api::Mod.new(
         | 
| 142 150 | 
             
                  uid: mod_json['uid'],
         | 
| 143 151 | 
             
                  mod_id: mod_json['mod_id'],
         | 
| 144 152 | 
             
                  game_id: mod_json['game_id'],
         | 
| @@ -152,7 +160,7 @@ class NexusMods | |
| 152 160 | 
             
                  contains_adult_content: mod_json['contains_adult_content'],
         | 
| 153 161 | 
             
                  status: mod_json['status'],
         | 
| 154 162 | 
             
                  available: mod_json['available'],
         | 
| 155 | 
            -
                  uploader: User.new(
         | 
| 163 | 
            +
                  uploader: Api::User.new(
         | 
| 156 164 | 
             
                    member_id: mod_json['user']['member_id'],
         | 
| 157 165 | 
             
                    member_group_id: mod_json['user']['member_group_id'],
         | 
| 158 166 | 
             
                    name: mod_json['user']['name'],
         | 
| @@ -185,11 +193,11 @@ class NexusMods | |
| 185 193 | 
             
              # Result::
         | 
| 186 194 | 
             
              # * Array<ModFile>: List of mod's files
         | 
| 187 195 | 
             
              def mod_files(game_domain_name: @game_domain_name, mod_id: @mod_id)
         | 
| 188 | 
            -
                api("games/#{game_domain_name}/mods/#{mod_id}/files")['files'].map do |file_json|
         | 
| 196 | 
            +
                @api_client.api("games/#{game_domain_name}/mods/#{mod_id}/files")['files'].map do |file_json|
         | 
| 189 197 | 
             
                  category_id = FILE_CATEGORIES[file_json['category_id']]
         | 
| 190 198 | 
             
                  raise "Unknown file category: #{file_json['category_id']}" if category_id.nil?
         | 
| 191 199 |  | 
| 192 | 
            -
                  ModFile.new(
         | 
| 200 | 
            +
                  Api::ModFile.new(
         | 
| 193 201 | 
             
                    ids: file_json['id'],
         | 
| 194 202 | 
             
                    uid: file_json['uid'],
         | 
| 195 203 | 
             
                    id: file_json['file_id'],
         | 
| @@ -210,66 +218,4 @@ class NexusMods | |
| 210 218 | 
             
                end
         | 
| 211 219 | 
             
              end
         | 
| 212 220 |  | 
| 213 | 
            -
              private
         | 
| 214 | 
            -
             | 
| 215 | 
            -
              # Send an HTTP request to the API and get back the answer as a JSON
         | 
| 216 | 
            -
              #
         | 
| 217 | 
            -
              # Parameters::
         | 
| 218 | 
            -
              # * *path* (String): API path to contact (from v1/ and without .json)
         | 
| 219 | 
            -
              # * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
         | 
| 220 | 
            -
              # Result::
         | 
| 221 | 
            -
              # * Object: The JSON response
         | 
| 222 | 
            -
              def api(path, verb: :get)
         | 
| 223 | 
            -
                res = http(path, verb:)
         | 
| 224 | 
            -
                json = JSON.parse(res.body)
         | 
| 225 | 
            -
                uri = api_uri(path)
         | 
| 226 | 
            -
                @logger.debug "[API call] - #{verb} #{uri} => #{res.status}\n#{
         | 
| 227 | 
            -
                    JSON.
         | 
| 228 | 
            -
                      pretty_generate(json).
         | 
| 229 | 
            -
                      split("\n").
         | 
| 230 | 
            -
                      map { |line| "  #{line}" }.
         | 
| 231 | 
            -
                      join("\n")
         | 
| 232 | 
            -
                  }\n#{
         | 
| 233 | 
            -
                    res.
         | 
| 234 | 
            -
                      headers.
         | 
| 235 | 
            -
                      map { |header, value| "  #{header}: #{value}" }.
         | 
| 236 | 
            -
                      join("\n")
         | 
| 237 | 
            -
                  }"
         | 
| 238 | 
            -
                case res.status
         | 
| 239 | 
            -
                when 200
         | 
| 240 | 
            -
                  # Happy
         | 
| 241 | 
            -
                when 429
         | 
| 242 | 
            -
                  # Some limits of the API have been reached
         | 
| 243 | 
            -
                  raise LimitsExceededError, "Exceeding limits of API calls: #{res.headers.select { |header, _value| header =~ /^x-rl-.+$/ }}"
         | 
| 244 | 
            -
                else
         | 
| 245 | 
            -
                  raise ApiError, "API #{uri} returned error code #{res.status}" unless res.status == '200'
         | 
| 246 | 
            -
                end
         | 
| 247 | 
            -
                json
         | 
| 248 | 
            -
              end
         | 
| 249 | 
            -
             | 
| 250 | 
            -
              # Send an HTTP request to the API and get back the HTTP response
         | 
| 251 | 
            -
              #
         | 
| 252 | 
            -
              # Parameters::
         | 
| 253 | 
            -
              # * *path* (String): API path to contact (from v1/ and without .json)
         | 
| 254 | 
            -
              # * *verb* (Symbol): Verb to be used (:get, :post...) [default: :get]
         | 
| 255 | 
            -
              # Result::
         | 
| 256 | 
            -
              # * Faraday::Response: The HTTP response
         | 
| 257 | 
            -
              def http(path, verb: :get)
         | 
| 258 | 
            -
                @http_client.send(verb) do |req|
         | 
| 259 | 
            -
                  req.url api_uri(path)
         | 
| 260 | 
            -
                  req.headers['apikey'] = @api_key
         | 
| 261 | 
            -
                  req.headers['User-Agent'] = "nexus_mods (#{RUBY_PLATFORM}) Ruby/#{RUBY_VERSION}"
         | 
| 262 | 
            -
                end
         | 
| 263 | 
            -
              end
         | 
| 264 | 
            -
             | 
| 265 | 
            -
              # Get the real URI to query for a given API path
         | 
| 266 | 
            -
              #
         | 
| 267 | 
            -
              # Parameters::
         | 
| 268 | 
            -
              # * *path* (String): API path to contact (from v1/ and without .json)
         | 
| 269 | 
            -
              # Result::
         | 
| 270 | 
            -
              # * String: The URI
         | 
| 271 | 
            -
              def api_uri(path)
         | 
| 272 | 
            -
                "https://api.nexusmods.com/v1/#{path}.json"
         | 
| 273 | 
            -
              end
         | 
| 274 | 
            -
             | 
| 275 221 | 
             
            end
         | 
| @@ -0,0 +1,135 @@ | |
| 1 | 
            +
            module NexusModsTest
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              module Factories
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                module Games
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  # Test game with id 100
         | 
| 8 | 
            +
                  def json_game100
         | 
| 9 | 
            +
                    {
         | 
| 10 | 
            +
                      'id' => 100,
         | 
| 11 | 
            +
                      'name' => 'Morrowind',
         | 
| 12 | 
            +
                      'forum_url' => 'https://forums.nexusmods.com/index.php?/forum/111-morrowind/',
         | 
| 13 | 
            +
                      'nexusmods_url' => 'http://www.nexusmods.com/morrowind',
         | 
| 14 | 
            +
                      'genre' => 'RPG',
         | 
| 15 | 
            +
                      'file_count' => 14_143,
         | 
| 16 | 
            +
                      'downloads' => 20_414_985,
         | 
| 17 | 
            +
                      'domain_name' => 'morrowind',
         | 
| 18 | 
            +
                      'approved_date' => 1,
         | 
| 19 | 
            +
                      'file_views' => 100_014_750,
         | 
| 20 | 
            +
                      'authors' => 2062,
         | 
| 21 | 
            +
                      'file_endorsements' => 719_262,
         | 
| 22 | 
            +
                      'mods' => 6080,
         | 
| 23 | 
            +
                      'categories' => [
         | 
| 24 | 
            +
                        {
         | 
| 25 | 
            +
                          'category_id' => 1,
         | 
| 26 | 
            +
                          'name' => 'Morrowind',
         | 
| 27 | 
            +
                          'parent_category' => false
         | 
| 28 | 
            +
                        },
         | 
| 29 | 
            +
                        {
         | 
| 30 | 
            +
                          'category_id' => 2,
         | 
| 31 | 
            +
                          'name' => 'Buildings',
         | 
| 32 | 
            +
                          'parent_category' => 1
         | 
| 33 | 
            +
                        }
         | 
| 34 | 
            +
                      ]
         | 
| 35 | 
            +
                    }
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  # Test game with id 101
         | 
| 39 | 
            +
                  def json_game101
         | 
| 40 | 
            +
                    {
         | 
| 41 | 
            +
                      'id' => 101,
         | 
| 42 | 
            +
                      'name' => 'Oblivion',
         | 
| 43 | 
            +
                      'forum_url' => 'https://forums.nexusmods.com/index.php?/forum/131-oblivion/',
         | 
| 44 | 
            +
                      'nexusmods_url' => 'http://www.nexusmods.com/oblivion',
         | 
| 45 | 
            +
                      'genre' => 'RPG',
         | 
| 46 | 
            +
                      'file_count' => 52_775,
         | 
| 47 | 
            +
                      'downloads' => 187_758_634,
         | 
| 48 | 
            +
                      'domain_name' => 'oblivion',
         | 
| 49 | 
            +
                      'approved_date' => 1,
         | 
| 50 | 
            +
                      'file_views' => 880_508_188,
         | 
| 51 | 
            +
                      'authors' => 10_673,
         | 
| 52 | 
            +
                      'file_endorsements' => 4_104_067,
         | 
| 53 | 
            +
                      'mods' => 29_220,
         | 
| 54 | 
            +
                      'categories' => [
         | 
| 55 | 
            +
                        {
         | 
| 56 | 
            +
                          'category_id' => 20,
         | 
| 57 | 
            +
                          'name' => 'Oblivion',
         | 
| 58 | 
            +
                          'parent_category' => false
         | 
| 59 | 
            +
                        },
         | 
| 60 | 
            +
                        {
         | 
| 61 | 
            +
                          'category_id' => 22,
         | 
| 62 | 
            +
                          'name' => 'New structures - Buildings',
         | 
| 63 | 
            +
                          'parent_category' => 20
         | 
| 64 | 
            +
                        }
         | 
| 65 | 
            +
                      ]
         | 
| 66 | 
            +
                    }
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  # Expect a game to be the test game of id 100
         | 
| 70 | 
            +
                  #
         | 
| 71 | 
            +
                  # Parameters::
         | 
| 72 | 
            +
                  # * *game* (NexusMods::Api::Game): Game to validate
         | 
| 73 | 
            +
                  def expect_game_to_be_game100(game)
         | 
| 74 | 
            +
                    expect(game.id).to eq 100
         | 
| 75 | 
            +
                    expect(game.name).to eq 'Morrowind'
         | 
| 76 | 
            +
                    expect(game.forum_url).to eq 'https://forums.nexusmods.com/index.php?/forum/111-morrowind/'
         | 
| 77 | 
            +
                    expect(game.nexusmods_url).to eq 'http://www.nexusmods.com/morrowind'
         | 
| 78 | 
            +
                    expect(game.genre).to eq 'RPG'
         | 
| 79 | 
            +
                    expect(game.files_count).to eq 14_143
         | 
| 80 | 
            +
                    expect(game.downloads_count).to eq 20_414_985
         | 
| 81 | 
            +
                    expect(game.domain_name).to eq 'morrowind'
         | 
| 82 | 
            +
                    expect(game.approved_date).to eq Time.parse('1970-01-01 00:00:01 +0000')
         | 
| 83 | 
            +
                    expect(game.files_views).to eq 100_014_750
         | 
| 84 | 
            +
                    expect(game.authors_count).to eq 2062
         | 
| 85 | 
            +
                    expect(game.files_endorsements).to eq 719_262
         | 
| 86 | 
            +
                    expect(game.mods_count).to eq 6080
         | 
| 87 | 
            +
                    game_categories = game.categories
         | 
| 88 | 
            +
                    expect(game_categories.size).to eq 2
         | 
| 89 | 
            +
                    expect(game_categories.first.id).to eq 1
         | 
| 90 | 
            +
                    expect(game_categories.first.name).to eq 'Morrowind'
         | 
| 91 | 
            +
                    expect(game_categories.first.parent_category).to be_nil
         | 
| 92 | 
            +
                    expect(game_categories[1].id).to eq 2
         | 
| 93 | 
            +
                    expect(game_categories[1].name).to eq 'Buildings'
         | 
| 94 | 
            +
                    expect(game_categories[1].parent_category).not_to be_nil
         | 
| 95 | 
            +
                    expect(game_categories[1].parent_category.id).to eq 1
         | 
| 96 | 
            +
                    expect(game_categories[1].parent_category.name).to eq 'Morrowind'
         | 
| 97 | 
            +
                    expect(game_categories[1].parent_category.parent_category).to be_nil
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  # Expect a game to be the test game of id 101
         | 
| 101 | 
            +
                  #
         | 
| 102 | 
            +
                  # Parameters::
         | 
| 103 | 
            +
                  # * *game* (NexusMods::Api::Game): Game to validate
         | 
| 104 | 
            +
                  def expect_game_to_be_game101(game)
         | 
| 105 | 
            +
                    expect(game.id).to eq 101
         | 
| 106 | 
            +
                    expect(game.name).to eq 'Oblivion'
         | 
| 107 | 
            +
                    expect(game.forum_url).to eq 'https://forums.nexusmods.com/index.php?/forum/131-oblivion/'
         | 
| 108 | 
            +
                    expect(game.nexusmods_url).to eq 'http://www.nexusmods.com/oblivion'
         | 
| 109 | 
            +
                    expect(game.genre).to eq 'RPG'
         | 
| 110 | 
            +
                    expect(game.files_count).to eq 52_775
         | 
| 111 | 
            +
                    expect(game.downloads_count).to eq 187_758_634
         | 
| 112 | 
            +
                    expect(game.domain_name).to eq 'oblivion'
         | 
| 113 | 
            +
                    expect(game.approved_date).to eq Time.parse('1970-01-01 00:00:01 +0000')
         | 
| 114 | 
            +
                    expect(game.files_views).to eq 880_508_188
         | 
| 115 | 
            +
                    expect(game.authors_count).to eq 10_673
         | 
| 116 | 
            +
                    expect(game.files_endorsements).to eq 4_104_067
         | 
| 117 | 
            +
                    expect(game.mods_count).to eq 29_220
         | 
| 118 | 
            +
                    game_categories = game.categories
         | 
| 119 | 
            +
                    expect(game_categories.size).to eq 2
         | 
| 120 | 
            +
                    expect(game_categories.first.id).to eq 20
         | 
| 121 | 
            +
                    expect(game_categories.first.name).to eq 'Oblivion'
         | 
| 122 | 
            +
                    expect(game_categories.first.parent_category).to be_nil
         | 
| 123 | 
            +
                    expect(game_categories[1].id).to eq 22
         | 
| 124 | 
            +
                    expect(game_categories[1].name).to eq 'New structures - Buildings'
         | 
| 125 | 
            +
                    expect(game_categories[1].parent_category).not_to be_nil
         | 
| 126 | 
            +
                    expect(game_categories[1].parent_category.id).to eq 20
         | 
| 127 | 
            +
                    expect(game_categories[1].parent_category.name).to eq 'Oblivion'
         | 
| 128 | 
            +
                    expect(game_categories[1].parent_category.parent_category).to be_nil
         | 
| 129 | 
            +
                  end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
              end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
            end
         | 
| @@ -0,0 +1,113 @@ | |
| 1 | 
            +
            module NexusModsTest
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              module Factories
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                module ModFiles
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  # Test mod file with id 2472
         | 
| 8 | 
            +
                  def json_mod_file2472
         | 
| 9 | 
            +
                    {
         | 
| 10 | 
            +
                      'id' => [
         | 
| 11 | 
            +
                        2472,
         | 
| 12 | 
            +
                        1704
         | 
| 13 | 
            +
                      ],
         | 
| 14 | 
            +
                      'uid' => 7_318_624_274_856,
         | 
| 15 | 
            +
                      'file_id' => 2472,
         | 
| 16 | 
            +
                      'name' => 'ApachiiSkyHair_v_1_6_Full',
         | 
| 17 | 
            +
                      'version' => '1.6.Full',
         | 
| 18 | 
            +
                      'category_id' => 4,
         | 
| 19 | 
            +
                      'category_name' => 'OLD_VERSION',
         | 
| 20 | 
            +
                      'is_primary' => false,
         | 
| 21 | 
            +
                      'file_name' => 'ApachiiSkyHair_v_1_6_Full-2014-1-6-Full.7z',
         | 
| 22 | 
            +
                      'uploaded_timestamp' => 1_477_967_645,
         | 
| 23 | 
            +
                      'uploaded_time' => '2016-11-01T02:34:05.000+00:00',
         | 
| 24 | 
            +
                      'mod_version' => '1.6.Full',
         | 
| 25 | 
            +
                      'external_virus_scan_url' => 'https://www.virustotal.com/file/3dcc96dce0b846ea643d626c48bd6ad08752da8232f3d29be644d36e1fd627cf/analysis/1477978674/',
         | 
| 26 | 
            +
                      'description' => '[b][color=orange] NOT optimized meshes. Standalone. Adds 42 new hairstyles for females, 21 hair for males and 5 hairs for Female Khajiit- 2 hairs for Male Khajiit[/color][/b]  ',
         | 
| 27 | 
            +
                      'size' => 304_347,
         | 
| 28 | 
            +
                      'size_kb' => 304_347,
         | 
| 29 | 
            +
                      'size_in_bytes' => 309_251_227,
         | 
| 30 | 
            +
                      'changelog_html' => nil,
         | 
| 31 | 
            +
                      'content_preview_link' => 'https://file-metadata.nexusmods.com/file/nexus-files-meta/1704/2014/ApachiiSkyHair_v_1_6_Full-2014-1-6-Full.7z.json'
         | 
| 32 | 
            +
                    }
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  # Test mod file with id 2487
         | 
| 36 | 
            +
                  def json_mod_file2487
         | 
| 37 | 
            +
                    {
         | 
| 38 | 
            +
                      'id' => [
         | 
| 39 | 
            +
                        2487,
         | 
| 40 | 
            +
                        1705
         | 
| 41 | 
            +
                      ],
         | 
| 42 | 
            +
                      'uid' => 7_318_624_274_857,
         | 
| 43 | 
            +
                      'file_id' => 2487,
         | 
| 44 | 
            +
                      'name' => 'ApachiiSkyHairMale_v_1_2',
         | 
| 45 | 
            +
                      'version' => '1.2',
         | 
| 46 | 
            +
                      'category_id' => 4,
         | 
| 47 | 
            +
                      'category_name' => 'OLD_VERSION',
         | 
| 48 | 
            +
                      'is_primary' => false,
         | 
| 49 | 
            +
                      'file_name' => 'ApachiiSkyHairMale_v_1_2-2014-1-2.7z',
         | 
| 50 | 
            +
                      'uploaded_timestamp' => 1_477_968_373,
         | 
| 51 | 
            +
                      'uploaded_time' => '2016-11-01T02:46:13.000+00:00',
         | 
| 52 | 
            +
                      'mod_version' => '1.2',
         | 
| 53 | 
            +
                      'external_virus_scan_url' => 'https://www.virustotal.com/file/3e86106233499ac43383c32ce4a2d8e162dc6e940b4d228f649a701b71ee5676/analysis/1477979366/',
         | 
| 54 | 
            +
                      'description' => 'NOT optimezed meshes. Standalone 55 Male hairs -  Not included in ApachiiSkyHair v_1_6_Full ',
         | 
| 55 | 
            +
                      'size' => 204_347,
         | 
| 56 | 
            +
                      'size_kb' => 204_347,
         | 
| 57 | 
            +
                      'size_in_bytes' => 209_251_227,
         | 
| 58 | 
            +
                      'changelog_html' => nil,
         | 
| 59 | 
            +
                      'content_preview_link' => 'https://file-metadata.nexusmods.com/file/nexus-files-meta/1704/2014/ApachiiSkyHairMale_v_1_2-2014-1-2.7z.json'
         | 
| 60 | 
            +
                    }
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  # Expect a mod's file to be the example one with id 2472
         | 
| 64 | 
            +
                  #
         | 
| 65 | 
            +
                  # Parameters::
         | 
| 66 | 
            +
                  # * *mod_file* (NexusMods::Api::File): Mod file to validate
         | 
| 67 | 
            +
                  def expect_mod_file_to_be2472(mod_file)
         | 
| 68 | 
            +
                    expect(mod_file.ids).to eq [2472, 1704]
         | 
| 69 | 
            +
                    expect(mod_file.uid).to eq 7_318_624_274_856
         | 
| 70 | 
            +
                    expect(mod_file.id).to eq 2472
         | 
| 71 | 
            +
                    expect(mod_file.name).to eq 'ApachiiSkyHair_v_1_6_Full'
         | 
| 72 | 
            +
                    expect(mod_file.version).to eq '1.6.Full'
         | 
| 73 | 
            +
                    expect(mod_file.category_id).to eq :old
         | 
| 74 | 
            +
                    expect(mod_file.category_name).to eq 'OLD_VERSION'
         | 
| 75 | 
            +
                    expect(mod_file.is_primary).to be false
         | 
| 76 | 
            +
                    expect(mod_file.size).to eq 309_251_227
         | 
| 77 | 
            +
                    expect(mod_file.file_name).to eq 'ApachiiSkyHair_v_1_6_Full-2014-1-6-Full.7z'
         | 
| 78 | 
            +
                    expect(mod_file.uploaded_time).to eq Time.parse('2016-11-01T02:34:05.000+00:00')
         | 
| 79 | 
            +
                    expect(mod_file.mod_version).to eq '1.6.Full'
         | 
| 80 | 
            +
                    expect(mod_file.external_virus_scan_url).to eq 'https://www.virustotal.com/file/3dcc96dce0b846ea643d626c48bd6ad08752da8232f3d29be644d36e1fd627cf/analysis/1477978674/'
         | 
| 81 | 
            +
                    expect(mod_file.description).to eq '[b][color=orange] NOT optimized meshes. Standalone. Adds 42 new hairstyles for females, 21 hair for males and 5 hairs for Female Khajiit- 2 hairs for Male Khajiit[/color][/b]  '
         | 
| 82 | 
            +
                    expect(mod_file.changelog_html).to be_nil
         | 
| 83 | 
            +
                    expect(mod_file.content_preview_url).to eq 'https://file-metadata.nexusmods.com/file/nexus-files-meta/1704/2014/ApachiiSkyHair_v_1_6_Full-2014-1-6-Full.7z.json'
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  # Expect a mod's file to be the example one with id 2487
         | 
| 87 | 
            +
                  #
         | 
| 88 | 
            +
                  # Parameters::
         | 
| 89 | 
            +
                  # * *mod_file* (NexusMods::Api::File): Mod file to validate
         | 
| 90 | 
            +
                  def expect_mod_file_to_be2487(mod_file)
         | 
| 91 | 
            +
                    expect(mod_file.ids).to eq [2487, 1705]
         | 
| 92 | 
            +
                    expect(mod_file.uid).to eq 7_318_624_274_857
         | 
| 93 | 
            +
                    expect(mod_file.id).to eq 2487
         | 
| 94 | 
            +
                    expect(mod_file.name).to eq 'ApachiiSkyHairMale_v_1_2'
         | 
| 95 | 
            +
                    expect(mod_file.version).to eq '1.2'
         | 
| 96 | 
            +
                    expect(mod_file.category_id).to eq :old
         | 
| 97 | 
            +
                    expect(mod_file.category_name).to eq 'OLD_VERSION'
         | 
| 98 | 
            +
                    expect(mod_file.is_primary).to be false
         | 
| 99 | 
            +
                    expect(mod_file.size).to eq 209_251_227
         | 
| 100 | 
            +
                    expect(mod_file.file_name).to eq 'ApachiiSkyHairMale_v_1_2-2014-1-2.7z'
         | 
| 101 | 
            +
                    expect(mod_file.uploaded_time).to eq Time.parse('2016-11-01T02:46:13.000+00:00')
         | 
| 102 | 
            +
                    expect(mod_file.mod_version).to eq '1.2'
         | 
| 103 | 
            +
                    expect(mod_file.external_virus_scan_url).to eq 'https://www.virustotal.com/file/3e86106233499ac43383c32ce4a2d8e162dc6e940b4d228f649a701b71ee5676/analysis/1477979366/'
         | 
| 104 | 
            +
                    expect(mod_file.description).to eq 'NOT optimezed meshes. Standalone 55 Male hairs -  Not included in ApachiiSkyHair v_1_6_Full '
         | 
| 105 | 
            +
                    expect(mod_file.changelog_html).to be_nil
         | 
| 106 | 
            +
                    expect(mod_file.content_preview_url).to eq 'https://file-metadata.nexusmods.com/file/nexus-files-meta/1704/2014/ApachiiSkyHairMale_v_1_2-2014-1-2.7z.json'
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
              end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            end
         | 
| @@ -0,0 +1,144 @@ | |
| 1 | 
            +
            module NexusModsTest
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              module Factories
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                module Mods
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  # Example of JSON object returned by the API for a mod information, having all possible fields
         | 
| 8 | 
            +
                  def json_complete_mod
         | 
| 9 | 
            +
                    {
         | 
| 10 | 
            +
                      'name' => 'ApachiiSkyHair SSE',
         | 
| 11 | 
            +
                      'summary' => 'New Female and Male Hairstyles for Humans, Elves and Orcs. Converted hair from Sims2 and Sims3.<br />Standalone version.',
         | 
| 12 | 
            +
                      'description' => 'Mod description',
         | 
| 13 | 
            +
                      'picture_url' => 'https://staticdelivery.nexusmods.com/mods/1704/images/10168-1-1392817986.jpg',
         | 
| 14 | 
            +
                      'mod_downloads' => 13_634_545,
         | 
| 15 | 
            +
                      'mod_unique_downloads' => 4_052_221,
         | 
| 16 | 
            +
                      'uid' => 7_318_624_272_650,
         | 
| 17 | 
            +
                      'mod_id' => 2014,
         | 
| 18 | 
            +
                      'game_id' => 1704,
         | 
| 19 | 
            +
                      'allow_rating' => true,
         | 
| 20 | 
            +
                      'domain_name' => 'skyrimspecialedition',
         | 
| 21 | 
            +
                      'category_id' => 26,
         | 
| 22 | 
            +
                      'version' => '1.6.Full',
         | 
| 23 | 
            +
                      'endorsement_count' => 298_845,
         | 
| 24 | 
            +
                      'created_timestamp' => 1_477_972_056,
         | 
| 25 | 
            +
                      'created_time' => '2016-11-01T03:47:36.000+00:00',
         | 
| 26 | 
            +
                      'updated_timestamp' => 1_507_398_546,
         | 
| 27 | 
            +
                      'updated_time' => '2017-10-07T17:49:06.000+00:00',
         | 
| 28 | 
            +
                      'author' => 'Apachii',
         | 
| 29 | 
            +
                      'uploaded_by' => 'apachii',
         | 
| 30 | 
            +
                      'uploaded_users_profile_url' => 'http://www.nexusmods.com/games/users/283148',
         | 
| 31 | 
            +
                      'contains_adult_content' => false,
         | 
| 32 | 
            +
                      'status' => 'published',
         | 
| 33 | 
            +
                      'available' => true,
         | 
| 34 | 
            +
                      'user' => {
         | 
| 35 | 
            +
                        'member_id' => 283_148,
         | 
| 36 | 
            +
                        'member_group_id' => 27,
         | 
| 37 | 
            +
                        'name' => 'apachii'
         | 
| 38 | 
            +
                      },
         | 
| 39 | 
            +
                      'endorsement' => {
         | 
| 40 | 
            +
                        'endorse_status' => 'Undecided',
         | 
| 41 | 
            +
                        'timestamp' => nil,
         | 
| 42 | 
            +
                        'version' => nil
         | 
| 43 | 
            +
                      }
         | 
| 44 | 
            +
                    }
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  # Example of JSON object returned by the API for a mod information, having minimum fields
         | 
| 48 | 
            +
                  def json_partial_mod
         | 
| 49 | 
            +
                    {
         | 
| 50 | 
            +
                      'mod_downloads' => 13_634_545,
         | 
| 51 | 
            +
                      'mod_unique_downloads' => 4_052_221,
         | 
| 52 | 
            +
                      'uid' => 7_318_624_272_650,
         | 
| 53 | 
            +
                      'mod_id' => 2014,
         | 
| 54 | 
            +
                      'game_id' => 1704,
         | 
| 55 | 
            +
                      'allow_rating' => true,
         | 
| 56 | 
            +
                      'domain_name' => 'skyrimspecialedition',
         | 
| 57 | 
            +
                      'category_id' => 26,
         | 
| 58 | 
            +
                      'version' => '1.6.Full',
         | 
| 59 | 
            +
                      'endorsement_count' => 298_845,
         | 
| 60 | 
            +
                      'created_timestamp' => 1_477_972_056,
         | 
| 61 | 
            +
                      'created_time' => '2016-11-01T03:47:36.000+00:00',
         | 
| 62 | 
            +
                      'updated_timestamp' => 1_507_398_546,
         | 
| 63 | 
            +
                      'updated_time' => '2017-10-07T17:49:06.000+00:00',
         | 
| 64 | 
            +
                      'author' => 'Apachii',
         | 
| 65 | 
            +
                      'uploaded_by' => 'apachii',
         | 
| 66 | 
            +
                      'uploaded_users_profile_url' => 'http://www.nexusmods.com/games/users/283148',
         | 
| 67 | 
            +
                      'contains_adult_content' => false,
         | 
| 68 | 
            +
                      'status' => 'published',
         | 
| 69 | 
            +
                      'available' => true,
         | 
| 70 | 
            +
                      'user' => {
         | 
| 71 | 
            +
                        'member_id' => 283_148,
         | 
| 72 | 
            +
                        'member_group_id' => 27,
         | 
| 73 | 
            +
                        'name' => 'apachii'
         | 
| 74 | 
            +
                      }
         | 
| 75 | 
            +
                    }
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                  # Expect a mod to be the example complete one
         | 
| 79 | 
            +
                  #
         | 
| 80 | 
            +
                  # Parameters::
         | 
| 81 | 
            +
                  # * *mod* (NexusMods::Api::Mod): Mod to validate
         | 
| 82 | 
            +
                  def expect_mod_to_be_complete(mod)
         | 
| 83 | 
            +
                    expect(mod.name).to eq 'ApachiiSkyHair SSE'
         | 
| 84 | 
            +
                    expect(mod.summary).to eq 'New Female and Male Hairstyles for Humans, Elves and Orcs. Converted hair from Sims2 and Sims3.<br />Standalone version.'
         | 
| 85 | 
            +
                    expect(mod.description).to eq 'Mod description'
         | 
| 86 | 
            +
                    expect(mod.picture_url).to eq 'https://staticdelivery.nexusmods.com/mods/1704/images/10168-1-1392817986.jpg'
         | 
| 87 | 
            +
                    expect(mod.downloads_count).to eq 13_634_545
         | 
| 88 | 
            +
                    expect(mod.unique_downloads_count).to eq 4_052_221
         | 
| 89 | 
            +
                    expect(mod.uid).to eq 7_318_624_272_650
         | 
| 90 | 
            +
                    expect(mod.mod_id).to eq 2014
         | 
| 91 | 
            +
                    expect(mod.game_id).to eq 1704
         | 
| 92 | 
            +
                    expect(mod.allow_rating).to be true
         | 
| 93 | 
            +
                    expect(mod.domain_name).to eq 'skyrimspecialedition'
         | 
| 94 | 
            +
                    expect(mod.category_id).to eq 26
         | 
| 95 | 
            +
                    expect(mod.version).to eq '1.6.Full'
         | 
| 96 | 
            +
                    expect(mod.endorsements_count).to eq 298_845
         | 
| 97 | 
            +
                    expect(mod.created_time).to eq Time.parse('2016-11-01T03:47:36.000+00:00')
         | 
| 98 | 
            +
                    expect(mod.updated_time).to eq Time.parse('2017-10-07T17:49:06.000+00:00')
         | 
| 99 | 
            +
                    expect(mod.author).to eq 'Apachii'
         | 
| 100 | 
            +
                    expect(mod.contains_adult_content).to be false
         | 
| 101 | 
            +
                    expect(mod.status).to eq 'published'
         | 
| 102 | 
            +
                    expect(mod.available).to be true
         | 
| 103 | 
            +
                    expect(mod.uploader.member_id).to eq 283_148
         | 
| 104 | 
            +
                    expect(mod.uploader.member_group_id).to eq 27
         | 
| 105 | 
            +
                    expect(mod.uploader.name).to eq 'apachii'
         | 
| 106 | 
            +
                    expect(mod.uploader.profile_url).to eq 'http://www.nexusmods.com/games/users/283148'
         | 
| 107 | 
            +
                  end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                  # Expect a mod to be the example partial one
         | 
| 110 | 
            +
                  #
         | 
| 111 | 
            +
                  # Parameters::
         | 
| 112 | 
            +
                  # * *mod* (NexusMods::Api::Mod): Mod to validate
         | 
| 113 | 
            +
                  def expect_mod_to_be_partial(mod)
         | 
| 114 | 
            +
                    expect(mod.name).to be_nil
         | 
| 115 | 
            +
                    expect(mod.summary).to be_nil
         | 
| 116 | 
            +
                    expect(mod.description).to be_nil
         | 
| 117 | 
            +
                    expect(mod.picture_url).to be_nil
         | 
| 118 | 
            +
                    expect(mod.downloads_count).to eq 13_634_545
         | 
| 119 | 
            +
                    expect(mod.unique_downloads_count).to eq 4_052_221
         | 
| 120 | 
            +
                    expect(mod.uid).to eq 7_318_624_272_650
         | 
| 121 | 
            +
                    expect(mod.mod_id).to eq 2014
         | 
| 122 | 
            +
                    expect(mod.game_id).to eq 1704
         | 
| 123 | 
            +
                    expect(mod.allow_rating).to be true
         | 
| 124 | 
            +
                    expect(mod.domain_name).to eq 'skyrimspecialedition'
         | 
| 125 | 
            +
                    expect(mod.category_id).to eq 26
         | 
| 126 | 
            +
                    expect(mod.version).to eq '1.6.Full'
         | 
| 127 | 
            +
                    expect(mod.endorsements_count).to eq 298_845
         | 
| 128 | 
            +
                    expect(mod.created_time).to eq Time.parse('2016-11-01T03:47:36.000+00:00')
         | 
| 129 | 
            +
                    expect(mod.updated_time).to eq Time.parse('2017-10-07T17:49:06.000+00:00')
         | 
| 130 | 
            +
                    expect(mod.author).to eq 'Apachii'
         | 
| 131 | 
            +
                    expect(mod.contains_adult_content).to be false
         | 
| 132 | 
            +
                    expect(mod.status).to eq 'published'
         | 
| 133 | 
            +
                    expect(mod.available).to be true
         | 
| 134 | 
            +
                    expect(mod.uploader.member_id).to eq 283_148
         | 
| 135 | 
            +
                    expect(mod.uploader.member_group_id).to eq 27
         | 
| 136 | 
            +
                    expect(mod.uploader.name).to eq 'apachii'
         | 
| 137 | 
            +
                    expect(mod.uploader.profile_url).to eq 'http://www.nexusmods.com/games/users/283148'
         | 
| 138 | 
            +
                  end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
              end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
            end
         |