55require_relative "purl/package_url"
66require_relative "purl/registry_url"
77
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/[email protected] ") 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
825module Purl
26+ # Base error class for all PURL-related errors
927 class Error < StandardError ; end
1028
1129 # Load PURL types configuration from JSON file
@@ -21,6 +39,15 @@ def self.load_types_config
2139 KNOWN_TYPES = load_types_config [ "types" ] . keys . sort . freeze
2240
2341 # 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/[email protected] ") 50+ # puts purl.name # "rails"
2451 def self . parse ( purl_string )
2552 PackageURL . parse ( purl_string )
2653 end
@@ -33,26 +60,66 @@ def self.from_registry_url(registry_url, type: nil)
3360 end
3461
3562 # 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
3669 def self . known_types
3770 KNOWN_TYPES . dup
3871 end
3972
4073 # 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
4180 def self . registry_supported_types
4281 RegistryURL . supported_types
4382 end
4483
4584 # 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
4691 def self . reverse_parsing_supported_types
4792 RegistryURL . supported_reverse_types
4893 end
4994
5095 # 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
51103 def self . known_type? ( type )
52104 KNOWN_TYPES . include? ( type . to_s . downcase )
53105 end
54106
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"
56123 def self . type_info ( type )
57124 normalized_type = type . to_s . downcase
58125 {
@@ -68,6 +135,13 @@ def self.type_info(type)
68135 end
69136
70137 # 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"]
71145 def self . all_type_info
72146 result = { }
73147
@@ -87,20 +161,38 @@ def self.all_type_info
87161 end
88162
89163 # 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
90168 def self . type_config ( type )
91169 config = load_types_config [ "types" ] [ type . to_s . downcase ]
92170 return nil unless config
93171
94172 config . dup # Return a copy to prevent modification
95173 end
96174
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"
98183 def self . type_description ( type )
99184 config = type_config ( type )
100185 config ? config [ "description" ] : nil
101186 end
102187
103- # Get examples for a type
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/[email protected] " 104196 def self . type_examples ( type )
105197 config = type_config ( type )
106198 return [ ] unless config
@@ -109,6 +201,10 @@ def self.type_examples(type)
109201 end
110202
111203 # 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
112208 def self . registry_config ( type )
113209 config = type_config ( type )
114210 return nil unless config
@@ -117,6 +213,13 @@ def self.registry_config(type)
117213 end
118214
119215 # 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"
120223 def self . default_registry ( type )
121224 config = type_config ( type )
122225 return nil unless config
@@ -125,6 +228,19 @@ def self.default_registry(type)
125228 end
126229
127230 # 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]}"
128244 def self . types_config_metadata
129245 config = load_types_config
130246 {
0 commit comments