purl 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/Rakefile +19 -1
- data/lib/purl/errors.rb +56 -2
- data/lib/purl/package_url.rb +140 -2
- data/lib/purl/version.rb +1 -1
- data/lib/purl.rb +119 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae14d9c24e0b1f4f8d76b97c59ed34d8e34090d5329ac9358bf40be4e4acdf14
|
|
4
|
+
data.tar.gz: 4ff6fa43fd388006d553bae72b4685ef228cff186b135ec335d7a6b724abb034
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f965309ce3f2dddc29065c247082f11541a8ead617c2ad398a7dbd71affd6417e7f09959d88ea59492256dec7039b458507688da9288723bda2d83942f6683f
|
|
7
|
+
data.tar.gz: eb9fd5d72ba8a6c40cafe9c736594f76d20ea40e1886af1693d54dbb36b1ba1105ed05b415b6989098b7288df79a6e9f3c3ddb7aa095eb6dad069a7755374f39
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@ This library features comprehensive error handling with namespaced error types,
|
|
|
8
8
|
[](https://rubygems.org/gems/purl)
|
|
9
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
10
|
|
|
11
|
-
**[Available on RubyGems](https://rubygems.org/gems/purl)**
|
|
11
|
+
**[Available on RubyGems](https://rubygems.org/gems/purl)** | **[API Documentation](https://rdoc.info/github/andrew/purl)**
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
data/Rakefile
CHANGED
|
@@ -2,9 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
4
|
require "minitest/test_task"
|
|
5
|
+
require "rdoc/task"
|
|
5
6
|
|
|
6
7
|
Minitest::TestTask.create
|
|
7
8
|
|
|
9
|
+
RDoc::Task.new do |rdoc|
|
|
10
|
+
rdoc.rdoc_dir = "doc"
|
|
11
|
+
rdoc.title = "PURL - Package URL Library"
|
|
12
|
+
rdoc.main = "README.md"
|
|
13
|
+
rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
|
|
14
|
+
rdoc.options << "--line-numbers"
|
|
15
|
+
rdoc.options << "--all"
|
|
16
|
+
rdoc.options << "--charset=UTF-8"
|
|
17
|
+
end
|
|
18
|
+
|
|
8
19
|
task default: :test
|
|
9
20
|
|
|
10
21
|
namespace :spec do
|
|
@@ -396,7 +407,14 @@ namespace :spec do
|
|
|
396
407
|
desc "Validate JSON files against their schemas"
|
|
397
408
|
task :validate_schemas do
|
|
398
409
|
require "json"
|
|
399
|
-
|
|
410
|
+
|
|
411
|
+
begin
|
|
412
|
+
require "json-schema"
|
|
413
|
+
rescue LoadError => e
|
|
414
|
+
puts "❌ json-schema gem not available: #{e.message}"
|
|
415
|
+
puts " Install with: gem install json-schema"
|
|
416
|
+
exit 1
|
|
417
|
+
end
|
|
400
418
|
|
|
401
419
|
puts "🔍 Validating JSON files against schemas..."
|
|
402
420
|
puts "=" * 50
|
data/lib/purl/errors.rb
CHANGED
|
@@ -5,9 +5,31 @@ module Purl
|
|
|
5
5
|
class Error < StandardError; end
|
|
6
6
|
|
|
7
7
|
# Validation errors for PURL components
|
|
8
|
+
#
|
|
9
|
+
# Contains additional context about which component failed validation
|
|
10
|
+
# and what rule was violated.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# begin
|
|
14
|
+
# PackageURL.new(type: "123invalid", name: "test")
|
|
15
|
+
# rescue ValidationError => e
|
|
16
|
+
# puts e.component # :type
|
|
17
|
+
# puts e.rule # "cannot start with number"
|
|
18
|
+
# end
|
|
8
19
|
class ValidationError < Error
|
|
9
|
-
|
|
20
|
+
# @return [Symbol, nil] the PURL component that failed validation
|
|
21
|
+
attr_reader :component
|
|
22
|
+
|
|
23
|
+
# @return [Object, nil] the value that failed validation
|
|
24
|
+
attr_reader :value
|
|
25
|
+
|
|
26
|
+
# @return [String, nil] the validation rule that was violated
|
|
27
|
+
attr_reader :rule
|
|
10
28
|
|
|
29
|
+
# @param message [String] error message
|
|
30
|
+
# @param component [Symbol, nil] component that failed validation
|
|
31
|
+
# @param value [Object, nil] value that failed validation
|
|
32
|
+
# @param rule [String, nil] validation rule that was violated
|
|
11
33
|
def initialize(message, component: nil, value: nil, rule: nil)
|
|
12
34
|
super(message)
|
|
13
35
|
@component = component
|
|
@@ -19,40 +41,71 @@ module Purl
|
|
|
19
41
|
# Parsing errors for malformed PURL strings
|
|
20
42
|
class ParseError < Error; end
|
|
21
43
|
|
|
22
|
-
# Specific validation errors
|
|
44
|
+
# Specific validation errors for PURL components
|
|
45
|
+
|
|
46
|
+
# Raised when a PURL type is invalid
|
|
23
47
|
class InvalidTypeError < ValidationError; end
|
|
48
|
+
|
|
49
|
+
# Raised when a PURL name is invalid
|
|
24
50
|
class InvalidNameError < ValidationError; end
|
|
51
|
+
|
|
52
|
+
# Raised when a PURL namespace is invalid
|
|
25
53
|
class InvalidNamespaceError < ValidationError; end
|
|
54
|
+
|
|
55
|
+
# Raised when a PURL qualifier is invalid
|
|
26
56
|
class InvalidQualifierError < ValidationError; end
|
|
57
|
+
|
|
58
|
+
# Raised when a PURL version is invalid
|
|
27
59
|
class InvalidVersionError < ValidationError; end
|
|
60
|
+
|
|
61
|
+
# Raised when a PURL subpath is invalid
|
|
28
62
|
class InvalidSubpathError < ValidationError; end
|
|
29
63
|
|
|
30
64
|
# Parsing-specific errors
|
|
65
|
+
|
|
66
|
+
# Raised when a PURL string doesn't start with "pkg:"
|
|
31
67
|
class InvalidSchemeError < ParseError; end
|
|
68
|
+
|
|
69
|
+
# Raised when a PURL string is malformed
|
|
32
70
|
class MalformedUrlError < ParseError; end
|
|
33
71
|
|
|
34
72
|
# Registry URL generation errors
|
|
73
|
+
#
|
|
74
|
+
# Contains additional context about which type caused the error.
|
|
35
75
|
class RegistryError < Error
|
|
76
|
+
# @return [String, nil] the PURL type that caused the error
|
|
36
77
|
attr_reader :type
|
|
37
78
|
|
|
79
|
+
# @param message [String] error message
|
|
80
|
+
# @param type [String, nil] PURL type that caused the error
|
|
38
81
|
def initialize(message, type: nil)
|
|
39
82
|
super(message)
|
|
40
83
|
@type = type
|
|
41
84
|
end
|
|
42
85
|
end
|
|
43
86
|
|
|
87
|
+
# Raised when trying to generate registry URLs for unsupported types
|
|
44
88
|
class UnsupportedTypeError < RegistryError
|
|
89
|
+
# @return [Array<String>] list of supported types
|
|
45
90
|
attr_reader :supported_types
|
|
46
91
|
|
|
92
|
+
# @param message [String] error message
|
|
93
|
+
# @param type [String, nil] unsupported type
|
|
94
|
+
# @param supported_types [Array<String>] list of supported types
|
|
47
95
|
def initialize(message, type: nil, supported_types: [])
|
|
48
96
|
super(message, type: type)
|
|
49
97
|
@supported_types = supported_types
|
|
50
98
|
end
|
|
51
99
|
end
|
|
52
100
|
|
|
101
|
+
# Raised when required registry information is missing
|
|
53
102
|
class MissingRegistryInfoError < RegistryError
|
|
103
|
+
# @return [String, nil] the missing information (e.g., "namespace")
|
|
54
104
|
attr_reader :missing
|
|
55
105
|
|
|
106
|
+
# @param message [String] error message
|
|
107
|
+
# @param type [String, nil] PURL type
|
|
108
|
+
# @param missing [String, nil] what information is missing
|
|
56
109
|
def initialize(message, type: nil, missing: nil)
|
|
57
110
|
super(message, type: type)
|
|
58
111
|
@missing = missing
|
|
@@ -60,5 +113,6 @@ module Purl
|
|
|
60
113
|
end
|
|
61
114
|
|
|
62
115
|
# Legacy compatibility - matches packageurl-ruby's exception name
|
|
116
|
+
# @deprecated Use {ParseError} instead
|
|
63
117
|
InvalidPackageURL = ParseError
|
|
64
118
|
end
|
data/lib/purl/package_url.rb
CHANGED
|
@@ -3,12 +3,74 @@
|
|
|
3
3
|
require "uri"
|
|
4
4
|
|
|
5
5
|
module Purl
|
|
6
|
+
# Represents a Package URL (PURL) - a mostly universal standard to reference
|
|
7
|
+
# a software package in a uniform way across many tools, programming languages
|
|
8
|
+
# and ecosystems.
|
|
9
|
+
#
|
|
10
|
+
# A PURL has the following components:
|
|
11
|
+
# - +type+: the package type (e.g., "gem", "npm", "maven")
|
|
12
|
+
# - +namespace+: optional namespace/scope (e.g., "@babel" for npm)
|
|
13
|
+
# - +name+: the package name (required)
|
|
14
|
+
# - +version+: optional version
|
|
15
|
+
# - +qualifiers+: optional key-value pairs
|
|
16
|
+
# - +subpath+: optional path within the package
|
|
17
|
+
#
|
|
18
|
+
# @example Creating a PackageURL
|
|
19
|
+
# purl = PackageURL.new(
|
|
20
|
+
# type: "gem",
|
|
21
|
+
# name: "rails",
|
|
22
|
+
# version: "7.0.0"
|
|
23
|
+
# )
|
|
24
|
+
# puts purl.to_s # "pkg:gem/rails@7.0.0"
|
|
25
|
+
#
|
|
26
|
+
# @example Parsing a PURL string
|
|
27
|
+
# purl = PackageURL.parse("pkg:npm/@babel/core@7.0.0")
|
|
28
|
+
# puts purl.namespace # "@babel"
|
|
29
|
+
# puts purl.name # "core"
|
|
30
|
+
#
|
|
31
|
+
# @see https://github.com/package-url/purl-spec PURL Specification
|
|
6
32
|
class PackageURL
|
|
7
|
-
|
|
33
|
+
# @return [String] the package type (e.g., "gem", "npm", "maven")
|
|
34
|
+
attr_reader :type
|
|
35
|
+
|
|
36
|
+
# @return [String, nil] the package namespace/scope
|
|
37
|
+
attr_reader :namespace
|
|
38
|
+
|
|
39
|
+
# @return [String] the package name
|
|
40
|
+
attr_reader :name
|
|
41
|
+
|
|
42
|
+
# @return [String, nil] the package version
|
|
43
|
+
attr_reader :version
|
|
44
|
+
|
|
45
|
+
# @return [Hash<String, String>, nil] key-value qualifier pairs
|
|
46
|
+
attr_reader :qualifiers
|
|
47
|
+
|
|
48
|
+
# @return [String, nil] subpath within the package
|
|
49
|
+
attr_reader :subpath
|
|
8
50
|
|
|
9
51
|
VALID_TYPE_CHARS = /\A[a-zA-Z0-9\.\+\-]+\z/
|
|
10
52
|
VALID_QUALIFIER_KEY_CHARS = /\A[a-zA-Z0-9\.\-_]+\z/
|
|
11
53
|
|
|
54
|
+
# Create a new PackageURL instance
|
|
55
|
+
#
|
|
56
|
+
# @param type [String, Symbol] the package type (required)
|
|
57
|
+
# @param name [String] the package name (required)
|
|
58
|
+
# @param namespace [String, nil] optional namespace/scope
|
|
59
|
+
# @param version [String, nil] optional version
|
|
60
|
+
# @param qualifiers [Hash, nil] optional key-value qualifier pairs
|
|
61
|
+
# @param subpath [String, nil] optional subpath within package
|
|
62
|
+
#
|
|
63
|
+
# @raise [InvalidTypeError] if type is invalid
|
|
64
|
+
# @raise [InvalidNameError] if name is invalid
|
|
65
|
+
# @raise [ValidationError] if any component fails type-specific validation
|
|
66
|
+
#
|
|
67
|
+
# @example
|
|
68
|
+
# purl = PackageURL.new(
|
|
69
|
+
# type: "npm",
|
|
70
|
+
# namespace: "@babel",
|
|
71
|
+
# name: "core",
|
|
72
|
+
# version: "7.0.0"
|
|
73
|
+
# )
|
|
12
74
|
def initialize(type:, name:, namespace: nil, version: nil, qualifiers: nil, subpath: nil)
|
|
13
75
|
@type = validate_and_normalize_type(type)
|
|
14
76
|
@name = validate_name(name)
|
|
@@ -21,6 +83,25 @@ module Purl
|
|
|
21
83
|
validate_type_specific_rules
|
|
22
84
|
end
|
|
23
85
|
|
|
86
|
+
# Parse a PURL string into a PackageURL object
|
|
87
|
+
#
|
|
88
|
+
# @param purl_string [String] PURL string starting with "pkg:"
|
|
89
|
+
# @return [PackageURL] parsed package URL object
|
|
90
|
+
# @raise [InvalidSchemeError] if string doesn't start with "pkg:"
|
|
91
|
+
# @raise [MalformedUrlError] if string is malformed
|
|
92
|
+
# @raise [ValidationError] if parsed components fail validation
|
|
93
|
+
#
|
|
94
|
+
# @example Basic parsing
|
|
95
|
+
# purl = PackageURL.parse("pkg:gem/rails@7.0.0")
|
|
96
|
+
# puts purl.type # "gem"
|
|
97
|
+
# puts purl.name # "rails"
|
|
98
|
+
# puts purl.version # "7.0.0"
|
|
99
|
+
#
|
|
100
|
+
# @example Complex parsing with all components
|
|
101
|
+
# purl = PackageURL.parse("pkg:npm/@babel/core@7.0.0?arch=x64#lib/index.js")
|
|
102
|
+
# puts purl.namespace # "@babel"
|
|
103
|
+
# puts purl.qualifiers # {"arch" => "x64"}
|
|
104
|
+
# puts purl.subpath # "lib/index.js"
|
|
24
105
|
def self.parse(purl_string)
|
|
25
106
|
raise InvalidSchemeError, "PURL must start with 'pkg:'" unless purl_string.start_with?("pkg:")
|
|
26
107
|
|
|
@@ -134,6 +215,13 @@ module Purl
|
|
|
134
215
|
)
|
|
135
216
|
end
|
|
136
217
|
|
|
218
|
+
# Convert the PackageURL to its canonical string representation
|
|
219
|
+
#
|
|
220
|
+
# @return [String] canonical PURL string
|
|
221
|
+
#
|
|
222
|
+
# @example
|
|
223
|
+
# purl = PackageURL.new(type: "gem", name: "rails", version: "7.0.0")
|
|
224
|
+
# puts purl.to_s # "pkg:gem/rails@7.0.0"
|
|
137
225
|
def to_s
|
|
138
226
|
result = "pkg:#{type.downcase}"
|
|
139
227
|
|
|
@@ -183,6 +271,15 @@ module Purl
|
|
|
183
271
|
result
|
|
184
272
|
end
|
|
185
273
|
|
|
274
|
+
# Convert the PackageURL to a hash representation
|
|
275
|
+
#
|
|
276
|
+
# @return [Hash<Symbol, Object>] hash with component keys and values
|
|
277
|
+
#
|
|
278
|
+
# @example
|
|
279
|
+
# purl = PackageURL.new(type: "gem", name: "rails", version: "7.0.0")
|
|
280
|
+
# hash = purl.to_h
|
|
281
|
+
# # => {:type=>"gem", :namespace=>nil, :name=>"rails", :version=>"7.0.0",
|
|
282
|
+
# # :qualifiers=>nil, :subpath=>nil}
|
|
186
283
|
def to_h
|
|
187
284
|
{
|
|
188
285
|
type: type,
|
|
@@ -194,28 +291,69 @@ module Purl
|
|
|
194
291
|
}
|
|
195
292
|
end
|
|
196
293
|
|
|
294
|
+
# Compare two PackageURL objects for equality
|
|
295
|
+
#
|
|
296
|
+
# Two PURLs are equal if their canonical string representations are identical.
|
|
297
|
+
#
|
|
298
|
+
# @param other [Object] object to compare with
|
|
299
|
+
# @return [Boolean] true if equal, false otherwise
|
|
300
|
+
#
|
|
301
|
+
# @example
|
|
302
|
+
# purl1 = PackageURL.parse("pkg:gem/rails@7.0.0")
|
|
303
|
+
# purl2 = PackageURL.parse("pkg:gem/rails@7.0.0")
|
|
304
|
+
# puts purl1 == purl2 # true
|
|
197
305
|
def ==(other)
|
|
198
306
|
return false unless other.is_a?(PackageURL)
|
|
199
307
|
|
|
200
308
|
to_s == other.to_s
|
|
201
309
|
end
|
|
202
310
|
|
|
311
|
+
# Generate hash code for the PackageURL
|
|
312
|
+
#
|
|
313
|
+
# @return [Integer] hash code based on canonical string representation
|
|
203
314
|
def hash
|
|
204
315
|
to_s.hash
|
|
205
316
|
end
|
|
206
317
|
|
|
207
318
|
# Pattern matching support for Ruby 2.7+
|
|
319
|
+
#
|
|
320
|
+
# Allows destructuring PackageURL in pattern matching.
|
|
321
|
+
#
|
|
322
|
+
# @return [Array] array of [type, namespace, name, version, qualifiers, subpath]
|
|
323
|
+
#
|
|
324
|
+
# @example Ruby 2.7+ pattern matching
|
|
325
|
+
# case purl
|
|
326
|
+
# in ["gem", nil, name, version, nil, nil]
|
|
327
|
+
# puts "Simple gem: #{name} v#{version}"
|
|
328
|
+
# end
|
|
208
329
|
def deconstruct
|
|
209
330
|
[type, namespace, name, version, qualifiers, subpath]
|
|
210
331
|
end
|
|
211
332
|
|
|
333
|
+
# Pattern matching support for Ruby 2.7+ (hash patterns)
|
|
334
|
+
#
|
|
335
|
+
# @param keys [Array<Symbol>, nil] keys to extract, or nil for all keys
|
|
336
|
+
# @return [Hash<Symbol, Object>] hash with requested keys
|
|
337
|
+
#
|
|
338
|
+
# @example Ruby 2.7+ hash pattern matching
|
|
339
|
+
# case purl
|
|
340
|
+
# in {type: "gem", name:, version:}
|
|
341
|
+
# puts "Gem #{name} version #{version}"
|
|
342
|
+
# end
|
|
212
343
|
def deconstruct_keys(keys)
|
|
213
344
|
return to_h.slice(*keys) if keys
|
|
214
345
|
to_h
|
|
215
346
|
end
|
|
216
347
|
|
|
217
348
|
# Create a new PackageURL with modified attributes
|
|
218
|
-
#
|
|
349
|
+
#
|
|
350
|
+
# @param changes [Hash] attributes to change
|
|
351
|
+
# @return [PackageURL] new PackageURL instance with changes applied
|
|
352
|
+
#
|
|
353
|
+
# @example
|
|
354
|
+
# purl = PackageURL.parse("pkg:gem/rails@7.0.0")
|
|
355
|
+
# new_purl = purl.with(version: "7.1.0", qualifiers: {"arch" => "x64"})
|
|
356
|
+
# puts new_purl.to_s # "pkg:gem/rails@7.1.0?arch=x64"
|
|
219
357
|
def with(**changes)
|
|
220
358
|
current_attrs = to_h
|
|
221
359
|
new_attrs = current_attrs.merge(changes)
|
data/lib/purl/version.rb
CHANGED
data/lib/purl.rb
CHANGED
|
@@ -5,7 +5,25 @@ require_relative "purl/errors"
|
|
|
5
5
|
require_relative "purl/package_url"
|
|
6
6
|
require_relative "purl/registry_url"
|
|
7
7
|
|
|
8
|
+
# The main PURL (Package URL) module providing functionality to parse,
|
|
9
|
+
# validate, and generate package URLs according to the PURL specification.
|
|
10
|
+
#
|
|
11
|
+
# A Package URL is a mostly universal standard to reference a software package
|
|
12
|
+
# in a uniform way across many tools, programming languages and ecosystems.
|
|
13
|
+
#
|
|
14
|
+
# @example Basic usage
|
|
15
|
+
# purl = Purl.parse("pkg:gem/rails@7.0.0")
|
|
16
|
+
# puts purl.type # "gem"
|
|
17
|
+
# puts purl.name # "rails"
|
|
18
|
+
# puts purl.version # "7.0.0"
|
|
19
|
+
#
|
|
20
|
+
# @example Registry URL conversion
|
|
21
|
+
# purl = Purl.from_registry_url("https://rubygems.org/gems/rails")
|
|
22
|
+
# puts purl.to_s # "pkg:gem/rails"
|
|
23
|
+
#
|
|
24
|
+
# @see https://github.com/package-url/purl-spec PURL Specification
|
|
8
25
|
module Purl
|
|
26
|
+
# Base error class for all PURL-related errors
|
|
9
27
|
class Error < StandardError; end
|
|
10
28
|
|
|
11
29
|
# Load PURL types configuration from JSON file
|
|
@@ -21,6 +39,15 @@ module Purl
|
|
|
21
39
|
KNOWN_TYPES = load_types_config["types"].keys.sort.freeze
|
|
22
40
|
|
|
23
41
|
# Convenience method for parsing PURL strings
|
|
42
|
+
#
|
|
43
|
+
# @param purl_string [String] a PURL string starting with "pkg:"
|
|
44
|
+
# @return [PackageURL] parsed package URL object
|
|
45
|
+
# @raise [InvalidSchemeError] if string doesn't start with "pkg:"
|
|
46
|
+
# @raise [MalformedUrlError] if string is malformed
|
|
47
|
+
#
|
|
48
|
+
# @example
|
|
49
|
+
# purl = Purl.parse("pkg:gem/rails@7.0.0")
|
|
50
|
+
# puts purl.name # "rails"
|
|
24
51
|
def self.parse(purl_string)
|
|
25
52
|
PackageURL.parse(purl_string)
|
|
26
53
|
end
|
|
@@ -33,26 +60,66 @@ module Purl
|
|
|
33
60
|
end
|
|
34
61
|
|
|
35
62
|
# Returns all known PURL types
|
|
63
|
+
#
|
|
64
|
+
# @return [Array<String>] sorted array of known PURL type names
|
|
65
|
+
#
|
|
66
|
+
# @example
|
|
67
|
+
# types = Purl.known_types
|
|
68
|
+
# puts types.include?("gem") # true
|
|
36
69
|
def self.known_types
|
|
37
70
|
KNOWN_TYPES.dup
|
|
38
71
|
end
|
|
39
72
|
|
|
40
73
|
# Returns types that have registry URL support
|
|
74
|
+
#
|
|
75
|
+
# @return [Array<String>] sorted array of types that can generate registry URLs
|
|
76
|
+
#
|
|
77
|
+
# @example
|
|
78
|
+
# types = Purl.registry_supported_types
|
|
79
|
+
# puts types.include?("npm") # true if npm has registry support
|
|
41
80
|
def self.registry_supported_types
|
|
42
81
|
RegistryURL.supported_types
|
|
43
82
|
end
|
|
44
83
|
|
|
45
84
|
# Returns types that support reverse parsing from registry URLs
|
|
85
|
+
#
|
|
86
|
+
# @return [Array<String>] sorted array of types that can parse registry URLs back to PURLs
|
|
87
|
+
#
|
|
88
|
+
# @example
|
|
89
|
+
# types = Purl.reverse_parsing_supported_types
|
|
90
|
+
# puts types.include?("gem") # true if gem has reverse parsing support
|
|
46
91
|
def self.reverse_parsing_supported_types
|
|
47
92
|
RegistryURL.supported_reverse_types
|
|
48
93
|
end
|
|
49
94
|
|
|
50
95
|
# Check if a type is known/valid
|
|
96
|
+
#
|
|
97
|
+
# @param type [String, Symbol] the type to check
|
|
98
|
+
# @return [Boolean] true if type is known, false otherwise
|
|
99
|
+
#
|
|
100
|
+
# @example
|
|
101
|
+
# Purl.known_type?("gem") # true
|
|
102
|
+
# Purl.known_type?("unknown") # false
|
|
51
103
|
def self.known_type?(type)
|
|
52
104
|
KNOWN_TYPES.include?(type.to_s.downcase)
|
|
53
105
|
end
|
|
54
106
|
|
|
55
|
-
# Get type information including registry support
|
|
107
|
+
# Get comprehensive type information including registry support
|
|
108
|
+
#
|
|
109
|
+
# @param type [String, Symbol] the type to get information for
|
|
110
|
+
# @return [Hash] hash containing type information with keys:
|
|
111
|
+
# - +:type+: normalized type name
|
|
112
|
+
# - +:known+: whether type is known
|
|
113
|
+
# - +:description+: human-readable description
|
|
114
|
+
# - +:default_registry+: default registry URL
|
|
115
|
+
# - +:examples+: array of example PURLs
|
|
116
|
+
# - +:registry_url_generation+: whether registry URL generation is supported
|
|
117
|
+
# - +:reverse_parsing+: whether reverse parsing is supported
|
|
118
|
+
# - +:route_patterns+: array of URL patterns for this type
|
|
119
|
+
#
|
|
120
|
+
# @example
|
|
121
|
+
# info = Purl.type_info("gem")
|
|
122
|
+
# puts info[:description] # "Ruby gems from RubyGems.org"
|
|
56
123
|
def self.type_info(type)
|
|
57
124
|
normalized_type = type.to_s.downcase
|
|
58
125
|
{
|
|
@@ -68,6 +135,13 @@ module Purl
|
|
|
68
135
|
end
|
|
69
136
|
|
|
70
137
|
# Get comprehensive information about all types
|
|
138
|
+
#
|
|
139
|
+
# @return [Hash<String, Hash>] hash mapping type names to their information
|
|
140
|
+
# @see #type_info for structure of individual type information
|
|
141
|
+
#
|
|
142
|
+
# @example
|
|
143
|
+
# all_info = Purl.all_type_info
|
|
144
|
+
# gem_info = all_info["gem"]
|
|
71
145
|
def self.all_type_info
|
|
72
146
|
result = {}
|
|
73
147
|
|
|
@@ -87,6 +161,10 @@ module Purl
|
|
|
87
161
|
end
|
|
88
162
|
|
|
89
163
|
# Get type configuration from JSON
|
|
164
|
+
#
|
|
165
|
+
# @param type [String, Symbol] the type to get configuration for
|
|
166
|
+
# @return [Hash, nil] configuration hash or nil if type not found
|
|
167
|
+
# @api private
|
|
90
168
|
def self.type_config(type)
|
|
91
169
|
config = load_types_config["types"][type.to_s.downcase]
|
|
92
170
|
return nil unless config
|
|
@@ -94,13 +172,27 @@ module Purl
|
|
|
94
172
|
config.dup # Return a copy to prevent modification
|
|
95
173
|
end
|
|
96
174
|
|
|
97
|
-
# Get description for a type
|
|
175
|
+
# Get human-readable description for a type
|
|
176
|
+
#
|
|
177
|
+
# @param type [String, Symbol] the type to get description for
|
|
178
|
+
# @return [String, nil] description string or nil if not available
|
|
179
|
+
#
|
|
180
|
+
# @example
|
|
181
|
+
# desc = Purl.type_description("gem")
|
|
182
|
+
# puts desc # "Ruby gems from RubyGems.org"
|
|
98
183
|
def self.type_description(type)
|
|
99
184
|
config = type_config(type)
|
|
100
185
|
config ? config["description"] : nil
|
|
101
186
|
end
|
|
102
187
|
|
|
103
|
-
# Get
|
|
188
|
+
# Get example PURLs for a type
|
|
189
|
+
#
|
|
190
|
+
# @param type [String, Symbol] the type to get examples for
|
|
191
|
+
# @return [Array<String>] array of example PURL strings
|
|
192
|
+
#
|
|
193
|
+
# @example
|
|
194
|
+
# examples = Purl.type_examples("gem")
|
|
195
|
+
# puts examples.first # "pkg:gem/rails@7.0.0"
|
|
104
196
|
def self.type_examples(type)
|
|
105
197
|
config = type_config(type)
|
|
106
198
|
return [] unless config
|
|
@@ -109,6 +201,10 @@ module Purl
|
|
|
109
201
|
end
|
|
110
202
|
|
|
111
203
|
# Get registry configuration for a type
|
|
204
|
+
#
|
|
205
|
+
# @param type [String, Symbol] the type to get registry config for
|
|
206
|
+
# @return [Hash, nil] registry configuration hash or nil if not available
|
|
207
|
+
# @api private
|
|
112
208
|
def self.registry_config(type)
|
|
113
209
|
config = type_config(type)
|
|
114
210
|
return nil unless config
|
|
@@ -117,6 +213,13 @@ module Purl
|
|
|
117
213
|
end
|
|
118
214
|
|
|
119
215
|
# Get default registry URL for a type
|
|
216
|
+
#
|
|
217
|
+
# @param type [String, Symbol] the type to get default registry for
|
|
218
|
+
# @return [String, nil] default registry URL or nil if not available
|
|
219
|
+
#
|
|
220
|
+
# @example
|
|
221
|
+
# registry = Purl.default_registry("gem")
|
|
222
|
+
# puts registry # "https://rubygems.org"
|
|
120
223
|
def self.default_registry(type)
|
|
121
224
|
config = type_config(type)
|
|
122
225
|
return nil unless config
|
|
@@ -125,6 +228,19 @@ module Purl
|
|
|
125
228
|
end
|
|
126
229
|
|
|
127
230
|
# Get metadata about the types configuration
|
|
231
|
+
#
|
|
232
|
+
# @return [Hash] metadata hash with keys:
|
|
233
|
+
# - +:version+: configuration version
|
|
234
|
+
# - +:description+: configuration description
|
|
235
|
+
# - +:source+: source of the configuration
|
|
236
|
+
# - +:last_updated+: when configuration was last updated
|
|
237
|
+
# - +:total_types+: total number of types
|
|
238
|
+
# - +:registry_supported_types+: number of types with registry support
|
|
239
|
+
# - +:types_with_default_registry+: number of types with default registry
|
|
240
|
+
#
|
|
241
|
+
# @example
|
|
242
|
+
# metadata = Purl.types_config_metadata
|
|
243
|
+
# puts "Total types: #{metadata[:total_types]}"
|
|
128
244
|
def self.types_config_metadata
|
|
129
245
|
config = load_types_config
|
|
130
246
|
{
|