A Ruby library for parsing, comparing and sorting versions according to the VERS specification.
This gem provides tools for working with version ranges across different package managers, using a mathematical interval model internally and supporting the vers specification from the Package URL (PURL) project.
Available on RubyGems | API Documentation | GitHub Repository
- Universal version range parsing with support for 6 package ecosystems (npm, gem, pypi, maven, debian, rpm)
- Mathematical interval model for precise set operations (union, intersection, complement)
- VERS specification compliance with full support for the Package URL version range specification
- Native syntax support - parse native package manager syntax (^1.2.3, ~>1.0, >=1.0,<2.0, [1.0,2.0))
- Bidirectional conversion between native syntax and universal vers URI format
- Semantic versioning features - version increment, constraint checking, prerelease handling
- Comprehensive error handling with detailed parsing exceptions
- 100% test coverage with 113 tests and 366 assertions
Add this line to your application's Gemfile:
gem'vers'And then execute:
bundle installOr install it yourself as:
gem install versrequire'vers'# Parse a vers URIrange=Vers.parse("vers:npm/>=1.2.3|<2.0.0")range.contains?("1.5.0")# => truerange.contains?("2.1.0")# => false# Parse native package manager syntaxnpm_range=Vers.parse_native("^1.2.3","npm")gem_range=Vers.parse_native("~> 1.0","gem")# Check version containmentVers.satisfies?("1.5.0",">=1.0.0,<2.0.0")# => true# Compare versionsVers.compare("1.2.3","1.2.4")# => -1# Version operationsversion=Vers::Version.new("1.2.3")version.increment_major# => #<Vers::Version "2.0.0">version.satisfies?("~> 1.2")# => true- npm (Node.js): Caret ranges (^1.2.3), tilde ranges (~1.2.3), hyphen ranges (1.2.3 - 2.3.4), OR logic (||), wildcards (1.x, *)
- RubyGems (Ruby): Pessimistic operator (~> 1.2), standard operators (>=, <=, etc.), comma-separated constraints
- PyPI (Python): Comma-separated constraints (>=1.0,<2.0), exclusions (!=1.5.0), compatible release (~=1.4.2)
- Maven (Java): Bracket notation ([1.0,2.0], (1.0,2.0)), union ranges, open ranges
- NuGet (.NET): Bracket notation ([1.0,2.0], (1.0,2.0)), mixed brackets, open ranges
- Packagist (PHP Composer): Caret ranges (^1.2.3), tilde ranges (~1.2), stability flags (@dev, @alpha)
- Debian (apt): Standard comparison operators (>=1.0.0, <<2.0.0)
- RPM (yum/dnf): Standard comparison operators (>=1.0.0, <=2.0.0)
Many other package managers are also supported using standard comparison operators (>=, <=, <, >, =, !=), including Cargo (Rust), Go modules, and more.
Internally, all version ranges are represented as mathematical intervals, similar to those used in mathematics:
[1.0.0, 2.0.0)represents versions from 1.0.0 (inclusive) to 2.0.0 (exclusive)(1.0.0, 2.0.0]represents versions from 1.0.0 (exclusive) to 2.0.0 (inclusive)
This allows for precise set operations like union, intersection, and complement, regardless of the original package manager syntax.
require'vers'# Parse vers URI formatrange=Vers.parse("vers:npm/>=1.2.3|<2.0.0")putsrange.contains?("1.5.0")# => trueputsrange.contains?("2.1.0")# => false# Parse native package manager syntaxnpm_range=Vers.parse_native("^1.2.3","npm")gem_range=Vers.parse_native("~> 1.0","gem")pypi_range=Vers.parse_native(">=1.0,<2.0","pypi")maven_range=Vers.parse_native("[1.0,2.0)","maven")# Create exact version rangeexact=Vers.exact("1.2.3")putsexact.contains?("1.2.3")# => trueputsexact.contains?("1.2.4")# => false# Create comparison rangesgreater=Vers.greater_than("1.0.0",inclusive: true)less=Vers.less_than("2.0.0",inclusive: false)# Create unbounded and empty rangesall_versions=Vers.unboundedno_versions=Vers.empty# Parse native syntax and convert to vers URInpm_range=Vers.parse_native("^1.2.3","npm")vers_string=Vers.to_vers_string(npm_range,"npm")putsvers_string# => "vers:npm/>=1.2.3|<2.0.0"# Parse vers URI and use in your applicationrange=Vers.parse("vers:gem/~>1.0")putsrange.contains?("1.5.0")# => truerange1=Vers.parse("vers:npm/>=1.0.0|<2.0.0")range2=Vers.parse("vers:npm/>=1.5.0|<3.0.0")# Union: versions in either rangeunion=range1.union(range2)putsunion.contains?("0.9.0")# => falseputsunion.contains?("1.2.0")# => trueputsunion.contains?("2.5.0")# => true# Intersection: versions in both rangesintersection=range1.intersect(range2)putsintersection.contains?("1.2.0")# => falseputsintersection.contains?("1.7.0")# => trueputsintersection.contains?("2.5.0")# => false# Complement: versions NOT in rangecomplement=range1.complementputscomplement.contains?("0.5.0")# => trueputscomplement.contains?("1.5.0")# => false# Exclusions: remove specific versionsexcluded=range1.exclude("1.5.0")putsexcluded.contains?("1.4.0")# => trueputsexcluded.contains?("1.5.0")# => falseputsexcluded.contains?("1.6.0")# => trueversion=Vers::Version.new("1.2.3-alpha.1+build.123")# Access version componentsputsversion.major# => 1putsversion.minor# => 2putsversion.patch# => 3putsversion.prerelease# => "alpha.1"putsversion.build# => "build.123"# Compare versionsputsVers.compare("1.2.3","1.2.4")# => -1putsVers.compare("2.0.0","1.9.9")# => 1putsVers.compare("1.0.0","1.0.0")# => 0# Increment versions (returns new Version objects)putsversion.increment_major# => #<Vers::Version "2.0.0">putsversion.increment_minor# => #<Vers::Version "1.3.0"> putsversion.increment_patch# => #<Vers::Version "1.2.4"># Version propertiesputsversion.stable?# => false (has prerelease)putsversion.prerelease?# => trueputsversion.to_h# => {major: 1, minor: 2, patch: 3, ...}version=Vers::Version.new("1.2.5")# Pessimistic constraint checking (Ruby-style)putsversion.satisfies?("~> 1.2")# => true (>= 1.2.0, < 1.3.0)putsversion.satisfies?("~> 1.2.3")# => true (>= 1.2.3, < 1.3.0)putsversion.satisfies?("~> 1.3")# => false# General satisfaction checkingputsVers.satisfies?("1.5.0","vers:npm/>=1.0.0|<2.0.0")# => trueputsVers.satisfies?("1.5.0","^1.2.3","npm")# => trueThis gem implements the PURL Version Range Specification, providing a universal way to express version ranges across different software packaging ecosystems.
Learn more about the motivation and design behind VERS in the presentation from Open Source Summit NA 2025 (slides PDF) by Eve Martin-Jones and Elitsa Bankova. The following table from their talk shows how different package managers express the same version constraints:
| Operator | NPM | Cargo | Carthage | RubyGems | PyPI | Maven | NuGet |
|---|---|---|---|---|---|---|---|
| behavior with no op | 1.0.0 | ^1.0.0⁴ | illegal | 1.0.0 | illegal | *⁵ | >=1.0 |
| = == | =1.0 | = | == | = | == | [1.0]⁵ | [1.0]⁸ |
| > | > | > | > | > | (1.0,) | (1.0,) | |
| >= | >= | >= | >= | >= | >= | 1.0⁵ | 1.0⁷ᵇᵃ |
| < | < | < | < | < | (,1.0) | (,1.0) | |
| <= | <= | <= | <= | <= | (,1.0] | (,1.0] | |
| != | != | != | |||||
| ^ | ^ | ^² | |||||
| ~ | ~, ~>¹ | ~ | ~= | ||||
| ~> | ¹ | ~> | ~> | ||||
| wildcards | * x X | * x X | * | * | |||
| OR | || | , | |||||
| AND | space | , | ,³ | , | |||
| RANGE | - | [,],(,)⁶ | [,],(,) |
This complexity across ecosystems is exactly why VERS provides a universal format that works consistently across all package managers.
After checking out the repo, run bin/setup to install dependencies. Then, run rake test 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 the created tag, and push the .gem file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/andrew/vers. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.
- purl - Ruby implementation of Package URL (PURL)
- semantic_range - Semantic version parsing (JavaScript style)
- univers - Python implementation of version ranges
- versatile - Java implementation of version ranges
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the Vers project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.