From 1fafb328d971a4a2a3a789c6e8f9c15d79598d7c Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:17:47 +0100 Subject: [PATCH 01/25] feat: upgrade to v2 --- .github/workflows/main.yml | 32 ++++++ .gitignore | 3 + .rspec | 3 + .rubocop.yml | 9 ++ .vscode/extensions.json | 7 ++ CHANGELOG.md | 5 + CODE_OF_CONDUCT.md | 10 ++ Gemfile | 4 +- Gemfile.lock | 190 ++++++++++++++++++++++++++++++----- README.md | 140 +++----------------------- Rakefile | 12 ++- bin/console | 2 +- lib/xivapi.rb | 86 +++++++++------- lib/xivapi/assets.rb | 24 +++++ lib/xivapi/errors.rb | 24 ----- lib/xivapi/http.rb | 75 -------------- lib/xivapi/page.rb | 18 ---- lib/xivapi/paginator.rb | 40 -------- lib/xivapi/request.rb | 153 ---------------------------- lib/xivapi/sheets.rb | 32 ++++++ lib/xivapi/version.rb | 5 +- lib/xivapi/versions.rb | 17 ++++ sig/xivapi.rbs | 4 + spec/spec_helper.rb | 15 +++ spec/xivapi/assets_spec.rb | 24 +++++ spec/xivapi/client_spec.rb | 56 +++++++++++ spec/xivapi/sheets_spec.rb | 27 +++++ spec/xivapi/versions_spec.rb | 11 ++ xivapi.gemspec | 48 +++++---- 29 files changed, 554 insertions(+), 522 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100644 .rspec create mode 100644 .rubocop.yml create mode 100644 .vscode/extensions.json create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 lib/xivapi/assets.rb delete mode 100644 lib/xivapi/errors.rb delete mode 100644 lib/xivapi/http.rb delete mode 100644 lib/xivapi/page.rb delete mode 100644 lib/xivapi/paginator.rb delete mode 100644 lib/xivapi/request.rb create mode 100644 lib/xivapi/sheets.rb create mode 100644 lib/xivapi/versions.rb create mode 100644 sig/xivapi.rbs create mode 100644 spec/spec_helper.rb create mode 100644 spec/xivapi/assets_spec.rb create mode 100644 spec/xivapi/client_spec.rb create mode 100644 spec/xivapi/sheets_spec.rb create mode 100644 spec/xivapi/versions_spec.rb diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..6366e7c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,32 @@ +name: Ruby + +on: + push: + branches: + - main + + pull_request: + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + name: Ruby ${{ matrix.ruby }} + strategy: + matrix: + ruby: + - '4.0.3' + + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run the default task + run: bundle exec rake diff --git a/.gitignore b/.gitignore index 9106b2a..b04a8c8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ /pkg/ /spec/reports/ /tmp/ + +# rspec failure tracking +.rspec_status diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..34c5164 --- /dev/null +++ b/.rspec @@ -0,0 +1,3 @@ +--format documentation +--color +--require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..4205ba0 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,9 @@ +AllCops: + NewCops: enable + TargetRubyVersion: 3.2 + +Style/StringLiterals: + EnforcedStyle: double_quotes + +Style/StringLiteralsInInterpolation: + EnforcedStyle: double_quotes diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..f4f6c7e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "shopify.ruby-extensions-pack", + "misogi.ruby-rubocop", + "castwide.solargraph" + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a311144 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +## [Unreleased] + +## [0.1.0] - 2026-05-02 + +- Initial release diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0cf3350 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,10 @@ +# Code of Conduct + +"xivapi" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.): + +* Participants will be tolerant of opposing views. +* Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks. +* When interpreting the words and actions of others, participants should always assume good intentions. +* Behaviour which can be reasonably considered harassment will not be tolerated. + +If you have any concerns about behaviour within this project, please contact us at ["TODO: Write your email address"](mailto:"TODO: Write your email address"). diff --git a/Gemfile b/Gemfile index 073f456..ea35979 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ -source "https://rubygems.org" +# frozen_string_literal: true -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } +source "https://rubygems.org" # Specify your gem's dependencies in xivapi.gemspec gemspec diff --git a/Gemfile.lock b/Gemfile.lock index ef01861..efa1b4c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,40 +1,178 @@ PATH remote: . specs: - xivapi (0.3.3) - rest-client (>= 2.0) + xivapi (0.4.0) + net-http (~> 0.9.1) GEM remote: https://rubygems.org/ specs: - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) - http-accept (1.7.0) - http-cookie (1.0.3) - domain_name (~> 0.5) - mime-types (3.3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2020.0512) - netrc (0.11.0) - rake (10.4.2) - rest-client (2.1.0) - http-accept (>= 1.7.0, < 2.0) - http-cookie (>= 1.0.2, < 2.0) - mime-types (>= 1.16, < 4.0) - netrc (~> 0.8) - unf (0.1.4) - unf_ext - unf_ext (0.0.7.7) - yard (0.9.19) + ast (2.4.3) + backport (1.2.0) + benchmark (0.5.0) + diff-lcs (1.6.2) + jaro_winkler (1.7.0) + json (2.19.4) + kramdown (2.5.2) + rexml (>= 3.4.4) + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) + net-http (0.9.1) + uri (>= 0.11.1) + nokogiri (1.19.3-x64-mingw-ucrt) + racc (~> 1.4) + observer (0.1.2) + open3 (0.2.1) + ostruct (0.6.3) + parallel (2.1.0) + parser (3.3.11.1) + ast (~> 2.4.1) + racc + prism (1.9.0) + racc (1.8.1) + rainbow (3.1.1) + rake (13.4.2) + rbs (3.10.4) + logger + tsort + regexp_parser (2.12.0) + reverse_markdown (3.0.2) + nokogiri + rexml (3.4.4) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.8) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.7) + rubocop (1.86.1) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (>= 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.49.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.49.1) + parser (>= 3.3.7.2) + prism (~> 1.7) + rubocop-rake (0.7.1) + lint_roller (~> 1.1) + rubocop (>= 1.72.1) + rubocop-rspec (3.9.0) + lint_roller (~> 1.1) + rubocop (~> 1.81) + ruby-progressbar (1.13.0) + solargraph (0.58.3) + ast (~> 2.4.3) + backport (~> 1.2) + benchmark (~> 0.4) + bundler (>= 2.0) + diff-lcs (~> 1.4) + jaro_winkler (~> 1.6, >= 1.6.1) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.1) + logger (~> 1.6) + observer (~> 0.1) + open3 (~> 0.2.1) + ostruct (~> 0.6) + parser (~> 3.0) + prism (~> 1.4) + rbs (>= 3.6.1, <= 4.0.0.dev.4) + reverse_markdown (~> 3.0) + rubocop (~> 1.76) + thor (~> 1.0) + tilt (~> 2.0) + yard (~> 0.9, >= 0.9.24) + yard-activesupport-concern (~> 0.0) + yard-solargraph (~> 0.1) + thor (1.5.0) + tilt (2.7.0) + tsort (0.2.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) + yard (0.9.43) + yard-activesupport-concern (0.0.1) + yard (>= 0.8) + yard-solargraph (0.1.0) + yard (~> 0.9) PLATFORMS - ruby + x64-mingw-ucrt DEPENDENCIES - bundler (~> 1.16) - rake (~> 10.0) + bundler (>= 4.0) + rake (~> 13.0) + rspec (~> 3.0) + rubocop (~> 1.21) + rubocop-rake (~> 0.7.1) + rubocop-rspec (~> 3.9) + solargraph (>= 0.58) xivapi! - yard (~> 0.9.19) + +CHECKSUMS + ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 + backport (1.2.0) sha256=912c7dfdd9ee4625d013ddfccb6205c3f92da69a8990f65c440e40f5b2fc7f75 + benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c + diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962 + jaro_winkler (1.7.0) sha256=8daa05a5cd05bf7d27c1c65caa8ab0d47c05419d80210feaa17126db24044ada + json (2.19.4) sha256=670a7d333fb3b18ca5b29cb255eb7bef099e40d88c02c80bd42a3f30fe5239ac + kramdown (2.5.2) sha256=1ba542204c66b6f9111ff00dcc26075b95b220b07f2905d8261740c82f7f02fa + kramdown-parser-gfm (1.1.0) sha256=fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729 + language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc + lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87 + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996 + nokogiri (1.19.3-x64-mingw-ucrt) sha256=8bb7132cad356c879a1286eaabcb5e68326cb2490317984280fbc62f456d506a + observer (0.1.2) sha256=d8a3107131ba661138d748e7be3dbafc0d82e732fffba9fccb3d7829880950ac + open3 (0.2.1) sha256=8e2d7d2113526351201438c1aa35c8139f0141c9e8913baa007c898973bf3952 + ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 + parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356 + parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54 + prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 + racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f + rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a + rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 + rbs (3.10.4) sha256=b17d7c4be4bb31a11a3b529830f0aa206a807ca42f2e7921a3027dfc6b7e5ce8 + regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb + reverse_markdown (3.0.2) sha256=818ebb92ce39dbb1a291690dd1ec9a6d62530d4725296b17e9c8f668f9a5b8af + rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 + rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587 + rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d + rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 + rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47 + rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c + rubocop (1.86.1) sha256=44415f3f01d01a21e01132248d2fd0867572475b566ca188a0a42133a08d4531 + rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035 + rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d + rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2 + ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 + solargraph (0.58.3) sha256=debefdc927d1e72383b2c4add89e71373d902b9904617efdb687749509fe2e69 + thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 + tilt (2.7.0) sha256=0d5b9ba69f6a36490c64b0eee9f6e9aad517e20dcc848800a06eb116f08c6ab3 + tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f + unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42 + unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f + uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 + xivapi (0.4.0) + yard (0.9.43) sha256=cf8733a8f0485df2a162927e9b5f182215a61f6d22de096b8f402c726a1c5821 + yard-activesupport-concern (0.0.1) sha256=be790cb0efc23e2e87677063598ac8b743586154657bbd9655a7f03ce78390ef + yard-solargraph (0.1.0) sha256=a19a4619c942181a618fb9458970a9d2534cf7fda69fc43949629a7948a5930e BUNDLED WITH - 1.17.1 + 4.0.6 diff --git a/README.md b/README.md index e0e7b05..e8e3110 100644 --- a/README.md +++ b/README.md @@ -1,149 +1,41 @@ -# XIVAPI +# xivapi-ruby -A Ruby library for [XIVAPI](https://www.xivapi.com/). +An asynchronous Ruby client gem for working with [XIVAPI v2](https://v2.xivapi.com/), providing access to Final Fantasy XIV game data. It lets you fetch, search, and work with FFXIV data using a clean, modern Ruby gem interface. + +If you need help or run into any issues, please [open an issue](https://github.com/miichom/xivapi-ruby/issues) on GitHub or join the [XIVAPI Discord server](https://discord.gg/MFFVHWC) for support. ## Installation Add this line to your application's Gemfile: ```ruby -gem 'xivapi', git: 'https://github.com/xivapi/xivapi-ruby.git', tag: 'v0.3.3' +gem 'xivapi, git: 'https://github.com/miichom/xivapi-ruby.git', tag: 'v0.4.0' ``` -And then run: +Then run the following in your terminal: +```bash +bundle install ``` -$ bundle install -``` - -## Documentation - -Full documentation for this library can be found [here](https://xivapi.github.io/xivapi-ruby/). For basic usage information, please see below. ## Usage -Start by initializing a client. You can obtain an API key by signing in to XIVAPI [here](https://www.xivapi.com/app). The key is available under the Dev Account page after signing in. - -```rb -require 'xivapi' - -# Basic configuration -client = XIVAPI::Client.new(api_key: 'abc123') - -# Advanced configuration -client = XIVAPI::Client.new(api_key: 'abc123', language: 'fr') -``` - -Now that you have a client, you can use it to contact the API. Examples have been provided below for the various endpoints. For the full list of endpoints and their parameters, please reference the [request documentation](https://xivapi.github.io/xivapi-ruby/XIVAPI/Request.html). - -### Response data -The data returned from the API is automatically converted into [OpenStruct](https://ruby-doc.org/stdlib-2.0.0/libdoc/ostruct/rdoc/OpenStruct.html) objects with snake_cased keys. If the request returns multiple results (e.g. character search), it will be provided to you in the form of an `XIVAPI::Paginator` object. The paginator is [Enumerable](https://ruby-doc.org/core-2.4.1/Enumerable.html), allowing you to access the data with methods like `first`, `each`, `map` and `to_a`. - -See the examples below to get a better idea of how to access the data. - -### Examples -#### Search -```rb ->> achievements = client.search(indexes: 'achievement', string: 'tankless') -=> ... ->> achievements.map(&:name) -=> ["A Tankless Job II (Dark Knight)", "A Tankless Job I (Paladin)", "A Tankless Job I (Warrior)", "A Tankless Job II (Warrior)", "A Tankless Job I (Dark Knight)", "A Tankless Job II (Paladin)"] ->> achievements.first.points -=> 10 -``` - -#### Content -```rb ->> client.content -=> ["Achievement", "AchievementCategory", "AchievementKind", ...] - ->> achievement = client.content(name: 'Achievement', limit: 1).first -=> ... ->> achievement.name -=> "To Crush Your Enemies I" - ->> achievements = client.content(name: 'Achievement', ids: 4..5) -=> ... ->> achievements.map(&:name) -=> ["To Crush Your Enemies IV", "To Crush Your Enemies V"] -``` - -#### Servers -```rb ->> client.servers -=> ["Adamantoise", "Aegis", "Alexander", ...] -``` - -#### Character -```rb ->> characters = client.character_search(name: 'raelys skyborn', server: 'behemoth') -=> ... ->> id = characters.first.id -=> 7660136 ->> character = client.character(id: id, all_data: true) -=> ... ->> character.character.name -=> "Raelys Skyborn" ->> character.achievements.list.last.id -=> 692 -``` - -#### Free Company -```rb ->> fcs = client.free_company_search(name: 'lodestone', server: 'behemoth') -=> ... ->> id = fcs.first.id -=> "9234349560946590421" ->> fc = client.free_company(id: id, members: true) -=> ... ->> fc.free_company.name -=> "Lodestone" ->> fc.free_company_members.first.name -=> "Raelys Skyborn" -``` - -#### Linkshell -```rb ->> linkshells = client.linkshell_search(name: 'thunderbirds', server: 'behemoth') -=> ... ->> id = linkshells.first.id -=> "21955048183495181" ->> linkshell = client.linkshell(id: id).linkshell -=> ... ->> linkshell.name -=> "Thunderbirds" -``` - -#### PVP Team -```rb ->> teams = client.pvp_team_search(name: 'kill', server: 'chaos') -=> ... ->> team = client.pvp_team(id: teams.first.id).pvp_team -=> ... ->> team.name -=> "!Kill_For_A_Friend!" -``` - -#### Patch List -```rb ->> patch = client.patch_list.last -=> ... ->> patch.name -=> "Patch 4.4: Prelude In Violet" ->> Time.at(patch.release_date.to_i) -=> 2018-09-18 10:00:00 +0000 -``` +TODO: Write usage instructions here ## Development -After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. +After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. -To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/xivapi/xivapi-ruby. +Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/xivapi. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/xivapi/blob/main/CODE_OF_CONDUCT.md). ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). + +## Code of Conduct + +Everyone interacting in the Xivapi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/xivapi/blob/main/CODE_OF_CONDUCT.md). diff --git a/Rakefile b/Rakefile index 43022f7..cca7175 100644 --- a/Rakefile +++ b/Rakefile @@ -1,2 +1,12 @@ +# frozen_string_literal: true + require "bundler/gem_tasks" -task :default => :spec +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new(:spec) + +require "rubocop/rake_task" + +RuboCop::RakeTask.new + +task default: %i[spec rubocop] diff --git a/bin/console b/bin/console index fc7127f..d00fc3d 100755 --- a/bin/console +++ b/bin/console @@ -1,8 +1,8 @@ #!/usr/bin/env ruby +# frozen_string_literal: true require "bundler/setup" require "xivapi" require "irb" -@client = XIVAPI::Client.new IRB.start(__FILE__) diff --git a/lib/xivapi.rb b/lib/xivapi.rb index 7953fe2..8f30efc 100644 --- a/lib/xivapi.rb +++ b/lib/xivapi.rb @@ -1,59 +1,67 @@ -require 'xivapi/version' +# frozen_string_literal: true -require 'xivapi/errors' -require 'xivapi/http' -require 'xivapi/page' -require 'xivapi/paginator' -require 'xivapi/request' +require_relative "xivapi/version" # The version of the gem -require 'rest-client' -require 'json' -require 'ostruct' +require_relative "xivapi/assets" +require_relative "xivapi/sheets" +require_relative "xivapi/versions" -# Base module for the XIVAPI library -module XIVAPI - include XIVAPI::Errors +require "uri" +require "net/http" +require "json" - # The allowed language options - LANGUAGE_OPTIONS = %w(en ja de fr cn kr).freeze +module XIVAPI + # Known languages supported by the gata data format. + LANGUAGE_CODES = %w[ja en de fr chs cht kr].freeze - # Client for making requests to XIVAPI + # An asynchronous Ruby wrapper for XIVAPI. class Client - include XIVAPI::Request - - # @return [String] The API key - attr_accessor :api_key - - # @return [true, false] Whether or not to query the staging API instead of production - attr_accessor :staging - - # Initializes a new client for querying XIVAPI - # @param api_key [String] API key provided by XIVAPI - # @param language [String] Requested response langauge - # @param staging [true, false] Whether or not to query the staging API instead of production - def initialize(api_key: nil, language: :en, staging: false) - @api_key = api_key + attr_accessor :logger, :verbose, :version + # Initialize a new client for making API requests. + # @param language [String] The supported by the gata data format + # @param version [String] The supported version of the game to use for the API. + # @param verbose [true, false] Whether to enable verbose logging. + def initialize(language = "en", version = "latest", verbose = false) self.language = language - self.staging = staging + @version = version + @verbose = verbose end - # @return [String] The language - def language - @language - end + # @return [String] The supported by the gata data format + attr_reader :language # @param language [String, Symbol] The language to set for the client - # @return The language + # @return The supported by the gata data format def language=(language) lang = language.to_s.downcase - raise ArgumentError, 'Unsupported language' unless LANGUAGE_OPTIONS.include?(lang) + raise ArgumentError, "Unsupported language" unless LANGUAGE_CODES.include?(lang) + @language = lang end - # @return [Hash] The default parameters for the client - def default_params - { private_key: @api_key, language: @language } + def search(params = {}) + request("search", params) + end + + def request(path, params = {}) + merged = { language: @language, version: @version }.merge(params) + + uri = URI.join("https://v2.xivapi.com/api/", path) + uri.query = URI.encode_www_form(merged.compact) unless merged.empty? + puts "=> #{uri}" if @verbose + response = Net::HTTP.get_response(uri) + raise response.error! unless response.is_a?(Net::HTTPSuccess) + + if response.content_type&.include?("application/json") + json = JSON.parse(response.body) + + raise json["error"].to_s if json.is_a?(Hash) && json["error"] + + json + else + response.body + end end end end diff --git a/lib/xivapi/assets.rb b/lib/xivapi/assets.rb new file mode 100644 index 0000000..2ec211a --- /dev/null +++ b/lib/xivapi/assets.rb @@ -0,0 +1,24 @@ +module XIVAPI + # Endpoints for accessing game data on a file-by-file basis. Commonly useful for fetching icons or other textures to display on the web. + class Assets + attr_accessor :client + + def initialize(client) + @client = client + end + + # Read an asset from the game at the specified path, converting it into a usable format. If no valid conversion between the game file type and specified format exists, an error will be returned. + # @param params [Hash] Query parameters accepted by the asset endpoint. + # @return [Buffer, String] An image of the specified asset in the specified format. + def get(params = {}) + self.client.request("asset", params) + end + + # Retrieve the specified map, composing it from split source files if necessary. + # @param params [Hash] Query parameters accepted by the map endpoint. + # @return [Buffer, String] An image of the specified map in the specified format. + def map(territory, index, params = {}) + self.client.request("asset/map/#{territory}/#{index}", params) + end + end +end \ No newline at end of file diff --git a/lib/xivapi/errors.rb b/lib/xivapi/errors.rb deleted file mode 100644 index 7696e6b..0000000 --- a/lib/xivapi/errors.rb +++ /dev/null @@ -1,24 +0,0 @@ -module XIVAPI - # Custom errors - module Errors - # Standard request error - class RequestError < StandardError - def initialize(response) - if response.headers[:content_type] =~ /json/ - message = JSON.parse(response)['Message'] - else - message = 'Error contacting the API.' - end - - super(message) - end - end - - # Rate limited - class RateLimitError < StandardError - def initialize - super('Too many requests.') - end - end - end -end diff --git a/lib/xivapi/http.rb b/lib/xivapi/http.rb deleted file mode 100644 index d1dd3f3..0000000 --- a/lib/xivapi/http.rb +++ /dev/null @@ -1,75 +0,0 @@ -module XIVAPI - # Makes HTTP request to XIVAPI - module HTTP - # Base URL for XIVAPI - API_BASE = 'https://xivapi.com'.freeze - - # Base URL for the staging environment of XIVAPI - STAGING_API_BASE = 'https://staging.xivapi.com'.freeze - - # Makes a request to XIVAPI - # @param client [XIVAPI::Client] The client making the request - # @param endpoint [String, Symbol] The endpoint to request - # @param params [Hash] Request parameters - # @param payload [Hash] Request body - # @return the results of the request - def request(client, endpoint, params = {}, payload = nil) - url = request_url(client, endpoint) - query_params = params.merge(client.default_params) - .reject { |_, v| v.nil? || v.size == 0 } - - begin - if payload - response = RestClient::Request.execute(method: :get, url: url, headers: { params: query_params }, - payload: payload.to_json) - else - response = RestClient.get(url, params: query_params) - end - - body = JSON.parse(response.body) - objectify(body) - rescue RestClient::ExceptionWithResponse => e - if e.http_code == 429 - raise XIVAPI::RateLimitError.new - else - raise XIVAPI::RequestError.new(e.response) - end - rescue RestClient::Exception => e - raise e - end - end - - private - def request_url(client, endpoint) - "#{client.staging ? STAGING_API_BASE : API_BASE}/#{endpoint}" - end - - def objectify(response) - case response - when Hash - result = {} - - response.each do |key, value| - case value - when Hash, Array - new_value = objectify(value) - else - new_value = value - end - - result[underscore(key)] = new_value - end - - OpenStruct.new(result) - when Array - response.map { |data| objectify(data) } - else - response - end - end - - def underscore(key) - key.gsub('PvPTeam', 'PvpTeam').gsub(/([a-z\d])([A-Z])/,'\1_\2').gsub('.', '_').downcase - end - end -end diff --git a/lib/xivapi/page.rb b/lib/xivapi/page.rb deleted file mode 100644 index 6109cbd..0000000 --- a/lib/xivapi/page.rb +++ /dev/null @@ -1,18 +0,0 @@ -module XIVAPI - # A single page of results from XIVAPI - class Page - # @return the results - attr_reader :results - - # @return [Integer] the page number of the next results - attr_reader :next_page - - # Creates an object for storing a Page of results from XIVAPI - # @param response The response of an HTTP request - def initialize(response) - @results = response.results - pagination = response.pagination - @next_page = pagination.page_next unless pagination.page == pagination.page_total - end - end -end diff --git a/lib/xivapi/paginator.rb b/lib/xivapi/paginator.rb deleted file mode 100644 index bf0106e..0000000 --- a/lib/xivapi/paginator.rb +++ /dev/null @@ -1,40 +0,0 @@ -module XIVAPI - # Paginates XIVAPI results - class Paginator - include Enumerable - include XIVAPI::HTTP - - # @param client [XIVAPI::Client] Client that is sending the request - # @param params [Hash] Query parameters - # @param endpoint [String] API endpoint - # @param limit [Integer] Total number of results to limit to - # @param body [Hash] Request body (for advanced search) - # @param per_page [Integer] Number of results per page, defaults to limit - def initialize(client, params, endpoint, limit, body = nil, per_page = limit) - @client = client - @params = params.merge(limit: per_page) - @endpoint = endpoint - @limit = limit - @body = body - end - - # An enumerator for XIVAPI results - def each - total = 0 - next_page = 1 - - while next_page && total < @limit - page = self.next(next_page) - page.results.take(@limit - total).each { |result| yield result } - next_page = page.next_page - total += page.results.size - end - end - - # The next page in the enumeration of results - def next(page) - response = request(@client, @endpoint, @params.merge(page: page), @body) - Page.new(response) - end - end -end diff --git a/lib/xivapi/request.rb b/lib/xivapi/request.rb deleted file mode 100644 index 8051296..0000000 --- a/lib/xivapi/request.rb +++ /dev/null @@ -1,153 +0,0 @@ -# Collection of requests that can be made to XIVAPI -module XIVAPI::Request - include XIVAPI::HTTP - - # The results limit applied to Lodestone requests - LODESTONE_LIMIT = 50.freeze - - # Options used to retrieve all data when querying a character - ALL_CHARACTER_DATA = 'AC,MIMO,CJ,FR,FC,FCM,PVP'.freeze - - # @param indexes [String, Array ] One or more indexes to search on - # @param string [String] Value to search for in the string column - # @param string_column [String] Column to perform the string search on - # @param string_algo [String] Algorithm to use for the search - # @param sort_field [String] Column to sort results by - # @param sort_order [String] Order to sort results by - # @param limit [Integer] Total number of results to limit to - # @param body [Hash] Request body for advanced ElasticSearch queries - # @param filters [String, Array ] One or more filters to search on - # @param columns [String, Array ] One or more columns to limit results to - # @return [XIVAPI::Paginator] Enumerable search results - def search(indexes: [], string: '', string_column: nil, string_algo: nil, - sort_field: nil, sort_order: nil, limit: 100, body: nil, filters: [], columns: []) - params = { indexes: [*indexes].join(','), string: string, string_column: string_column, string_algo: string_algo, - sort_field: sort_field, sort_order: sort_order, filters: [*filters].join(','), columns: [*columns].join(',') } - XIVAPI::Paginator.new(self, params, 'search', limit, body) - end - - # @param string [String] String of lore to search for - # @param limit [Integer] Total number of results to limit to - # @return [XIVAPI::Paginator] Enumerable lore results - def lore(string: '', limit: 100) - XIVAPI::Paginator.new(self, { string: string }, 'lore', limit) - end - - # @param name [String] Name of the content (e.g. Achievement, Action, Item) - # @param ids [Integer, Array] One or more IDs to retrieve - # @param minify [true, false] Minify resulting data where depth > 1 - # @param limit [Integer] Total number of results to limit to - # @param columns [String, Array ] One or more columns to limit results to - # @return [Array, OpenStruct, XIVAPI::Paginator] - # Calling with no parameters will return the list of content names - # Calling with a name and a single ID will return that specific content - # Calling with a name and not a singe ID will return enumerable results - def content(name: nil, ids: [], minify: false, limit: 100, columns: []) - if name.nil? - request(self, 'content') - elsif [*ids].size == 1 - params = { minify: minify ? 1 : 0, columns: [*columns].join(',') } - request(self, "#{name}/#{[*ids].first}", params) - else - params = { ids: [*ids].join(','), columns: [*columns].join(',') } - XIVAPI::Paginator.new(self, params, name, limit) - end - end - - # @param group [true, false] Group the servers by data center - # @return [Array] List of servers - def servers(group: false) - endpoint = group ? 'servers/dc' : 'servers' - request(self, endpoint) - end - - # @param id [Integer] Character ID - # @param all_data [true, false] Return the full set of character data - # @param extended [true, false] Return additional data for various fields (e.g. name, icon) - # @param data [String, Array ] Additional data to request, see: https://xivapi.com/docs/Character#character - # @param columns [String, Array ] One or more columns to limit results to - # @return [OpenStruct] The requested character - def character(id: nil, all_data: false, extended: false, data: [], columns: []) - params = { data: character_data(all_data, data), columns: [*columns].join(',') } - params[:extended] = 1 if extended - request(self, "character/#{id}", params) - end - - # @param name [String] Character name - # @param server [String] Character server - # @param columns [String, Array ] One or more columns to limit results to - # @return [XIVAPI::Paginator] Enumerable search results - def character_search(name: nil, server: nil, columns: []) - params = { name: name, server: server, columns: [*columns].join(',') } - XIVAPI::Paginator.new(self, params, 'character/search', LODESTONE_LIMIT) - end - - # @param id [Integer] Character ID - # @param token [String] Verification token to check for - # @return [true, false] Whether or not the character is verified - def character_verified?(id: nil, token: nil) - character(id: id, columns: 'Character.Bio').character.bio.match?(token) - end - - # @param id [Integer] Free company ID - # @param members [true, false] Return member data - # @param columns [String, Array ] One or more columns to limit results to - # @return [OpenStruct] The requested free company - def free_company(id: nil, members: false, columns: []) - params = { data: members ? 'FCM' : nil, columns: [*columns].join(',') } - request(self, "freecompany/#{id}", params) - end - - # @param name [String] Free company name - # @param server [String] Free company server - # @param columns [String, Array ] One or more columns to limit results to - # @return [XIVAPI::Paginator] Enumerable search results - def free_company_search(name: nil, server: nil, columns: []) - params = { name: name, server: server, columns: [*columns].join(',') } - XIVAPI::Paginator.new(self, params, 'freecompany/search', LODESTONE_LIMIT) - end - - # @param id [Integer] Linkshell ID - # @param columns [String, Array ] One or more columns to limit results to - # @return [OpenStruct] The requested linkshell - def linkshell(id: nil, columns: []) - params = { columns: [*columns].join(',') } - request(self, "linkshell/#{id}", params) - end - - # @param name [String] Linkshell name - # @param server [String] Linkshell server - # @param columns [String, Array ] One or more columns to limit results to - # @return [XIVAPI::Paginator] Enumerable search results - def linkshell_search(name: nil, server: nil, columns: []) - params = { name: name, server: server, columns: [*columns].join(',') } - XIVAPI::Paginator.new(self, params, 'linkshell/search', LODESTONE_LIMIT) - end - - # @param id [Integer] PVP team ID - # @param columns [String, Array ] One or more columns to limit results to - # @return [OpenStruct] The requested PVP team - def pvp_team(id: nil, columns: []) - params = { columns: [*columns].join(',') } - request(self, "pvpteam/#{id}", params) - end - - # @param name [String] PVP team name - # @param server [String] PVP team server - # @param columns [String, Array ] One or more columns to limit results to - # @return [XIVAPI::Paginator] Enumerable search results - def pvp_team_search(name: nil, server: nil, columns: []) - params = { name: name, server: server, columns: [*columns].join(',') } - XIVAPI::Paginator.new(self, params, 'pvpteam/search', LODESTONE_LIMIT) - end - - # @return [Array] List of game patches - def patch_list - request(self, 'patchlist') - end - - private - def character_data(all_data, data) - all_data ? ALL_CHARACTER_DATA : [*data].join(',') - end -end diff --git a/lib/xivapi/sheets.rb b/lib/xivapi/sheets.rb new file mode 100644 index 0000000..0869d67 --- /dev/null +++ b/lib/xivapi/sheets.rb @@ -0,0 +1,32 @@ +module XIVAPI + class Sheets + attr_accessor :client + + def initialize(client) + @client = client + end + + # List known excel sheets that can be read by the API. + # @return [Array] The list of known sheets. + def all + self.client.request("sheet") + end + + # Read information about one or more rows and their related data. + # @param sheet [String] The sheet to fetch the rows from. + # @param params [Hash] The parameters to fetch the rows with. + # @return [Array] A list of rows with typed fields. + def list(sheet, params = {}) + self.client.request("sheet/#{sheet}", params) + end + + # Read detailed, filterable information from a single sheet row and its related data. + # @param sheet [String] Name of the sheet to read. + # @param row_id [Integer] The ID of the row to read. + # @param params [Hash] The parameters to fetch the row with. + # @return [Hash] The row with typed fields. + def get(sheet, row_id, params = {}) + self.client.request("sheet/#{sheet}/#{row_id}", params) + end + end +end \ No newline at end of file diff --git a/lib/xivapi/version.rb b/lib/xivapi/version.rb index 32d6666..b56ff87 100644 --- a/lib/xivapi/version.rb +++ b/lib/xivapi/version.rb @@ -1,4 +1,5 @@ +# frozen_string_literal: true + module XIVAPI - # Gem version - VERSION = "0.3.3" + VERSION = "0.4.0" end diff --git a/lib/xivapi/versions.rb b/lib/xivapi/versions.rb new file mode 100644 index 0000000..945ccc4 --- /dev/null +++ b/lib/xivapi/versions.rb @@ -0,0 +1,17 @@ +module XIVAPI + # Endpoints for querying metadata about the versions recorded by the boilmaster system. + class Versions + attr_accessor :client + + def initialize(client) + @client = client + end + + # List versions understood by the API. + # @return [Array] The list of known versions. + def all + result = self.client.request("version") + result["versions"].flat_map { |v| v["names"] } + end + end +end \ No newline at end of file diff --git a/sig/xivapi.rbs b/sig/xivapi.rbs new file mode 100644 index 0000000..e9b8497 --- /dev/null +++ b/sig/xivapi.rbs @@ -0,0 +1,4 @@ +module Xivapi + VERSION: String + # See the writing guide of rbs: https://github.com/ruby/rbs#guides +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..a2a74af --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require "xivapi" + +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end diff --git a/spec/xivapi/assets_spec.rb b/spec/xivapi/assets_spec.rb new file mode 100644 index 0000000..00f29d0 --- /dev/null +++ b/spec/xivapi/assets_spec.rb @@ -0,0 +1,24 @@ +RSpec.describe XIVAPI::Assets do + let(:client) { XIVAPI::Client.new } + let(:assets) { described_class.new(client) } + + it "fetches an asset" do + result = assets.get( + path: "ui/icon/051000/051474_hr1.tex", + format: "png" + ) + expect(result).to be_a(String) + expect(result.bytesize).to be > 0 + end + + it "fetches a map" do + result = assets.map("s1d1", "00", version: "latest") + expect(result.bytesize).to be > 0 + end + + it "raises on invalid asset" do + expect { + assets.get(path: "invalid/path.tex", format: "png") + }.to raise_error(Net::HTTPClientException) + end +end \ No newline at end of file diff --git a/spec/xivapi/client_spec.rb b/spec/xivapi/client_spec.rb new file mode 100644 index 0000000..d1a4fe3 --- /dev/null +++ b/spec/xivapi/client_spec.rb @@ -0,0 +1,56 @@ +RSpec.describe XIVAPI::Client do + let(:client) { described_class.new } + + describe "#search" do + it "performs a search request and returns results" do + result = client.search( + query: 'Name="Iron War Axe"', + sheets: "Item", + limit: 3 + ) + + expect(result).to be_a(Hash) + expect(result["results"]).to be_an(Array) + expect(result["results"].length).to be <= 3 + end + + it "supports partial text search" do + result = client.search( + query: 'Name~"sword"', + sheets: "Item", + limit: 3 + ) + + expect(result["results"]).to be_an(Array) + + result["results"].each do |item| + next unless item["fields"]["Name"] + expect(item["fields"]["Name"].downcase).to include("sword") + end + end + + it "raises an error for invalid query syntax" do + expect { + client.search(query: "invalid syntax", sheets: "Item") + }.to raise_error(Net::HTTPClientException) + end + end + + describe "verbose logging" do + it "prints debug output when verbose is enabled" do + client.verbose = true + + expect { + client.search(query: 'Name="Gil"', sheets: "Item", limit: 1) + }.to output(/https:\/\/v2.xivapi.com\/api\/search/i).to_stdout + end + + it "does not print debug output when verbose is disabled" do + client.verbose = false + + expect { + client.search(query: 'Name="Gil"', sheets: "Item", limit: 1) + }.not_to output.to_stdout + end + end +end \ No newline at end of file diff --git a/spec/xivapi/sheets_spec.rb b/spec/xivapi/sheets_spec.rb new file mode 100644 index 0000000..fd9bafc --- /dev/null +++ b/spec/xivapi/sheets_spec.rb @@ -0,0 +1,27 @@ +RSpec.describe XIVAPI::Sheets do + let(:client) { XIVAPI::Client.new } + let(:sheets) { described_class.new(client) } + + it "lists all sheets" do + result = sheets.all + expect(result["sheets"]).to be_an(Array) + expect(result["sheets"]).not_to be_empty + end + + it "lists rows" do + result = sheets.list("Item", limit: 5) + expect(result["rows"]).to be_an(Array) + expect(result["rows"]).not_to be_empty + end + + it "gets a row" do + result = sheets.get("Item", 1, fields: "Name") + expect(result["fields"]["Name"]).to eq("Gil") + end + + it "raises for invalid sheet" do + expect { + sheets.list("NotASheet") + }.to raise_error(Net::HTTPClientException) + end +end \ No newline at end of file diff --git a/spec/xivapi/versions_spec.rb b/spec/xivapi/versions_spec.rb new file mode 100644 index 0000000..9175d3f --- /dev/null +++ b/spec/xivapi/versions_spec.rb @@ -0,0 +1,11 @@ +RSpec.describe XIVAPI::Versions do + let(:client) { XIVAPI::Client.new } + let(:versions) { described_class.new(client) } + + it "fetches versions" do + result = versions.all + expect(result).to be_an(Array) + expect(result).not_to be_empty + expect(result.all? { |v| v.is_a?(String) }).to be true + end +end \ No newline at end of file diff --git a/xivapi.gemspec b/xivapi.gemspec index 2449d76..d31e9a5 100644 --- a/xivapi.gemspec +++ b/xivapi.gemspec @@ -1,25 +1,39 @@ +# frozen_string_literal: true -lib = File.expand_path("../lib", __FILE__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "xivapi/version" +require_relative "lib/xivapi/version" Gem::Specification.new do |spec| - spec.name = "xivapi" - spec.version = XIVAPI::VERSION - spec.authors = ["Matt Antonelli"] - spec.email = ["contact@raelys.com"] + spec.name = "xivapi" + spec.version = XIVAPI::VERSION + spec.authors = ["Matt Antonelli"] + spec.email = ["matt@antonelli.dev"] - spec.summary = %q{A Ruby library for XIVAPI} - spec.description = %q{A Ruby library for XIVAPI (http://www.xivapi.com)} - spec.homepage = "https://github.com/xivapi/xivapi-ruby" - spec.license = "MIT" + spec.summary = "%q{A Ruby wrapper for XIVAPI}" + spec.description = "%q{A Ruby wrapper for XIVAPI (https://v2.xivapi.com/)}" + spec.homepage = "https://github.com/xivapi/xivapi-ruby" + spec.license = "MIT" + spec.required_ruby_version = ">= 3.2.0" - spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do - `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + gemspec = File.basename(__FILE__) + spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| + ls.readlines("\x0", chomp: true).reject do |f| + (f == gemspec) || + f.start_with?(*%w[bin/ Gemfile .gitignore .rspec spec/ .github/ .rubocop.yml]) + end end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] - spec.add_development_dependency "bundler", "~> 1.16" - spec.add_development_dependency "rake", "~> 10.0" - spec.add_development_dependency "yard", "~> 0.9.19" - spec.add_dependency "rest-client", ">= 2.0" + spec.add_development_dependency "bundler", ">= 4.0" + spec.add_development_dependency "rake", "~> 13.0" + spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "rubocop", "~> 1.21" + spec.add_development_dependency "rubocop-rake", "~> 0.7.1" + spec.add_development_dependency "rubocop-rspec", "~> 3.9" + spec.add_development_dependency "solargraph", ">= 0.58" + spec.add_dependency "net-http", "~> 0.9.1" + spec.metadata["rubygems_mfa_required"] = "true" end From 563a6deabf040543dab39dd22a53cdde232a99a9 Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:21:43 +0100 Subject: [PATCH 02/25] feat: add GitHub Actions workflows for Ruby gem publishing and testing --- .github/workflows/gem_push.yml | 46 ++++++++++++++++++++++++++++++++++ .github/workflows/main.yml | 32 ----------------------- .github/workflows/ruby.yml | 38 ++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/gem_push.yml delete mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/ruby.yml diff --git a/.github/workflows/gem_push.yml b/.github/workflows/gem_push.yml new file mode 100644 index 0000000..c3ada1b --- /dev/null +++ b/.github/workflows/gem_push.yml @@ -0,0 +1,46 @@ +name: Ruby Gem + +on: + release: + types: [published] + +jobs: + build: + name: Build + Publish + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + - name: Set up Ruby 2.6 + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + # uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + with: + ruby-version: 2.6.x + + - name: Publish to GPR + run: | + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials + gem build *.gemspec + gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem + env: + GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}" + OWNER: ${{ github.repository_owner }} + + # - name: Publish to RubyGems + # run: | + # mkdir -p $HOME/.gem + # touch $HOME/.gem/credentials + # chmod 0600 $HOME/.gem/credentials + # printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials + # gem build *.gemspec + # gem push *.gem + # env: + # GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}" \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 6366e7c..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Ruby - -on: - push: - branches: - - main - - pull_request: - -permissions: - contents: read - -jobs: - build: - runs-on: ubuntu-latest - name: Ruby ${{ matrix.ruby }} - strategy: - matrix: - ruby: - - '4.0.3' - - steps: - - uses: actions/checkout@v6 - with: - persist-credentials: false - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - - name: Run the default task - run: bundle exec rake diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 0000000..f87cc89 --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,38 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake +# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby + +name: Ruby + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + test: + + runs-on: ubuntu-latest + strategy: + matrix: + ruby-version: ['2.6', '2.7', '3.0'] + + steps: + - uses: actions/checkout@v4 + - name: Set up Ruby + # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, + # change this to (see https://github.com/ruby/setup-ruby#versioning): + # uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: Run tests + run: bundle exec rake From 79628cd1cdb3f4b0c6af9c220d0e01fe35e8bc9d Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:24:40 +0100 Subject: [PATCH 03/25] fix: revert to stable version of ruby/setup-ruby action --- .github/workflows/ruby.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index f87cc89..230fdef 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -27,10 +27,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Ruby - # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, - # change this to (see https://github.com/ruby/setup-ruby#versioning): - # uses: ruby/setup-ruby@v1 - uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true # runs 'bundle install' and caches installed gems automatically From 7c771106387b59b202a4fbdb1500a96200429d3d Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:27:47 +0100 Subject: [PATCH 04/25] feat: update Ruby versions in GitHub Actions workflow --- .github/workflows/ruby.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 230fdef..1cbe670 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.6', '2.7', '3.0'] + ruby-version: ['3.2', '3.3'] steps: - uses: actions/checkout@v4 From 1ab18684e3d48b3992e0154cc8c48ae550b896e8 Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:29:23 +0100 Subject: [PATCH 05/25] fix: update nokogiri and mini_portile2 dependencies in Gemfile.lock --- Gemfile.lock | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index efa1b4c..5cad4f2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -20,10 +20,16 @@ GEM language_server-protocol (3.17.0.5) lint_roller (1.1.0) logger (1.7.0) + mini_portile2 (2.8.9) net-http (0.9.1) uri (>= 0.11.1) + nokogiri (1.19.3) + mini_portile2 (~> 2.8.2) + racc (~> 1.4) nokogiri (1.19.3-x64-mingw-ucrt) racc (~> 1.4) + nokogiri (1.19.3-x86_64-linux-gnu) + racc (~> 1.4) observer (0.1.2) open3 (0.2.1) ostruct (0.6.3) @@ -113,7 +119,9 @@ GEM yard (~> 0.9) PLATFORMS + ruby x64-mingw-ucrt + x86_64-linux DEPENDENCIES bundler (>= 4.0) @@ -137,8 +145,11 @@ CHECKSUMS language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87 logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289 net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996 + nokogiri (1.19.3) sha256=78312cbac32a40c812780d9678221b79d51288eec00054c1a8d15f7ce05960e8 nokogiri (1.19.3-x64-mingw-ucrt) sha256=8bb7132cad356c879a1286eaabcb5e68326cb2490317984280fbc62f456d506a + nokogiri (1.19.3-x86_64-linux-gnu) sha256=2f5078620fe12e83669b5b17311b32532a8153d02eee7ad06948b926d6080976 observer (0.1.2) sha256=d8a3107131ba661138d748e7be3dbafc0d82e732fffba9fccb3d7829880950ac open3 (0.2.1) sha256=8e2d7d2113526351201438c1aa35c8139f0141c9e8913baa007c898973bf3952 ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 From 8ab35875570aaa025e2dcb5d574dabd7dc1d4e17 Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:39:44 +0100 Subject: [PATCH 06/25] chore(ci): update Ruby matrix to 3.3, 3.4, and 4.0; drop 3.2 due to incompatibility --- .github/workflows/ruby.yml | 2 +- .rubocop.yml | 2 +- xivapi.gemspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index 1cbe670..b200a45 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['3.2', '3.3'] + ruby-version: ['3.3', '3.4', '4.0'] steps: - uses: actions/checkout@v4 diff --git a/.rubocop.yml b/.rubocop.yml index 4205ba0..c7d7266 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,6 +1,6 @@ AllCops: NewCops: enable - TargetRubyVersion: 3.2 + TargetRubyVersion: 3.3 Style/StringLiterals: EnforcedStyle: double_quotes diff --git a/xivapi.gemspec b/xivapi.gemspec index d31e9a5..2e1e98c 100644 --- a/xivapi.gemspec +++ b/xivapi.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |spec| spec.description = "%q{A Ruby wrapper for XIVAPI (https://v2.xivapi.com/)}" spec.homepage = "https://github.com/xivapi/xivapi-ruby" spec.license = "MIT" - spec.required_ruby_version = ">= 3.2.0" + spec.required_ruby_version = ">= 3.3.0" # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. From 44c994424b76fed3eeee1a3ca5415502281faae6 Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:42:44 +0100 Subject: [PATCH 07/25] fix: revert ruby/setup-ruby action to use stable version and update ruby version to head --- .github/workflows/gem_push.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gem_push.yml b/.github/workflows/gem_push.yml index c3ada1b..96936f7 100644 --- a/.github/workflows/gem_push.yml +++ b/.github/workflows/gem_push.yml @@ -15,12 +15,9 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Ruby 2.6 - # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, - # change this to (see https://github.com/ruby/setup-ruby#versioning): - # uses: ruby/setup-ruby@v1 - uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0 + uses: ruby/setup-ruby@v1 with: - ruby-version: 2.6.x + ruby-version: head - name: Publish to GPR run: | From 4cad4034b0e240c5d92dc92f7199168d438f5637 Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:57:04 +0100 Subject: [PATCH 08/25] chore: remove CHANGELOG.md as it is no longer needed --- CHANGELOG.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a311144..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,5 +0,0 @@ -## [Unreleased] - -## [0.1.0] - 2026-05-02 - -- Initial release From e72e591636f45cebb81122e6f4a7e35f110feb58 Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:57:23 +0100 Subject: [PATCH 09/25] docs: update contact information in Code of Conduct and README, and improve gemspec summary and description --- CODE_OF_CONDUCT.md | 2 +- README.md | 23 +++++++++++++++++------ xivapi.gemspec | 4 ++-- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 0cf3350..4904b7b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -7,4 +7,4 @@ * When interpreting the words and actions of others, participants should always assume good intentions. * Behaviour which can be reasonably considered harassment will not be tolerated. -If you have any concerns about behaviour within this project, please contact us at ["TODO: Write your email address"](mailto:"TODO: Write your email address"). +If you have any concerns about behaviour within this project, please contact us on our [Discord server](https://discord.gg/MFFVHWC). diff --git a/README.md b/README.md index e8e3110..9311151 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ # xivapi-ruby -An asynchronous Ruby client gem for working with [XIVAPI v2](https://v2.xivapi.com/), providing access to Final Fantasy XIV game data. It lets you fetch, search, and work with FFXIV data using a clean, modern Ruby gem interface. +An asynchronous Ruby client gem for working with [XIVAPI](https://v2.xivapi.com/), providing access to Final Fantasy XIV game data. It lets you fetch, search, and work with FFXIV data using a clean, modern Ruby gem interface. -If you need help or run into any issues, please [open an issue](https://github.com/miichom/xivapi-ruby/issues) on GitHub or join the [XIVAPI Discord server](https://discord.gg/MFFVHWC) for support. +If you need help or run into any issues, please [open an issue](https://github.com/xivapi/xivapi-ruby/issues) on GitHub or join the [XIVAPI Discord server](https://discord.gg/MFFVHWC) for support. ## Installation Add this line to your application's Gemfile: ```ruby -gem 'xivapi, git: 'https://github.com/miichom/xivapi-ruby.git', tag: 'v0.4.0' +gem 'xivapi, git: 'https://github.com/xivapi/xivapi-ruby.git', tag: 'v0.4.0' ``` Then run the following in your terminal: @@ -20,7 +20,18 @@ bundle install ## Usage -TODO: Write usage instructions here +```ruby +# Client inialization +client = XIVAPI::Client.new +client = XIVAPI::Client.new(language: "jp", version: "7.0", verbose: true) + +# Raw requests +client.request("/sheet/Item/1") + +# Or using typed endpoints +sheets = XIVAPI::Sheets.new(client) # ~ must pass the client +sheets.get("Item", 1) +``` ## Development @@ -30,7 +41,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/xivapi. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/xivapi/blob/main/CODE_OF_CONDUCT.md). +Bug reports and pull requests are welcome on GitHub at https://github.com/miichom/xivapi. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/xivapi/xivapi-ruby/blob/main/CODE_OF_CONDUCT.md). ## License @@ -38,4 +49,4 @@ The gem is available as open source under the terms of the [MIT License](https:/ ## Code of Conduct -Everyone interacting in the Xivapi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/xivapi/blob/main/CODE_OF_CONDUCT.md). +Everyone interacting in the Xivapi project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/xivapi/xivapi-ruby/blob/main/CODE_OF_CONDUCT.md). diff --git a/xivapi.gemspec b/xivapi.gemspec index 2e1e98c..fe991f1 100644 --- a/xivapi.gemspec +++ b/xivapi.gemspec @@ -8,8 +8,8 @@ Gem::Specification.new do |spec| spec.authors = ["Matt Antonelli"] spec.email = ["matt@antonelli.dev"] - spec.summary = "%q{A Ruby wrapper for XIVAPI}" - spec.description = "%q{A Ruby wrapper for XIVAPI (https://v2.xivapi.com/)}" + spec.summary = "An asynchronous Ruby client gem for working with [XIVAPI](https://v2.xivapi.com/)" + spec.description = "An asynchronous Ruby client gem for working with [XIVAPI](https://v2.xivapi.com/)" spec.homepage = "https://github.com/xivapi/xivapi-ruby" spec.license = "MIT" spec.required_ruby_version = ">= 3.3.0" From dd78aae6c853c3def6572ef2bd00cea42ba82eb3 Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:58:36 +0100 Subject: [PATCH 10/25] fix: correct syntax error in Gemfile example in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9311151..cbc735c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ If you need help or run into any issues, please [open an issue](https://github.c Add this line to your application's Gemfile: ```ruby -gem 'xivapi, git: 'https://github.com/xivapi/xivapi-ruby.git', tag: 'v0.4.0' +gem 'xivapi', git: 'https://github.com/xivapi/xivapi-ruby.git', tag: 'v0.4.0' ``` Then run the following in your terminal: From 5a113b24d83e45dbd32386cc4ac9356ce5c8462a Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 10:59:28 +0100 Subject: [PATCH 11/25] docs: update GitHub link in contributing section of README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cbc735c..d05e5b0 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/miichom/xivapi. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/xivapi/xivapi-ruby/blob/main/CODE_OF_CONDUCT.md). +Bug reports and pull requests are welcome on GitHub at https://github.com/xivapi/xivapi-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/xivapi/xivapi-ruby/blob/main/CODE_OF_CONDUCT.md). ## License From ab0b48430de9c5a8df0beb5cfce08155068ad31b Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 11:02:24 +0100 Subject: [PATCH 12/25] fix: remove markdown links from gemspec summary and description --- xivapi.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xivapi.gemspec b/xivapi.gemspec index fe991f1..556b065 100644 --- a/xivapi.gemspec +++ b/xivapi.gemspec @@ -8,8 +8,8 @@ Gem::Specification.new do |spec| spec.authors = ["Matt Antonelli"] spec.email = ["matt@antonelli.dev"] - spec.summary = "An asynchronous Ruby client gem for working with [XIVAPI](https://v2.xivapi.com/)" - spec.description = "An asynchronous Ruby client gem for working with [XIVAPI](https://v2.xivapi.com/)" + spec.summary = "An asynchronous Ruby client gem for working with XIVAPI" + spec.description = "An asynchronous Ruby client gem for working with XIVAPI" spec.homepage = "https://github.com/xivapi/xivapi-ruby" spec.license = "MIT" spec.required_ruby_version = ">= 3.3.0" From 02c1892b9e17725a898731c0e3593b76f02c3918 Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 11:47:10 +0100 Subject: [PATCH 13/25] feat: add .editorconfig, update Gemfile with development dependencies, and enhance Rakefile for standard task --- .editorconfig | 6 ++ .rubocop.yml | 15 ++-- Gemfile | 20 ++++-- Gemfile.lock | 39 ++++++---- Rakefile | 26 +++---- bin/console | 16 ++--- bin/setup | 12 ++-- lib/xivapi.rb | 134 +++++++++++++++++------------------ lib/xivapi/assets.rb | 48 ++++++------- lib/xivapi/sheets.rb | 64 ++++++++--------- lib/xivapi/version.rb | 10 +-- lib/xivapi/versions.rb | 34 ++++----- spec/spec_helper.rb | 30 ++++---- spec/xivapi/assets_spec.rb | 48 ++++++------- spec/xivapi/client_spec.rb | 112 ++++++++++++++--------------- spec/xivapi/sheets_spec.rb | 54 +++++++------- spec/xivapi/versions_spec.rb | 22 +++--- xivapi.gemspec | 71 +++++++++---------- 18 files changed, 390 insertions(+), 371 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9176fc3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 diff --git a/.rubocop.yml b/.rubocop.yml index c7d7266..4980bd1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,9 +1,6 @@ -AllCops: - NewCops: enable - TargetRubyVersion: 3.3 - -Style/StringLiterals: - EnforcedStyle: double_quotes - -Style/StringLiteralsInInterpolation: - EnforcedStyle: double_quotes +plugins: + - rubocop-rake +require: + - standard +inherit_gem: + standard: config/base.yml \ No newline at end of file diff --git a/Gemfile b/Gemfile index ea35979..0365dd3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,14 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -# Specify your gem's dependencies in xivapi.gemspec -gemspec +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "bundler", ">= 4.0", group: :development +gem "rake", "~> 13.0", group: :development +gem "rspec", "~> 3.0", group: :development +gem "rubocop-rspec", "~> 3.9", group: :development +gem "rubocop-rake", "~> 0.7.1", group: :development +gem "solargraph", ">= 0.58", group: :development +gem "standard", ">= 1.35.1", group: :development + +# Specify your gem's dependencies in xivapi.gemspec +gemspec diff --git a/Gemfile.lock b/Gemfile.lock index 5cad4f2..d602f64 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -20,12 +20,8 @@ GEM language_server-protocol (3.17.0.5) lint_roller (1.1.0) logger (1.7.0) - mini_portile2 (2.8.9) net-http (0.9.1) uri (>= 0.11.1) - nokogiri (1.19.3) - mini_portile2 (~> 2.8.2) - racc (~> 1.4) nokogiri (1.19.3-x64-mingw-ucrt) racc (~> 1.4) nokogiri (1.19.3-x86_64-linux-gnu) @@ -33,7 +29,7 @@ GEM observer (0.1.2) open3 (0.2.1) ostruct (0.6.3) - parallel (2.1.0) + parallel (1.28.0) parser (3.3.11.1) ast (~> 2.4.1) racc @@ -61,11 +57,11 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.7) - rubocop (1.86.1) + rubocop (1.84.2) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) - parallel (>= 1.10) + parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) @@ -75,6 +71,10 @@ GEM rubocop-ast (1.49.1) parser (>= 3.3.7.2) prism (~> 1.7) + rubocop-performance (1.26.1) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) rubocop-rake (0.7.1) lint_roller (~> 1.1) rubocop (>= 1.72.1) @@ -105,6 +105,18 @@ GEM yard (~> 0.9, >= 0.9.24) yard-activesupport-concern (~> 0.0) yard-solargraph (~> 0.1) + standard (1.54.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.84.0) + standard-custom (~> 1.0.0) + standard-performance (~> 1.8) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.9.0) + lint_roller (~> 1.1) + rubocop-performance (~> 1.26.0) thor (1.5.0) tilt (2.7.0) tsort (0.2.0) @@ -119,7 +131,6 @@ GEM yard (~> 0.9) PLATFORMS - ruby x64-mingw-ucrt x86_64-linux @@ -127,10 +138,10 @@ DEPENDENCIES bundler (>= 4.0) rake (~> 13.0) rspec (~> 3.0) - rubocop (~> 1.21) rubocop-rake (~> 0.7.1) rubocop-rspec (~> 3.9) solargraph (>= 0.58) + standard (>= 1.35.1) xivapi! CHECKSUMS @@ -145,15 +156,13 @@ CHECKSUMS language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87 logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 - mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289 net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996 - nokogiri (1.19.3) sha256=78312cbac32a40c812780d9678221b79d51288eec00054c1a8d15f7ce05960e8 nokogiri (1.19.3-x64-mingw-ucrt) sha256=8bb7132cad356c879a1286eaabcb5e68326cb2490317984280fbc62f456d506a nokogiri (1.19.3-x86_64-linux-gnu) sha256=2f5078620fe12e83669b5b17311b32532a8153d02eee7ad06948b926d6080976 observer (0.1.2) sha256=d8a3107131ba661138d748e7be3dbafc0d82e732fffba9fccb3d7829880950ac open3 (0.2.1) sha256=8e2d7d2113526351201438c1aa35c8139f0141c9e8913baa007c898973bf3952 ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 - parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356 + parallel (1.28.0) sha256=33e6de1484baf2524792d178b0913fc8eb94c628d6cfe45599ad4458c638c970 parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54 prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f @@ -168,12 +177,16 @@ CHECKSUMS rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47 rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c - rubocop (1.86.1) sha256=44415f3f01d01a21e01132248d2fd0867572475b566ca188a0a42133a08d4531 + rubocop (1.84.2) sha256=5692cea54168f3dc8cb79a6fe95c5424b7ea893c707ad7a4307b0585e88dbf5f rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035 + rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834 rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2 ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 solargraph (0.58.3) sha256=debefdc927d1e72383b2c4add89e71373d902b9904617efdb687749509fe2e69 + standard (1.54.0) sha256=7a4b08f83d9893083c8f03bc486f0feeb6a84d48233b40829c03ef4767ea0100 + standard-custom (1.0.2) sha256=424adc84179a074f1a2a309bb9cf7cd6bfdb2b6541f20c6bf9436c0ba22a652b + standard-performance (1.9.0) sha256=49483d31be448292951d80e5e67cdcb576c2502103c7b40aec6f1b6e9c88e3f2 thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 tilt (2.7.0) sha256=0d5b9ba69f6a36490c64b0eee9f6e9aad517e20dcc848800a06eb116f08c6ab3 tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f diff --git a/Rakefile b/Rakefile index cca7175..f620652 100644 --- a/Rakefile +++ b/Rakefile @@ -1,12 +1,14 @@ -# frozen_string_literal: true - -require "bundler/gem_tasks" -require "rspec/core/rake_task" - -RSpec::Core::RakeTask.new(:spec) - -require "rubocop/rake_task" - -RuboCop::RakeTask.new - -task default: %i[spec rubocop] +# frozen_string_literal: true + +require "bundler/gem_tasks" +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new(:spec) + +require "rubocop/rake_task" + +RuboCop::RakeTask.new(:standard) do |task| + task.options = ["--require", "standard", "--format", "progress"] +end + +task default: %i[spec standard] diff --git a/bin/console b/bin/console index d00fc3d..7527aef 100755 --- a/bin/console +++ b/bin/console @@ -1,8 +1,8 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require "bundler/setup" -require "xivapi" - -require "irb" -IRB.start(__FILE__) +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "bundler/setup" +require "xivapi" + +require "irb" +IRB.start(__FILE__) diff --git a/bin/setup b/bin/setup index cf4ad25..5020b1b 100755 --- a/bin/setup +++ b/bin/setup @@ -1,6 +1,6 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' -set -vx - -bundle install +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install diff --git a/lib/xivapi.rb b/lib/xivapi.rb index 8f30efc..c5ada5a 100644 --- a/lib/xivapi.rb +++ b/lib/xivapi.rb @@ -1,67 +1,67 @@ -# frozen_string_literal: true - -require_relative "xivapi/version" # The version of the gem - -require_relative "xivapi/assets" -require_relative "xivapi/sheets" -require_relative "xivapi/versions" - -require "uri" -require "net/http" -require "json" - -module XIVAPI - # Known languages supported by the gata data format. - LANGUAGE_CODES = %w[ja en de fr chs cht kr].freeze - - # An asynchronous Ruby wrapper for XIVAPI. - class Client - attr_accessor :logger, :verbose, :version - - # Initialize a new client for making API requests. - # @param language [String] The supported by the gata data format - # @param version [String] The supported version of the game to use for the API. - # @param verbose [true, false] Whether to enable verbose logging. - def initialize(language = "en", version = "latest", verbose = false) - self.language = language - @version = version - @verbose = verbose - end - - # @return [String] The supported by the gata data format - attr_reader :language - - # @param language [String, Symbol] The language to set for the client - # @return The supported by the gata data format - def language=(language) - lang = language.to_s.downcase - raise ArgumentError, "Unsupported language" unless LANGUAGE_CODES.include?(lang) - - @language = lang - end - - def search(params = {}) - request("search", params) - end - - def request(path, params = {}) - merged = { language: @language, version: @version }.merge(params) - - uri = URI.join("https://v2.xivapi.com/api/", path) - uri.query = URI.encode_www_form(merged.compact) unless merged.empty? - puts "=> #{uri}" if @verbose - response = Net::HTTP.get_response(uri) - raise response.error! unless response.is_a?(Net::HTTPSuccess) - - if response.content_type&.include?("application/json") - json = JSON.parse(response.body) - - raise json["error"].to_s if json.is_a?(Hash) && json["error"] - - json - else - response.body - end - end - end -end +# frozen_string_literal: true + +require_relative "xivapi/version" # The version of the gem + +require_relative "xivapi/assets" +require_relative "xivapi/sheets" +require_relative "xivapi/versions" + +require "uri" +require "net/http" +require "json" + +module XIVAPI + # Known languages supported by the gata data format. + LANGUAGE_CODES = %w[ja en de fr chs cht kr].freeze + + # An asynchronous Ruby wrapper for XIVAPI. + class Client + attr_accessor :logger, :verbose, :version + + # Initialize a new client for making API requests. + # @param language [String] The supported by the gata data format + # @param version [String] The supported version of the game to use for the API. + # @param verbose [true, false] Whether to enable verbose logging. + def initialize(language = "en", version = "latest", verbose = false) + self.language = language + @version = version + @verbose = verbose + end + + # @return [String] The supported by the gata data format + attr_reader :language + + # @param language [String, Symbol] The language to set for the client + # @return The supported by the gata data format + def language=(language) + lang = language.to_s.downcase + raise ArgumentError, "Unsupported language" unless LANGUAGE_CODES.include?(lang) + + @language = lang + end + + def search(params = {}) + request("search", params) + end + + def request(path, params = {}) + merged = {language: @language, version: @version}.merge(params) + + uri = URI.join("https://v2.xivapi.com/api/", path) + uri.query = URI.encode_www_form(merged.compact) unless merged.empty? + puts "=> #{uri}" if @verbose + response = Net::HTTP.get_response(uri) + raise response.error! unless response.is_a?(Net::HTTPSuccess) + + if response.content_type&.include?("application/json") + json = JSON.parse(response.body) + + raise json["error"].to_s if json.is_a?(Hash) && json["error"] + + json + else + response.body + end + end + end +end diff --git a/lib/xivapi/assets.rb b/lib/xivapi/assets.rb index 2ec211a..9ea0590 100644 --- a/lib/xivapi/assets.rb +++ b/lib/xivapi/assets.rb @@ -1,24 +1,24 @@ -module XIVAPI - # Endpoints for accessing game data on a file-by-file basis. Commonly useful for fetching icons or other textures to display on the web. - class Assets - attr_accessor :client - - def initialize(client) - @client = client - end - - # Read an asset from the game at the specified path, converting it into a usable format. If no valid conversion between the game file type and specified format exists, an error will be returned. - # @param params [Hash] Query parameters accepted by the asset endpoint. - # @return [Buffer, String] An image of the specified asset in the specified format. - def get(params = {}) - self.client.request("asset", params) - end - - # Retrieve the specified map, composing it from split source files if necessary. - # @param params [Hash] Query parameters accepted by the map endpoint. - # @return [Buffer, String] An image of the specified map in the specified format. - def map(territory, index, params = {}) - self.client.request("asset/map/#{territory}/#{index}", params) - end - end -end \ No newline at end of file +module XIVAPI + # Endpoints for accessing game data on a file-by-file basis. Commonly useful for fetching icons or other textures to display on the web. + class Assets + attr_accessor :client + + def initialize(client) + @client = client + end + + # Read an asset from the game at the specified path, converting it into a usable format. If no valid conversion between the game file type and specified format exists, an error will be returned. + # @param params [Hash] Query parameters accepted by the asset endpoint. + # @return [Buffer, String] An image of the specified asset in the specified format. + def get(params = {}) + client.request("asset", params) + end + + # Retrieve the specified map, composing it from split source files if necessary. + # @param params [Hash] Query parameters accepted by the map endpoint. + # @return [Buffer, String] An image of the specified map in the specified format. + def map(territory, index, params = {}) + client.request("asset/map/#{territory}/#{index}", params) + end + end +end diff --git a/lib/xivapi/sheets.rb b/lib/xivapi/sheets.rb index 0869d67..8068caa 100644 --- a/lib/xivapi/sheets.rb +++ b/lib/xivapi/sheets.rb @@ -1,32 +1,32 @@ -module XIVAPI - class Sheets - attr_accessor :client - - def initialize(client) - @client = client - end - - # List known excel sheets that can be read by the API. - # @return [Array] The list of known sheets. - def all - self.client.request("sheet") - end - - # Read information about one or more rows and their related data. - # @param sheet [String] The sheet to fetch the rows from. - # @param params [Hash] The parameters to fetch the rows with. - # @return [Array] A list of rows with typed fields. - def list(sheet, params = {}) - self.client.request("sheet/#{sheet}", params) - end - - # Read detailed, filterable information from a single sheet row and its related data. - # @param sheet [String] Name of the sheet to read. - # @param row_id [Integer] The ID of the row to read. - # @param params [Hash] The parameters to fetch the row with. - # @return [Hash] The row with typed fields. - def get(sheet, row_id, params = {}) - self.client.request("sheet/#{sheet}/#{row_id}", params) - end - end -end \ No newline at end of file +module XIVAPI + class Sheets + attr_accessor :client + + def initialize(client) + @client = client + end + + # List known excel sheets that can be read by the API. + # @return [Array] The list of known sheets. + def all + client.request("sheet") + end + + # Read information about one or more rows and their related data. + # @param sheet [String] The sheet to fetch the rows from. + # @param params [Hash] The parameters to fetch the rows with. + # @return [Array] A list of rows with typed fields. + def list(sheet, params = {}) + client.request("sheet/#{sheet}", params) + end + + # Read detailed, filterable information from a single sheet row and its related data. + # @param sheet [String] Name of the sheet to read. + # @param row_id [Integer] The ID of the row to read. + # @param params [Hash] The parameters to fetch the row with. + # @return [Hash] The row with typed fields. + def get(sheet, row_id, params = {}) + client.request("sheet/#{sheet}/#{row_id}", params) + end + end +end diff --git a/lib/xivapi/version.rb b/lib/xivapi/version.rb index b56ff87..d327e75 100644 --- a/lib/xivapi/version.rb +++ b/lib/xivapi/version.rb @@ -1,5 +1,5 @@ -# frozen_string_literal: true - -module XIVAPI - VERSION = "0.4.0" -end +# frozen_string_literal: true + +module XIVAPI + VERSION = "0.4.0" +end diff --git a/lib/xivapi/versions.rb b/lib/xivapi/versions.rb index 945ccc4..8d33473 100644 --- a/lib/xivapi/versions.rb +++ b/lib/xivapi/versions.rb @@ -1,17 +1,17 @@ -module XIVAPI - # Endpoints for querying metadata about the versions recorded by the boilmaster system. - class Versions - attr_accessor :client - - def initialize(client) - @client = client - end - - # List versions understood by the API. - # @return [Array] The list of known versions. - def all - result = self.client.request("version") - result["versions"].flat_map { |v| v["names"] } - end - end -end \ No newline at end of file +module XIVAPI + # Endpoints for querying metadata about the versions recorded by the boilmaster system. + class Versions + attr_accessor :client + + def initialize(client) + @client = client + end + + # List versions understood by the API. + # @return [Array] The list of known versions. + def all + result = client.request("version") + result["versions"].flat_map { |v| v["names"] } + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a2a74af..d3ed417 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,15 +1,15 @@ -# frozen_string_literal: true - -require "xivapi" - -RSpec.configure do |config| - # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = ".rspec_status" - - # Disable RSpec exposing methods globally on `Module` and `main` - config.disable_monkey_patching! - - config.expect_with :rspec do |c| - c.syntax = :expect - end -end +# frozen_string_literal: true + +require "xivapi" + +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end diff --git a/spec/xivapi/assets_spec.rb b/spec/xivapi/assets_spec.rb index 00f29d0..7ecb7a8 100644 --- a/spec/xivapi/assets_spec.rb +++ b/spec/xivapi/assets_spec.rb @@ -1,24 +1,24 @@ -RSpec.describe XIVAPI::Assets do - let(:client) { XIVAPI::Client.new } - let(:assets) { described_class.new(client) } - - it "fetches an asset" do - result = assets.get( - path: "ui/icon/051000/051474_hr1.tex", - format: "png" - ) - expect(result).to be_a(String) - expect(result.bytesize).to be > 0 - end - - it "fetches a map" do - result = assets.map("s1d1", "00", version: "latest") - expect(result.bytesize).to be > 0 - end - - it "raises on invalid asset" do - expect { - assets.get(path: "invalid/path.tex", format: "png") - }.to raise_error(Net::HTTPClientException) - end -end \ No newline at end of file +RSpec.describe XIVAPI::Assets do + let(:client) { XIVAPI::Client.new } + let(:assets) { described_class.new(client) } + + it "fetches an asset" do + result = assets.get( + path: "ui/icon/051000/051474_hr1.tex", + format: "png" + ) + expect(result).to be_a(String) + expect(result.bytesize).to be > 0 + end + + it "fetches a map" do + result = assets.map("s1d1", "00", version: "latest") + expect(result.bytesize).to be > 0 + end + + it "raises on invalid asset" do + expect { + assets.get(path: "invalid/path.tex", format: "png") + }.to raise_error(Net::HTTPClientException) + end +end diff --git a/spec/xivapi/client_spec.rb b/spec/xivapi/client_spec.rb index d1a4fe3..3b9bef0 100644 --- a/spec/xivapi/client_spec.rb +++ b/spec/xivapi/client_spec.rb @@ -1,56 +1,56 @@ -RSpec.describe XIVAPI::Client do - let(:client) { described_class.new } - - describe "#search" do - it "performs a search request and returns results" do - result = client.search( - query: 'Name="Iron War Axe"', - sheets: "Item", - limit: 3 - ) - - expect(result).to be_a(Hash) - expect(result["results"]).to be_an(Array) - expect(result["results"].length).to be <= 3 - end - - it "supports partial text search" do - result = client.search( - query: 'Name~"sword"', - sheets: "Item", - limit: 3 - ) - - expect(result["results"]).to be_an(Array) - - result["results"].each do |item| - next unless item["fields"]["Name"] - expect(item["fields"]["Name"].downcase).to include("sword") - end - end - - it "raises an error for invalid query syntax" do - expect { - client.search(query: "invalid syntax", sheets: "Item") - }.to raise_error(Net::HTTPClientException) - end - end - - describe "verbose logging" do - it "prints debug output when verbose is enabled" do - client.verbose = true - - expect { - client.search(query: 'Name="Gil"', sheets: "Item", limit: 1) - }.to output(/https:\/\/v2.xivapi.com\/api\/search/i).to_stdout - end - - it "does not print debug output when verbose is disabled" do - client.verbose = false - - expect { - client.search(query: 'Name="Gil"', sheets: "Item", limit: 1) - }.not_to output.to_stdout - end - end -end \ No newline at end of file +RSpec.describe XIVAPI::Client do + let(:client) { described_class.new } + + describe "#search" do + it "performs a search request and returns results" do + result = client.search( + query: 'Name="Iron War Axe"', + sheets: "Item", + limit: 3 + ) + + expect(result).to be_a(Hash) + expect(result["results"]).to be_an(Array) + expect(result["results"].length).to be <= 3 + end + + it "supports partial text search" do + result = client.search( + query: 'Name~"sword"', + sheets: "Item", + limit: 3 + ) + + expect(result["results"]).to be_an(Array) + + result["results"].each do |item| + next unless item["fields"]["Name"] + expect(item["fields"]["Name"].downcase).to include("sword") + end + end + + it "raises an error for invalid query syntax" do + expect { + client.search(query: "invalid syntax", sheets: "Item") + }.to raise_error(Net::HTTPClientException) + end + end + + describe "verbose logging" do + it "prints debug output when verbose is enabled" do + client.verbose = true + + expect { + client.search(query: 'Name="Gil"', sheets: "Item", limit: 1) + }.to output(/https:\/\/v2.xivapi.com\/api\/search/i).to_stdout + end + + it "does not print debug output when verbose is disabled" do + client.verbose = false + + expect { + client.search(query: 'Name="Gil"', sheets: "Item", limit: 1) + }.not_to output.to_stdout + end + end +end diff --git a/spec/xivapi/sheets_spec.rb b/spec/xivapi/sheets_spec.rb index fd9bafc..c9654eb 100644 --- a/spec/xivapi/sheets_spec.rb +++ b/spec/xivapi/sheets_spec.rb @@ -1,27 +1,27 @@ -RSpec.describe XIVAPI::Sheets do - let(:client) { XIVAPI::Client.new } - let(:sheets) { described_class.new(client) } - - it "lists all sheets" do - result = sheets.all - expect(result["sheets"]).to be_an(Array) - expect(result["sheets"]).not_to be_empty - end - - it "lists rows" do - result = sheets.list("Item", limit: 5) - expect(result["rows"]).to be_an(Array) - expect(result["rows"]).not_to be_empty - end - - it "gets a row" do - result = sheets.get("Item", 1, fields: "Name") - expect(result["fields"]["Name"]).to eq("Gil") - end - - it "raises for invalid sheet" do - expect { - sheets.list("NotASheet") - }.to raise_error(Net::HTTPClientException) - end -end \ No newline at end of file +RSpec.describe XIVAPI::Sheets do + let(:client) { XIVAPI::Client.new } + let(:sheets) { described_class.new(client) } + + it "lists all sheets" do + result = sheets.all + expect(result["sheets"]).to be_an(Array) + expect(result["sheets"]).not_to be_empty + end + + it "lists rows" do + result = sheets.list("Item", limit: 5) + expect(result["rows"]).to be_an(Array) + expect(result["rows"]).not_to be_empty + end + + it "gets a row" do + result = sheets.get("Item", 1, fields: "Name") + expect(result["fields"]["Name"]).to eq("Gil") + end + + it "raises for invalid sheet" do + expect { + sheets.list("NotASheet") + }.to raise_error(Net::HTTPClientException) + end +end diff --git a/spec/xivapi/versions_spec.rb b/spec/xivapi/versions_spec.rb index 9175d3f..0dde43d 100644 --- a/spec/xivapi/versions_spec.rb +++ b/spec/xivapi/versions_spec.rb @@ -1,11 +1,11 @@ -RSpec.describe XIVAPI::Versions do - let(:client) { XIVAPI::Client.new } - let(:versions) { described_class.new(client) } - - it "fetches versions" do - result = versions.all - expect(result).to be_an(Array) - expect(result).not_to be_empty - expect(result.all? { |v| v.is_a?(String) }).to be true - end -end \ No newline at end of file +RSpec.describe XIVAPI::Versions do + let(:client) { XIVAPI::Client.new } + let(:versions) { described_class.new(client) } + + it "fetches versions" do + result = versions.all + expect(result).to be_an(Array) + expect(result).not_to be_empty + expect(result.all? { |v| v.is_a?(String) }).to be true + end +end diff --git a/xivapi.gemspec b/xivapi.gemspec index 556b065..429fd4e 100644 --- a/xivapi.gemspec +++ b/xivapi.gemspec @@ -1,39 +1,32 @@ -# frozen_string_literal: true - -require_relative "lib/xivapi/version" - -Gem::Specification.new do |spec| - spec.name = "xivapi" - spec.version = XIVAPI::VERSION - spec.authors = ["Matt Antonelli"] - spec.email = ["matt@antonelli.dev"] - - spec.summary = "An asynchronous Ruby client gem for working with XIVAPI" - spec.description = "An asynchronous Ruby client gem for working with XIVAPI" - spec.homepage = "https://github.com/xivapi/xivapi-ruby" - spec.license = "MIT" - spec.required_ruby_version = ">= 3.3.0" - - # Specify which files should be added to the gem when it is released. - # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - gemspec = File.basename(__FILE__) - spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| - ls.readlines("\x0", chomp: true).reject do |f| - (f == gemspec) || - f.start_with?(*%w[bin/ Gemfile .gitignore .rspec spec/ .github/ .rubocop.yml]) - end - end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] - - spec.add_development_dependency "bundler", ">= 4.0" - spec.add_development_dependency "rake", "~> 13.0" - spec.add_development_dependency "rspec", "~> 3.0" - spec.add_development_dependency "rubocop", "~> 1.21" - spec.add_development_dependency "rubocop-rake", "~> 0.7.1" - spec.add_development_dependency "rubocop-rspec", "~> 3.9" - spec.add_development_dependency "solargraph", ">= 0.58" - spec.add_dependency "net-http", "~> 0.9.1" - spec.metadata["rubygems_mfa_required"] = "true" -end +# frozen_string_literal: true + +require_relative "lib/xivapi/version" + +Gem::Specification.new do |spec| + spec.name = "xivapi" + spec.version = XIVAPI::VERSION + spec.authors = ["Matt Antonelli"] + spec.email = ["matt@antonelli.dev"] + + spec.summary = "An asynchronous Ruby client gem for working with XIVAPI" + spec.description = "An asynchronous Ruby client gem for working with XIVAPI" + spec.homepage = "https://github.com/xivapi/xivapi-ruby" + spec.license = "MIT" + spec.required_ruby_version = ">= 3.3.0" + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + gemspec = File.basename(__FILE__) + spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| + ls.readlines("\x0", chomp: true).reject do |f| + (f == gemspec) || + f.start_with?(*%w[bin/ Gemfile .gitignore .rspec spec/ .github/ .rubocop.yml]) + end + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_dependency "net-http", "~> 0.9.1" + spec.metadata["rubygems_mfa_required"] = "true" +end From 62f7e2d61ed01a44b1c8e24143b1dbbf470f7794 Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 11:50:37 +0100 Subject: [PATCH 14/25] chore: change end of line sequence from crlf to lf --- .editorconfig | 12 ++-- .rubocop.yml | 10 +-- Gemfile | 28 ++++---- bin/console | 16 ++--- bin/setup | 12 ++-- lib/xivapi.rb | 134 +++++++++++++++++------------------ lib/xivapi/assets.rb | 48 ++++++------- lib/xivapi/sheets.rb | 64 ++++++++--------- lib/xivapi/version.rb | 10 +-- lib/xivapi/versions.rb | 34 ++++----- spec/spec_helper.rb | 30 ++++---- spec/xivapi/assets_spec.rb | 48 ++++++------- spec/xivapi/client_spec.rb | 112 ++++++++++++++--------------- spec/xivapi/sheets_spec.rb | 54 +++++++------- spec/xivapi/versions_spec.rb | 22 +++--- xivapi.gemspec | 64 ++++++++--------- 16 files changed, 349 insertions(+), 349 deletions(-) diff --git a/.editorconfig b/.editorconfig index 9176fc3..e62b828 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,6 @@ -root = true - -[*] -end_of_line = lf -insert_final_newline = true -charset = utf-8 +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 diff --git a/.rubocop.yml b/.rubocop.yml index 4980bd1..7a74a57 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,6 +1,6 @@ -plugins: - - rubocop-rake -require: - - standard -inherit_gem: +plugins: + - rubocop-rake +require: + - standard +inherit_gem: standard: config/base.yml \ No newline at end of file diff --git a/Gemfile b/Gemfile index 0365dd3..fde918e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,14 +1,14 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -gem "bundler", ">= 4.0", group: :development -gem "rake", "~> 13.0", group: :development -gem "rspec", "~> 3.0", group: :development -gem "rubocop-rspec", "~> 3.9", group: :development -gem "rubocop-rake", "~> 0.7.1", group: :development -gem "solargraph", ">= 0.58", group: :development -gem "standard", ">= 1.35.1", group: :development - -# Specify your gem's dependencies in xivapi.gemspec -gemspec +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "bundler", ">= 4.0", group: :development +gem "rake", "~> 13.0", group: :development +gem "rspec", "~> 3.0", group: :development +gem "rubocop-rspec", "~> 3.9", group: :development +gem "rubocop-rake", "~> 0.7.1", group: :development +gem "solargraph", ">= 0.58", group: :development +gem "standard", ">= 1.35.1", group: :development + +# Specify your gem's dependencies in xivapi.gemspec +gemspec diff --git a/bin/console b/bin/console index 7527aef..d00fc3d 100755 --- a/bin/console +++ b/bin/console @@ -1,8 +1,8 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require "bundler/setup" -require "xivapi" - -require "irb" -IRB.start(__FILE__) +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "bundler/setup" +require "xivapi" + +require "irb" +IRB.start(__FILE__) diff --git a/bin/setup b/bin/setup index 5020b1b..cf4ad25 100755 --- a/bin/setup +++ b/bin/setup @@ -1,6 +1,6 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' -set -vx - -bundle install +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install diff --git a/lib/xivapi.rb b/lib/xivapi.rb index c5ada5a..b425aa1 100644 --- a/lib/xivapi.rb +++ b/lib/xivapi.rb @@ -1,67 +1,67 @@ -# frozen_string_literal: true - -require_relative "xivapi/version" # The version of the gem - -require_relative "xivapi/assets" -require_relative "xivapi/sheets" -require_relative "xivapi/versions" - -require "uri" -require "net/http" -require "json" - -module XIVAPI - # Known languages supported by the gata data format. - LANGUAGE_CODES = %w[ja en de fr chs cht kr].freeze - - # An asynchronous Ruby wrapper for XIVAPI. - class Client - attr_accessor :logger, :verbose, :version - - # Initialize a new client for making API requests. - # @param language [String] The supported by the gata data format - # @param version [String] The supported version of the game to use for the API. - # @param verbose [true, false] Whether to enable verbose logging. - def initialize(language = "en", version = "latest", verbose = false) - self.language = language - @version = version - @verbose = verbose - end - - # @return [String] The supported by the gata data format - attr_reader :language - - # @param language [String, Symbol] The language to set for the client - # @return The supported by the gata data format - def language=(language) - lang = language.to_s.downcase - raise ArgumentError, "Unsupported language" unless LANGUAGE_CODES.include?(lang) - - @language = lang - end - - def search(params = {}) - request("search", params) - end - - def request(path, params = {}) - merged = {language: @language, version: @version}.merge(params) - - uri = URI.join("https://v2.xivapi.com/api/", path) - uri.query = URI.encode_www_form(merged.compact) unless merged.empty? - puts "=> #{uri}" if @verbose - response = Net::HTTP.get_response(uri) - raise response.error! unless response.is_a?(Net::HTTPSuccess) - - if response.content_type&.include?("application/json") - json = JSON.parse(response.body) - - raise json["error"].to_s if json.is_a?(Hash) && json["error"] - - json - else - response.body - end - end - end -end +# frozen_string_literal: true + +require_relative "xivapi/version" # The version of the gem + +require_relative "xivapi/assets" +require_relative "xivapi/sheets" +require_relative "xivapi/versions" + +require "uri" +require "net/http" +require "json" + +module XIVAPI + # Known languages supported by the gata data format. + LANGUAGE_CODES = %w[ja en de fr chs cht kr].freeze + + # An asynchronous Ruby wrapper for XIVAPI. + class Client + attr_accessor :logger, :verbose, :version + + # Initialize a new client for making API requests. + # @param language [String] The supported by the gata data format + # @param version [String] The supported version of the game to use for the API. + # @param verbose [true, false] Whether to enable verbose logging. + def initialize(language = "en", version = "latest", verbose = false) + self.language = language + @version = version + @verbose = verbose + end + + # @return [String] The supported by the gata data format + attr_reader :language + + # @param language [String, Symbol] The language to set for the client + # @return The supported by the gata data format + def language=(language) + lang = language.to_s.downcase + raise ArgumentError, "Unsupported language" unless LANGUAGE_CODES.include?(lang) + + @language = lang + end + + def search(params = {}) + request("search", params) + end + + def request(path, params = {}) + merged = {language: @language, version: @version}.merge(params) + + uri = URI.join("https://v2.xivapi.com/api/", path) + uri.query = URI.encode_www_form(merged.compact) unless merged.empty? + puts "=> #{uri}" if @verbose + response = Net::HTTP.get_response(uri) + raise response.error! unless response.is_a?(Net::HTTPSuccess) + + if response.content_type&.include?("application/json") + json = JSON.parse(response.body) + + raise json["error"].to_s if json.is_a?(Hash) && json["error"] + + json + else + response.body + end + end + end +end diff --git a/lib/xivapi/assets.rb b/lib/xivapi/assets.rb index 9ea0590..4016e59 100644 --- a/lib/xivapi/assets.rb +++ b/lib/xivapi/assets.rb @@ -1,24 +1,24 @@ -module XIVAPI - # Endpoints for accessing game data on a file-by-file basis. Commonly useful for fetching icons or other textures to display on the web. - class Assets - attr_accessor :client - - def initialize(client) - @client = client - end - - # Read an asset from the game at the specified path, converting it into a usable format. If no valid conversion between the game file type and specified format exists, an error will be returned. - # @param params [Hash] Query parameters accepted by the asset endpoint. - # @return [Buffer, String] An image of the specified asset in the specified format. - def get(params = {}) - client.request("asset", params) - end - - # Retrieve the specified map, composing it from split source files if necessary. - # @param params [Hash] Query parameters accepted by the map endpoint. - # @return [Buffer, String] An image of the specified map in the specified format. - def map(territory, index, params = {}) - client.request("asset/map/#{territory}/#{index}", params) - end - end -end +module XIVAPI + # Endpoints for accessing game data on a file-by-file basis. Commonly useful for fetching icons or other textures to display on the web. + class Assets + attr_accessor :client + + def initialize(client) + @client = client + end + + # Read an asset from the game at the specified path, converting it into a usable format. If no valid conversion between the game file type and specified format exists, an error will be returned. + # @param params [Hash] Query parameters accepted by the asset endpoint. + # @return [Buffer, String] An image of the specified asset in the specified format. + def get(params = {}) + client.request("asset", params) + end + + # Retrieve the specified map, composing it from split source files if necessary. + # @param params [Hash] Query parameters accepted by the map endpoint. + # @return [Buffer, String] An image of the specified map in the specified format. + def map(territory, index, params = {}) + client.request("asset/map/#{territory}/#{index}", params) + end + end +end diff --git a/lib/xivapi/sheets.rb b/lib/xivapi/sheets.rb index 8068caa..473a11e 100644 --- a/lib/xivapi/sheets.rb +++ b/lib/xivapi/sheets.rb @@ -1,32 +1,32 @@ -module XIVAPI - class Sheets - attr_accessor :client - - def initialize(client) - @client = client - end - - # List known excel sheets that can be read by the API. - # @return [Array] The list of known sheets. - def all - client.request("sheet") - end - - # Read information about one or more rows and their related data. - # @param sheet [String] The sheet to fetch the rows from. - # @param params [Hash] The parameters to fetch the rows with. - # @return [Array] A list of rows with typed fields. - def list(sheet, params = {}) - client.request("sheet/#{sheet}", params) - end - - # Read detailed, filterable information from a single sheet row and its related data. - # @param sheet [String] Name of the sheet to read. - # @param row_id [Integer] The ID of the row to read. - # @param params [Hash] The parameters to fetch the row with. - # @return [Hash] The row with typed fields. - def get(sheet, row_id, params = {}) - client.request("sheet/#{sheet}/#{row_id}", params) - end - end -end +module XIVAPI + class Sheets + attr_accessor :client + + def initialize(client) + @client = client + end + + # List known excel sheets that can be read by the API. + # @return [Array] The list of known sheets. + def all + client.request("sheet") + end + + # Read information about one or more rows and their related data. + # @param sheet [String] The sheet to fetch the rows from. + # @param params [Hash] The parameters to fetch the rows with. + # @return [Array] A list of rows with typed fields. + def list(sheet, params = {}) + client.request("sheet/#{sheet}", params) + end + + # Read detailed, filterable information from a single sheet row and its related data. + # @param sheet [String] Name of the sheet to read. + # @param row_id [Integer] The ID of the row to read. + # @param params [Hash] The parameters to fetch the row with. + # @return [Hash] The row with typed fields. + def get(sheet, row_id, params = {}) + client.request("sheet/#{sheet}/#{row_id}", params) + end + end +end diff --git a/lib/xivapi/version.rb b/lib/xivapi/version.rb index d327e75..b56ff87 100644 --- a/lib/xivapi/version.rb +++ b/lib/xivapi/version.rb @@ -1,5 +1,5 @@ -# frozen_string_literal: true - -module XIVAPI - VERSION = "0.4.0" -end +# frozen_string_literal: true + +module XIVAPI + VERSION = "0.4.0" +end diff --git a/lib/xivapi/versions.rb b/lib/xivapi/versions.rb index 8d33473..8d4fd82 100644 --- a/lib/xivapi/versions.rb +++ b/lib/xivapi/versions.rb @@ -1,17 +1,17 @@ -module XIVAPI - # Endpoints for querying metadata about the versions recorded by the boilmaster system. - class Versions - attr_accessor :client - - def initialize(client) - @client = client - end - - # List versions understood by the API. - # @return [Array] The list of known versions. - def all - result = client.request("version") - result["versions"].flat_map { |v| v["names"] } - end - end -end +module XIVAPI + # Endpoints for querying metadata about the versions recorded by the boilmaster system. + class Versions + attr_accessor :client + + def initialize(client) + @client = client + end + + # List versions understood by the API. + # @return [Array] The list of known versions. + def all + result = client.request("version") + result["versions"].flat_map { |v| v["names"] } + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d3ed417..a2a74af 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,15 +1,15 @@ -# frozen_string_literal: true - -require "xivapi" - -RSpec.configure do |config| - # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = ".rspec_status" - - # Disable RSpec exposing methods globally on `Module` and `main` - config.disable_monkey_patching! - - config.expect_with :rspec do |c| - c.syntax = :expect - end -end +# frozen_string_literal: true + +require "xivapi" + +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end +end diff --git a/spec/xivapi/assets_spec.rb b/spec/xivapi/assets_spec.rb index 7ecb7a8..13f73be 100644 --- a/spec/xivapi/assets_spec.rb +++ b/spec/xivapi/assets_spec.rb @@ -1,24 +1,24 @@ -RSpec.describe XIVAPI::Assets do - let(:client) { XIVAPI::Client.new } - let(:assets) { described_class.new(client) } - - it "fetches an asset" do - result = assets.get( - path: "ui/icon/051000/051474_hr1.tex", - format: "png" - ) - expect(result).to be_a(String) - expect(result.bytesize).to be > 0 - end - - it "fetches a map" do - result = assets.map("s1d1", "00", version: "latest") - expect(result.bytesize).to be > 0 - end - - it "raises on invalid asset" do - expect { - assets.get(path: "invalid/path.tex", format: "png") - }.to raise_error(Net::HTTPClientException) - end -end +RSpec.describe XIVAPI::Assets do + let(:client) { XIVAPI::Client.new } + let(:assets) { described_class.new(client) } + + it "fetches an asset" do + result = assets.get( + path: "ui/icon/051000/051474_hr1.tex", + format: "png" + ) + expect(result).to be_a(String) + expect(result.bytesize).to be > 0 + end + + it "fetches a map" do + result = assets.map("s1d1", "00", version: "latest") + expect(result.bytesize).to be > 0 + end + + it "raises on invalid asset" do + expect { + assets.get(path: "invalid/path.tex", format: "png") + }.to raise_error(Net::HTTPClientException) + end +end diff --git a/spec/xivapi/client_spec.rb b/spec/xivapi/client_spec.rb index 3b9bef0..973c8a9 100644 --- a/spec/xivapi/client_spec.rb +++ b/spec/xivapi/client_spec.rb @@ -1,56 +1,56 @@ -RSpec.describe XIVAPI::Client do - let(:client) { described_class.new } - - describe "#search" do - it "performs a search request and returns results" do - result = client.search( - query: 'Name="Iron War Axe"', - sheets: "Item", - limit: 3 - ) - - expect(result).to be_a(Hash) - expect(result["results"]).to be_an(Array) - expect(result["results"].length).to be <= 3 - end - - it "supports partial text search" do - result = client.search( - query: 'Name~"sword"', - sheets: "Item", - limit: 3 - ) - - expect(result["results"]).to be_an(Array) - - result["results"].each do |item| - next unless item["fields"]["Name"] - expect(item["fields"]["Name"].downcase).to include("sword") - end - end - - it "raises an error for invalid query syntax" do - expect { - client.search(query: "invalid syntax", sheets: "Item") - }.to raise_error(Net::HTTPClientException) - end - end - - describe "verbose logging" do - it "prints debug output when verbose is enabled" do - client.verbose = true - - expect { - client.search(query: 'Name="Gil"', sheets: "Item", limit: 1) - }.to output(/https:\/\/v2.xivapi.com\/api\/search/i).to_stdout - end - - it "does not print debug output when verbose is disabled" do - client.verbose = false - - expect { - client.search(query: 'Name="Gil"', sheets: "Item", limit: 1) - }.not_to output.to_stdout - end - end -end +RSpec.describe XIVAPI::Client do + let(:client) { described_class.new } + + describe "#search" do + it "performs a search request and returns results" do + result = client.search( + query: 'Name="Iron War Axe"', + sheets: "Item", + limit: 3 + ) + + expect(result).to be_a(Hash) + expect(result["results"]).to be_an(Array) + expect(result["results"].length).to be <= 3 + end + + it "supports partial text search" do + result = client.search( + query: 'Name~"sword"', + sheets: "Item", + limit: 3 + ) + + expect(result["results"]).to be_an(Array) + + result["results"].each do |item| + next unless item["fields"]["Name"] + expect(item["fields"]["Name"].downcase).to include("sword") + end + end + + it "raises an error for invalid query syntax" do + expect { + client.search(query: "invalid syntax", sheets: "Item") + }.to raise_error(Net::HTTPClientException) + end + end + + describe "verbose logging" do + it "prints debug output when verbose is enabled" do + client.verbose = true + + expect { + client.search(query: 'Name="Gil"', sheets: "Item", limit: 1) + }.to output(/https:\/\/v2.xivapi.com\/api\/search/i).to_stdout + end + + it "does not print debug output when verbose is disabled" do + client.verbose = false + + expect { + client.search(query: 'Name="Gil"', sheets: "Item", limit: 1) + }.not_to output.to_stdout + end + end +end diff --git a/spec/xivapi/sheets_spec.rb b/spec/xivapi/sheets_spec.rb index c9654eb..41252c8 100644 --- a/spec/xivapi/sheets_spec.rb +++ b/spec/xivapi/sheets_spec.rb @@ -1,27 +1,27 @@ -RSpec.describe XIVAPI::Sheets do - let(:client) { XIVAPI::Client.new } - let(:sheets) { described_class.new(client) } - - it "lists all sheets" do - result = sheets.all - expect(result["sheets"]).to be_an(Array) - expect(result["sheets"]).not_to be_empty - end - - it "lists rows" do - result = sheets.list("Item", limit: 5) - expect(result["rows"]).to be_an(Array) - expect(result["rows"]).not_to be_empty - end - - it "gets a row" do - result = sheets.get("Item", 1, fields: "Name") - expect(result["fields"]["Name"]).to eq("Gil") - end - - it "raises for invalid sheet" do - expect { - sheets.list("NotASheet") - }.to raise_error(Net::HTTPClientException) - end -end +RSpec.describe XIVAPI::Sheets do + let(:client) { XIVAPI::Client.new } + let(:sheets) { described_class.new(client) } + + it "lists all sheets" do + result = sheets.all + expect(result["sheets"]).to be_an(Array) + expect(result["sheets"]).not_to be_empty + end + + it "lists rows" do + result = sheets.list("Item", limit: 5) + expect(result["rows"]).to be_an(Array) + expect(result["rows"]).not_to be_empty + end + + it "gets a row" do + result = sheets.get("Item", 1, fields: "Name") + expect(result["fields"]["Name"]).to eq("Gil") + end + + it "raises for invalid sheet" do + expect { + sheets.list("NotASheet") + }.to raise_error(Net::HTTPClientException) + end +end diff --git a/spec/xivapi/versions_spec.rb b/spec/xivapi/versions_spec.rb index 0dde43d..d8ed94b 100644 --- a/spec/xivapi/versions_spec.rb +++ b/spec/xivapi/versions_spec.rb @@ -1,11 +1,11 @@ -RSpec.describe XIVAPI::Versions do - let(:client) { XIVAPI::Client.new } - let(:versions) { described_class.new(client) } - - it "fetches versions" do - result = versions.all - expect(result).to be_an(Array) - expect(result).not_to be_empty - expect(result.all? { |v| v.is_a?(String) }).to be true - end -end +RSpec.describe XIVAPI::Versions do + let(:client) { XIVAPI::Client.new } + let(:versions) { described_class.new(client) } + + it "fetches versions" do + result = versions.all + expect(result).to be_an(Array) + expect(result).not_to be_empty + expect(result.all? { |v| v.is_a?(String) }).to be true + end +end diff --git a/xivapi.gemspec b/xivapi.gemspec index 429fd4e..e2841bf 100644 --- a/xivapi.gemspec +++ b/xivapi.gemspec @@ -1,32 +1,32 @@ -# frozen_string_literal: true - -require_relative "lib/xivapi/version" - -Gem::Specification.new do |spec| - spec.name = "xivapi" - spec.version = XIVAPI::VERSION - spec.authors = ["Matt Antonelli"] - spec.email = ["matt@antonelli.dev"] - - spec.summary = "An asynchronous Ruby client gem for working with XIVAPI" - spec.description = "An asynchronous Ruby client gem for working with XIVAPI" - spec.homepage = "https://github.com/xivapi/xivapi-ruby" - spec.license = "MIT" - spec.required_ruby_version = ">= 3.3.0" - - # Specify which files should be added to the gem when it is released. - # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - gemspec = File.basename(__FILE__) - spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| - ls.readlines("\x0", chomp: true).reject do |f| - (f == gemspec) || - f.start_with?(*%w[bin/ Gemfile .gitignore .rspec spec/ .github/ .rubocop.yml]) - end - end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] - - spec.add_dependency "net-http", "~> 0.9.1" - spec.metadata["rubygems_mfa_required"] = "true" -end +# frozen_string_literal: true + +require_relative "lib/xivapi/version" + +Gem::Specification.new do |spec| + spec.name = "xivapi" + spec.version = XIVAPI::VERSION + spec.authors = ["Matt Antonelli"] + spec.email = ["matt@antonelli.dev"] + + spec.summary = "An asynchronous Ruby client gem for working with XIVAPI" + spec.description = "An asynchronous Ruby client gem for working with XIVAPI" + spec.homepage = "https://github.com/xivapi/xivapi-ruby" + spec.license = "MIT" + spec.required_ruby_version = ">= 3.3.0" + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + gemspec = File.basename(__FILE__) + spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| + ls.readlines("\x0", chomp: true).reject do |f| + (f == gemspec) || + f.start_with?(*%w[bin/ Gemfile .gitignore .rspec spec/ .github/ .rubocop.yml]) + end + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_dependency "net-http", "~> 0.9.1" + spec.metadata["rubygems_mfa_required"] = "true" +end From cd40a75ef81a364743a1c50606bec2a4c8e82247 Mon Sep 17 00:00:00 2001 From: miichom Date: Thu, 7 May 2026 11:51:15 +0100 Subject: [PATCH 15/25] style: reformat Rakefile for consistent code style --- Rakefile | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Rakefile b/Rakefile index f620652..25c166f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,14 +1,14 @@ -# frozen_string_literal: true - -require "bundler/gem_tasks" -require "rspec/core/rake_task" - -RSpec::Core::RakeTask.new(:spec) - -require "rubocop/rake_task" - -RuboCop::RakeTask.new(:standard) do |task| - task.options = ["--require", "standard", "--format", "progress"] -end - -task default: %i[spec standard] +# frozen_string_literal: true + +require "bundler/gem_tasks" +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new(:spec) + +require "rubocop/rake_task" + +RuboCop::RakeTask.new(:standard) do |task| + task.options = ["--require", "standard", "--format", "progress"] +end + +task default: %i[spec standard] From 41f9e4a033787556ef96cae4e08be73b98237fa2 Mon Sep 17 00:00:00 2001 From: miichom Date: Sun, 10 May 2026 17:19:53 +0100 Subject: [PATCH 16/25] chore: remove GPR workflow action --- .github/workflows/gem_push.yml | 43 ---------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 .github/workflows/gem_push.yml diff --git a/.github/workflows/gem_push.yml b/.github/workflows/gem_push.yml deleted file mode 100644 index 96936f7..0000000 --- a/.github/workflows/gem_push.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Ruby Gem - -on: - release: - types: [published] - -jobs: - build: - name: Build + Publish - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - uses: actions/checkout@v4 - - name: Set up Ruby 2.6 - uses: ruby/setup-ruby@v1 - with: - ruby-version: head - - - name: Publish to GPR - run: | - mkdir -p $HOME/.gem - touch $HOME/.gem/credentials - chmod 0600 $HOME/.gem/credentials - printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials - gem build *.gemspec - gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem - env: - GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}" - OWNER: ${{ github.repository_owner }} - - # - name: Publish to RubyGems - # run: | - # mkdir -p $HOME/.gem - # touch $HOME/.gem/credentials - # chmod 0600 $HOME/.gem/credentials - # printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials - # gem build *.gemspec - # gem push *.gem - # env: - # GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}" \ No newline at end of file From 9af02d6ef511eb9055535fab1c124acbccc6e839 Mon Sep 17 00:00:00 2001 From: miichom Date: Sun, 10 May 2026 17:24:09 +0100 Subject: [PATCH 17/25] fix: update gemspec file for bundle --- xivapi.gemspec | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/xivapi.gemspec b/xivapi.gemspec index e2841bf..de1d518 100644 --- a/xivapi.gemspec +++ b/xivapi.gemspec @@ -14,14 +14,9 @@ Gem::Specification.new do |spec| spec.license = "MIT" spec.required_ruby_version = ">= 3.3.0" - # Specify which files should be added to the gem when it is released. - # The `git ls-files -z` loads the files in the RubyGem that have been added into git. gemspec = File.basename(__FILE__) - spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls| - ls.readlines("\x0", chomp: true).reject do |f| - (f == gemspec) || - f.start_with?(*%w[bin/ Gemfile .gitignore .rspec spec/ .github/ .rubocop.yml]) - end + spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do + `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end spec.bindir = "exe" spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } From 807e5c6dde81061d34c1496c4511e82371b89cfb Mon Sep 17 00:00:00 2001 From: miichom Date: Sun, 10 May 2026 17:27:18 +0100 Subject: [PATCH 18/25] fix: clean up gemspec by removing unnecessary lines --- xivapi.gemspec | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/xivapi.gemspec b/xivapi.gemspec index de1d518..88ba67c 100644 --- a/xivapi.gemspec +++ b/xivapi.gemspec @@ -14,13 +14,10 @@ Gem::Specification.new do |spec| spec.license = "MIT" spec.required_ruby_version = ">= 3.3.0" - gemspec = File.basename(__FILE__) + spec.require_paths = ["lib"] spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] spec.add_dependency "net-http", "~> 0.9.1" spec.metadata["rubygems_mfa_required"] = "true" From 916a31e8584bd18a1a57b49c89eac237f63588bb Mon Sep 17 00:00:00 2001 From: miichom Date: Sun, 10 May 2026 17:48:03 +0100 Subject: [PATCH 19/25] chore: update GitHub Actions workflow for deployment and add YARD documentation generation --- .github/workflows/ruby.yml | 40 +++++++++++++++++++++++++++----------- Gemfile | 1 + Gemfile.lock | 1 + 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml index b200a45..767f5b9 100644 --- a/.github/workflows/ruby.yml +++ b/.github/workflows/ruby.yml @@ -1,35 +1,53 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake -# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby - -name: Ruby +name: Rake and Deploy on: push: branches: [ "master" ] pull_request: branches: [ "master" ] + workflow_dispatch: permissions: contents: read + pages: write + id-token: write jobs: test: - runs-on: ubuntu-latest strategy: matrix: ruby-version: ['3.3', '3.4', '4.0'] - steps: - uses: actions/checkout@v4 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} - bundler-cache: true # runs 'bundle install' and caches installed gems automatically + bundler-cache: true - name: Run tests run: bundle exec rake + + deploy: + if: github.ref == 'refs/heads/master' + needs: test + runs-on: ubuntu-latest + permissions: + contents: write + pages: write + id-token: write + steps: + - uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + - name: Generate YARD docs + run: bundle exec yard doc + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./doc + - name: Deploy to GitHub Pages + uses: actions/deploy-pages@v4 diff --git a/Gemfile b/Gemfile index fde918e..9618562 100644 --- a/Gemfile +++ b/Gemfile @@ -9,6 +9,7 @@ gem "rubocop-rspec", "~> 3.9", group: :development gem "rubocop-rake", "~> 0.7.1", group: :development gem "solargraph", ">= 0.58", group: :development gem "standard", ">= 1.35.1", group: :development +gem "yard", group: :development # Specify your gem's dependencies in xivapi.gemspec gemspec diff --git a/Gemfile.lock b/Gemfile.lock index d602f64..e2d2440 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -143,6 +143,7 @@ DEPENDENCIES solargraph (>= 0.58) standard (>= 1.35.1) xivapi! + yard CHECKSUMS ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 From 3d7e6b5914043d69b29c78c082c48dd6dd590336 Mon Sep 17 00:00:00 2001 From: miichom Date: Sun, 10 May 2026 17:53:41 +0100 Subject: [PATCH 20/25] chore: remove unused RBS signature file for Xivapi module --- sig/xivapi.rbs | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 sig/xivapi.rbs diff --git a/sig/xivapi.rbs b/sig/xivapi.rbs deleted file mode 100644 index e9b8497..0000000 --- a/sig/xivapi.rbs +++ /dev/null @@ -1,4 +0,0 @@ -module Xivapi - VERSION: String - # See the writing guide of rbs: https://github.com/ruby/rbs#guides -end From 48a9274b98b6d765d6163c923d2302d75aeafbc1 Mon Sep 17 00:00:00 2001 From: miichom Date: Fri, 15 May 2026 09:36:51 +0100 Subject: [PATCH 21/25] fix: update .rubocop.yml to enforce LF line endings in local dev --- .rubocop.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 7a74a57..af8b4f9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,5 +2,7 @@ plugins: - rubocop-rake require: - standard +Layout/EndOfLine: + EnforcedStyle: lf inherit_gem: standard: config/base.yml \ No newline at end of file From d817913ba7ebaab3741e4e91cf3d77fe5a875803 Mon Sep 17 00:00:00 2001 From: miichom Date: Fri, 15 May 2026 09:36:57 +0100 Subject: [PATCH 22/25] docs: add YARD documentation for client initialization and search methods --- lib/xivapi.rb | 7 +++++++ lib/xivapi/assets.rb | 3 +++ lib/xivapi/sheets.rb | 3 +++ lib/xivapi/versions.rb | 3 +++ 4 files changed, 16 insertions(+) diff --git a/lib/xivapi.rb b/lib/xivapi.rb index b425aa1..566d884 100644 --- a/lib/xivapi.rb +++ b/lib/xivapi.rb @@ -40,10 +40,17 @@ def language=(language) @language = lang end + # Fetch information about rows and their related data that match the provided search query. + # @param params [Hash] Query paramters accepted by the search endpoint. + # @return [Hash] Response structure for the search endpoint. def search(params = {}) request("search", params) end + # Make a raw request to the API at the specified path with the specified parameters. + # @param path [String] The path to make the request to, relative to the base API URL. + # @param params [Hash] The query parameters to include in the request. + # @return [Hash, String] The parsed JSON response or a bytestring of the response body if the content type is not JSON. def request(path, params = {}) merged = {language: @language, version: @version}.merge(params) diff --git a/lib/xivapi/assets.rb b/lib/xivapi/assets.rb index 4016e59..f133016 100644 --- a/lib/xivapi/assets.rb +++ b/lib/xivapi/assets.rb @@ -3,6 +3,9 @@ module XIVAPI class Assets attr_accessor :client + # Initialize a new Assets client for making API requests. + # @param client [XIVAPI::Client] The client to use for making API + # @return [XIVAPI::Assets] A new Assets client instance. def initialize(client) @client = client end diff --git a/lib/xivapi/sheets.rb b/lib/xivapi/sheets.rb index 473a11e..cdd21ad 100644 --- a/lib/xivapi/sheets.rb +++ b/lib/xivapi/sheets.rb @@ -2,6 +2,9 @@ module XIVAPI class Sheets attr_accessor :client + # Initialize a new Sheets client for making API requests. + # @param client [XIVAPI::Client] The client to use for making API + # @return [XIVAPI::Sheets] A new Sheets client instance. def initialize(client) @client = client end diff --git a/lib/xivapi/versions.rb b/lib/xivapi/versions.rb index 8d4fd82..11fa921 100644 --- a/lib/xivapi/versions.rb +++ b/lib/xivapi/versions.rb @@ -3,6 +3,9 @@ module XIVAPI class Versions attr_accessor :client + # Initialize a new Versions client for making API requests. + # @param client [XIVAPI::Client] The client to use for making API + # @return [XIVAPI::Versions] A new Versions client instance. def initialize(client) @client = client end From 78909c6ab672882cc313e88ed20d9c3e9a371b35 Mon Sep 17 00:00:00 2001 From: miichom Date: Tue, 19 May 2026 18:38:56 +0100 Subject: [PATCH 23/25] fix: add validation for query parameters in request method --- lib/xivapi.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/xivapi.rb b/lib/xivapi.rb index 566d884..7ab190a 100644 --- a/lib/xivapi.rb +++ b/lib/xivapi.rb @@ -53,6 +53,11 @@ def search(params = {}) # @return [Hash, String] The parsed JSON response or a bytestring of the response body if the content type is not JSON. def request(path, params = {}) merged = {language: @language, version: @version}.merge(params) + if merged.any? { |k, v| v.nil? } + raise ArgumentError, "Nil values are not allowed in query parameters" + elsif merged[:verbose] == true + raise ArgumentError, "Verbose mode is not supported as a query parameter. Set verbose mode on the client instance instead." + end uri = URI.join("https://v2.xivapi.com/api/", path) uri.query = URI.encode_www_form(merged.compact) unless merged.empty? From 0e7ec142c65a8bce25abafa1a3803472c72f500a Mon Sep 17 00:00:00 2001 From: miichom Date: Tue, 19 May 2026 18:39:06 +0100 Subject: [PATCH 24/25] docs: update usage example to include optional parameters for request method --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d05e5b0..51321b3 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ client = XIVAPI::Client.new(language: "jp", version: "7.0", verbose: true) # Raw requests client.request("/sheet/Item/1") +client.request("/sheet/Item/1", language: "jp", version: "7.0") # optional # Or using typed endpoints sheets = XIVAPI::Sheets.new(client) # ~ must pass the client From e95a05010ca616f616839c8845a02b22e2ee11c1 Mon Sep 17 00:00:00 2001 From: miichom Date: Tue, 19 May 2026 18:39:12 +0100 Subject: [PATCH 25/25] fix: add missing AllCops configuration to .rubocop.yml --- .rubocop.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index af8b4f9..d8c0c4a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,3 +1,5 @@ +AllCops: + SuggestExtensions: false plugins: - rubocop-rake require: