module RDF::Vocabulary::Term
A Vocabulary
Term
is a {RDF::Resource} that can also act as an {Enumerable} to generate the RDF
definition of vocabulary terms as defined within the vocabulary definition.
Terms include {Term#attributes} where values a embedded resources, lists or other terms. This allows, for example, navigation of a concept heirarchy.
Term
attributes can also be accessed using {Term#properties} where the attribute values are transformed into different types of {RDF::Value}. Properties can be indexed by key, where a key is defined (See {Term::ATTR_URIs}), absolute URI
, or PName, where the prefix is associated with a loaded vocabulary.
Constants
- ATTR_URIs
Look up URIs for attribute symbols
@return [Hash{Symbol =>
RDF::URI
}]- URI_ATTRs
Look up attribute symbols from URIs
@return [Hash{RDF::URI => Symbol}]
Attributes
Attributes of this vocabulary term, used for finding ‘label` and `comment` and to serialize the term back to RDF
.
Attributes are indexed by symbol. Symbols directly interpreted by a term are the accessors defined for the {RDF::Vocabulary::Term} class, also in {Term::ATTR_URIs}. Other keys are interpreted as absolute URIs or PNames for properties defined on this term.
Symbols which are accessors may also be looked up by their associated URI
.
Values may be Strings, Hash (Map), or Terms, or an Array including a combination of these. A Hash (Map) is used to create a datatype/language map to one or more string values which represent either datatyped literals, or language-tagged literals as interpreted by {#attribute_value}.
In general, this accessor is used internally. The {#properties} method interprets these values as {RDF::Value}.
@note lookup by PName is DEPRECATED and will be removed in a future version.
@example looking up term label
RDF::RDFS.Literal.attributes[:label] #=> "Literal" RDF::RDFS.Literal.attributes[:"rdfs:label"] #=> "Literal" RDF::RDFS.Literal.attributes[RDF::RDFS.label] #=> "Literal" RDF::RDFS.Literal.attributes["http://www.w3.org/2000/01/rdf-schema#label"] #=> "Literal" RDF::RDFS.Literal.attributes[:"http://www.w3.org/2000/01/rdf-schema#label"] #=> "Literal"
@return [Hash{Symbol => String, Term
, Hash{Symbol => String}, Array<String, Term
, Hash{Symbol => String}>}] @see properties
Vocabulary
of this term.
@return [RDF::Vocabulary]
Public Class Methods
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.
@param (see initialize) @return [RDF::URI] an immutable, frozen URI
object
# File lib/rdf/vocabulary.rb, line 1008 def self.intern(str, *args, **options) (URI.cache[(str = str.to_s).to_sym] ||= self.new(str, *args, **options)).freeze end
@overload new(uri, attributes:, vocab:, **options)
@param [URI, String, #to_s] uri @param [Vocabulary] vocab Vocabulary of this term. @param [Hash{Symbol => String,Term,Hash{Symbol=>String,Array<String>},Array<String>}] attributes ({}) Attributes of this vocabulary term, used for finding `label` and `comment` and to serialize the term back to RDF. See {#attributes} and {#properties} for other ways to access. @param [Hash{Symbol => Object}] options Options from {URI#initialize}
@overload new(attributes:, vocab:, **options)
@param [Vocabulary] vocab Vocabulary of this term. @param [Hash{Symbol => String,Term,Hash{Symbol=>String,Array<String>},Array<String>}] attributes ({}) Attributes of this vocabulary term, used for finding `label`, `comment` and other term properties, and to serialize the term back to RDF. See {#attributes} and {#properties} for other ways to access. @param [Hash{Symbol => Object}] options Options from {URI#initialize}
# File lib/rdf/vocabulary.rb, line 950 def self.new(*args, vocab: nil, attributes: {}, **options) klass = if args.first.nil? RDF::Node elsif args.first.is_a?(Hash) args.unshift(nil) RDF::Node elsif args.first.to_s.start_with?("_:") args = args[1..-1].unshift($1) RDF::Node else RDF::URI end # Create default proc on attributes to allow lookup by different key types. attributes = attributes.dup if attributes.frozen? attributes.default_proc = -> (hash, key) do sym = case key when RDF::URI URI_ATTRs.fetch(key, key.to_s.to_sym) when String URI_ATTRs.fetch(RDF::URI(key), key.to_s.to_sym) when Symbol case key.to_s when /^https?:/ # Lookup by associated attribute, or pname URI_ATTRs.fetch(RDF::URI(key.to_s), RDF::URI(key).pname.to_sym) when /:/ uri = RDF::Vocabulary.expand_pname(key) # Lookup by associated attribute or URI URI_ATTRs.fetch(uri, uri.to_s.to_sym) end end hash.fetch(sym, nil) end term = klass.allocate.extend(Term) term.send(:initialize, *args) term.instance_variable_set(:@vocab, vocab) term.instance_variable_set(:@attributes, attributes) term end
Public Instance Methods
Values of an attributes as {RDF::Value}.
Attribute values are returned as either an {RDF::Value} or {Array<RDf::Value} if there is more than one value.
Attribute values which are not already a {RDF::Value} (including strings and symbols) are converted by a heuristic loookup as follows:
-
An {RDF::URI} if it can be turned into a valid
IRI
using {RDF::Vocabulary.expand_pname}. This includes IRIs already in non-relative form. -
A {Hash{Symbol=>String,Array<String>}} is interpreted as a datatype/language map. If the key contains a ‘:’, it is treated as a PName or
IRI
datatype applied to the values. Otherwise, it is treated as a language-tag applied to the values. -
{RDF::Literal::Date} if valid,
-
{RDF::Literal::DateTime} if valid,
-
{RDF::Literal::Integer} if valid,
-
{RDF::Literal::Decimal} if valid,
-
{RDF::Literal::Double} if valid,
-
{RDF::Literal::Boolean} if valid
-
Otherwise, {RDF::Literal} where type may be inferred by the class of the value.
@param [Symbol] prop @return [RDF::Value, Array<RDF::Value>]
# File lib/rdf/vocabulary.rb, line 1111 def attribute_value(prop) values = attributes[prop] return nil if values.nil? values = [values].compact unless values.is_a?(Array) prop_values = values.map do |value| v = value.is_a?(Symbol) ? value.to_s : value value = (RDF::Vocabulary.expand_pname(v) rescue nil) if v.is_a?(String) && v.include?(':') value = value.to_uri if value.respond_to?(:to_uri) value = if value.is_a?(RDF::Value) && value.valid? value elsif value.is_a?(Hash) # type/language map value.inject([]) do |memo, (k,v)| vv = [v] unless v.is_a?(Array) memo << if k.to_s.include?(':') dt = RDF::Vocabulary.expand_pname(v) rescue nil vv.map {|val| RDF::Literal(val, datatype: dt)} else vv.map {|val| RDF::Literal(val, language: k)} end end.flatten.compact.select(&:valid?) else # Use as most appropriate literal [ RDF::Literal::Date, RDF::Literal::DateTime, RDF::Literal::Integer, RDF::Literal::Decimal, RDF::Literal::Double, RDF::Literal::Boolean, RDF::Literal ].inject(nil) do |m, klass| m || begin l = klass.new(v) l if l.valid? end end end end.flatten prop_values.length <= 1 ? prop_values.first : prop_values end
Is this a class term? @return [Boolean]
# File lib/rdf/vocabulary.rb, line 1036 def class? Array(self.type).any? {|t| t.to_s.include?('Class')} end
Is this a class term? @return [Boolean]
# File lib/rdf/vocabulary.rb, line 1050 def datatype? Array(self.type).any? {|t| t.to_s.include?('Datatype')} end
Accessor for ‘schema:domainIncludes` @return [RDF::URI]
# File lib/rdf/vocabulary.rb, line 1223 def domain_includes domainIncludes end
Returns a duplicate copy of ‘self`.
@return [RDF::URI]
# File lib/rdf/vocabulary.rb, line 1016 def dup term = super.extend(Term) term.instance_variable_set(:@vocab, vocab) term.instance_variable_set(:@attributes, attributes) term end
Enumerate each statement constructed from the defined vocabulary terms
If a property value is known to be a {URI}, or expands to a {URI}, the ‘object` is a URI
, otherwise, it will be a {Literal}.
@yield statement @yieldparam [RDF::Statement]
# File lib/rdf/vocabulary.rb, line 1161 def each_statement attributes.keys.each do |p| prop = ATTR_URIs.fetch(p) { RDF::Vocabulary::expand_pname(p)} values = attribute_value(p) values = [values].compact unless values.is_a?(Array) values.each do |value| yield RDF::Statement(self, prop, value) if prop.is_a?(RDF::URI) # Enumerate over value statements, if enumerable if value.is_a?(RDF::Enumerable) || (value.is_a?(Term) && value.node?) value.each_statement {|s| yield s} end end end end
Return an enumerator over {RDF::Statement} defined for this vocabulary. @return [RDF::Enumerable::Enumerator] @see Object#enum_for
# File lib/rdf/vocabulary.rb, line 1181 def enum_for(method = :each_statement, *args) # Ensure that enumerators are, themselves, queryable this = self Enumerable::Enumerator.new do |yielder| this.send(method, *args) {|*y| yielder << (y.length > 1 ? y : y.first)} end end
Is this neither a class, property or datatype term? @return [Boolean]
# File lib/rdf/vocabulary.rb, line 1064 def other? Array(self.type).none? {|t| t.to_s.match?(/(Class|Property|Datatype|Restriction)/)} end
Enumerate attributes with values transformed into {RDF::Value} instances Uses an empty hash with a default_proc which looks up values in attributes. The prevents specific attributes from being evaluated until acessed.
Properties are indexed by symbol. Symbols directly interpreted by a term are the accessors defined for the {RDF::Vocabulary::Term} class, also in {Term::ATTR_URIs}. Other keys are interpreted as absolute URIs or PNames for properties defined on this term.
Symbols which are accessors may also be looked up by their associated URI
.
@note lookup by PName is DEPRECATED and will be removed in a future version.
@example looking up term label
RDF::RDFS.Literal.label #=> RDF::Literal("Literal") RDF::RDFS.Literal.properties[:label] #=> RDF::Literal("Literal") RDF::RDFS.Literal.properties[:"rdfs:label"] #=> RDF::Literal("Literal") RDF::RDFS.Literal.properties[RDF::RDFS.label] #=> RDF::Literal("Literal") RDF::RDFS.Literal.properties["http://www.w3.org/2000/01/rdf-schema#label"] #=> RDF::Literal("Literal") RDF::RDFS.Literal.properties[:"http://www.w3.org/2000/01/rdf-schema#label"] #=> RDF::Literal("Literal")
@return [Hash{Symbol => Array<RDF::Value>}] @see attribute_value
# File lib/rdf/vocabulary.rb, line 1088 def properties Hash.new {|hash, key| attribute_value(key)} end
Is this a class term? @return [Boolean]
# File lib/rdf/vocabulary.rb, line 1043 def property? Array(self.type).any? {|t| t.to_s.include?('Property')} end
Accessor for ‘schema:rangeIncludes` @return [RDF::URI]
# File lib/rdf/vocabulary.rb, line 1229 def range_includes rangeIncludes end
Implement accessor to symbol attributes
# File lib/rdf/vocabulary.rb, line 1199 def respond_to?(method, include_all = false) case method when :comment, :notation, :note, :editorialNote, :definition, :label, :altLabel, :prefLabel, :type, :isDefinedBy true when :subClassOf, :subPropertyOf, :domainIncludes, :rangeIncludes, :equivalentClass, :intersectionOf, :unionOf self.class? when :domain, :range, :equivalentProperty, :inverseOf self.property? when :allValuesFrom, :cardinality, :maxCardinality, :minCardinality, :onProperty, :someValuesFrom self.restriction? when :broader, :exactMatch, :hasTopConcept, :inScheme, :member, :narrower, :related @attributes.key?(method) else super end end
Is this a Restriction term? @return [Boolean]
# File lib/rdf/vocabulary.rb, line 1057 def restriction? Array(self.type).any? {|t| t.to_s.include?('Restriction')} end
Serialize back to a Ruby source initializer. This is used primarily by {RDF::Vocabulary::Writer}.
@param [String] indent @return [String]
# File lib/rdf/vocabulary.rb, line 1238 def to_ruby(indent: "") "term(" + (self.uri? ? self.to_s.inspect + ",\n" : "\n") + "#{indent} " + attributes.keys.sort.map do |k| values = attribute_value(k) values = [values].compact unless values.is_a?(Array) values = values.map do |value| if value.is_a?(Literal) && %w(: comment definition notation note editorialNote).include?(k.to_s) "%(#{value.to_s.gsub('(', '\(').gsub(')', '\)')})" elsif value.node? && value.is_a?(RDF::Vocabulary::Term) "#{value.to_ruby(indent: indent + " ")}" elsif value.is_a?(RDF::Term) "#{value.to_s.inspect}" elsif value.is_a?(RDF::List) list_elements = value.map do |u| if u.uri? "#{u.to_s.inspect}" elsif u.node? && u.respond_to?(:to_ruby) u.to_ruby(indent: indent + " ") else "#{u.to_s.inspect}" end end "list(#{list_elements.join(', ')})" else "#{value.inspect}" end end "#{k.to_s.include?(':') ? k.to_s.inspect : k}: " + (values.length == 1 ? values.first : ('[' + values.join(',') + ']')) end.join(",\n#{indent} ") + "\n#{indent})" end
Determine if the URI
is a valid according to RFC3987
@return [Boolean] ‘true` or `false` @since 0.3.9
# File lib/rdf/vocabulary.rb, line 1028 def valid? # Validate relative to RFC3987 node? || RDF::URI::IRI.match?(to_s) || false end
Protected Instance Methods
Implement accessor to symbol attributes
# File lib/rdf/vocabulary.rb, line 1274 def method_missing(method, *args, &block) case method when :comment, :notation, :note, :editorialNote, :definition attribute_value(method) when :label, :altLabel, :prefLabel # Defaults to URI fragment or path tail begin attribute_value(method) rescue KeyError to_s.split(/[\/\#]/).last end when :type, :subClassOf, :subPropertyOf, :domain, :range, :isDefinedBy, :allValuesFrom, :cardinality, :equivalentClass, :equivalentProperty, :imports, :intersectionOf, :inverseOf, :maxCardinality, :minCardinality, :onProperty, :someValuesFrom, :unionOf, :domainIncludes, :rangeIncludes, :broader, :exactMatch, :hasTopConcept, :inScheme, :member, :narrower, :related # Return value as an Array, unless it is a list case value = attribute_value(method) when Array, RDF::List then value else [value].compact end else super end end