{"id":29272040,"url":"https://github.com/ruby-oauth/version_gem","last_synced_at":"2025-07-05T00:10:27.452Z","repository":{"id":39002093,"uuid":"504230044","full_name":"ruby-oauth/version_gem","owner":"ruby-oauth","description":"Enhance that VERSION! Sugar for boring Version modules","archived":false,"fork":false,"pushed_at":"2025-05-29T01:59:16.000Z","size":326,"stargazers_count":9,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-27T16:53:46.176Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ruby-oauth.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"buy_me_a_coffee":"pboling","community_bridge":null,"github":["pboling"],"issuehunt":"pboling","ko_fi":"pboling","liberapay":"pboling","open_collective":null,"patreon":"galtzo","polar":"pboling","thanks_dev":"gh/pboling","tidelift":"rubygems/version_gem"}},"created_at":"2022-06-16T16:33:51.000Z","updated_at":"2025-06-27T13:29:56.000Z","dependencies_parsed_at":"2023-01-17T16:16:01.798Z","dependency_job_id":"c5ff8a0a-458d-4d26-8f94-868270edba10","html_url":"https://github.com/ruby-oauth/version_gem","commit_stats":null,"previous_names":["ruby-oauth/version_gem"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/ruby-oauth/version_gem","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruby-oauth%2Fversion_gem","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruby-oauth%2Fversion_gem/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruby-oauth%2Fversion_gem/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruby-oauth%2Fversion_gem/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ruby-oauth","download_url":"https://codeload.github.com/ruby-oauth/version_gem/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ruby-oauth%2Fversion_gem/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263052428,"owners_count":23406103,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-07-05T00:10:26.602Z","updated_at":"2025-07-05T00:10:27.441Z","avatar_url":"https://github.com/ruby-oauth.png","language":"Ruby","funding_links":["https://buymeacoffee.com/pboling","https://github.com/sponsors/pboling","https://issuehunt.io/r/pboling","https://ko-fi.com/pboling","https://liberapay.com/pboling","https://patreon.com/galtzo","https://polar.sh/pboling","https://thanks.dev/gh/pboling","https://tidelift.com/funding/github/rubygems/version_gem","https://tidelift.com/subscription","https://blog.tidelift.com/tidelift-joins-sonar","https://liberapay.com/pboling/donate","https://ko-fi.com/O5O86SNP4","https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte\u0026emoji=\u0026slug=pboling\u0026button_colour=FFDD00\u0026font_colour=000000\u0026font_family=Cookie\u0026outline_colour=000000\u0026coffee_colour=ffffff","https://www.buymeacoffee.com/pboling"],"categories":["Ruby"],"sub_categories":[],"readme":"# 🔖 VersionGem\n\n[![Version][👽versioni]][👽version]\n[![License: MIT][📄license-img]][📄license-ref]\n[![Downloads Rank][👽dl-ranki]][👽dl-rank]\n[![Open Source Helpers][👽oss-helpi]][👽oss-help]\n[![Depfu][🔑depfui♻️]][🔑depfu]\n[![CodeCov Test Coverage][🔑codecovi♻️]][🔑codecov]\n[![Coveralls Test Coverage][🔑coveralls-img]][🔑coveralls]\n[![CodeClimate Test Coverage][🔑cc-covi♻️]][🔑cc-cov]\n[![Maintainability][🔑cc-mnti♻️]][🔑cc-mnt]\n[![CI Heads][🚎3-hd-wfi]][🚎3-hd-wf]\n[![CI Current][🚎11-c-wfi]][🚎11-c-wf]\n[![CI Truffle Ruby][🚎9-t-wfi]][🚎9-t-wf]\n[![CI JRuby][🚎10-j-wfi]][🚎10-j-wf]\n[![CI Supported][🚎6-s-wfi]][🚎6-s-wf]\n[![CI Legacy][🚎4-lg-wfi]][🚎4-lg-wf]\n[![CI Unsupported][🚎7-us-wfi]][🚎7-us-wf]\n[![CI Ancient][🚎1-an-wfi]][🚎1-an-wf]\n[![CI Test Coverage][🚎2-cov-wfi]][🚎2-cov-wf]\n[![CI Style][🚎5-st-wfi]][🚎5-st-wf]\n\n---\n\n[![Liberapay Patrons][⛳liberapay-img]][⛳liberapay]\n[![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor]\n[![Buy me a coffee][🖇buyme-small-img]][🖇buyme]\n[![Donate on Polar][🖇polar-img]][🖇polar]\n[![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi]\n[![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon]\n\n## 🧐 Alternatives\n\nThis gem has a very niche purpose, which is:\n\n1. providing introspection of a `Version` module based on a `VERSION` constant string within it,\n2. while not interfering with `gemspec` parsing where the `VERSION` string is traditionally used,\n3. allowing 100% test coverage of Ruby code, including the `Version` module.\n\nAs proof in the pudding, this gem achieves 100% test coverage for lines and branches,\nall 118 and 4 of them, respectively; coverage enabled in part by patterns from this library.\nYou can make it happen for your library too!\n\nIf this isn't **precisely** your use case you may be better off looking at\n_[versionaire](https://www.alchemists.io/projects/versionaire)_, a wonderful, performant, well-maintained,\ngem from the Alchemists, or _[version_sorter](https://rubygems.org/gems/version_sorter)_ from GitHub.\n\nFor more discussion about this [see issue #2](https://gitlab.com/oauth-xx/version_gem/-/issues/2)\n\n## 🚀 Still here?\n\nGive your next library an introspectable `Version` module without breaking your Gemspec.\n\n```ruby\nMyLib::Version.to_s # =\u003e \"1.2.3.rc3\"\nMyLib::Version.major # =\u003e 1\nMyLib::Version.minor # =\u003e 2\nMyLib::Version.patch # =\u003e 3\nMyLib::Version.pre # =\u003e \"rc3\"\nMyLib::Version.to_a # =\u003e [1, 2, 3, \"rc3\"]\nMyLib::Version.to_h # =\u003e { major: 1, minor: 2, patch: 3, pre: \"rc3\" }\n```\n\nThis library was extracted from the gem _[oauth2](https://gitlab.com/oauth-xx/oauth2)_.\n\nThis gem has no runtime dependencies.\n\n## 💡 Info you can shake a stick at\n\n| Tokens to Remember      | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace]                                                                                                                                                                                                                                                                                                                                                                          |\n|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| Works with JRuby        | [![JRuby 9.1 Compat][💎jruby-9.1i]][🚎10-j-wf] [![JRuby 9.2 Compat][💎jruby-9.2i]][🚎10-j-wf] [![JRuby 9.3 Compat][💎jruby-9.3i]][🚎10-j-wf] [![JRuby 9.4 Compat][💎jruby-9.4i]][🚎10-j-wf] [![JRuby 10.0 Compat][💎jruby-c-i]][🚎11-c-wf] [![JRuby HEAD Compat][💎jruby-headi]][🚎3-hd-wf]                                                                                                                                                                         |\n| Works with Truffle Ruby | [![Truffle Ruby 22.3 Compat][💎truby-22.3i]][🚎9-t-wf] [![Truffle Ruby 23.0 Compat][💎truby-23.0i]][🚎9-t-wf] [![Truffle Ruby 23.1 Compat][💎truby-23.1i]][🚎9-t-wf] [![Truffle Ruby 24.1 Compat][💎truby-c-i]][🚎11-c-wf] [![Truffle Ruby HEAD Compat][💎truby-headi]][🚎3-hd-wf]                                                                                                                                                                                  |\n| Works with MRI Ruby 3   | [![Ruby 3.0 Compat][💎ruby-3.0i]][🚎4-lg-wf] [![Ruby 3.1 Compat][💎ruby-3.1i]][🚎6-s-wf] [![Ruby 3.2 Compat][💎ruby-3.2i]][🚎6-s-wf] [![Ruby 3.3 Compat][💎ruby-3.3i]][🚎6-s-wf] [![Ruby 3.4 Compat][💎ruby-c-i]][🚎11-c-wf] [![Ruby HEAD Compat][💎ruby-headi]][🚎3-hd-wf]                                                                                                                                                                                         |\n| Works with MRI Ruby 2   | [![Ruby 2.3 Compat][💎ruby-2.3i]][🚎1-an-wf] [![Ruby 2.4 Compat][💎ruby-2.4i]][🚎1-an-wf] [![Ruby 2.5 Compat][💎ruby-2.5i]][🚎1-an-wf] [![Ruby 2.6 Compat][💎ruby-2.6i]][🚎7-us-wf] [![Ruby 2.7 Compat][💎ruby-2.7i]][🚎7-us-wf]                                                                                                                                                                                                                                    |\n| Source                  | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on Github.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc]                                                                                                                                                                                                                                                                                                             |\n| Documentation           | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![HEAD on RubyDoc.info][📜docs-head-rd-img]][🚎yard-head] [![BDFL Blog][🚂bdfl-blog-img]][🚂bdfl-blog] [![Wiki][📜wiki-img]][📜wiki]                                                                                                                                                                                                                                                        |\n| Compliance              | [![License: MIT][📄license-img]][📄license-ref] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Enforced Code Style][💎rlts-img]][💎rlts] [![CodeQL][🖐codeQL-img]][🖐codeQL] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji]                |\n| Expert 1:1 Support      | [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] `or` [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor]                                                                                                                                                                                                                                                                                    |\n| Enterprise Support      | [![Get help from me on Tidelift][🏙️entsup-tidelift-img]][🏙️entsup-tidelift]\u003cbr/\u003e💡Subscribe for support guarantees covering _all_ FLOSS dependencies!\u003cbr/\u003e💡Tidelift is part of [Sonar][🏙️entsup-tidelift-sonar]!\u003cbr/\u003e💡Tidelift pays maintainers to maintain the software you depend on!\u003cbr/\u003e📊`@`Pointy Haired Boss: An [enterprise support][🏙️entsup-tidelift] subscription is \"[never gonna let you down][🧮kloc]\", and *supports* open source maintainers! |\n| Comrade BDFL 🎖️        | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact BDFL][🚂bdfl-contact-img]][🚂bdfl-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto]                                                                                                                                                              |\n| `...` 💖                | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub]  [🛖][💖🛖hut] [🧪][💖🧪lab]                                                                                                                                                                   |\n\n## ✨ Installation\n\nInstall the gem and add to the application's Gemfile by executing:\n\n    $ bundle add version_gem\n\nIf bundler is not being used to manage dependencies, install the gem by executing:\n\n    $ gem install version_gem\n\n### 🔒 Secure Installation\n\n`version_gem` is cryptographically signed, and has verifiable [SHA-256 and SHA-512][💎SHA_checksums] checksums by\n[stone_checksums][💎stone_checksums]. Be sure the gem you install hasn’t been tampered with\nby following the instructions below.\n\nAdd my public key (if you haven’t already, expires 2045-04-29) as a trusted certificate:\n\n```shell\ngem cert --add \u003c(curl -Ls https://raw.github.com/oauth-xx/version_gem/main/certs/pboling.pem)\n```\n\nYou only need to do that once.  Then proceed to install with:\n\n```shell\ngem install version_gem -P HighSecurity\n```\n\nThe `HighSecurity` trust profile will verify signed gems, and not allow the installation of unsigned dependencies.\n\nIf you want to up your security game full-time:\n\n```shell\nbundle config set --global trust-policy MediumSecurity\n```\n\n`MediumSecurity` instead of `HighSecurity` is necessary if not all the gems you use are signed.\n\nNOTE: Be prepared to track down certs for signed gems and add them the same way you added mine.\n\n## 🔧 Basic Usage\n\nIn the standard `bundle gem my_lib` code you get the following in `lib/my_lib/version.rb`:\n\n```ruby\nmodule MyLib\n  VERSION = \"0.1.0\"\nend\n```\n\nChange it to a nested `Version` namespace (the one implied by the path =\u003e namespace convention):\n\n```ruby\nmodule MyLib\n  module Version\n    VERSION = \"0.1.0\"\n  end\nend\n```\n\nNow add the following near the top of the file the manages requiring external libraries.\nUsing the same example of `bundle gem my_lib`, this would be `lib/my_lib.rb`.\n\n```ruby\nrequire \"version_gem\"\n```\n\nThen, add the following wherever you want in the same file (recommend the bottom).\n\n```ruby\nMyLib::Version.class_eval do\n  extend VersionGem::Basic\nend\n```\n\nAnd now you have some version introspection methods available:\n\n```ruby\nMyLib::Version.to_s # =\u003e \"0.1.0\"\nMyLib::Version.major # =\u003e 0\nMyLib::Version.minor # =\u003e 1\nMyLib::Version.patch # =\u003e 0\nMyLib::Version.pre # =\u003e \"\"\nMyLib::Version.to_a # =\u003e [0, 1, 0]\nMyLib::Version.to_h # =\u003e { major: 0, minor: 1, patch: 0, pre: \"\" }\n```\n\n### Side benefit #1\n\nYou can reference the version from your gemspec, keeping the version string DRY,\n*and* still get accurate code coverage!\n\n```ruby\n# Get the GEMFILE_VERSION without *require* \"my_gem/version\", for code coverage accuracy\n# See: https://github.com/simplecov-ruby/simplecov/issues/557#issuecomment-2630782358\n# Kernel.load because load is overloaded in RubyGems during gemspec evaluation\nKernel.load(\"lib/my_gem/version.rb\")\ngem_version = MyGem::Version::VERSION\nMyGem::Version.send(:remove_const, :VERSION)\n\nGem::Specification.new do |spec|\n  # ...\n  spec.version = gem_version\nend\n```\n\n### Side benefit #2\n\nYour `version.rb` file now abides the Ruby convention of directory / path matching the namespace / class!\n\n## Epoch Usage (Epoch Semantic Versioning, as of version 1.1.7)\n\nIn the standard `bundle gem my_lib` code you get the following in `lib/my_lib/version.rb`:\n\n```ruby\nmodule MyLib\n  VERSION = \"0.1.0\"\nend\n```\n\nChange it to a nested `Version` namespace (the one implied by the path =\u003e namespace convention):\n\n```ruby\nmodule MyLib\n  module Version\n    VERSION = \"0.1.0\"\n  end\nend\n```\n\nThe Epoch and Major versions are derived from the formula:\n\n```\n{EPOCH * 1000 + MAJOR}.MINOR.PATCH\n```\n\nThis will start your library with the following version segments:\n\n* `epoch = 0`\n* `major = 0`\n* `minor = 1`\n* `patch = 0`\n* `pre = nil`\n\nAnd the segments are defined as:\n\n```\nEPOCH: Increment when you make significant or groundbreaking changes.\nMAJOR: Increment when you make minor incompatible API changes.\nMINOR: Increment when you add functionality in a backwards-compatible manner.\nPATCH: Increment when you make backwards-compatible bug fixes.\n```\n\nTherefore, if you set your version number to:\n\n```ruby\nVERSION = \"27016.42.86-pre.7\"\n```\n\nYou will get the following version segments:\n\n```\n{\n  epoch: 27,\n  major: 16,\n  minor: 42,\n  patch: 86,\n  pre: \"pre-7\",\n}\n```\n\nNow add the following near the top of the file the manages requiring external libraries.\nUsing the same example of `bundle gem my_lib`, this would be `lib/my_lib.rb`.\n\n```ruby\nrequire \"version_gem\"\n```\n\nThen, add the following wherever you want in the same file (recommend the bottom).\n\n```ruby\nMyLib::Version.class_eval do\n  extend VersionGem::Epoch\nend\n```\n\nAnd now you have some version introspection methods available:\n\n```ruby\nMyLib::Version.to_s # =\u003e \"1024.3.8\"\nMyLib::Version.epoch # =\u003e 1\nMyLib::Version.major # =\u003e 24\nMyLib::Version.minor # =\u003e 3\nMyLib::Version.patch # =\u003e 8\nMyLib::Version.pre # =\u003e \"\"\nMyLib::Version.to_a # =\u003e [1, 24, 3, 8]\nMyLib::Version.to_h # =\u003e { epoch: 1, major: 24, minor: 3, patch: 8, pre: \"\" }\n```\n\n### Usage with Zeitwerk\n\nThe pattern of `version.rb` breaking the ruby convention of directory / path matching the namespace / class\nis so entrenched that the `zeitwerk` library has a special carve-out for it. 🥺\nRubyGems using this \"bad is actually good\" pattern are encouraged to use `Zeitwerk.for_gem`.\n\n**Do not do that ^** if you use this gem.\n\n#### Simple Zeitwerk Example\n\nCreate a gem like this (keeping with the `MyLib` theme):\n\n```shell\nbundle gem my_lib\n```\n\nThen following the usage instructions above, you edit your primary namespace file @ `lib/my_lib.rb`,\nbut inject the Zeitwerk loader.\n\n```ruby\n# frozen_string_literal: true\n\nrequire_relative \"my_lib/version\"\n\nmodule MyLib\n  class Error \u003c StandardError; end\n  # Your code goes here...\nend\n\nloader = Zeitwerk::Loader.new\nloader.tag = File.basename(__FILE__, \".rb\")\nloader.push_dir(\"lib/my_lib\", namespace: MyLib)\nloader.setup # ready!\nloader.eager_load(force: true) # optional!\n\nMyLib::Version.class_eval do\n  extend VersionGem::Basic\nend\n```\n\n#### Complex Zeitwerk Example\n\nMaybe you would like to contribute one?\n\n#### Query Ruby Version (as of version 1.1.2)\n\nIn Continuous Integration environments for libraries that run against many versions of Ruby,\nI often need to configure things discretely per Ruby version, and doing so forced me to repeat\na significant amount of boilerplate code across each project.\n\nThus `VersionGem::Ruby` was born.  It has the two optimized methods I always need:\n\n```ruby\nengine = \"ruby\"\nversion = \"2.7.7\"\ngte_minimum_version?(version, engine)  # Is the current version of Ruby greater than or equal to some minimum?\n\nmajor = 3\nminor = 2\nactual_minor_version?(major, minor, engine) # Is the current version of Ruby precisely a specific minor version of Ruby?\n```\n\n`Version::Ruby` is *not loaded* by default.  If you want to use it, you must require it as:\n```ruby\nrequire \"version_gem/ruby\"\n```\n\nNormally I do this in my `spec/spec_helper.rb`, and/or `.simplecov` files.\nOccasionally in my `Rakefile`.\n\n### Caveat\n\nThis design keeps your `version.rb` file compatible with the way `gemspec` files use them.\nThis means that the introspection is _not_ available within the gemspec.\nThe enhancement from this gem is only available at runtime.\n\n### RSpec Matchers\n\nIn `spec_helper.rb`:\n```ruby\nrequire \"version_gem/rspec\"\n```\n\nThen you can write a test like:\n\n```ruby\nRSpec.describe(MyLib::Version) do\n  it_behaves_like \"a Version module\", described_class\nend\n\n# Or, if you want to write your own, here is the á la carte menu:\nRSpec.describe(MyLib::Version) do\n  it \"is a Version module\" do\n    expect(described_class).is_a?(Module)\n    expect(described_class).to(have_version_constant)\n    expect(described_class).to(have_version_as_string)\n    expect(described_class.to_s).to(be_a(String))\n    expect(described_class).to(have_major_as_integer)\n    expect(described_class).to(have_epoch_as_integer)\n    expect(described_class).to(have_minor_as_integer)\n    expect(described_class).to(have_patch_as_integer)\n    expect(described_class).to(have_pre_as_nil_or_string)\n    # This would be %i[epoch major minor patch pre] for epoch version schemes\n    expect(described_class.to_h.keys).to(match_array(%i[major minor patch pre]))\n    expect(described_class.to_a).to(be_a(Array))\n  end\nend\n```\n\n### 🚀 Release Instructions\n\nSee [CONTRIBUTING.md][🤝contributing].\n\n## 🔐 Security\n\nSee [SECURITY.md][🔐security].\n\n## 🤝 Contributing\n\nIf you need some ideas of where to help, you could work on adding more code coverage,\nor if it is already 💯 (see [below](#code-coverage)) then check [issues][🤝issues], or [PRs][🤝pulls],\nor use the gem and think about how it could be better.\n\nWe [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.\n\nSee [CONTRIBUTING.md][🤝contributing] for more detailed instructions.\n\n### Code Coverage\n\n[![Coverage Graph][🔑codecov-g♻️]][🔑codecov]\n\n### 🪇 Code of Conduct\n\nEveryone interacting in this project's codebases, issue trackers,\nchat rooms and mailing lists is expected to follow the [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct].\n\n## 🌈 Contributors\n\n[![Contributors][🖐contributors-img]][🖐contributors]\n\nMade with [contributors-img][🖐contrib-rocks].\n\nAlso see GitLab Contributors: [https://gitlab.com/oauth-xx/version_gem/-/graphs/main][🚎contributors-gl]\n\n## ⭐️ Star History\n\n\u003ca href=\"https://star-history.com/#oauth-xx/version_gem\u0026Date\"\u003e\n \u003cpicture\u003e\n   \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://api.star-history.com/svg?repos=oauth-xx/version_gem\u0026type=Date\u0026theme=dark\" /\u003e\n   \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://api.star-history.com/svg?repos=oauth-xx/version_gem\u0026type=Date\" /\u003e\n   \u003cimg alt=\"Star History Chart\" src=\"https://api.star-history.com/svg?repos=oauth-xx/version_gem\u0026type=Date\" /\u003e\n \u003c/picture\u003e\n\u003c/a\u003e\n\n## 📌 Versioning\n\nThis Library adheres to [![Epoch Semantic Versioning][📌semver-img]][📌semver].\nViolations of this scheme should be reported as bugs.\nSpecifically, if a minor or patch version is released that breaks backward compatibility,\na new version should be immediately released that restores compatibility.\nBreaking changes to the public API, including dropping a supported platform (i.e. minor version of Ruby),\nwill only be introduced with new major versions.\nEpoch will only be bumped if there are dramatic changes, and that is not expected to happen ever.\n\n### 📌 Is \"Platform Support\" part of the public API?\n\nYes.  But I'm obligated to include notes...\n\nSemVer should, but doesn't explicitly, say that dropping support for specific Platforms\nis a *breaking change* to an API.\nIt is obvious to many, but not all, and since the spec is silent, the bike shedding is endless.\n\n\u003e dropping support for a platform is both obviously and objectively a breaking change\n\n- Jordan Harband (@ljharb, maintainer of SemVer) [in SemVer issue 716][📌semver-breaking]\n\nTo get a better understanding of how SemVer is intended to work over a project's lifetime,\nread this article from the creator of SemVer:\n\n- [\"Major Version Numbers are Not Sacred\"][📌major-versions-not-sacred]\n\nAs a result of this policy, and the interpretive lens used by the maintainer,\nyou can (and should) specify a dependency on these libraries using\nthe [Pessimistic Version Constraint][📌pvc] with two digits of precision.\n\nFor example:\n\n```ruby\nspec.add_dependency(\"version_gem\", \"~\u003e 1.1\")\n```\n\nSee [CHANGELOG.md][📌changelog] for list of releases.\n\n## 📄 License\n\nThe gem is available as open source under the terms of\nthe [MIT License][📄license] [![License: MIT][📄license-img]][📄license-ref].\nSee [LICENSE.txt][📄license] for the official [Copyright Notice][📄copyright-notice-explainer].\n\n### © Copyright\n\n\u003cp\u003e\n  Copyright (c) 2022 - 2025 Peter H. Boling,\n  \u003ca href=\"https://railsbling.com\"\u003e\n    RailsBling.com\n    \u003cpicture\u003e\n      \u003cimg alt=\"Rails Bling\" height=\"20\" src=\"https://railsbling.com/images/logos/RailsBling-TrainLogo.svg\" /\u003e\n    \u003c/picture\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n## 🤑 One more thing\n\nYou made it to the bottom of the page,\nso perhaps you'll indulge me for another 20 seconds.\nI maintain many dozens of gems, including this one,\nbecause I want Ruby to be a great place for people to solve problems, big and small.\nPlease consider supporting my efforts via the giant yellow link below,\nor one of the others at the head of this README.\n\n[![Buy me a latte][🖇buyme-img]][🖇buyme]\n\n[✇bundle-group-pattern]: https://gist.github.com/pboling/4564780\n[🏘chat]: https://gitter.im/oauth-xx/version_gem\n[🏘chat-img]: https://img.shields.io/gitter/room/oauth-xx/version_gem.svg\n[⛳️gem-namespace]: https://github.com/oauth-xx/version_gem\n[⛳️namespace-img]: https://img.shields.io/badge/namespace-VersionGem-brightgreen.svg?style=flat\u0026logo=ruby\u0026logoColor=white\n[⛳️gem-name]: https://rubygems.org/gems/version_gem\n[⛳️name-img]: https://img.shields.io/badge/name-version__gem-brightgreen.svg?style=flat\u0026logo=rubygems\u0026logoColor=red\n[🚂bdfl-blog]: http://www.railsbling.com/tags/version_gem\n[🚂bdfl-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge\u0026logo=rubyonrails\u0026logoColor=orange\n[🚂bdfl-contact]: http://www.railsbling.com/contact\n[🚂bdfl-contact-img]: https://img.shields.io/badge/Contact-BDFL-0093D0.svg?style=flat\u0026logo=rubyonrails\u0026logoColor=red\n[💖🖇linkedin]: http://www.linkedin.com/in/peterboling\n[💖🖇linkedin-img]: https://img.shields.io/badge/PeterBoling-LinkedIn-0B66C2?style=flat\u0026logo=newjapanprowrestling\n[💖✌️wellfound]: https://angel.co/u/peter-boling\n[💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat\u0026logo=wellfound\n[💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling\n[💖💲crunchbase-img]: https://img.shields.io/badge/peter--boling-purple?style=flat\u0026logo=crunchbase\n[💖🐘ruby-mast]: https://ruby.social/@galtzo\n[💖🐘ruby-mast-img]: https://img.shields.io/mastodon/follow/109447111526622197?domain=https%3A%2F%2Fruby.social\u0026style=flat\u0026logo=mastodon\u0026label=Ruby%20%40galtzo\n[💖🦋bluesky]: https://bsky.app/profile/galtzo.com\n[💖🦋bluesky-img]: https://img.shields.io/badge/@galtzo.com-0285FF?style=flat\u0026logo=bluesky\u0026logoColor=white\n[💖🌳linktree]: https://linktr.ee/galtzo\n[💖🌳linktree-img]: https://img.shields.io/badge/galtzo-purple?style=flat\u0026logo=linktree\n[💖💁🏼‍♂️devto]: https://dev.to/galtzo\n[💖💁🏼‍♂️devto-img]: https://img.shields.io/badge/dev.to-0A0A0A?style=flat\u0026logo=devdotto\u0026logoColor=white\n[💖💁🏼‍♂️aboutme]: https://about.me/peter.boling\n[💖💁🏼‍♂️aboutme-img]: https://img.shields.io/badge/about.me-0A0A0A?style=flat\u0026logo=aboutme\u0026logoColor=white\n[💖🧊berg]: https://codeberg.org/pboling\n[💖🐙hub]: https://github.org/pboling\n[💖🛖hut]: https://sr.ht/~galtzo/\n[💖🧪lab]: https://gitlab.com/pboling\n[👨🏼‍🏫expsup-upwork]: https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share\n[👨🏼‍🏫expsup-upwork-img]: https://img.shields.io/badge/UpWork-13544E?style=for-the-badge\u0026logo=Upwork\u0026logoColor=white\n[👨🏼‍🏫expsup-codementor]: https://www.codementor.io/peterboling?utm_source=github\u0026utm_medium=button\u0026utm_term=peterboling\u0026utm_campaign=github\n[👨🏼‍🏫expsup-codementor-img]: https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge\u0026logo=CodeMentor\u0026logoColor=white\n[🏙️entsup-tidelift]: https://tidelift.com/subscription\n[🏙️entsup-tidelift-img]: https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge\u0026logo=sonar\u0026logoColor=white\n[🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar\n[💁🏼‍♂️peterboling]: http://www.peterboling.com\n[🚂railsbling]: http://www.railsbling.com\n[📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge\u0026logo=Gitlab\u0026logoColor=orange\n[📜src-gl]: https://gitlab.com/oauth-xx/version_gem/\n[📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge\u0026logo=Github\u0026logoColor=green\n[📜src-gh]: https://github.com/oauth-xx/version_gem\n[📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge\u0026logo=readthedocs\u0026logoColor=white\n[📜docs-head-rd-img]: https://img.shields.io/badge/RubyDoc-HEAD-943CD2?style=for-the-badge\u0026logo=readthedocs\u0026logoColor=white\n[📜wiki]: https://gitlab.com/oauth-xx/version_gem/-/wikis/home\n[📜wiki-img]: https://img.shields.io/badge/wiki-examples-943CD2.svg?style=for-the-badge\u0026logo=Wiki\u0026logoColor=white\n[👽dl-rank]: https://rubygems.org/gems/version_gem\n[👽dl-ranki]: https://img.shields.io/gem/rd/version_gem.svg\n[👽oss-help]: https://www.codetriage.com/oauth-xx/version_gem\n[👽oss-helpi]: https://www.codetriage.com/oauth-xx/version_gem/badges/users.svg\n[👽version]: https://rubygems.org/gems/version_gem\n[👽versioni]: https://img.shields.io/gem/v/version_gem.svg\n[🔑cc-mnt]: https://qlty.sh/gh/oauth-xx/projects/version_gem\n[🔑cc-mnti♻️]: https://qlty.sh/badges/19404e90-9168-451a-8dac-882382cb768d/maintainability.svg\n[🔑cc-cov]: https://qlty.sh/gh/oauth-xx/projects/version_gem\n[🔑cc-covi♻️]: https://qlty.sh/badges/19404e90-9168-451a-8dac-882382cb768d/test_coverage.svg\n[🔑codecov]: https://codecov.io/gh/oauth-xx/version_gem\n[🔑codecovi♻️]: https://codecov.io/gh/oauth-xx/version_gem/branch/main/graph/badge.svg?token=cc6UdZCpAL\n[🔑coveralls]: https://coveralls.io/github/oauth-xx/version_gem?branch=main\n[🔑coveralls-img]: https://coveralls.io/repos/github/oauth-xx/version_gem/badge.svg?branch=main\n[🔑depfu]: https://depfu.com/github/oauth-xx/version_gem\n[🔑depfui♻️]: https://badges.depfu.com/badges/6c9b45362951b872127f9e46d39bed76/count.svg\n[🖐codeQL]: https://github.com/oauth-xx/version_gem/security/code-scanning\n[🖐codeQL-img]: https://github.com/oauth-xx/version_gem/actions/workflows/codeql-analysis.yml/badge.svg\n[🚎1-an-wf]: https://github.com/oauth-xx/version_gem/actions/workflows/ancient.yml\n[🚎1-an-wfi]: https://github.com/oauth-xx/version_gem/actions/workflows/ancient.yml/badge.svg\n[🚎2-cov-wf]: https://github.com/oauth-xx/version_gem/actions/workflows/coverage.yml\n[🚎2-cov-wfi]: https://github.com/oauth-xx/version_gem/actions/workflows/coverage.yml/badge.svg\n[🚎3-hd-wf]: https://github.com/oauth-xx/version_gem/actions/workflows/heads.yml\n[🚎3-hd-wfi]: https://github.com/oauth-xx/version_gem/actions/workflows/heads.yml/badge.svg\n[🚎4-lg-wf]: https://github.com/oauth-xx/version_gem/actions/workflows/legacy.yml\n[🚎4-lg-wfi]: https://github.com/oauth-xx/version_gem/actions/workflows/legacy.yml/badge.svg\n[🚎5-st-wf]: https://github.com/oauth-xx/version_gem/actions/workflows/style.yml\n[🚎5-st-wfi]: https://github.com/oauth-xx/version_gem/actions/workflows/style.yml/badge.svg\n[🚎6-s-wf]: https://github.com/oauth-xx/version_gem/actions/workflows/supported.yml\n[🚎6-s-wfi]: https://github.com/oauth-xx/version_gem/actions/workflows/supported.yml/badge.svg\n[🚎7-us-wf]: https://github.com/oauth-xx/version_gem/actions/workflows/unsupported.yml\n[🚎7-us-wfi]: https://github.com/oauth-xx/version_gem/actions/workflows/unsupported.yml/badge.svg\n[🚎9-t-wf]: https://github.com/oauth-xx/version_gem/actions/workflows/truffle.yml\n[🚎9-t-wfi]: https://github.com/oauth-xx/version_gem/actions/workflows/truffle.yml/badge.svg\n[🚎10-j-wf]: https://github.com/oauth-xx/version_gem/actions/workflows/jruby.yml\n[🚎10-j-wfi]: https://github.com/oauth-xx/version_gem/actions/workflows/jruby.yml/badge.svg\n[🚎11-c-wf]: https://github.com/oauth-xx/version_gem/actions/workflows/current.yml\n[🚎11-c-wfi]: https://github.com/oauth-xx/version_gem/actions/workflows/current.yml/badge.svg\n[⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay\n[⛳liberapay]: https://liberapay.com/pboling/donate\n[🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social\u0026logo=github\n[🖇sponsor]: https://github.com/sponsors/pboling\n[🖇polar-img]: https://img.shields.io/badge/polar-donate-yellow.svg\n[🖇polar]: https://polar.sh/pboling\n[🖇kofi-img]: https://img.shields.io/badge/a_more_different_coffee-✓-yellow.svg\n[🖇kofi]: https://ko-fi.com/O5O86SNP4\n[🖇patreon-img]: https://img.shields.io/badge/patreon-donate-yellow.svg\n[🖇patreon]: https://patreon.com/galtzo\n[🖇buyme-img]: https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte\u0026emoji=\u0026slug=pboling\u0026button_colour=FFDD00\u0026font_colour=000000\u0026font_family=Cookie\u0026outline_colour=000000\u0026coffee_colour=ffffff\n[🖇buyme]: https://www.buymeacoffee.com/pboling\n[🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-✓-yellow.svg?style=flat\n[💎ruby-2.3i]: https://img.shields.io/badge/Ruby-2.3-DF00CA?style=for-the-badge\u0026logo=ruby\u0026logoColor=white\n[💎ruby-2.4i]: https://img.shields.io/badge/Ruby-2.4-DF00CA?style=for-the-badge\u0026logo=ruby\u0026logoColor=white\n[💎ruby-2.5i]: https://img.shields.io/badge/Ruby-2.5-DF00CA?style=for-the-badge\u0026logo=ruby\u0026logoColor=white\n[💎ruby-2.6i]: https://img.shields.io/badge/Ruby-2.6-DF00CA?style=for-the-badge\u0026logo=ruby\u0026logoColor=white\n[💎ruby-2.7i]: https://img.shields.io/badge/Ruby-2.7-DF00CA?style=for-the-badge\u0026logo=ruby\u0026logoColor=white\n[💎ruby-3.0i]: https://img.shields.io/badge/Ruby-3.0-CC342D?style=for-the-badge\u0026logo=ruby\u0026logoColor=white\n[💎ruby-3.1i]: https://img.shields.io/badge/Ruby-3.1-CC342D?style=for-the-badge\u0026logo=ruby\u0026logoColor=white\n[💎ruby-3.2i]: https://img.shields.io/badge/Ruby-3.2-CC342D?style=for-the-badge\u0026logo=ruby\u0026logoColor=white\n[💎ruby-3.3i]: https://img.shields.io/badge/Ruby-3.3-CC342D?style=for-the-badge\u0026logo=ruby\u0026logoColor=white\n[💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge\u0026logo=ruby\u0026logoColor=green\n[💎ruby-headi]: https://img.shields.io/badge/Ruby-HEAD-CC342D?style=for-the-badge\u0026logo=ruby\u0026logoColor=blue\n[💎truby-22.3i]: https://img.shields.io/badge/Truffle_Ruby-22.3-34BCB1?style=for-the-badge\u0026logo=ruby\u0026logoColor=pink\n[💎truby-23.0i]: https://img.shields.io/badge/Truffle_Ruby-23.0-34BCB1?style=for-the-badge\u0026logo=ruby\u0026logoColor=pink\n[💎truby-23.1i]: https://img.shields.io/badge/Truffle_Ruby-23.1-34BCB1?style=for-the-badge\u0026logo=ruby\u0026logoColor=pink\n[💎truby-c-i]: https://img.shields.io/badge/Truffle_Ruby-current-34BCB1?style=for-the-badge\u0026logo=ruby\u0026logoColor=green\n[💎truby-headi]: https://img.shields.io/badge/Truffle_Ruby-HEAD-34BCB1?style=for-the-badge\u0026logo=ruby\u0026logoColor=blue\n[💎jruby-9.1i]: https://img.shields.io/badge/JRuby-9.1-FBE742?style=for-the-badge\u0026logo=ruby\u0026logoColor=red\n[💎jruby-9.2i]: https://img.shields.io/badge/JRuby-9.2-FBE742?style=for-the-badge\u0026logo=ruby\u0026logoColor=red\n[💎jruby-9.3i]: https://img.shields.io/badge/JRuby-9.3-FBE742?style=for-the-badge\u0026logo=ruby\u0026logoColor=red\n[💎jruby-9.4i]: https://img.shields.io/badge/JRuby-9.4-FBE742?style=for-the-badge\u0026logo=ruby\u0026logoColor=red\n[💎jruby-c-i]: https://img.shields.io/badge/JRuby-current-FBE742?style=for-the-badge\u0026logo=ruby\u0026logoColor=green\n[💎jruby-headi]: https://img.shields.io/badge/JRuby-HEAD-FBE742?style=for-the-badge\u0026logo=ruby\u0026logoColor=blue\n[🤝issues]: https://github.com/oauth-xx/version_gem/issues\n[🤝pulls]: https://github.com/oauth-xx/version_gem/pulls\n[🤝contributing]: CONTRIBUTING.md\n[🔑codecov-g♻️]: https://codecov.io/gh/oauth-xx/version_gem/graphs/tree.svg?token=cc6UdZCpAL\n[🖐contrib-rocks]: https://contrib.rocks\n[🖐contributors]: https://github.com/oauth-xx/version_gem/graphs/contributors\n[🖐contributors-img]: https://contrib.rocks/image?repo=oauth-xx/version_gem\n[🚎contributors-gl]: https://gitlab.com/oauth-xx/version_gem/-/graphs/main\n[🪇conduct]: CODE_OF_CONDUCT.md\n[🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-4baaaa.svg\n[📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint\n[📌semver]: https://antfu.me/posts/epoch-semver\n[📌semver-img]: https://img.shields.io/badge/epoch-semver-FFDD67.svg?style=flat\n[📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139\n[📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html\n[📌changelog]: CHANGELOG.md\n[📗keep-changelog]: https://keepachangelog.com/en/1.0.0/\n[📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-FFDD67.svg?style=flat\n[📌gitmoji]:https://gitmoji.dev\n[📌gitmoji-img]:https://img.shields.io/badge/gitmoji-%20😜%20😍-FFDD67.svg?style=flat-square\n[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ\n[🧮kloc-img]: https://img.shields.io/badge/KLOC-0.118-FFDD67.svg?style=for-the-badge\u0026logo=YouTube\u0026logoColor=blue\n[🔐security]: SECURITY.md\n[🔐security-img]: https://img.shields.io/badge/security-policy-brightgreen.svg?style=flat\n[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year\n[📄license]: LICENSE.txt\n[📄license-ref]: https://opensource.org/licenses/MIT\n[📄license-img]: https://img.shields.io/badge/License-MIT-green.svg\n[📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm\n[📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-brightgreen.svg?style=flat\n[🚎yard-current]: http://rubydoc.info/gems/version_gem\n[🚎yard-head]: https://rubydoc.info/github/oauth-xx/version_gem/main\n[💎stone_checksums]: https://github.com/pboling/stone_checksums\n[💎SHA_checksums]: https://gitlab.com/oauth-xx/version_gem/-/tree/main/checksums\n[💎rlts]: https://github.com/rubocop-lts/rubocop-lts\n[💎rlts-img]: https://img.shields.io/badge/code_style-rubocop--lts-brightgreen.svg?plastic\u0026logo=ruby\u0026logoColor=white\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruby-oauth%2Fversion_gem","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruby-oauth%2Fversion_gem","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruby-oauth%2Fversion_gem/lists"}