rubygems

package module
v0.0.0-...-411e87d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 5 Imported by: 0

README

go-ruby-rubygems/rubygems

rubygems — go-ruby-rubygems

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of the pure-compute core of Ruby's RubyGemsGem::Version, Gem::Requirement, Gem::Dependency and a usable subset of Gem::Specification. These are the comparable, in-memory version algebra that the entire gem ecosystem is built on. The library is byte-for-byte faithful to MRI's bundled RubyGems (target 3.6.x; the algorithms are unchanged through RubyGems 4.x) on the two methods that matter most — Gem::Version#<=> and Gem::Requirement#satisfied_by? (including the pessimistic ~> bound math) — validated against the ruby binary on every supported platform.

It is a sibling of go-ruby-regexp (the Onigmo engine), go-ruby-yaml (the Psych port) and go-ruby-erb (the ERB compiler), and the version-algebra foundation that rbgo and a future go-ruby-bundler build their dependency resolution on — keeping the whole stack CGO=0.

What it is — and isn't. The version algebra — parsing a version string, comparing two versions, deciding whether a version satisfies a requirement — is fully deterministic and needs no interpreter, no network, no filesystem, so it lives here as pure Go. Fetching the gem index, downloading and installing .gem files, evaluating a real gemspec (arbitrary Ruby) and require-time activation are host-side concerns and are out of scope. See doc.go for the precise boundary.

Features

Gem::Version
  • Parsing of version strings ("1.2.3", "1.0.0.beta.1"), with the "-" -> ".pre." rewrite and empty-string-as-0 rule.
  • Prerelease detection — a version is a prerelease iff it contains a letter.
  • The exact <=> algorithm: split into numeric / letter segments, canonicalize (strip trailing zeros; strip leading zeros before the first prerelease letter), then compare segment-by-segment with prerelease < release and String < Numeric at each position. Matches RubyGems on the full matrix, including "1.0" == "1.0.0", "1.0.a" < "1.0", and "1.0.0.rc1" < "1.0.0".
  • Bump, Release, Prerelease, CanonicalSegments, Segments, ApproximateRecommendation, String, Equal (==), Eql (eql?), HashKey (a map-safe hash surrogate).
Gem::Requirement
  • Constraint parsing"~> 1.2", ">= 1.0", "!= 2.0", bare versions (default =), and multiple constraints; duplicates removed, empty set -> ">= 0".
  • SatisfiedBy with all operators (=, !=, >, >=, <, <=, ~>), including the pessimistic ~> bound math: ~> 1.2 is >= 1.2, < 2.0; ~> 1.2.3 is >= 1.2.3, < 1.3.0; ~> 1 is >= 1.0, < 2.0.
  • None, Exact, Specific, Prerelease, AsList, String, and Equal (==) with the tilde-precision refinement (~> 1.2 != ~> 1.2.0 while >= 1.2 == >= 1.2.0).
Gem::Dependency
  • Name + Requirement + type (runtime / development), Match, MatchesSpec, Merge, Prerelease, LatestVersion, Specific, Compare (by name), Equal and String.
Gem::Specification (subset)
  • The metadata model (name, version, runtime/development dependency fields), SatisfiesRequirement, FullName, and a filesystem-free Validate-lite. The full gemspec eval, file manifest, signing and gem install are host-side.

Usage

import "github.com/go-ruby-rubygems/rubygems"

v1 := rubygems.MustVersion("1.0.0.rc1")
v2 := rubygems.MustVersion("1.0.0")
v1.Compare(v2)          // -1  (a prerelease sorts below its release)

req := rubygems.MustRequirement("~> 1.2")
req.SatisfiedBy(rubygems.MustVersion("1.9.9")) // true
req.SatisfiedBy(rubygems.MustVersion("2.0.0")) // false

Tests & coverage

go test ./... runs a deterministic, Ruby-free suite (which alone holds coverage at 100%, error branches included) plus a differential MRI oracle: it shells out to the real ruby binary and checks Version.Compare against Gem::Version#<=> and Requirement.SatisfiedBy against Gem::Requirement#satisfied_by? over a broad version × version × operator matrix. The oracle skips itself where ruby is absent (the Windows lane and the qemu cross-arch lanes), so the deterministic suite keeps the gate green there.

CI builds and tests on ubuntu / macos / windows and on all six 64-bit Go targets (amd64, arm64 native; riscv64, loong64, ppc64le, s390x under qemu-user). The library is CGO=0 and has no third-party dependencies.

License

BSD-3-Clause — see LICENSE. Copyright (c) 2026, the go-ruby-rubygems/rubygems authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package rubygems is a pure-Go (CGO=0), MRI-faithful re-implementation of the pure-compute core of Ruby's RubyGems: Gem::Version, Gem::Requirement, Gem::Dependency and a usable subset of Gem::Specification.

It matches MRI's bundled RubyGems byte-for-byte on the two algorithms that the rest of the gem ecosystem depends on:

  • Version#<=> (the segment-comparison / canonicalization rules)
  • Requirement#satisfied_by? (including the pessimistic "~>" bound math)

The gem index fetch, gem install, gemspec evaluation and require-time activation are deliberately out of scope: those are host-side concerns that touch the network and the filesystem. This package is the comparable, in-memory model everything else is built on. See doc.go for the boundary.

Index

Constants

View Source
const VersionPattern = `[0-9]+(?:\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?`

VersionPattern is the body of RubyGems' Gem::Version::VERSION_PATTERN. It matches a leading numeric segment, optional dotted alphanumeric segments, and an optional dash-prefixed build tail (which RubyGems rewrites to ".pre.").

Variables

This section is empty.

Functions

func CorrectVersion

func CorrectVersion(s string) bool

CorrectVersion reports whether s is a well-formed version string, mirroring Gem::Version.correct?. A nil-equivalent (handled by callers) and the empty string are accepted by RubyGems; here the empty string matches the anchored pattern's optional group.

Types

type Dependency

type Dependency struct {
	Name string
	// contains filtered or unexported fields
}

Dependency holds a gem name, a Requirement and a type (Gem::Dependency).

func MustDependency

func MustDependency(name string, typ DependencyType, constraints ...string) *Dependency

MustDependency is like NewDependency but panics on error.

func NewDependency

func NewDependency(name string, typ DependencyType, constraints ...string) (*Dependency, error)

NewDependency constructs a dependency with the given name, type and constraint strings (Gem::Dependency#initialize). An empty constraint set is the default ">= 0".

func (*Dependency) Compare

func (d *Dependency) Compare(other *Dependency) int

Compare orders dependencies by name (Gem::Dependency#<=>).

func (*Dependency) Equal

func (d *Dependency) Equal(other *Dependency) bool

Equal reports dependency equality (Gem::Dependency#==): same name, type and requirement.

func (*Dependency) LatestVersion

func (d *Dependency) LatestVersion() bool

LatestVersion reports whether the dependency simply asks for the latest version, i.e. has no real requirement (Gem::Dependency#latest_version?).

func (*Dependency) Match

func (d *Dependency) Match(name string, version *Version, allowPrerelease bool) bool

Match reports whether this dependency matches a gem identified by name and version (Gem::Dependency#match?). A prerelease version is rejected unless allowPrerelease is set or this dependency is itself a prerelease.

func (*Dependency) MatchesSpec

func (d *Dependency) MatchesSpec(spec *Specification) bool

MatchesSpec reports whether the dependency matches a spec (Gem::Dependency#matches_spec?). Unlike Match, a prerelease spec version is accepted even when the dependency is not a prerelease dependency.

func (*Dependency) Merge

func (d *Dependency) Merge(other *Dependency) (*Dependency, error)

Merge merges the requirements of other into this dependency, returning a new Dependency (Gem::Dependency#merge). The names must match.

func (*Dependency) Prerelease

func (d *Dependency) Prerelease() bool

Prerelease reports whether the dependency requires a prerelease, either because it was forced or because its requirement is a prerelease (Gem::Dependency#prerelease?).

func (*Dependency) Requirement

func (d *Dependency) Requirement() *Requirement

Requirement returns the dependency's requirement (Gem::Dependency#requirement).

func (*Dependency) Runtime

func (d *Dependency) Runtime() bool

Runtime reports whether this is a runtime dependency (Gem::Dependency#runtime?).

func (*Dependency) SetPrerelease

func (d *Dependency) SetPrerelease(p bool)

SetPrerelease forces this dependency to be treated as a prerelease (Gem::Dependency#prerelease=).

func (*Dependency) Specific

func (d *Dependency) Specific() bool

Specific reports whether the dependency will not always match the latest version (Gem::Dependency#specific?).

func (*Dependency) String

func (d *Dependency) String() string

String returns the dependency's display form (Gem::Dependency#to_s).

func (*Dependency) Type

func (d *Dependency) Type() DependencyType

Type returns the dependency type (Gem::Dependency#type). The constructor normalizes an empty type to runtime, so the stored value is always set.

type DependencyType

type DependencyType string

DependencyType is a Gem::Dependency type: runtime or development.

const (
	// RuntimeType is the default dependency type (:runtime).
	RuntimeType DependencyType = "runtime"
	// DevelopmentType is the :development dependency type.
	DevelopmentType DependencyType = "development"
)

type Requirement

type Requirement struct {
	// contains filtered or unexported fields
}

Requirement is a set of one or more version constraints (Gem::Requirement).

func DefaultPrereleaseRequirement

func DefaultPrereleaseRequirement() *Requirement

DefaultPrereleaseRequirement returns ">= 0.a" (Gem::Requirement.default_prerelease).

func DefaultRequirement

func DefaultRequirement() *Requirement

DefaultRequirement returns the default requirement ">= 0" (Gem::Requirement.default).

func MustRequirement

func MustRequirement(constraints ...string) *Requirement

MustRequirement is like NewRequirement but panics on a malformed constraint.

func NewRequirement

func NewRequirement(constraints ...string) (*Requirement, error)

NewRequirement constructs a Requirement from one or more constraint strings, mirroring Gem::Requirement#initialize. nil/duplicate entries are removed and an empty set becomes the default ">= 0".

func (*Requirement) AsList

func (r *Requirement) AsList() []string

AsList returns the constraints as "op version" strings (Gem::Requirement#as_list).

func (*Requirement) Equal

func (r *Requirement) Equal(other *Requirement) bool

Equal reports requirement equality (Gem::Requirement#==). It mirrors MRI: the constraints, sorted by their string form, must match pairwise where the versions are compared by VALUE (so ">= 1.2" == ">= 1.2.0"); then, if any "~>" is present, the tilde constraints are additionally compared with the stricter eql? that also requires identical version precision (so "~> 1.2" != "~> 1.2.0").

func (*Requirement) Exact

func (r *Requirement) Exact() bool

Exact reports whether the requirement is a single "=" constraint (Gem::Requirement#exact?).

func (*Requirement) None

func (r *Requirement) None() bool

None reports whether the requirement is exactly the default ">= 0" (Gem::Requirement#none?).

func (*Requirement) Prerelease

func (r *Requirement) Prerelease() bool

Prerelease reports whether any constraint version is a prerelease (Gem::Requirement#prerelease?).

func (*Requirement) SatisfiedBy

func (r *Requirement) SatisfiedBy(version *Version) bool

SatisfiedBy reports whether version satisfies every constraint (Gem::Requirement#satisfied_by?). The "~>" operator uses the pessimistic bound math: v >= r && v.release < r.bump.

func (*Requirement) Specific

func (r *Requirement) Specific() bool

Specific reports whether the requirement will not always match the latest version (Gem::Requirement#specific?).

func (*Requirement) String

func (r *Requirement) String() string

String returns the comma-joined constraint list (Gem::Requirement#to_s).

type Specification

type Specification struct {
	Name    string
	Version *Version
	Summary string
	// contains filtered or unexported fields
}

Specification is a usable subset of Gem::Specification: the in-memory metadata model plus the pure-compute methods (version/dependency fields, satisfies_requirement?, a validate-lite). The full gemspec eval, the file manifest, signing, and gem install are host-side and out of scope.

func NewSpecification

func NewSpecification(name, version string) (*Specification, error)

NewSpecification constructs a Specification from a name and version string.

func (*Specification) AddDevelopmentDependency

func (s *Specification) AddDevelopmentDependency(name string, constraints ...string) error

AddDevelopmentDependency adds a development dependency (Gem::Specification#add_development_dependency).

func (*Specification) AddRuntimeDependency

func (s *Specification) AddRuntimeDependency(name string, constraints ...string) error

AddRuntimeDependency adds a runtime dependency (Gem::Specification#add_runtime_dependency).

func (*Specification) Dependencies

func (s *Specification) Dependencies() []*Dependency

Dependencies returns all dependencies (Gem::Specification#dependencies).

func (*Specification) DevelopmentDependencies

func (s *Specification) DevelopmentDependencies() []*Dependency

DevelopmentDependencies returns only the development dependencies (Gem::Specification#development_dependencies).

func (*Specification) FullName

func (s *Specification) FullName() string

FullName returns "name-version" (Gem::Specification#full_name).

func (*Specification) RuntimeDependencies

func (s *Specification) RuntimeDependencies() []*Dependency

RuntimeDependencies returns only the runtime dependencies (Gem::Specification#runtime_dependencies).

func (*Specification) SatisfiesRequirement

func (s *Specification) SatisfiesRequirement(dep *Dependency) bool

SatisfiesRequirement reports whether this spec satisfies a dependency: the names match and the dependency's requirement is satisfied by this spec's version (Gem::Specification#satisfies_requirement?).

func (*Specification) Validate

func (s *Specification) Validate() error

Validate performs the pure-compute subset of Gem::Specification#validate that does not touch the filesystem: it checks that the name is a well-formed gem name and that the version is present. It returns an error describing the first problem found. The full validate (file lists, licenses, metadata URIs, signing) is host-side.

type Version

type Version struct {
	// contains filtered or unexported fields
}

Version is a comparable RubyGems version (Gem::Version).

func MustVersion

func MustVersion(s string) *Version

MustVersion is like NewVersion but panics on malformed input. It is a convenience for tests and for callers that know the input is well-formed.

func NewVersion

func NewVersion(s string) (*Version, error)

NewVersion constructs a Version from s, mirroring Gem::Version#initialize. It returns an error (rather than raising ArgumentError) for malformed input.

func ParseConstraint

func ParseConstraint(s string) (op string, version *Version, err error)

ParseConstraint parses a single requirement string into an [op, version] pair, mirroring Gem::Requirement.parse. A bare version defaults to "=".

func (*Version) ApproximateRecommendation

func (v *Version) ApproximateRecommendation() string

ApproximateRecommendation returns a "~>" requirement string recommended for this version (Gem::Version#approximate_recommendation). Two segments are always used; a prerelease appends ".a".

func (*Version) Bump

func (v *Version) Bump() *Version

Bump returns a new version where the next-to-last numeric segment is incremented and the rest dropped (Gem::Version#bump). Prerelease tails are ignored: 5.3.1 -> 5.4, 5.3.1.b.2 -> 5.4.

func (*Version) CanonicalSegments

func (v *Version) CanonicalSegments() []any

CanonicalSegments returns a copy of the canonical segments (Gem::Version#canonical_segments).

func (*Version) Compare

func (v *Version) Compare(other *Version) int

Compare returns -1, 0, or 1 as v is less than, equal to, or greater than other, faithfully porting Gem::Version#<=> over canonical segments.

func (*Version) Eql

func (v *Version) Eql(other *Version) bool

Eql reports whether v and other are eql?: equal AND specified to the same precision (Gem::Version#eql?). "1.0" is not eql? "1".

func (*Version) Equal

func (v *Version) Equal(other *Version) bool

Equal reports value equality using the comparison ordering (==), i.e. Compare(other) == 0. Note "1.0" == "1.0.0".

func (*Version) HashKey

func (v *Version) HashKey() string

HashKey returns a stable key derived from the canonical segments, suitable for use as a Go map key. Two versions that are Eql? share a HashKey, mirroring the contract of Gem::Version#hash (which hashes canonical_segments).

func (*Version) Prerelease

func (v *Version) Prerelease() bool

Prerelease reports whether the version contains a letter (Gem::Version#prerelease?).

func (*Version) Release

func (v *Version) Release() *Version

Release returns the release version (Gem::Version#release): for a prerelease, drop all string segments; otherwise return the version itself.

func (*Version) Segments

func (v *Version) Segments() []any

Segments returns a copy of the raw parsed segments. Numeric segments are returned as int64; string segments as string (Gem::Version#segments).

func (*Version) String

func (v *Version) String() string

String returns the version string (Gem::Version#to_s / #version).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL