Skip to content

Commit

Permalink
Finish 3.2.3
Browse files Browse the repository at this point in the history
  • Loading branch information
gkellogg committed Jan 17, 2022
2 parents 78a0dbc + 8981cdd commit ad71b66
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 61 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
ruby:
- 2.6
- 2.7
- 3.0
- "3.0"
- 3.1
- ruby-head
- jruby
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2.2
3.2.3
26 changes: 23 additions & 3 deletions lib/rdf/model/uri.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ class URI
tag tel turn turns tv urn javascript
).freeze

# Characters in a PName which must be escaped
PN_ESCAPE_CHARS = /[~\.\-!\$&'\(\)\*\+,;=\/\?\#@%_]/.freeze
PN_ESCAPES = /\\#{PN_ESCAPE_CHARS}/.freeze

##
# Cache size may be set through {RDF.config} using `uri_cache_size`.
#
Expand Down Expand Up @@ -627,10 +631,14 @@ def parent
# RDF::URI('http://www.w3.org/2000/01/rdf-schema#').qname #=> [:rdfs, nil]
# RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').qname #=> [:rdfs, :label]
# RDF::RDFS.label.qname #=> [:rdfs, :label]
# RDF::Vocab::DC.title.qname(
# prefixes: {dcterms: 'http://purl.org/dc/terms/'}) #=> [:dcterms, :title]
#
# @note 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](https://www.w3.org/TR/2006/REC-xml-names11-20060816/#ns-qualnames) from XML, which are a subset of Prefixed Names.
#
# @param [Hash{Symbol => String}] prefixes
# Explicit set of prefixes to look for matches, defaults to loaded vocabularies.
# @return [Array(Symbol, Symbol)] or `nil` if no QName found
# @return [Array(Symbol, Symbol)] or `nil` if no QName found. The suffix component will not have [reserved characters](https://www.w3.org/TR/turtle/#reserved) escaped.
def qname(prefixes: nil)
if prefixes
prefixes.each do |prefix, uri|
Expand Down Expand Up @@ -659,13 +667,25 @@ def qname(prefixes: nil)
end

##
# Returns a string version of the QName or the full IRI
# Returns a Prefixed Name (PName) or the full IRI with any [reserved characters](https://www.w3.org/TR/turtle/#reserved) in the suffix escaped.
#
# @example Using a custom prefix for creating a PNname.
# RDF::URI('http://purl.org/dc/terms/creator').
# pname(prefixes: {dcterms: 'http://purl.org/dc/terms/'})
# #=> "dcterms:creator"
#
# @param [Hash{Symbol => String}] prefixes
# Explicit set of prefixes to look for matches, defaults to loaded vocabularies.
# @return [String] or `nil`
# @see #qname
# @see https://www.w3.org/TR/rdf-sparql-query/#prefNames
def pname(prefixes: nil)
(q = self.qname(prefixes: prefixes)) ? q.join(":") : to_s
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

##
Expand Down
44 changes: 34 additions & 10 deletions lib/rdf/vocabulary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ module RDF
# "rdfs:subClassOf" => "http://example/SuperClass"
# end
#
# @see http://www.w3.org/TR/curie/
# @see http://en.wikipedia.org/wiki/QName
# @see https://www.w3.org/TR/rdf-sparql-query/#prefNames
class Vocabulary
extend ::Enumerable

Expand Down Expand Up @@ -379,15 +378,20 @@ def properties
alias_method :__properties__, :properties

##
# Attempt to expand a Compact IRI/PName/QName using loaded vocabularies
# Attempt to expand a Compact IRI/PName using loaded vocabularies
#
# @param [String, #to_s] pname
# The local-part of the PName will will have [reserved character escapes](https://www.w3.org/TR/turtle/#reserved) unescaped.
# @return [Term]
# @raise [KeyError] if pname suffix not found in identified vocabulary
# @raise [KeyError] if pname suffix not found in identified vocabulary.
# @raise [ArgumentError] if resulting URI is not valid
def expand_pname(pname)
return pname unless pname.is_a?(String) || pname.is_a?(Symbol)
prefix, suffix = pname.to_s.split(":", 2)
# Unescape escaped PN_ESCAPE_CHARS
if suffix.match?(/\\#{RDF::URI::PN_ESCAPE_CHARS}/)
suffix = suffix.gsub(RDF::URI::PN_ESCAPES) {|matched| matched[1..-1]}
end
if prefix == "rdf"
RDF[suffix]
elsif vocab_detail = RDF::Vocabulary.vocab_map[prefix.to_sym]
Expand Down Expand Up @@ -417,9 +421,10 @@ def find(uri)
end

##
# Return the Vocabulary term associated with a URI
# Return the Vocabulary term associated with a URI
#
# @param [RDF::URI] uri
# @param [RDF::URI, String] uri
# If `uri` has is a pname in a locded vocabulary, the suffix portion of the PName will have escape characters unescaped before resolving against the vocabulary.
# @return [Vocabulary::Term]
def find_term(uri)
uri = RDF::URI(uri)
Expand All @@ -428,7 +433,8 @@ def find_term(uri)
if vocab.ontology == uri
vocab.ontology
else
vocab[uri.to_s[vocab.to_uri.to_s.length..-1].to_s]
suffix = uri.to_s[vocab.to_uri.to_s.length..-1].to_s
vocab[suffix]
end
end
end
Expand Down Expand Up @@ -574,7 +580,6 @@ def from_graph(graph, url: nil, class_name: nil, extra: nil)
term_defs
end

#require 'byebug'; byebug
# Pass over embedded_defs with anonymous references, once
embedded_defs.each do |term, attributes|
attributes.each do |ak, avs|
Expand Down Expand Up @@ -643,12 +648,31 @@ def inspect
alias_method :__name__, :name

##
# Returns a suggested CURIE/PName prefix for this vocabulary class.
# Returns a suggested vocabulary prefix for this vocabulary class.
#
# @return [Symbol]
# @since 0.3.0
def __prefix__
__name__.split('::').last.downcase.to_sym
instance_variable_defined?(:@__prefix__) ?
@__prefix__ :
__name__.split('::').last.downcase.to_sym
end

##
# Sets the vocabulary prefix to use for this vocabulary..
#
# @example Overriding a standard vocabulary prefix.
# RDF::Vocab::DC.__prefix__ = :dcterms
# RDF::Vocab::DC.title.pname #=> 'dcterms:title'
#
# @param [Symbol] prefix
# @return [Symbol]
# @since 3.2.3
def __prefix__=(prefix)
params = RDF::Vocabulary.vocab_map[__prefix__]
@__prefix__ = prefix.to_sym
RDF::Vocabulary.register(@__prefix__, self, **params)
@__prefix__
end

protected
Expand Down
100 changes: 64 additions & 36 deletions spec/model_uri_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,70 @@
end
end

describe "#qname" do
it "#qname" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').qname).to eql [:rdfs, nil]
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').qname).to eql [:rdfs, :label]
expect(RDF::RDFS.label.qname).to eql [:rdfs, :label]
end

it "#qname with empty prefixes" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').qname(prefixes: {})).to be_nil
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').qname(prefixes: {})).to be_nil
expect(RDF::RDFS.label.qname(prefixes: {})).to be_nil
end

it "#qname with explicit prefixes" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').qname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql [:rdfs, :""]
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').qname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql [:rdfs, :label]
expect(RDF::RDFS.label.qname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql [:rdfs, :label]
end
end

describe "#pname" do
it "#pname" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').pname).to eql 'rdfs:'
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').pname).to eql 'rdfs:label'
expect(RDF::RDFS.label.pname).to eql 'rdfs:label'
end

it "#pname with empty prefixes" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').pname(prefixes: {})).to eq 'http://www.w3.org/2000/01/rdf-schema#'
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').pname(prefixes: {})).to eq 'http://www.w3.org/2000/01/rdf-schema#label'
expect(RDF::RDFS.label.pname(prefixes: {})).to eq 'http://www.w3.org/2000/01/rdf-schema#label'
end

it "#pname with explicit prefixes" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').pname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql 'rdfs:'
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').pname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql 'rdfs:label'
expect(RDF::RDFS.label.pname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql 'rdfs:label'
end

context "escapes" do
{
"http://example.org/c-" => 'ex:c\-',
"http://example.org/c!" => 'ex:c\!',
"http://example.org/c$" => 'ex:c\$',
"http://example.org/c&" => 'ex:c\&',
"http://example.org/c'" => "ex:c\\'",
"http://example.org/c()" => 'ex:c\(\)',
"http://example.org/c*+" => 'ex:c\*\+',
"http://example.org/c;=" => 'ex:c\;\=',
"http://example.org/c/#" => 'ex:c\/\#',
"http://example.org/c@_" => 'ex:c\@\_',
"http://example.org/c:d?" => 'ex:c:d\?',
"http://example.org/c~z." => 'ex:c\~z\.',
}.each do |orig, result|
it "#{orig} => #{result}" do
uri = RDF::URI(orig)
pname = uri.pname(prefixes: {ex: "http://example.org/"})
expect(uri).to be_valid
expect(pname).to eql result
end
end
end
end

context "Examples" do
it "Creating a URI reference (1)" do
expect(RDF::URI.new("https://rubygems.org/gems/rdf")).to be_a_uri
Expand Down Expand Up @@ -956,42 +1020,6 @@
expect(RDF::URI('http://example.org/path/').parent).to eql RDF::URI('http://example.org/')
end

it "#qname" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').qname).to eql [:rdfs, nil]
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').qname).to eql [:rdfs, :label]
expect(RDF::RDFS.label.qname).to eql [:rdfs, :label]
end

it "#qname with empty prefixes" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').qname(prefixes: {})).to be_nil
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').qname(prefixes: {})).to be_nil
expect(RDF::RDFS.label.qname(prefixes: {})).to be_nil
end

it "#qname with explicit prefixes" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').qname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql [:rdfs, :""]
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').qname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql [:rdfs, :label]
expect(RDF::RDFS.label.qname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql [:rdfs, :label]
end

it "#pname" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').pname).to eql 'rdfs:'
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').pname).to eql 'rdfs:label'
expect(RDF::RDFS.label.pname).to eql 'rdfs:label'
end

it "#pname with empty prefixes" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').pname(prefixes: {})).to eq 'http://www.w3.org/2000/01/rdf-schema#'
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').pname(prefixes: {})).to eq 'http://www.w3.org/2000/01/rdf-schema#label'
expect(RDF::RDFS.label.pname(prefixes: {})).to eq 'http://www.w3.org/2000/01/rdf-schema#label'
end

it "#pname with explicit prefixes" do
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#').pname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql 'rdfs:'
expect(RDF::URI('http://www.w3.org/2000/01/rdf-schema#label').pname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql 'rdfs:label'
expect(RDF::RDFS.label.pname(prefixes: {rdfs: 'http://www.w3.org/2000/01/rdf-schema#'})).to eql 'rdfs:label'
end

it "#start_with?" do
expect(RDF::URI('http://example.org/')).to be_start_with('http')
expect(RDF::URI('http://example.org/')).not_to be_start_with('ftp')
Expand Down
58 changes: 48 additions & 10 deletions spec/vocabulary_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,39 @@
end
end

describe ".expand_pname" do
{
"rdfs:" => RDF::RDFS.to_uri,
"rdfs:label" => RDF::RDFS.label,
RDF::Value => RDF::Value,
}.each do |orig, result|
it "#{orig} => #{result}" do
expect(RDF::Vocabulary.expand_pname(orig)).to eql result
end
end

context "unescapes" do
{
'xsd:c\-' => "http://www.w3.org/2001/XMLSchema#c-",
'xsd:c\!' => "http://www.w3.org/2001/XMLSchema#c!",
'xsd:c\$' => "http://www.w3.org/2001/XMLSchema#c$",
'xsd:c\&' => "http://www.w3.org/2001/XMLSchema#c&",
"xsd:c\\'" => "http://www.w3.org/2001/XMLSchema#c'",
'xsd:c\(\)' => "http://www.w3.org/2001/XMLSchema#c()",
'xsd:c\*\+' => "http://www.w3.org/2001/XMLSchema#c*+",
'xsd:c\;\=' => "http://www.w3.org/2001/XMLSchema#c;=",
'xsd:c\/\#' => "http://www.w3.org/2001/XMLSchema#c/#",
'xsd:c\@\_' => "http://www.w3.org/2001/XMLSchema#c@_",
'xsd:c:d\?' => "http://www.w3.org/2001/XMLSchema#c:d?",
'xsd:c\~z\.' => "http://www.w3.org/2001/XMLSchema#c~z.",
}.each do |pname, result|
it "#{pname} => #{result}" do
expect(RDF::Vocabulary.expand_pname(pname).to_s).to eql result
end
end
end
end

describe ".limit_vocabs" do
before { RDF::Vocabulary.limit_vocabs(:rdf, :rdfs)}
after { RDF::Vocabulary.limit_vocabs()}
Expand Down Expand Up @@ -115,7 +148,7 @@
if cls
expect(RDF::Vocabulary.find(term)).to eql cls
else
expect(RDF::Vocabulary.find_term(term)).to be_nil
expect(RDF::Vocabulary.find(term)).to be_nil
end
end
end
Expand Down Expand Up @@ -276,12 +309,6 @@
end
end

it "should expand PName for vocabulary" do
expect(RDF::Vocabulary.expand_pname("rdfs:")).to eql RDF::RDFS.to_uri
expect(RDF::Vocabulary.expand_pname("rdfs:label")).to eql RDF::RDFS.label
expect(RDF::Vocabulary.expand_pname(RDF::Value)).to eql RDF::Value
end

it "should support Web Ontology Language (OWL)" do
expect(RDF::OWL).to be_a_vocabulary("http://www.w3.org/2002/07/owl#")
expect(RDF::OWL).to have_properties("http://www.w3.org/2002/07/owl#", %w(allValuesFrom annotatedProperty annotatedSource annotatedTarget assertionProperty backwardCompatibleWith bottomDataProperty bottomObjectProperty cardinality complementOf datatypeComplementOf deprecated differentFrom disjointUnionOf disjointWith distinctMembers equivalentClass equivalentProperty hasKey hasSelf hasValue imports incompatibleWith intersectionOf inverseOf maxCardinality maxQualifiedCardinality members minCardinality minQualifiedCardinality onClass onDataRange onDatatype onProperties onProperty oneOf priorVersion propertyChainAxiom propertyDisjointWith qualifiedCardinality sameAs someValuesFrom sourceIndividual targetIndividual targetValue topDataProperty topObjectProperty unionOf versionIRI versionInfo withRestrictions))
Expand Down Expand Up @@ -909,11 +936,22 @@
end

context 'Vocabs outside of the RDF::Vocab namespace' do
uri = 'urn:x-bogus:test-vocab:'
TestVocab = Class.new RDF::Vocabulary(uri)
RDF::Vocabulary.register :testvocab, TestVocab
let(:uri) {'urn:x-bogus:test-vocab:'}
before(:all) {
TestVocab = Class.new RDF::Vocabulary('urn:x-bogus:test-vocab:')
RDF::Vocabulary.register :testvocab, TestVocab
}

it 'correctly expands the pname of an arbitrary class' do
expect(RDF::Vocabulary.expand_pname('testvocab:test')).to eq(TestVocab.test)
end

it 'uses custom prefix' do
TestVocab.__prefix__ = :tv
term = RDF::Vocabulary.expand_pname('tv:test')
expect(term).to eq(TestVocab.test)
expect(term.pname).to eq 'tv:test'
expect(RDF::Vocabulary.find_term(term.to_s)).to eq TestVocab.test
end
end
end

0 comments on commit ad71b66

Please sign in to comment.