class RDF::Vocabulary

Vocabulary format specification. This can be used to generate a Ruby class definition from a loaded vocabulary.

A {Vocabulary} represents an RDFS or OWL vocabulary.

A {Vocabulary} can also serve as a Domain Specific Language (DSL) for generating an RDF Graph definition for the vocabulary (see {RDF::Vocabulary#to_enum}).

### Defining a vocabulary using the DSL Vocabularies can be defined based on {RDF::Vocabulary} or {RDF::StrictVocabulary} using a simple Domain Specific Language (DSL). Terms of the vocabulary are specified using either `property` or `term` (alias), with the attributes of the term listed in a hash. See {property} for description of the hash.

### Vocabularies:

The following vocabularies are pre-defined for your convenience:

Other vocabularies are defined in the [rdf-vocab](rubygems.org/gems/rdf-vocab) gem

@example Using pre-defined RDF vocabularies

include RDF

RDF.type      #=> RDF::URI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")
RDFS.seeAlso  #=> RDF::URI("http://www.w3.org/2000/01/rdf-schema#seeAlso")
OWL.sameAs    #=> RDF::URI("http://www.w3.org/2002/07/owl#sameAs")
XSD.dateTime  #=> RDF::URI("http://www.w3.org/2001/XMLSchema#dateTime")

@example Using ad-hoc RDF vocabularies

foaf = RDF::Vocabulary.new("http://xmlns.com/foaf/0.1/")
foaf.knows    #=> RDF::URI("http://xmlns.com/foaf/0.1/knows")
foaf[:name]   #=> RDF::URI("http://xmlns.com/foaf/0.1/name")
foaf['mbox']  #=> RDF::URI("http://xmlns.com/foaf/0.1/mbox")

@example Method calls are converted to the typical RDF camelcase convention

foaf = RDF::Vocabulary.new("http://xmlns.com/foaf/0.1/")
foaf.family_name    #=> RDF::URI("http://xmlns.com/foaf/0.1/familyName")
owl.same_as         #=> RDF::URI("http://www.w3.org/2002/07/owl#sameAs")
# []-style access is left as is
foaf['family_name'] #=> RDF::URI("http://xmlns.com/foaf/0.1/family_name")
foaf[:family_name]  #=> RDF::URI("http://xmlns.com/foaf/0.1/family_name")

@example Generating RDF from a vocabulary definition

graph = RDF::Graph.new << RDF::RDFS.to_enum
graph.dump(:ntriples)

@example Defining a simple vocabulary

class EX < RDF::StrictVocabulay("http://example/ns#")
  term :Class,
    label: "My Class",
    comment: "Good to use as an example",
    "rdf:type" => "rdfs:Class",
    "rdfs:subClassOf" => "http://example/SuperClass"
end

@see www.w3.org/TR/curie/ @see en.wikipedia.org/wiki/QName

Public Class Methods

[](property) click to toggle source

Returns the URI for the term `property` in this vocabulary.

@param [#to_s] property @return [RDF::URI]

# File lib/rdf/vocabulary.rb, line 189
def [](property)
  if props.has_key?(property.to_sym)
    props[property.to_sym]
  else
    Term.intern([to_s, property.to_s].join(''), attributes: {vocab: self})
  end
end
__imports__()
Alias for: imports
__prefix__() click to toggle source

Returns a suggested CURIE/PName prefix for this vocabulary class.

@return [Symbol] @since 0.3.0

# File lib/rdf/vocabulary.rb, line 348
def __prefix__
  __name__.split('::').last.downcase.to_sym
end
__properties__()
Alias for: properties
__property__(*args)
Alias for: property
each(&block) click to toggle source

Enumerates known RDF vocabulary classes.

@yield [klass] @yieldparam [Class] klass @return [Enumerator]

# File lib/rdf/vocabulary.rb, line 70
def each(&block)
  if self.equal?(Vocabulary)
    # This is needed since all vocabulary classes are defined using
    # Ruby's autoloading facility, meaning that `@@subclasses` will be
    # empty until each subclass has been touched or require'd.
    RDF::VOCABS.each { |v| require "rdf/vocab/#{v}" unless v == :rdf }
    @@subclasses.select(&:name).each(&block)
  else
    __properties__.each(&block)
  end
end
each_statement(&block) click to toggle source

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 251
def each_statement(&block)
  props.each do |name, subject|
    subject.each_statement(&block)
  end
end
enum_for(method = :each_statement, *args) click to toggle source

Return an enumerator over {RDF::Statement} defined for this vocabulary. @return [RDF::Enumerable::Enumerator] @see Object#enum_for

# File lib/rdf/vocabulary.rb, line 235
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
Also aliased as: to_enum
expand_pname(pname) click to toggle source

Attempt to expand a Compact IRI/PName/QName using loaded vocabularies

@param [String, to_s] pname @return [RDF::URI] @raise [KeyError] if pname suffix not found in identified vocabulary

# File lib/rdf/vocabulary.rb, line 146
def expand_pname(pname)
  prefix, suffix = pname.to_s.split(":", 2)
  if prefix == "rdf"
    RDF[suffix]
  elsif vocab = RDF::Vocabulary.each.detect {|v| v.__name__ && v.__prefix__ == prefix.to_sym}
    suffix.to_s.empty? ? vocab.to_uri : vocab[suffix]
  else
    RDF::Vocabulary.find_term(pname) || RDF::URI(pname)
  end
end
find(uri) click to toggle source

Return the Vocabulary associated with a URI. Allows the trailing '/' or '#' to be excluded

@param [RDF::URI] uri @return [Vocabulary]

# File lib/rdf/vocabulary.rb, line 162
def find(uri)
  RDF::Vocabulary.detect do |v|
    if uri.length >= v.to_uri.length
      RDF::URI(uri).start_with?(v.to_uri)
    else
      v.to_uri.to_s.sub(%r([/#]$), '') == uri.to_s
    end
  end
end
find_term(uri) click to toggle source

Return the Vocabulary term associated with a URI

@param [RDF::URI] uri @return [Vocabulary::Term]

# File lib/rdf/vocabulary.rb, line 177
def find_term(uri)
  uri = RDF::URI(uri)
  return uri if uri.is_a?(Vocabulary::Term)
  vocab = RDF::Vocabulary.detect {|v| uri.start_with?(v.to_uri)}
  vocab[uri.to_s[vocab.to_uri.to_s.length..-1]] if vocab
end
from_graph(graph, url: nil, class_name: nil, extra: nil) click to toggle source

Create a vocabulary from a graph or enumerable

@param [RDF::Enumerable] graph @param [URI, to_s] url @param [String] class_name

The class_name associated with the vocabulary, used for creating the class name of the vocabulary. This will create a new class named with a top-level constant based on `class_name`.

@param [Array<Symbol>, Hash{Symbol => Hash}] extra

Extra terms to add to the vocabulary. In the first form, it is an array of symbols, for which terms are created. In the second, it is a Hash mapping symbols to property attributes, as described in {RDF::Vocabulary.property}.

@return [RDF::Vocabulary] the loaded vocabulary

# File lib/rdf/vocabulary.rb, line 275
def from_graph(graph, url: nil, class_name: nil, extra: nil)
  vocab = if class_name
    Object.const_set(class_name, Class.new(self.create(url)))
  else
    Class.new(self.create(url))
  end

  term_defs = {}
  graph.each do |statement|
    next unless statement.subject.uri? && statement.subject.start_with?(url)
    name = statement.subject.to_s[url.to_s.length..-1] 
    term = (term_defs[name.to_sym] ||= {})
    key = case statement.predicate
    when RDF.type                                     then :type
    when RDF::RDFS.subClassOf                         then :subClassOf
    when RDF::RDFS.subPropertyOf                      then :subPropertyOf
    when RDF::RDFS.range                              then :range
    when RDF::RDFS.domain                             then :domain
    when RDF::RDFS.comment                            then :comment
    when RDF::RDFS.label                              then :label
    when RDF::URI("http://schema.org/inverseOf")      then :inverseOf
    when RDF::URI("http://schema.org/domainIncludes") then :domainIncludes
    when RDF::URI("http://schema.org/rangeIncludes")  then :rangeIncludes
    else                                              statement.predicate.pname
    end

    value = if statement.object.uri?
      statement.object.pname
    elsif statement.object.literal? && (statement.object.language || :en).to_s =~ /^en-?/
      statement.object.to_s
    end

    (term[key] ||= []) << value if value
  end

  # Create extra terms
  term_defs = case extra
  when Array
    extra.inject({}) {|memo, s| memo[s.to_sym] = {label: s.to_s}; memo}.merge(term_defs)
  when Hash
    extra.merge(term_defs)
  else
    term_defs
  end

  term_defs.each do |term, attributes|
    vocab.__property__ term, attributes
  end

  vocab
end
imported_from() click to toggle source

List of vocabularies which import this vocabulary @return [Array<RDF::Vocabulary>]

# File lib/rdf/vocabulary.rb, line 214
def imported_from
  @imported_from ||= begin
    RDF::Vocabulary.select {|v| v.__imports__.include?(self)}
  end
end
imports() click to toggle source

List of vocabularies this vocabulary `owl:imports`

@note the alias {__imports__} guards against `RDF::OWL.imports` returning a term, rather than an array of vocabularies @return [Array<RDF::Vocabulary>]

# File lib/rdf/vocabulary.rb, line 202
def imports
  @imports ||= begin
    Array(self[""].attributes[:"owl:imports"]).map {|pn|find(expand_pname(pn)) rescue nil}.compact
  rescue KeyError
    []
  end
end
Also aliased as: __imports__
inspect() click to toggle source

Returns a developer-friendly representation of this vocabulary class.

@return [String]

# File lib/rdf/vocabulary.rb, line 331
def inspect
  if self == Vocabulary
    self.to_s
  else
    sprintf("%s(%s)", superclass.to_s, to_s)
  end
end
new(uri) click to toggle source

@param [RDF::URI, String, to_s] uri

# File lib/rdf/vocabulary.rb, line 388
def initialize(uri)
  @uri = case uri
    when RDF::URI then uri.to_s
    else RDF::URI.parse(uri.to_s) ? uri.to_s : nil
  end
end
properties() click to toggle source

@return [Array<RDF::URI>] a list of properties in the current vocabulary

# File lib/rdf/vocabulary.rb, line 135
def properties
  props.values
end
Also aliased as: __properties__
property(*args) click to toggle source

@overload property

Returns `property` in the current vocabulary
@return [RDF::Vocabulary::Term]

@overload property(name, options)

Defines a new property or class in the vocabulary.

@param [String, #to_s] name
@param [Hash{Symbol => Object}] options
  Any other values are expected to be String which expands to a {URI} using built-in vocabulary prefixes. The value is a `String` or `Array<String>` which is interpreted according to the `range` of the associated property.
@option options [String, Array<String>] :label
  Shortcut for `rdfs:label`, values are String interpreted as a {Literal}.
@option options [String, Array<String>] :comment
  Shortcut for `rdfs:comment`, values are String interpreted as a {Literal}.
@option options [String, Array<String>] :subClassOf
  Shortcut for `rdfs:subClassOf`, values are String interpreted as a {URI}.
@option options [String, Array<String>] :subPropertyOf
  Shortcut for `rdfs:subPropertyOf`, values are String interpreted as a {URI}.
@option options [String, Array<String>] :domain
  Shortcut for `rdfs:domain`, values are String interpreted as a {URI}.
@option options [String, Array<String>] :range
  Shortcut for `rdfs:range`, values are String interpreted as a {URI}.
@option options [String, Array<String>] :type
  Shortcut for `rdf:type`, values are String interpreted as a {URI}.
@return [RDF::Vocabulary::Term]
# File lib/rdf/vocabulary.rb, line 112
def property(*args)
  case args.length
  when 0
    Term.intern("#{self}property", attributes: {label: "property", vocab: self})
  else
    name, options = args
    options = {label: name.to_s, vocab: self}.merge(options || {})
    uri_str = [to_s, name.to_s].join('')
    Term.cache.delete(uri_str)  # Clear any previous entry
    prop = Term.intern(uri_str, attributes: options)
    props[name.to_sym] = prop
    # Define an accessor, except for problematic properties
    (class << self; self; end).send(:define_method, name) { prop } unless %w(property hash).include?(name.to_s)
    prop
  end
end
Also aliased as: term, __property__
strict?() click to toggle source

Is this a strict vocabulary, or a liberal vocabulary allowing arbitrary properties?

# File lib/rdf/vocabulary.rb, line 84
def strict?; false; end
term(*args)

Alternate use for vocabulary terms, functionally equivalent to {#property}.

Alias for: property
to_enum(method = :each_statement, *args)
Alias for: enum_for
to_iri()

For IRI compatibility

Alias for: to_uri
to_s() click to toggle source

Returns a string representation of this vocabulary class.

@return [String]

Calls superclass method
# File lib/rdf/vocabulary.rb, line 261
def to_s
  @@uris.has_key?(self) ? @@uris[self].to_s : super
end
to_uri() click to toggle source

Returns the base URI for this vocabulary class.

@return [RDF::URI]

# File lib/rdf/vocabulary.rb, line 224
def to_uri
  RDF::URI.intern(to_s)
end
Also aliased as: to_iri

Protected Class Methods

camelize(str) click to toggle source
# File lib/rdf/vocabulary.rb, line 449
def self.camelize(str)
  str.split('_').inject([]) do |buffer, e| 
    buffer.push(buffer.empty? ? e : e.capitalize)
  end.join
end
create(uri) click to toggle source
# File lib/rdf/vocabulary.rb, line 433
def self.create(uri) # @private
  @@uri = uri
  self
end
inherited(subclass) click to toggle source
Calls superclass method
# File lib/rdf/vocabulary.rb, line 353
def inherited(subclass) # @private
  unless @@uri.nil?
    @@subclasses << subclass unless %w(http://www.w3.org/1999/02/22-rdf-syntax-ns#).include?(@@uri)
    subclass.send(:private_class_method, :new)
    @@uris[subclass] = @@uri
    @@uri = nil
  end
  super
end
method_missing(property, *args, &block) click to toggle source
Calls superclass method
# File lib/rdf/vocabulary.rb, line 363
def method_missing(property, *args, &block)
  property = RDF::Vocabulary.camelize(property.to_s)
  if %w(to_ary).include?(property.to_s)
    super
  elsif args.empty? && !to_s.empty?
    Term.intern([to_s, property.to_s].join(''), attributes: {vocab: self})
  else
    super
  end
end

Private Class Methods

props() click to toggle source
# File lib/rdf/vocabulary.rb, line 376
def props; @properties ||= {}; end

Public Instance Methods

[](property) click to toggle source

Returns the URI for the term `property` in this vocabulary.

@param [#to_s] property @return [URI]

# File lib/rdf/vocabulary.rb, line 400
def [](property)
  Term.intern([to_s, property.to_s].join(''), attributes: {vocab: self.class})
end
inspect() click to toggle source

Returns a developer-friendly representation of this vocabulary.

@return [String]

# File lib/rdf/vocabulary.rb, line 427
def inspect
  sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)
end
to_iri()

For IRI compatibility

Alias for: to_uri
to_s() click to toggle source

Returns a string representation of this vocabulary.

@return [String]

# File lib/rdf/vocabulary.rb, line 419
def to_s
  @uri.to_s
end
to_uri() click to toggle source

Returns the base URI for this vocabulary.

@return [URI]

# File lib/rdf/vocabulary.rb, line 408
def to_uri
  RDF::URI.intern(to_s)
end
Also aliased as: to_iri

Protected Instance Methods

method_missing(property, *args, &block) click to toggle source
Calls superclass method
# File lib/rdf/vocabulary.rb, line 438
def method_missing(property, *args, &block)
  property = self.class.camelize(property.to_s)
  if %w(to_ary).include?(property.to_s)
    super
  elsif args.empty?
    self[property]
  else
    super
  end
end