GitHub
ESC

Basic Usage

Creating Package URLs

The Purl::PackageURL constructor accepts all purl components:

require "purl"

# Minimal: type and name only
purl = Purl::PackageURL.new("gem", nil, "rails")
puts purl.to_s  # => "pkg:gem/rails"

# With namespace and version
purl = Purl::PackageURL.new("npm", "@babel", "core", "7.20.0")
puts purl.to_s  # => "pkg:npm/%40babel/core@7.20.0"

# With qualifiers
purl = Purl::PackageURL.new(
  "deb", "debian", "curl", "7.50.3-1",
  qualifiers: {"arch" => "amd64", "distro" => "jessie"}
)
puts purl.to_s
# => "pkg:deb/debian/curl@7.50.3-1?arch=amd64&distro=jessie"

# With subpath
purl = Purl::PackageURL.new(
  "github", "hahwul", "purl.cr", "v0.2.0",
  subpath: "src/purl.cr"
)
puts purl.to_s
# => "pkg:github/hahwul/purl.cr@v0.2.0#src/purl.cr"

Accessing Components

All components are available as properties:

purl = Purl::PackageURL.parse("pkg:maven/org.apache/commons-lang3@3.12.0?packaging=jar")

purl.type       # => "maven"
purl.namespace  # => "org.apache"
purl.name       # => "commons-lang3"
purl.version    # => "3.12.0"
purl.qualifiers # => {"packaging" => "jar"}
purl.subpath    # => nil

Modifying Components

Properties are mutable using Crystal's property syntax:

purl = Purl::PackageURL.new("npm", nil, "express", "4.18.0")

purl.version = "4.19.0"
purl.qualifiers = {"engine" => "node"}

puts purl.to_s
# => "pkg:npm/express@4.19.0?engine=node"

String Output

Call to_s to produce a valid, normalized purl string:

purl = Purl::PackageURL.new("pypi", nil, "Django_Rest_Framework", "3.14.0")
puts purl.to_s
# => "pkg:pypi/django-rest-framework@3.14.0"

Note how the name is automatically normalized per PyPI conventions (underscores replaced with hyphens, lowercased).

Equality

Two PackageURL instances are equal if all their normalized components match:

a = Purl::PackageURL.parse("pkg:npm/%40babel/core@7.20.0")
b = Purl::PackageURL.new("npm", "@babel", "core", "7.20.0")

a == b  # => true