A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://github.com/andrew/purl below:

andrew/purl: A Ruby library for parsing, validating, and generating Package URLs (PURLs) as defined by the PURL specification

Purl - Package URL Parser for Ruby

A Ruby library for parsing, validating, and generating Package URLs (PURLs) as defined by the PURL specification.

This library features comprehensive error handling with namespaced error types, bidirectional registry URL conversion, and JSON-based configuration for cross-language compatibility.

Available on RubyGems | API Documentation

Add this line to your application's Gemfile:

And then execute:

Or install it yourself as:

The purl gem includes a command-line interface that provides convenient access to all parsing, validation, conversion, and generation functionality.

The CLI is automatically available after installing the gem:

gem install purl
purl --help
purl parse <purl-string>              # Parse and display PURL components
purl validate <purl-string>           # Validate a PURL (exit code indicates success)
purl convert <registry-url>           # Convert registry URL to PURL
purl url <purl-string>                # Convert PURL to registry URL
purl generate [options]               # Generate PURL from components
purl info [type]                      # Show information about PURL types
purl lookup <purl-string>             # Look up package information from ecosyste.ms

All commands support JSON output with the --json flag:

purl --json parse "pkg:gem/rails@7.0.0"
purl --json info gem
purl --json lookup "pkg:cargo/rand"
$ purl parse "pkg:gem/rails@7.0.0"
Valid PURL: pkg:gem/rails@7.0.0
Components:
  Type:       gem
  Namespace:  (none)
  Name:       rails
  Version:    7.0.0
  Qualifiers: (none)
  Subpath:    (none)

$ purl --json parse "pkg:npm/@babel/core@7.0.0"
{
  "success": true,
  "purl": "pkg:npm/%40babel/core@7.0.0",
  "components": {
    "type": "npm",
    "namespace": "@babel",
    "name": "core",
    "version": "7.0.0",
    "qualifiers": {},
    "subpath": null
  }
}
$ purl validate "pkg:gem/rails@7.0.0"
Valid PURL

$ purl validate "invalid-purl"
Invalid PURL: PURL must start with 'pkg:'
Convert Registry URL to PURL
$ purl convert "https://rubygems.org/gems/rails"
pkg:gem/rails

$ purl convert "https://www.npmjs.com/package/@babel/core"
pkg:npm/@babel/core
Convert PURL to Registry URL
$ purl url "pkg:gem/rails@7.0.0"
https://rubygems.org/gems/rails

$ purl url "pkg:npm/@babel/core@7.0.0"
https://www.npmjs.com/package/@babel/core

$ purl --json url "pkg:gem/rails@7.0.0"
{
  "success": true,
  "purl": "pkg:gem/rails@7.0.0",
  "registry_url": "https://rubygems.org/gems/rails",
  "type": "gem"
}
$ purl generate --type gem --name rails --version 7.0.0
pkg:gem/rails@7.0.0

$ purl generate --type npm --namespace @babel --name core --version 7.0.0 --qualifiers "arch=x64,os=linux"
pkg:npm/%40babel/core@7.0.0?arch=x64&os=linux
$ purl info gem
Type: gem
Known: Yes
Description: RubyGems
Default registry: https://rubygems.org
Registry URL generation: Yes
Reverse parsing: Yes
Examples:
  pkg:gem/ruby-advisory-db-check@0.12.4
  pkg:gem/rails@7.0.4
  pkg:gem/bundler@2.3.26
Registry URL patterns:
  https://rubygems.org/gems/:name
  https://rubygems.org/gems/:name/versions/:version

$ purl info  # Shows all types
Known PURL types:

  alpm
    Description: Arch Linux packages
    Registry support: No
    Reverse parsing: No
  ...
Total types: 37
Registry supported: 20
Look Up Package Information
$ purl lookup "pkg:cargo/rand"
Package: rand (cargo)
Description: Random number generators and other randomness functionality.
Homepage: https://rust-random.github.io/book
Repository: https://github.com/rust-random/rand
License: MIT OR Apache-2.0
Downloads: 145,678,901
Latest Version: 0.8.5
Published: 2023-01-13T17:47:01.870Z

$ purl --json lookup "pkg:cargo/rand@0.8.5"
{
  "success": true,
  "purl": "pkg:cargo/rand@0.8.5",
  "package": {
    "name": "rand",
    "ecosystem": "cargo",
    "description": "Random number generators and other randomness functionality.",
    "homepage": "https://rust-random.github.io/book",
    "repository_url": "https://github.com/rust-random/rand",
    "registry_url": "https://crates.io/crates/rand",
    "licenses": "MIT OR Apache-2.0",
    "latest_version": "0.8.5",
    "latest_version_published_at": "2023-01-13T17:47:01.870Z",
    "versions_count": 89,
    "maintainers": [
      {
        "login": "dhardy",
        "name": "Diggory Hardy"
      }
    ]
  },
  "version": {
    "number": "0.8.5",
    "published_at": "2023-01-13T17:47:01.870Z",
    "registry_url": "https://crates.io/crates/rand/0.8.5",
    "downloads": 5678901,
    "size": 102400
  }
}

The generate command supports all PURL components:

purl generate --help
Usage: purl generate [options]
    --type TYPE                      Package type (required)
    --name NAME                      Package name (required)
    --namespace NAMESPACE            Package namespace
    --version VERSION                Package version
    --qualifiers QUALIFIERS          Qualifiers as key=value,key2=value2
    --subpath SUBPATH                Package subpath
    -h, --help                       Show this help

The CLI uses standard exit codes:

This makes the CLI suitable for use in scripts and CI/CD pipelines:

if purl validate "pkg:gem/rails@7.0.0"; then
  echo "Valid PURL"
else
  echo "Invalid PURL"
  exit 1
fi
require 'purl'

# Parse a PURL string
purl = Purl.parse("pkg:gem/rails@7.0.0")
puts purl.type        # => "gem"
puts purl.name        # => "rails"
puts purl.version     # => "7.0.0"
puts purl.namespace   # => nil

# Parse with namespace and qualifiers
purl = Purl.parse("pkg:npm/@babel/core@7.0.0?arch=x86_64")
puts purl.type        # => "npm"
puts purl.namespace   # => "@babel"
puts purl.name        # => "core"
puts purl.version     # => "7.0.0"
puts purl.qualifiers  # => {"arch" => "x86_64"}
# Create a PURL object
purl = Purl::PackageURL.new(
  type: "maven",
  namespace: "org.apache.commons",
  name: "commons-lang3",
  version: "3.12.0"
)

puts purl.to_s  # => "pkg:maven/org.apache.commons/commons-lang3@3.12.0"

PURL objects are immutable by design, but you can create new objects with modified attributes using the with method:

# Create original PURL
original = Purl::PackageURL.new(
  type: "npm",
  namespace: "@babel", 
  name: "core",
  version: "7.20.0",
  qualifiers: { "arch" => "x64" }
)

# Create new PURL with updated version
updated = original.with(version: "7.21.0")
puts updated.to_s  # => "pkg:npm/@babel/core@7.21.0?arch=x64"

# Update qualifiers
with_new_qualifiers = original.with(
  qualifiers: { "arch" => "arm64", "os" => "linux" }
)
puts with_new_qualifiers.to_s  # => "pkg:npm/@babel/core@7.20.0?arch=arm64&os=linux"

# Update multiple attributes at once
fully_updated = original.with(
  version: "8.0.0",
  qualifiers: { "dev" => "true" },
  subpath: "lib/index.js"
)
puts fully_updated.to_s  # => "pkg:npm/@babel/core@8.0.0#lib/index.js?dev=true"

# Original remains unchanged
puts original.to_s  # => "pkg:npm/@babel/core@7.20.0?arch=x64"
# Generate registry URLs from PURLs
purl = Purl.parse("pkg:gem/rails@7.0.0")
puts purl.registry_url                # => "https://rubygems.org/gems/rails"
puts purl.registry_url_with_version   # => "https://rubygems.org/gems/rails/versions/7.0.0"

# Check if registry URL generation is supported
puts purl.supports_registry_url?      # => true

# NPM with scoped packages
purl = Purl.parse("pkg:npm/@babel/core@7.0.0")
puts purl.registry_url                # => "https://www.npmjs.com/package/@babel/core"
Reverse Parsing: Registry URLs to PURLs
# Parse registry URLs back to PURLs
purl = Purl.from_registry_url("https://rubygems.org/gems/rails/versions/7.0.0")
puts purl.to_s  # => "pkg:gem/rails@7.0.0"

# Works with various registries
purl = Purl.from_registry_url("https://www.npmjs.com/package/@babel/core")
puts purl.to_s  # => "pkg:npm/@babel/core"

purl = Purl.from_registry_url("https://pypi.org/project/django/4.0.0/")
puts purl.to_s  # => "pkg:pypi/django@4.0.0"

You can parse registry URLs from custom domains or generate URLs for private registries:

# Parse from custom domain (specify type to help with parsing)
purl = Purl.from_registry_url("https://npm.company.com/package/@babel/core", type: "npm")
puts purl.to_s  # => "pkg:npm/@babel/core"

# Generate URLs for custom registries
purl = Purl.parse("pkg:gem/rails@7.0.0")
custom_url = purl.registry_url(base_url: "https://gems.internal.com/gems")
puts custom_url  # => "https://gems.internal.com/gems/rails"

# With version-specific URLs
with_version = purl.registry_url_with_version(base_url: "https://gems.internal.com/gems")
puts with_version  # => "https://gems.internal.com/gems/rails/versions/7.0.0"

# Works with all supported package types
composer_purl = Purl.parse("pkg:composer/symfony/console@5.4.0")
private_composer = composer_purl.registry_url(base_url: "https://packagist.company.com/packages")
puts private_composer  # => "https://packagist.company.com/packages/symfony/console"
# Get route patterns for a package type (Rails-style)
patterns = Purl::RegistryURL.route_patterns_for("gem")
# => ["https://rubygems.org/gems/:name", "https://rubygems.org/gems/:name/versions/:version"]

# Get all route patterns
all_patterns = Purl::RegistryURL.all_route_patterns
puts all_patterns["npm"]
# => ["https://www.npmjs.com/package/:namespace/:name", "https://www.npmjs.com/package/:name", ...]

Qualifiers are key-value pairs that provide additional metadata about packages:

# Create PURL with qualifiers
purl = Purl::PackageURL.new(
  type: "apk",
  name: "curl",
  version: "7.83.0-r0",
  qualifiers: {
    "distro" => "alpine-3.16",
    "arch" => "x86_64",
    "repository_url" => "https://dl-cdn.alpinelinux.org"
  }
)
puts purl.to_s  # => "pkg:apk/curl@7.83.0-r0?arch=x86_64&distro=alpine-3.16&repository_url=https://dl-cdn.alpinelinux.org"

# Access qualifiers
puts purl.qualifiers["distro"]  # => "alpine-3.16"
puts purl.qualifiers["arch"]    # => "x86_64"

# Parse PURL with qualifiers
parsed = Purl.parse("pkg:rpm/httpd@2.4.53?distro=fedora-36&arch=x86_64")
puts parsed.qualifiers  # => {"distro" => "fedora-36", "arch" => "x86_64"}

# Add qualifiers to existing PURL
with_qualifiers = purl.with(qualifiers: purl.qualifiers.merge("signed" => "true"))
# Get all known PURL types
puts Purl.known_types.length          # => 37
puts Purl.known_types.include?("gem") # => true

# Check type support
puts Purl.known_type?("gem")                    # => true
puts Purl.registry_supported_types              # => ["cargo", "gem", "maven", "npm", ...]
puts Purl.reverse_parsing_supported_types       # => ["bioconductor", "cargo", "clojars", ...]

# Get default registry for a type
puts Purl.default_registry("gem")               # => "https://rubygems.org"
puts Purl.default_registry("npm")               # => "https://registry.npmjs.org"
puts Purl.default_registry("golang")             # => nil (no default)

# Get official examples for a type
puts Purl.type_examples("gem")                  # => ["pkg:gem/rails@7.0.4", "pkg:gem/bundler@2.3.26", ...]
puts Purl.type_examples("npm")                  # => ["pkg:npm/lodash@4.17.21", "pkg:npm/@babel/core@7.20.0", ...]
puts Purl.type_examples("unknown")              # => []

# Get detailed type information
info = Purl.type_info("gem")
puts info[:known]                     # => true
puts info[:description]               # => "RubyGems"
puts info[:default_registry]          # => "https://rubygems.org"
puts info[:examples]                  # => ["pkg:gem/rails@7.0.4", ...]
puts info[:registry_url_generation]   # => true
puts info[:reverse_parsing]           # => true
puts info[:route_patterns]            # => ["https://rubygems.org/gems/:name", ...]
# Detailed error types with context
begin
  Purl.parse("invalid-purl")
rescue Purl::InvalidSchemeError => e
  puts "Scheme error: #{e.message}"
rescue Purl::ParseError => e
  puts "Parse error: #{e.message}"
  puts "Component: #{e.component}"
  puts "Value: #{e.value}"
end

# Type-specific validation errors
begin
  Purl::PackageURL.new(type: "swift", name: "Alamofire")  # Swift requires namespace
rescue Purl::ValidationError => e
  puts e.message  # => "Swift PURLs require a namespace to be unambiguous"
end

The library supports 37 package types (32 official + 5 additional ecosystems):

Registry URL Generation (20 types):

Reverse Parsing (20 types):

All 37 Supported Types: alpm, apk, bioconductor, bitbucket, bitnami, cargo, clojars, cocoapods, composer, conan, conda, cpan, cran, deb, deno, docker, elm, gem, generic, github, golang, hackage, hex, homebrew, huggingface, luarocks, maven, mlflow, npm, nuget, oci, pub, pypi, qpkg, rpm, swid, swift

Package types and registry patterns are stored in purl-types.json for easy contribution and cross-language compatibility:

{
  "version": "1.0.0",
  "description": "PURL types and registry URL patterns for package ecosystems",
  "source": "https://github.com/package-url/purl-spec/blob/main/PURL-TYPES.rst",
  "last_updated": "2025-07-24",
  "types": {
    "gem": {
      "description": "RubyGems",
      "default_registry": "https://rubygems.org",
      "registry_config": {
        "path_template": "/gems/:name",
        "version_path_template": "/gems/:name/versions/:version",
        "reverse_regex": "/gems/([^/?#]+)(?:/versions/([^/?#]+))?",
        "components": {
          "namespace": false,
          "version_in_url": true,
          "version_path": "/versions/"
        }
      }
    }
  }
}

Key Configuration Improvements:

The library includes JSON schemas for validation and documentation:

These schemas provide:

Validate JSON files against their schemas:

rake spec:validate_schemas

After checking out the repo, run bin/setup to install dependencies. Then:

# Run tests
rake test

# Run specification compliance tests
rake spec:compliance

# Update test cases from official PURL spec
rake spec:update

# Show type information
rake spec:types

# Verify types against official specification
rake spec:verify_types
Testing Against Official Specification

This library includes the official purl-spec repository as a git submodule for testing and validation:

# Initialize submodule (first time only)
git submodule update --init --recursive

# Update submodule to latest spec
git submodule update --remote purl-spec

The tests use files from the submodule to:

The submodule is automatically updated weekly via Dependabot, ensuring tests stay current with the latest specification changes. When the submodule updates, you can review and merge the PR to adopt new spec requirements.

If you find this project useful, please consider supporting its development:

Your support helps maintain and improve this library for the entire Ruby community.

Bug reports and pull requests are welcome on GitHub. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Make your changes
  4. Add tests for your changes
  5. Ensure all tests pass (rake test)
  6. Commit your changes (git commit -am 'Add some feature')
  7. Push to the branch (git push origin my-new-feature)
  8. Create new Pull Request

To add support for a new package type:

  1. Update purl-types.json with the new type configuration
  2. Add registry URL patterns if applicable
  3. Add type-specific validation rules if needed in lib/purl/package_url.rb
  4. Add tests for the new functionality

This gem is available as open source under the terms of the MIT License.

See CHANGELOG.md for a detailed history of changes and releases.

Everyone interacting in the Purl project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the Contributor Covenant Code of Conduct.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4