Class: RDF::URI
Overview
A Uniform Resource Identifier (URI). Also compatible with International Resource Identifier (IRI)
Constant Summary collapse
- UCSCHAR =
IRI components
Regexp.compile(<<-EOS.gsub(/\s+/, '')) [\\u00A0-\\uD7FF]|[\\uF900-\\uFDCF]|[\\uFDF0-\\uFFEF]| [\\u{10000}-\\u{1FFFD}]|[\\u{20000}-\\u{2FFFD}]|[\\u{30000}-\\u{3FFFD}]| [\\u{40000}-\\u{4FFFD}]|[\\u{50000}-\\u{5FFFD}]|[\\u{60000}-\\u{6FFFD}]| [\\u{70000}-\\u{7FFFD}]|[\\u{80000}-\\u{8FFFD}]|[\\u{90000}-\\u{9FFFD}]| [\\u{A0000}-\\u{AFFFD}]|[\\u{B0000}-\\u{BFFFD}]|[\\u{C0000}-\\u{CFFFD}]| [\\u{D0000}-\\u{DFFFD}]|[\\u{E1000}-\\u{EFFFD}] EOS
- IPRIVATE =
Regexp.compile("[\\uE000-\\uF8FF]|[\\u{F0000}-\\u{FFFFD}]|[\\u100000-\\u10FFFD]").freeze
- SCHEME =
Regexp.compile("[A-Za-z](?:[A-Za-z0-9+-\.])*").freeze
- PORT =
Regexp.compile("[0-9]*").freeze
- IP_literal =
Simplified, no IPvFuture
Regexp.compile("\\[[0-9A-Fa-f:\\.]*\\]").freeze
- PCT_ENCODED =
Regexp.compile("%[0-9A-Fa-f][0-9A-Fa-f]").freeze
- GEN_DELIMS =
Regexp.compile("[:/\\?\\#\\[\\]@]").freeze
- SUB_DELIMS =
Regexp.compile("[!\\$&'\\(\\)\\*\\+,;=]").freeze
- RESERVED =
Regexp.compile("(?:#{GEN_DELIMS}|#{SUB_DELIMS})").freeze
- UNRESERVED =
Regexp.compile("[A-Za-z0-9\._~-]").freeze
- IUNRESERVED =
Regexp.compile("[A-Za-z0-9\._~-]|#{UCSCHAR}").freeze
- IPCHAR =
Regexp.compile("(?:#{IUNRESERVED}|#{PCT_ENCODED}|#{SUB_DELIMS}|:|@)").freeze
- IQUERY =
Regexp.compile("(?:#{IPCHAR}|#{IPRIVATE}|/|\\?)*").freeze
- IFRAGMENT =
Regexp.compile("(?:#{IPCHAR}|/|\\?)*").freeze
- ISEGMENT =
Regexp.compile("(?:#{IPCHAR})*").freeze
- ISEGMENT_NZ =
Regexp.compile("(?:#{IPCHAR})+").freeze
- ISEGMENT_NZ_NC =
Regexp.compile("(?:(?:#{IUNRESERVED})|(?:#{PCT_ENCODED})|(?:#{SUB_DELIMS})|@)+").freeze
- IPATH_ABEMPTY =
Regexp.compile("(?:/#{ISEGMENT})*").freeze
- IPATH_ABSOLUTE =
Regexp.compile("/(?:(?:#{ISEGMENT_NZ})(/#{ISEGMENT})*)?").freeze
- IPATH_NOSCHEME =
Regexp.compile("(?:#{ISEGMENT_NZ_NC})(?:/#{ISEGMENT})*").freeze
- IPATH_ROOTLESS =
Regexp.compile("(?:#{ISEGMENT_NZ})(?:/#{ISEGMENT})*").freeze
- IPATH_EMPTY =
Regexp.compile("").freeze
- IREG_NAME =
Regexp.compile("(?:(?:#{IUNRESERVED})|(?:#{PCT_ENCODED})|(?:#{SUB_DELIMS}))*").freeze
- IHOST =
Regexp.compile("(?:#{IP_literal})|(?:#{IREG_NAME})").freeze
- IUSERINFO =
Regexp.compile("(?:(?:#{IUNRESERVED})|(?:#{PCT_ENCODED})|(?:#{SUB_DELIMS})|:)*").freeze
- IAUTHORITY =
Regexp.compile("(?:#{IUSERINFO}@)?#{IHOST}(?::#{PORT})?").freeze
- IRELATIVE_PART =
Regexp.compile("(?:(?://#{IAUTHORITY}(?:#{IPATH_ABEMPTY}))|(?:#{IPATH_ABSOLUTE})|(?:#{IPATH_NOSCHEME})|(?:#{IPATH_EMPTY}))").freeze
- IRELATIVE_REF =
Regexp.compile("^#{IRELATIVE_PART}(?:\\?#{IQUERY})?(?:\\##{IFRAGMENT})?$").freeze
- IHIER_PART =
Regexp.compile("(?:(?://#{IAUTHORITY}#{IPATH_ABEMPTY})|(?:#{IPATH_ABSOLUTE})|(?:#{IPATH_ROOTLESS})|(?:#{IPATH_EMPTY}))").freeze
- IRI =
Regexp.compile("^#{SCHEME}:(?:#{IHIER_PART})(?:\\?#{IQUERY})?(?:\\##{IFRAGMENT})?$").freeze
- IRI_PARTS =
Split an IRI into it's component parts scheme, authority, path, query, fragment
/^(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*)(\?[^#]*)?(#.*)?$/.freeze
- RDS_2A =
Remove dot expressions regular expressions
/^\.?\.\/(.*)$/.freeze
- RDS_2B1 =
/^\/\.$/.freeze
- RDS_2B2 =
/^(?:\/\.\/)(.*)$/.freeze
- RDS_2C1 =
/^\/\.\.$/.freeze
- RDS_2C2 =
/^(?:\/\.\.\/)(.*)$/.freeze
- RDS_2D =
/^\.\.?$/.freeze
- RDS_2E =
/^(\/?[^\/]*)(\/?.*)?$/.freeze
- PORT_MAPPING =
Remove port, if it is standard for the scheme when normalizing
{ "http" => 80, "https" => 443, "ftp" => 21, "tftp" => 69, "sftp" => 22, "ssh" => 22, "svn+ssh" => 22, "telnet" => 23, "nntp" => 119, "gopher" => 70, "wais" => 210, "ldap" => 389, "prospero" => 1525 }
- NON_HIER_SCHEMES =
List of schemes known not to be hierarchical
%w( about acct bitcoin callto cid data fax geo gtalk h323 iax icon im jabber jms magnet mailto maps news pres proxy session sip sips skype sms spotify stun stuns tag tel turn turns tv urn javascript ).freeze
- PN_ESCAPE_CHARS =
Characters in a PName which must be escaped Note: not all reserved characters need to be escaped in SPARQL/Turtle, but they must be unescaped when encountered
/[~\.!\$&'\(\)\*\+,;=\/\?\#@%]/.freeze
- PN_ESCAPES =
/\\#{Regexp.union(PN_ESCAPE_CHARS, /[\-_]/)}/.freeze
- HOST_FROM_AUTHORITY_RE =
/(?:[^@]+@)?([^:]+)(?::.*)?$/.freeze
- PORT_FROM_AUTHORITY_RE =
/:(\d+)$/.freeze
Class Method Summary collapse
-
._load(data) ⇒ RDF::URI
Load dumped data to reconsitute marshaled object This override is needed to avoid serializing @mutex.
-
.intern(str, *args, **options) ⇒ RDF::URI
Returns an interned
RDF::URI
instance based on the givenuri
string. -
.normalize_path(path) ⇒ String
Resolve paths to their simplest form.
-
.parse(str) ⇒ RDF::URI
Creates a new
RDF::URI
instance based on the givenuri
string.
Instance Method Summary collapse
-
#+(other) ⇒ RDF::URI
Simple concatenation operator.
-
#/(fragment) ⇒ RDF::URI
'Smart separator' URI builder.
-
#==(other) ⇒ Boolean
Checks whether this URI is equal to
other
(type checking). -
#===(other) ⇒ Boolean
Checks for case equality to the given
other
object. -
#=~(pattern) ⇒ Integer
Performs a pattern match using the given regular expression.
-
#_dump(level) ⇒ String
Dump of data needed to reconsitute this object using Marshal.load This override is needed to avoid serializing @mutex.
-
#absolute? ⇒ Boolean
A URI is absolute when it has a scheme.
-
#authority ⇒ Object
Authority is a combination of user, password, host and port.
-
#authority=(value) ⇒ RDF::URI
Self.
-
#canonicalize ⇒ RDF::URI
(also: #normalize)
Returns a copy of this URI converted into its canonical lexical representation.
-
#canonicalize! ⇒ RDF::URI
(also: #normalize!)
Converts this URI into its canonical lexical representation.
-
#dup ⇒ RDF::URI
Returns a duplicate copy of
self
. -
#end_with?(string) ⇒ Boolean
(also: #ends_with?)
Returns
true
if this URI ends with the givenstring
. -
#eql?(other) ⇒ Boolean
Checks whether this URI the same term as
other
. - #fragment ⇒ String
-
#fragment=(value) ⇒ RDF::URI
Self.
-
#hash ⇒ Integer
Returns a hash code for this URI.
-
#hier? ⇒ Boolean
Returns
true
if the URI scheme is hierarchical. - #host ⇒ String
-
#host=(value) ⇒ RDF::URI
Self.
-
#initialize(*args, validate: false, canonicalize: false, **options) ⇒ URI
constructor
A new instance of URI.
-
#inspect ⇒ String
Returns a
String
representation of the URI object's state. -
#join(*uris) ⇒ RDF::URI
Joins several URIs together.
-
#length ⇒ Integer
(also: #size)
Returns the string length of this URI.
-
#normalized_authority ⇒ String
Return normalized version of authority, if any.
-
#normalized_fragment ⇒ String
Normalized version of fragment.
-
#normalized_host ⇒ String
Normalized version of host.
-
#normalized_password ⇒ String
Normalized version of password.
-
#normalized_path ⇒ String
Normalized version of path.
-
#normalized_port ⇒ String
Normalized version of port.
-
#normalized_query ⇒ String
Normalized version of query.
-
#normalized_scheme ⇒ String
Return normalized version of scheme, if any.
-
#normalized_user ⇒ String
Normalized version of user.
-
#normalized_userinfo ⇒ String
Normalized version of userinfo.
-
#object ⇒ Hash{Symbol => String}
(also: #to_h)
Returns object representation of this URI, broken into components.
-
#parent ⇒ RDF::URI
Returns a copy of this URI with the path component ascended to the parent directory, if any.
-
#parent? ⇒ Boolean
(also: #has_parent?)
Returns
true
if this URI is hierarchical and it's path component isn't equal to/
. -
#parse(value) ⇒ Object{Symbol => String}
{ Parse a URI into it's components.
- #password ⇒ String
-
#password=(value) ⇒ RDF::URI
Self.
- #path ⇒ String
-
#path=(value) ⇒ RDF::URI
Self.
-
#pname(prefixes: nil) ⇒ String
Returns a Prefixed Name (PName) or the full IRI with any reserved characters in the suffix escaped.
- #port ⇒ String
-
#port=(value) ⇒ RDF::URI
Self.
-
#qname(prefixes: nil) ⇒ Array(Symbol, Symbol)
Returns a qualified name (QName) as a tuple of
[prefix, suffix]
for this URI based on available vocabularies, if possible. - #query ⇒ String
-
#query=(value) ⇒ RDF::URI
Self.
-
#query_values(return_type = Hash) ⇒ Hash, Array
Converts the query component to a Hash value.
-
#query_values=(value) ⇒ Object
Sets the query component for this URI from a Hash object.
-
#relative? ⇒ Boolean
A URI is relative when it does not have a scheme.
-
#relativize(base_uri) ⇒ RDF::URI
Attempt to make this URI relative to the provided
base_uri
. -
#request_uri ⇒ String
The HTTP request URI for this URI.
-
#root ⇒ RDF::URI
Returns a copy of this URI with the path component set to
/
. -
#root? ⇒ Boolean
Returns
true
if this URI's scheme is not hierarchical, or its path component is equal to/
. - #scheme ⇒ String
-
#scheme=(value) ⇒ RDF::URI
Self.
-
#to_str ⇒ String
(also: #to_s)
Returns the string representation of this URI.
-
#to_uri ⇒ RDF::URI
Returns
self
. -
#uri? ⇒ Boolean
Returns
true
. -
#url? ⇒ Boolean
Returns
true
if this URI is a URL. -
#urn? ⇒ Boolean
Returns
true
if this URI is a URN. - #user ⇒ String
-
#user=(value) ⇒ RDF::URI
Self.
-
#userinfo ⇒ Object
Userinfo is a combination of user and password.
-
#userinfo=(value) ⇒ RDF::URI
Self.
-
#valid? ⇒ Boolean
Determine if the URI is a valid according to RFC3987.
-
#validate! ⇒ RDF::URI
Validates this URI, raising an error if it is invalid.
-
#value ⇒ String
lexical representation of URI, either absolute or relative.
Methods included from Resource
Methods included from Term
#<=>, #compatible?, #escape, #term?, #terms, #to_base, #to_term
Methods included from Value
#anonymous?, #constant?, #graph?, #inspect!, #invalid?, #iri?, #list?, #literal?, #node?, #resource?, #start_with?, #statement?, #term?, #to_nquads, #to_ntriples, #to_rdf, #to_term, #type_error, #variable?
Constructor Details
#initialize(uri, **options) ⇒ URI #initialize(**options) ⇒ URI
Returns a new instance of URI.
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/rdf/model/uri.rb', line 226 def initialize(*args, validate: false, canonicalize: false, **) @value = @object = @hash = nil @mutex = Mutex.new uri = args.first if uri @value = uri.to_s.dup @value.dup.force_encoding(Encoding::UTF_8) if @value.encoding != Encoding::UTF_8 @value.freeze else %i( scheme user password userinfo host port authority path query fragment ).each do |meth| if .key?(meth) self.send("#{meth}=".to_sym, [meth]) else self.send(meth) end end end validate! if validate canonicalize! if canonicalize end |
Class Method Details
._load(data) ⇒ RDF::URI
Load dumped data to reconsitute marshaled object This override is needed to avoid serializing @mutex.
1324 1325 1326 |
# File 'lib/rdf/model/uri.rb', line 1324 def self._load(data) new(data) end |
.intern(str, *args, **options) ⇒ RDF::URI
Returns an interned RDF::URI
instance based on the given uri
string.
The maximum number of cached interned URI references is given by the
CACHE_SIZE
constant. This value is unlimited by default, in which
case an interned URI object will be purged only when the last strong
reference to it is garbage collected (i.e., when its finalizer runs).
Excepting special memory-limited circumstances, it should always be
safe and preferred to construct new URI references using
RDF::URI.intern
instead of RDF::URI.new
, since if an interned
object can't be returned for some reason, this method will fall back
to returning a freshly-allocated one.
(see #initialize)
147 148 149 |
# File 'lib/rdf/model/uri.rb', line 147 def self.intern(str, *args, **) (cache[(str = str.to_s).to_sym] ||= self.new(str, *args, **)).freeze end |
.normalize_path(path) ⇒ String
This process is correct, but overly iterative. It could be better done with a single regexp which handled most of the segment collapses all at once. Parent segments would still require iteration.
Resolve paths to their simplest form.
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/rdf/model/uri.rb', line 172 def self.normalize_path(path) output, input = "", path.to_s if input.encoding != Encoding::ASCII_8BIT input = input.dup.force_encoding(Encoding::ASCII_8BIT) end until input.empty? if input.match(RDS_2A) # If the input buffer begins with a prefix of "../" or "./", then remove that prefix from the input buffer; otherwise, input = $1 elsif input.match(RDS_2B1) || input.match(RDS_2B2) # if the input buffer begins with a prefix of "/./" or "/.", where "." is a complete path segment, then replace that prefix with "/" in the input buffer; otherwise, input = "/#{$1}" elsif input.match(RDS_2C1) || input.match(RDS_2C2) # if the input buffer begins with a prefix of "/../" or "/..", where ".." is a complete path segment, then replace that prefix with "/" in the input buffer input = "/#{$1}" # and remove the last segment and its preceding "/" (if any) from the output buffer; otherwise, output.sub!(/\/?[^\/]*$/, '') elsif input.match(RDS_2D) # if the input buffer consists only of "." or "..", then remove that from the input buffer; otherwise, input = "" elsif input.match(RDS_2E) # move the first path segment in the input buffer to the end of the output buffer, including the initial "/" character (if any) and any subsequent characters up to, but not including, the next "/" character or the end of the input buffer.end seg, input = $1, $2 output << seg end end output.force_encoding(Encoding::UTF_8) end |
.parse(str) ⇒ RDF::URI
Creates a new RDF::URI
instance based on the given uri
string.
This is just an alias for #initialize for compatibity
with Addressable::URI.parse
. Actual parsing is defered
until #object is accessed.
160 161 162 |
# File 'lib/rdf/model/uri.rb', line 160 def self.parse(str) self.new(str) end |
Instance Method Details
#+(other) ⇒ RDF::URI
Simple concatenation operator. Returns a URI formed from concatenating the string form of two elements.
For building URIs from fragments, you may want to use the smart
separator, #/
. #join
implements another set of URI building
semantics.
555 556 557 |
# File 'lib/rdf/model/uri.rb', line 555 def +(other) RDF::URI.intern(self.to_s + other.to_s) end |
#/(fragment) ⇒ RDF::URI
'Smart separator' URI builder
This method attempts to use some understanding of the most common use cases for URLs and URNs to create a simple method for building new URIs from fragments. This means that it will always insert a separator of some sort, will remove duplicate seperators, will always assume that a fragment argument represents a relative and not absolute path, and throws an exception when an absolute URI is received for a fragment argument.
This is separate from the semantics for #join
, which are well-defined by
RFC3986 section 5.2 as part of the merging and normalization process;
this method does not perform any normalization, removal of spurious
paths, or removal of parent directory references (/../)
.
When fragment
is a path segment containing a colon, best practice is to prepend a ./
and use #join, which resolves dot-segments.
See also #+
, which concatenates the string forms of two URIs without
any sort of checking or processing.
For an up-to-date list of edge case behavior, see the shared examples for RDF::URI in the rdf-spec project.
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 |
# File 'lib/rdf/model/uri.rb', line 500 def /(fragment) frag = fragment.respond_to?(:to_uri) ? fragment.to_uri : RDF::URI(fragment.to_s) raise ArgumentError, "Non-absolute URI or string required, got #{frag}" unless frag.relative? if urn? RDF::URI.intern(to_s.sub(/:+$/,'') + ':' + fragment.to_s.sub(/^:+/,'')) else # !urn? res = self.dup if res.fragment case fragment.to_s[0,1] when '/' # Base with a fragment, fragment beginning with '/'. The fragment wins, we use '/'. path, frag = fragment.to_s.split('#', 2) res.path = "#{res.path}/#{path.sub(/^\/*/,'')}" res.fragment = frag else # Replace fragment res.fragment = fragment.to_s.sub(/^#+/,'') end else # Not a fragment. includes '/'. Results from bases ending in '/' are the same as if there were no trailing slash. case fragment.to_s[0,1] when '#' # Base ending with '/', fragment beginning with '#'. The fragment wins, we use '#'. res.path = res.path.to_s.sub(/\/*$/, '') # Add fragment res.fragment = fragment.to_s.sub(/^#+/,'') else # Add fragment as path component path, frag = fragment.to_s.split('#', 2) res.path = res.path.to_s.sub(/\/*$/,'/') + path.sub(/^\/*/,'') res.fragment = frag end end RDF::URI.intern(res.to_s) end end |
#==(other) ⇒ Boolean
Checks whether this URI is equal to other
(type checking).
Per SPARQL data-r2/expr-equal/eq-2-2, numeric can't be compared with other types
760 761 762 763 764 765 766 767 768 769 |
# File 'lib/rdf/model/uri.rb', line 760 def ==(other) case other when Literal # If other is a Literal, reverse test to consolodate complex type checking logic other == self when String then to_s == other when URI then hash == other.hash && to_s == other.to_s else other.respond_to?(:to_uri) && to_s == other.to_uri.to_s end end |
#===(other) ⇒ Boolean
Checks for case equality to the given other
object.
784 785 786 787 788 789 |
# File 'lib/rdf/model/uri.rb', line 784 def ===(other) case other when Regexp then other === to_s else self == other end end |
#=~(pattern) ⇒ Integer
Performs a pattern match using the given regular expression.
802 803 804 805 806 807 |
# File 'lib/rdf/model/uri.rb', line 802 def =~(pattern) case pattern when Regexp then to_s =~ pattern else super # `Object#=~` returns `false` end end |
#_dump(level) ⇒ String
Dump of data needed to reconsitute this object using Marshal.load This override is needed to avoid serializing @mutex.
1314 1315 1316 |
# File 'lib/rdf/model/uri.rb', line 1314 def _dump(level) value end |
#absolute? ⇒ Boolean
A URI is absolute when it has a scheme
306 |
# File 'lib/rdf/model/uri.rb', line 306 def absolute?; !scheme.nil?; end |
#authority ⇒ Object
Authority is a combination of user, password, host and port
1152 1153 1154 1155 1156 |
# File 'lib/rdf/model/uri.rb', line 1152 def object.fetch(:authority) { @object[:authority] = ( if @object[:host]) } end |
#authority=(value) ⇒ RDF::URI
Returns self.
1161 1162 1163 1164 1165 1166 1167 |
# File 'lib/rdf/model/uri.rb', line 1161 def (value) object.delete_if {|k, v| %i(user password host port userinfo).include?(k)} object[:authority] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value) user; password; userinfo; host; port @value = nil self end |
#canonicalize ⇒ RDF::URI Also known as: normalize
Returns a copy of this URI converted into its canonical lexical representation.
376 377 378 |
# File 'lib/rdf/model/uri.rb', line 376 def canonicalize self.dup.canonicalize! end |
#canonicalize! ⇒ RDF::URI Also known as: normalize!
Converts this URI into its canonical lexical representation.
386 387 388 389 390 391 392 393 394 395 396 397 |
# File 'lib/rdf/model/uri.rb', line 386 def canonicalize! @object = { scheme: normalized_scheme, authority: , path: normalized_path.squeeze('/'), query: normalized_query, fragment: normalized_fragment } @value = nil @hash = nil self end |
#dup ⇒ RDF::URI
Returns a duplicate copy of self
.
697 698 699 |
# File 'lib/rdf/model/uri.rb', line 697 def dup self.class.new(@value, **(@object || {})) end |
#end_with?(string) ⇒ Boolean Also known as: ends_with?
Returns true
if this URI ends with the given string
.
728 729 730 |
# File 'lib/rdf/model/uri.rb', line 728 def end_with?(string) to_s.end_with?(string.to_s) end |
#eql?(other) ⇒ Boolean
Checks whether this URI the same term as other
.
743 744 745 |
# File 'lib/rdf/model/uri.rb', line 743 def eql?(other) other.is_a?(URI) && self.hash == other.hash && self == other end |
#fragment ⇒ String
1128 1129 1130 1131 1132 |
# File 'lib/rdf/model/uri.rb', line 1128 def fragment object.fetch(:fragment) do nil end end |
#fragment=(value) ⇒ RDF::URI
Returns self.
1137 1138 1139 1140 1141 |
# File 'lib/rdf/model/uri.rb', line 1137 def fragment=(value) object[:fragment] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value) @value = nil self end |
#hash ⇒ Integer
Returns a hash code for this URI.
853 854 855 |
# File 'lib/rdf/model/uri.rb', line 853 def hash @hash || @hash = (value.hash * -1) end |
#hier? ⇒ Boolean
Returns true
if the URI scheme is hierarchical.
286 287 288 |
# File 'lib/rdf/model/uri.rb', line 286 def hier? !NON_HIER_SCHEMES.include?(scheme) end |
#host ⇒ String
982 983 984 985 986 |
# File 'lib/rdf/model/uri.rb', line 982 def host object.fetch(:host) do @object[:host] = ($1 if @object[:authority] && HOST_FROM_AUTHORITY_RE.match(@object[:authority])) end end |
#host=(value) ⇒ RDF::URI
Returns self.
991 992 993 994 995 996 |
# File 'lib/rdf/model/uri.rb', line 991 def host=(value) object[:host] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value) @object[:authority] = @value = nil self end |
#inspect ⇒ String
Returns a String
representation of the URI object's state.
831 832 833 |
# File 'lib/rdf/model/uri.rb', line 831 def inspect sprintf("#<%s:%#0x URI:%s>", URI.to_s, self.object_id, self.to_s) end |
#join(*uris) ⇒ RDF::URI
Joins several URIs together.
This method conforms to join normalization semantics as per RFC3986, section 5.2. This method normalizes URIs, removes some duplicate path information, such as double slashes, and other behavior specified in the RFC.
Other URI building methods are #/
and #+
.
For an up-to-date list of edge case behavior, see the shared examples for RDF::URI in the rdf-spec project.
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 |
# File 'lib/rdf/model/uri.rb', line 424 def join(*uris) joined_parts = object.dup.delete_if {|k, v| %i(user password host port).include?(k)} uris.each do |uri| uri = RDF::URI.new(uri) unless uri.is_a?(RDF::URI) next if uri.to_s.empty? # Don't mess with base URI case when uri.scheme joined_parts = uri.object.merge(path: self.class.normalize_path(uri.path)) when uri. joined_parts[:authority] = uri. joined_parts[:path] = self.class.normalize_path(uri.path) joined_parts[:query] = uri.query when uri.path.to_s.empty? joined_parts[:query] = uri.query if uri.query when uri.path[0,1] == '/' joined_parts[:path] = self.class.normalize_path(uri.path) joined_parts[:query] = uri.query else # Merge path segments from section 5.2.3 # Note that if the path includes no segments, the entire path is removed # > return a string consisting of the reference's path component appended to all but the last segment of the base URI's path (i.e., excluding any characters after the right-most "/" in the base URI path, or excluding the entire base URI path if it does not contain any "/" characters). base_path = path.to_s.include?('/') ? path.to_s.sub(/\/[^\/]*$/, '/') : '' joined_parts[:path] = self.class.normalize_path(base_path + uri.path) joined_parts[:query] = uri.query end joined_parts[:fragment] = uri.fragment end # Return joined URI RDF::URI.new(**joined_parts) end |
#length ⇒ Integer Also known as: size
Returns the string length of this URI.
343 344 345 |
# File 'lib/rdf/model/uri.rb', line 343 def length to_s.length end |
#normalized_authority ⇒ String
Return normalized version of authority, if any
1172 1173 1174 1175 1176 1177 1178 |
# File 'lib/rdf/model/uri.rb', line 1172 def if (userinfo ? normalized_userinfo.to_s + "@" : "") + normalized_host.to_s + (normalized_port ? ":" + normalized_port.to_s : "") end end |
#normalized_fragment ⇒ String
Normalized version of fragment
1146 1147 1148 |
# File 'lib/rdf/model/uri.rb', line 1146 def normalized_fragment normalize_segment(fragment, IFRAGMENT) if fragment end |
#normalized_host ⇒ String
Normalized version of host
1001 1002 1003 1004 |
# File 'lib/rdf/model/uri.rb', line 1001 def normalized_host # Remove trailing '.' characters normalize_segment(host, IHOST, true).chomp('.') if host end |
#normalized_password ⇒ String
Normalized version of password
974 975 976 |
# File 'lib/rdf/model/uri.rb', line 974 def normalized_password URI.encode(CGI.unescape(password), /[^#{IUNRESERVED}|#{SUB_DELIMS}]/).force_encoding(Encoding::UTF_8) if password end |
#normalized_path ⇒ String
Normalized version of path
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 |
# File 'lib/rdf/model/uri.rb', line 1066 def normalized_path segments = path.to_s.split('/', -1) # preserve null segments norm_segs = case when # ipath-abempty segments.map {|s| normalize_segment(s, ISEGMENT)} when segments[0].nil? # ipath-absolute res = [nil] res << normalize_segment(segments[1], ISEGMENT_NZ) if segments.length > 1 res += segments[2..-1].map {|s| normalize_segment(s, ISEGMENT)} if segments.length > 2 res when segments[0].to_s.index(':') # ipath-noscheme res = [] res << normalize_segment(segments[0], ISEGMENT_NZ_NC) res += segments[1..-1].map {|s| normalize_segment(s, ISEGMENT)} if segments.length > 1 res when segments[0] # ipath-rootless # ipath-noscheme res = [] res << normalize_segment(segments[0], ISEGMENT_NZ) res += segments[1..-1].map {|s| normalize_segment(s, ISEGMENT)} if segments.length > 1 res else # Should be empty segments end res = self.class.normalize_path(norm_segs.join("/")) # Special rules for specific protocols having empty paths normalize_segment(res.empty? ? (%w(http https ftp tftp).include?(normalized_scheme) ? '/' : "") : res, IHIER_PART) end |
#normalized_port ⇒ String
Normalized version of port
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 |
# File 'lib/rdf/model/uri.rb', line 1029 def normalized_port if port np = normalize_segment(port.to_s, PORT) if PORT_MAPPING[normalized_scheme] == np.to_i nil else np.to_i end end end |
#normalized_query ⇒ String
Normalized version of query
1122 1123 1124 |
# File 'lib/rdf/model/uri.rb', line 1122 def normalized_query normalize_segment(query, IQUERY) if query end |
#normalized_scheme ⇒ String
Return normalized version of scheme, if any
922 923 924 |
# File 'lib/rdf/model/uri.rb', line 922 def normalized_scheme normalize_segment(scheme.strip, SCHEME, true) if scheme end |
#normalized_user ⇒ String
Normalized version of user
948 949 950 |
# File 'lib/rdf/model/uri.rb', line 948 def normalized_user URI.encode(CGI.unescape(user), /[^#{IUNRESERVED}|#{SUB_DELIMS}]/).force_encoding(Encoding::UTF_8) if user end |
#normalized_userinfo ⇒ String
Normalized version of userinfo
1202 1203 1204 |
# File 'lib/rdf/model/uri.rb', line 1202 def normalized_userinfo normalized_user + (password ? ":#{normalized_password}" : "") if userinfo end |
#object ⇒ Hash{Symbol => String} Also known as: to_h
Returns object representation of this URI, broken into components
861 862 863 |
# File 'lib/rdf/model/uri.rb', line 861 def object @object || @object = parse(@value) end |
#parent ⇒ RDF::URI
Returns a copy of this URI with the path component ascended to the parent directory, if any.
615 616 617 618 619 620 621 622 623 624 625 626 627 |
# File 'lib/rdf/model/uri.rb', line 615 def parent case when root? then nil else require 'pathname' unless defined?(Pathname) if path = Pathname.new(self.path).parent uri = self.dup uri.path = path.to_s uri.path << '/' unless uri.root? uri end end end |
#parent? ⇒ Boolean Also known as: has_parent?
Returns true
if this URI is hierarchical and it's path component isn't equal to /
.
601 602 603 |
# File 'lib/rdf/model/uri.rb', line 601 def parent? !root? end |
#parse(value) ⇒ Object{Symbol => String}
{ Parse a URI into it's components
871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 |
# File 'lib/rdf/model/uri.rb', line 871 def parse(value) value = value.to_s.dup.force_encoding(Encoding::ASCII_8BIT) parts = {} if matchdata = IRI_PARTS.match(value) scheme, , path, query, fragment = matchdata[1..-1] if Gem.win_platform? && scheme && ! && scheme.match?(/^[a-zA-Z]$/) # A drive letter, not a scheme scheme, path = nil, "#{scheme}:#{path}" end userinfo, hostport = .to_s.split('@', 2) hostport, userinfo = userinfo, nil unless hostport user, password = userinfo.to_s.split(':', 2) host, port = hostport.to_s.split(':', 2) parts[:scheme] = (scheme.dup.force_encoding(Encoding::UTF_8) if scheme) parts[:authority] = (.dup.force_encoding(Encoding::UTF_8) if ) parts[:userinfo] = (userinfo.dup.force_encoding(Encoding::UTF_8) if userinfo) parts[:user] = (user.dup.force_encoding(Encoding::UTF_8) if user) parts[:password] = (password.dup.force_encoding(Encoding::UTF_8) if password) parts[:host] = (host.dup.force_encoding(Encoding::UTF_8) if host) parts[:port] = (CGI.unescape(port).to_i if port) parts[:path] = (path.to_s.dup.force_encoding(Encoding::UTF_8) unless path.empty?) parts[:query] = (query[1..-1].dup.force_encoding(Encoding::UTF_8) if query) parts[:fragment] = (fragment[1..-1].dup.force_encoding(Encoding::UTF_8) if fragment) end parts end |
#password ⇒ String
954 955 956 957 958 |
# File 'lib/rdf/model/uri.rb', line 954 def password object.fetch(:password) do @object[:password] = (userinfo.split(':', 2)[1] if userinfo) end end |
#password=(value) ⇒ RDF::URI
Returns self.
963 964 965 966 967 968 969 |
# File 'lib/rdf/model/uri.rb', line 963 def password=(value) object[:password] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value) @object[:userinfo] = format_userinfo("") @object[:authority] = @value = nil self end |
#path ⇒ String
1042 1043 1044 1045 1046 |
# File 'lib/rdf/model/uri.rb', line 1042 def path object.fetch(:path) do nil end end |
#path=(value) ⇒ RDF::URI
Returns self.
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 |
# File 'lib/rdf/model/uri.rb', line 1051 def path=(value) if value # Always lead with a slash value = "/#{value}" if host && value.to_s.match?(/^[^\/]/) object[:path] = value.to_s.dup.force_encoding(Encoding::UTF_8) else object[:path] = nil end @value = nil self end |
#pname(prefixes: nil) ⇒ String
Returns a Prefixed Name (PName) or the full IRI with any reserved characters in the suffix escaped.
684 685 686 687 688 689 690 691 |
# File 'lib/rdf/model/uri.rb', line 684 def pname(prefixes: nil) q = self.qname(prefixes: prefixes) return self.to_s unless q prefix, suffix = q suffix = suffix.to_s.gsub(PN_ESCAPE_CHARS) {|c| "\\#{c}"} if suffix.to_s.match?(PN_ESCAPE_CHARS) [prefix, suffix].join(":") end |
#port ⇒ String
1010 1011 1012 1013 1014 |
# File 'lib/rdf/model/uri.rb', line 1010 def port object.fetch(:port) do @object[:port] = ($1 if @object[:authority] && PORT_FROM_AUTHORITY_RE.match(@object[:authority])) end end |
#port=(value) ⇒ RDF::URI
Returns self.
1019 1020 1021 1022 1023 1024 |
# File 'lib/rdf/model/uri.rb', line 1019 def port=(value) object[:port] = (value.to_s.to_i if value) @object[:authority] = @value = nil self end |
#qname(prefixes: nil) ⇒ Array(Symbol, Symbol)
within this software, the term QName is used to describe the tuple of prefix and suffix for a given IRI, where the prefix identifies some defined vocabulary. This somewhat contrasts with the notion of a Qualified Name from XML, which are a subset of Prefixed Names.
Returns a qualified name (QName) as a tuple of [prefix, suffix]
for this URI based on available vocabularies, if possible.
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 |
# File 'lib/rdf/model/uri.rb', line 644 def qname(prefixes: nil) if prefixes prefixes.each do |prefix, uri| return [prefix, self.to_s[uri.length..-1].to_sym] if self.start_with?(uri) end elsif self.to_s =~ %r([:/#]([^:/#]*)$) local_name = $1 vocab_uri = local_name.empty? ? self.to_s : self.to_s[0...-(local_name.length)] Vocabulary.each do |vocab| if vocab.to_uri == vocab_uri prefix = vocab.equal?(RDF) ? :rdf : vocab.__prefix__ return [prefix, local_name.empty? ? nil : local_name.to_sym] end end else Vocabulary.each do |vocab| vocab_uri = vocab.to_uri if self.start_with?(vocab_uri) prefix = vocab.equal?(RDF) ? :rdf : vocab.__prefix__ local_name = self.to_s[vocab_uri.length..-1] return [prefix, local_name.empty? ? nil : local_name.to_sym] end end end return nil # no QName found end |
#query ⇒ String
1104 1105 1106 1107 1108 |
# File 'lib/rdf/model/uri.rb', line 1104 def query object.fetch(:query) do nil end end |
#query=(value) ⇒ RDF::URI
Returns self.
1113 1114 1115 1116 1117 |
# File 'lib/rdf/model/uri.rb', line 1113 def query=(value) object[:query] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value) @value = nil self end |
#query_values(return_type = Hash) ⇒ Hash, Array
Converts the query component to a Hash value.
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 |
# File 'lib/rdf/model/uri.rb', line 1220 def query_values(return_type=Hash) raise ArgumentError, "Invalid return type. Must be Hash or Array." unless [Hash, Array].include?(return_type) return nil if query.nil? query.to_s.split('&'). inject(return_type == Hash ? {} : []) do |memo,kv| k,v = kv.to_s.split('=', 2) next if k.to_s.empty? k = CGI.unescape(k) v = CGI.unescape(v) if v if return_type == Hash case memo[k] when nil then memo[k] = v when Array then memo[k] << v else memo[k] = [memo[k], v] end else memo << [k, v].compact end memo end end |
#query_values=(value) ⇒ Object
Sets the query component for this URI from a Hash object. An empty Hash or Array will result in an empty query string.
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 |
# File 'lib/rdf/model/uri.rb', line 1267 def query_values=(value) if value.nil? self.query = nil return end value = value.to_hash if value.respond_to?(:to_hash) self.query = case value when Array, Hash value.map do |(k,v)| k = normalize_segment(k.to_s, UNRESERVED) if v.nil? k else Array(v).map do |vv| if vv === TrueClass k else "#{k}=#{normalize_segment(vv.to_s, UNRESERVED)}" end end.join("&") end end else raise TypeError, "Can't convert #{value.class} into Hash." end.join("&") end |
#relative? ⇒ Boolean
A URI is relative when it does not have a scheme
311 |
# File 'lib/rdf/model/uri.rb', line 311 def relative?; !absolute?; end |
#relativize(base_uri) ⇒ RDF::URI
Attempt to make this URI relative to the provided base_uri
. If successful, returns a relative URI, otherwise the original URI
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/rdf/model/uri.rb', line 316 def relativize(base_uri) if self.to_s.start_with?(base_uri.to_s) && %w(# ?).include?(self.to_s[base_uri.to_s.length, 1]) || base_uri.to_s.end_with?("/", "#") && self.to_s.start_with?(base_uri.to_s) return RDF::URI(self.to_s[base_uri.to_s.length..-1]) else # Create a list of parents, for which this IRI may be relative. u = RDF::URI(base_uri) iri_set = u.to_s.end_with?('/') ? [u.to_s] : [] iri_set << u.to_s while (u = u.parent) iri_set.each_with_index do |bb, index| next unless self.to_s.start_with?(bb) rel = "../" * index + self.to_s[bb.length..-1] return rel.empty? ? "./" : rel end end self end |
#request_uri ⇒ String
The HTTP request URI for this URI. This is the path and the query string.
1301 1302 1303 1304 1305 1306 |
# File 'lib/rdf/model/uri.rb', line 1301 def request_uri return nil if absolute? && scheme !~ /^https?$/ res = path.to_s.empty? ? "/" : path res += "?#{self.query}" if self.query return res end |
#root ⇒ RDF::URI
Returns a copy of this URI with the path component set to /
.
583 584 585 586 587 588 589 590 591 |
# File 'lib/rdf/model/uri.rb', line 583 def root if root? self else RDF::URI.new( **object.merge(path: '/'). keep_if {|k, v| %i(scheme authority path).include?(k)}) end end |
#root? ⇒ Boolean
Returns true
if this URI's scheme is not hierarchical,
or its path component is equal to /
.
Protocols not using hierarchical components are always considered
to be at the root.
571 572 573 |
# File 'lib/rdf/model/uri.rb', line 571 def root? !self.hier? || self.path == '/' || self.path.to_s.empty? end |
#scheme ⇒ String
904 905 906 907 908 |
# File 'lib/rdf/model/uri.rb', line 904 def scheme object.fetch(:scheme) do nil end end |
#scheme=(value) ⇒ RDF::URI
Returns self.
913 914 915 916 917 |
# File 'lib/rdf/model/uri.rb', line 913 def scheme=(value) object[:scheme] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value) @value = nil self end |
#to_str ⇒ String Also known as: to_s
Returns the string representation of this URI.
824 |
# File 'lib/rdf/model/uri.rb', line 824 def to_str; value; end |
#to_uri ⇒ RDF::URI
Returns self
.
813 814 815 |
# File 'lib/rdf/model/uri.rb', line 813 def to_uri self end |
#uri? ⇒ Boolean
Returns true
.
258 259 260 |
# File 'lib/rdf/model/uri.rb', line 258 def uri? true end |
#url? ⇒ Boolean
Returns true
if this URI is a URL.
299 300 301 |
# File 'lib/rdf/model/uri.rb', line 299 def url? !urn? end |
#urn? ⇒ Boolean
Returns true
if this URI is a URN.
271 272 273 |
# File 'lib/rdf/model/uri.rb', line 271 def urn? @object ? @object[:scheme] == 'urn' : start_with?('urn:') end |
#user ⇒ String
928 929 930 931 932 |
# File 'lib/rdf/model/uri.rb', line 928 def user object.fetch(:user) do @object[:user] = (userinfo.split(':', 2)[0] if userinfo) end end |
#user=(value) ⇒ RDF::URI
Returns self.
937 938 939 940 941 942 943 |
# File 'lib/rdf/model/uri.rb', line 937 def user=(value) object[:user] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value) @object[:userinfo] = format_userinfo("") @object[:authority] = @value = nil self end |
#userinfo ⇒ Object
Userinfo is a combination of user and password
1182 1183 1184 1185 1186 |
# File 'lib/rdf/model/uri.rb', line 1182 def userinfo object.fetch(:userinfo) { @object[:userinfo] = (format_userinfo("") if @object[:user]) } end |
#userinfo=(value) ⇒ RDF::URI
Returns self.
1191 1192 1193 1194 1195 1196 1197 |
# File 'lib/rdf/model/uri.rb', line 1191 def userinfo=(value) object.delete_if {|k, v| %i(user password authority).include?(k)} object[:userinfo] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value) user; password; @value = nil self end |
#valid? ⇒ Boolean
Determine if the URI is a valid according to RFC3987
Note that RDF URIs syntactically can contain Unicode escapes, which are unencoded in the internal representation. To validate, %-encode specifically excluded characters from IRIREF
355 356 357 |
# File 'lib/rdf/model/uri.rb', line 355 def valid? RDF::URI::IRI.match(to_s) || false end |
#validate! ⇒ RDF::URI
Validates this URI, raising an error if it is invalid.
365 366 367 368 |
# File 'lib/rdf/model/uri.rb', line 365 def validate! raise ArgumentError, "#{to_base.inspect} is not a valid IRI" if invalid? self end |
#value ⇒ String
lexical representation of URI, either absolute or relative
838 839 840 841 842 843 844 845 846 847 |
# File 'lib/rdf/model/uri.rb', line 838 def value return @value if @value @value = [ ("#{scheme}:" if absolute?), ("//#{}" if ), path, ("?#{query}" if query), ("##{fragment}" if fragment) ].compact.join("").freeze end |