diff --git a/lib/rdf.ex b/lib/rdf.ex
index 5e61112..8e93f11 100644
--- a/lib/rdf.ex
+++ b/lib/rdf.ex
@@ -1,8 +1,8 @@
defmodule RDF do
- alias RDF.{Vocabulary, Literal, BlankNode, Triple}
+ alias RDF.{Namespace, Literal, BlankNode, Triple}
@doc """
- Generator function for URIs from strings or term atoms of a `RDF.Vocabulary`.
+ Generator function for URIs from strings or term atoms of a `RDF.Namespace`.
## Examples
@@ -10,7 +10,7 @@ defmodule RDF do
%URI{authority: "www.example.com", fragment: nil, host: "www.example.com",
path: "/foo", port: 80, query: nil, scheme: "http", userinfo: nil}
- iex> RDF.uri(RDF.RDFS.Class)
+ iex> RDF.uri(RDF.NS.RDFS.Class)
%URI{authority: "www.w3.org", fragment: "Class", host: "www.w3.org",
path: "/2000/01/rdf-schema", port: 80, query: nil, scheme: "http",
userinfo: nil}
@@ -19,7 +19,8 @@ defmodule RDF do
** (RDF.InvalidURIError) string "not a uri" is not a valid URI
"""
@spec uri(URI.t | binary | atom) :: URI.t
- def uri(atom) when is_atom(atom), do: Vocabulary.__uri__(atom)
+ def uri(atom) when is_atom(atom), do: Namespace.resolve_term(atom)
+
def uri(string) do
parsed_uri = URI.parse(string)
if uri?(parsed_uri) do
@@ -114,7 +115,7 @@ defmodule RDF do
"""
def resource?(value)
def resource?(%URI{}), do: true
- def resource?(atom) when is_atom(atom), do: resource?(Vocabulary.__uri__(atom))
+ def resource?(atom) when is_atom(atom), do: resource?(Namespace.resolve_term(atom))
def resource?(%BlankNode{}), do: true
def resource?(_), do: false
diff --git a/lib/rdf/exceptions.ex b/lib/rdf/exceptions.ex
index 58cfd6d..13eae70 100644
--- a/lib/rdf/exceptions.ex
+++ b/lib/rdf/exceptions.ex
@@ -31,15 +31,11 @@ defmodule RDF.Quad.InvalidGraphContextError do
end
-defmodule RDF.Vocabulary.InvalidBaseURIError do
+defmodule RDF.Namespace.InvalidVocabBaseURIError do
defexception [:message]
end
-defmodule RDF.Vocabulary.UndefinedTermError do
- defexception [:message]
-end
-
-defmodule RDF.Vocabulary.InvalidTermError do
+defmodule RDF.Namespace.UndefinedTermError do
defexception [:message]
end
diff --git a/lib/rdf/literal.ex b/lib/rdf/literal.ex
index 4ac3f5f..2bcd423 100644
--- a/lib/rdf/literal.ex
+++ b/lib/rdf/literal.ex
@@ -6,7 +6,64 @@ defmodule RDF.Literal do
@type t :: module
- alias RDF.{XSD, RDFS}
+ # Since the capability of RDF.Vocabulary.Namespaces requires the compilation
+ # of the RDF.NTriples.Reader and the RDF.NTriples.Reader depends on RDF.Literals,
+ # we can't define the XSD namespace in RDF.NS.
+ defmodule NS do
+ @moduledoc false
+ @vocabdoc false
+ use RDF.Vocabulary.Namespace
+ defvocab XSD,
+ base_uri: "http://www.w3.org/2001/XMLSchema#",
+ terms: ~w[
+ string
+ normalizedString
+ token
+ language
+ Name
+ NCName
+ ID
+ IDREF
+ IDREFS
+ ENTITY
+ ENTITIES
+ NMTOKEN
+ NMTOKENS
+ boolean
+ float
+ double
+ decimal
+ integer
+ long
+ int
+ short
+ byte
+ nonPositiveInteger
+ negativeInteger
+ nonNegativeInteger
+ positiveInteger
+ unsignedLong
+ unsignedInt
+ unsignedShort
+ unsignedByte
+ duration
+ dateTime
+ time
+ date
+ gYearMonth
+ gYear
+ gMonthDay
+ gDay
+ gMonth
+ base64Binary
+ hexBinary
+ anyURI
+ QName
+ NOTATION
+ ]
+ end
+ alias NS.XSD
+
@doc """
Creates a new `RDF.Literal` of the given value and tries to infer an appropriate XSD datatype.
@@ -28,7 +85,7 @@ defmodule RDF.Literal do
# Examples
iex> RDF.Literal.new(42)
- %RDF.Literal{value: 42, language: nil, datatype: RDF.uri(RDF.XSD.integer)}
+ %RDF.Literal{value: 42, language: nil, datatype: RDF.uri(XSD.integer)}
"""
def new(value)
diff --git a/lib/rdf/namespace.ex b/lib/rdf/namespace.ex
new file mode 100644
index 0000000..111806c
--- /dev/null
+++ b/lib/rdf/namespace.ex
@@ -0,0 +1,60 @@
+defmodule RDF.Namespace do
+ @moduledoc """
+ A `RDF.Namespace` is a module ...
+
+ TODO: Rewrite this
+
+ A `RDF.Namespace` is a collection of URIs and serves as a namespace for its
+ elements, called terms. The terms can be accessed by qualification on the
+ resp. namespace module.
+
+ ## Using a `RDF.Namespace`
+
+ There are two types of terms in a `RDF.Namespace`, which are resolved
+ differently:
+
+ 1. Lowercased terms (usually used for RDF properties, but this is not
+ enforced) are represented as functions on a Vocabulary module and return the
+ URI directly.
+ 2. Capitalized terms are by standard Elixir semantics modules names, i.e.
+ atoms. In all places in RDF.ex, where an URI is expected, you can use atoms
+ qualified with a `RDF.Namespace` directly, but if you want to resolve it
+ manually, you can pass the `RDF.Namespace` qualified atom to `RDF.uri`.
+
+ Examples:
+
+ iex> RDF.NS.RDFS.subClassOf
+ %URI{authority: "www.w3.org", fragment: "subClassOf", host: "www.w3.org",
+ path: "/2000/01/rdf-schema", port: 80, query: nil, scheme: "http",
+ userinfo: nil}
+ iex> RDF.NS.RDFS.Class
+ RDF.NS.RDFS.Class
+ iex> RDF.uri(RDF.NS.RDFS.Class)
+ %URI{authority: "www.w3.org", fragment: "Class", host: "www.w3.org",
+ path: "/2000/01/rdf-schema", port: 80, query: nil, scheme: "http",
+ userinfo: nil}
+ iex> alias RDF.NS.RDFS
+ iex> RDF.triple(RDFS.Class, RDFS.subClassOf, RDFS.Resource)
+ {RDF.uri(RDFS.Class), RDF.uri(RDFS.subClassOf), RDF.uri(RDFS.Resource)}
+
+ """
+
+ @callback __resolve_term__(atom) :: URI.t
+
+ @callback __terms__() :: [atom]
+
+
+ def resolve_term(expr)
+
+ def resolve_term(uri = %URI{}), do: uri
+
+ def resolve_term(namespaced_atom) when is_atom(namespaced_atom) do
+ {term, namespace} =
+ namespaced_atom
+ |> Module.split
+ |> List.pop_at(-1)
+ {term, namespace} = {String.to_atom(term), Module.concat(namespace)}
+ namespace.__resolve_term__(term)
+ end
+
+end
diff --git a/lib/rdf/ns.ex b/lib/rdf/ns.ex
new file mode 100644
index 0000000..c7fe948
--- /dev/null
+++ b/lib/rdf/ns.ex
@@ -0,0 +1,49 @@
+defmodule RDF.NS do
+ use RDF.Vocabulary.Namespace
+
+ @vocabdoc """
+ The XML Schema datatypes vocabulary.
+
+ See
+ """
+ defvocab XSD,
+ base_uri: "http://www.w3.org/2001/XMLSchema#",
+ terms: RDF.Literal.NS.XSD.__terms__
+
+ @vocabdoc """
+ The RDF vocabulary.
+
+ See
+ """
+ defvocab RDF,
+ base_uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+ file: "rdf.nt"
+
+ @vocabdoc """
+ The RDFS vocabulary.
+
+ See
+ """
+ defvocab RDFS,
+ base_uri: "http://www.w3.org/2000/01/rdf-schema#",
+ file: "rdfs.nt"
+
+ @vocabdoc """
+ The OWL vocabulary.
+
+ See
+ """
+ defvocab OWL,
+ base_uri: "http://www.w3.org/2002/07/owl#",
+ file: "owl.nt"
+
+ @vocabdoc """
+ The SKOS vocabulary.
+
+ See
+ """
+ defvocab SKOS,
+ base_uri: "http://www.w3.org/2004/02/skos/core#",
+ file: "skos.nt"
+
+end
diff --git a/lib/rdf/vocabularies/rdfs.ex b/lib/rdf/vocabularies/rdfs.ex
deleted file mode 100644
index c6c166a..0000000
--- a/lib/rdf/vocabularies/rdfs.ex
+++ /dev/null
@@ -1,11 +0,0 @@
-defmodule RDF.RDFS do
- @moduledoc """
- The RDFS vocabulary.
-
- See
- """
-
- # TODO: This should be a strict vocabulary and loaded from a file.
- use RDF.Vocabulary, base_uri: "http://www.w3.org/2000/01/rdf-schema#"
-
-end
diff --git a/lib/rdf/vocabularies/xsd.ex b/lib/rdf/vocabularies/xsd.ex
deleted file mode 100644
index 5c3d31c..0000000
--- a/lib/rdf/vocabularies/xsd.ex
+++ /dev/null
@@ -1,55 +0,0 @@
-defmodule RDF.XSD do
- @moduledoc """
- The XML Schema datatypes vocabulary.
-
- See
- """
-
- # TODO: This should be a strict vocabulary and loaded from a file.
- use RDF.Vocabulary, base_uri: "http://www.w3.org/2001/XMLSchema#"
-
- defuri :string
- defuri :normalizedString
- defuri :token
- defuri :language
- defuri :Name
- defuri :NCName
- defuri :ID
- defuri :IDREF
- defuri :IDREFS
- defuri :ENTITY
- defuri :ENTITIES
- defuri :NMTOKEN
- defuri :NMTOKENS
- defuri :boolean
- defuri :float
- defuri :double
- defuri :decimal
- defuri :integer
- defuri :long
- defuri :int
- defuri :short
- defuri :byte
- defuri :nonPositiveInteger
- defuri :negativeInteger
- defuri :nonNegativeInteger
- defuri :positiveInteger
- defuri :unsignedLong
- defuri :unsignedInt
- defuri :unsignedShort
- defuri :unsignedByte
- defuri :duration
- defuri :dateTime
- defuri :time
- defuri :date
- defuri :gYearMonth
- defuri :gYear
- defuri :gMonthDay
- defuri :gDay
- defuri :gMonth
- defuri :base64Binary
- defuri :hexBinary
- defuri :anyURI
- defuri :QName
- defuri :NOTATION
-end
diff --git a/lib/rdf/vocabulary.ex b/lib/rdf/vocabulary.ex
deleted file mode 100644
index 32f97b6..0000000
--- a/lib/rdf/vocabulary.ex
+++ /dev/null
@@ -1,162 +0,0 @@
-defmodule RDF.Vocabulary do # or RDF.URI.Namespace?
- @moduledoc """
- Defines a RDF Vocabulary.
-
- A `RDF.Vocabulary` is a collection of URIs and serves as a namespace for its
- elements, called terms. The terms can be accessed by qualification on the
- resp. Vocabulary module.
-
- ## Using a Vocabulary
-
- There are two types of terms in a `RDF.Vocabulary`, which are resolved
- differently:
-
- 1. Lowercased terms (usually used for RDF properties, but this is not
- enforced) are represented as functions on a Vocabulary module and return the
- URI directly.
- 2. Uppercased terms are by standard Elixir semantics modules names, i.e.
- atoms. In many in RDF.ex, where an URI is expected, you can use atoms
- qualified with a `RDF.Vocabulary` directly, but if you want to resolve it
- manually, you can pass the `RDF.Vocabulary` qualified atom to `RDF.uri`.
-
- Examples:
-
- iex> RDF.RDFS.subClassOf
- %URI{authority: "www.w3.org", fragment: "subClassOf", host: "www.w3.org",
- path: "/2000/01/rdf-schema", port: 80, query: nil, scheme: "http",
- userinfo: nil}
- iex> RDF.RDFS.Class
- RDF.RDFS.Class
- iex> RDF.uri(RDF.RDFS.Class)
- %URI{authority: "www.w3.org", fragment: "Class", host: "www.w3.org",
- path: "/2000/01/rdf-schema", port: 80, query: nil, scheme: "http",
- userinfo: nil}
- iex> RDF.triple(RDF.RDFS.Class, RDF.RDFS.subClass, RDF.RDFS.Resource)
- {RDF.uri(RDF.RDFS.Class), RDF.uri(RDF.RDFS.subClass), RDF.uri(RDF.RDFS.Resource)}
-
-
- ## Strict vocabularies
-
- What is a strict vocabulary and why should I use them over non-strict
- vocabularies and define all terms ...
-
-
- ## Defining a vocabulary
-
- There are two basic ways to define a vocabulary:
-
- 1. You can define all terms manually.
- 2. You can load all terms from a specified namespace in a given dataset or
- graph.
-
- Either way, you'll first have to define a new module for your vocabulary:
-
- defmodule ExampleVocab do
- use RDF.Vocabulary, base_uri: "http://www.example.com/ns/"
-
- # Your term definitions
- end
-
- The `base_uri` argument with the URI prefix of all the terms in the defined
- vocabulary is required and expects a valid URI ending with either a `"/"` or
- a `"#"`.
-
-
- ## Reflection
-
- `__base_uri__` and `__terms__` ...
- """
-
- defmacro __using__(opts) do
- quote bind_quoted: [opts: opts], unquote: true do
- import unquote(__MODULE__)
-
- # TODO: @terms should be a MapSet for faster term lookup
- Module.register_attribute __MODULE__, :terms, accumulate: true
-
- @before_compile unquote(__MODULE__)
-
- @strict Keyword.get(opts, :strict, false)
-
- with {:ok, base_uri} <- Keyword.fetch(opts, :base_uri),
- true <- base_uri |> String.ends_with?(["/", "#"]) do
- @base_uri base_uri
- else
- :error ->
- raise RDF.Vocabulary.InvalidBaseURIError, "required base_uri missing"
- false ->
- raise RDF.Vocabulary.InvalidBaseURIError,
- "a base_uri without a trailing '/' or '#' is invalid"
- end
-
- def __base_uri__, do: @base_uri
- def __strict__, do: @strict
-
- unless @strict do
- def unquote(:"$handle_undefined_function")(term, args) do
- RDF.Vocabulary.term_to_uri(@base_uri, term)
- end
- end
- end
- end
-
- defmacro __before_compile__(_env) do
- quote do
- def __terms__, do: @terms
-
- if @strict do
- def uri(term) do
- if Enum.member?(@terms, term) do
- RDF.Vocabulary.term_to_uri(@base_uri, term)
- else
- raise RDF.Vocabulary.UndefinedTermError,
- "undefined term #{term} in strict vocabulary #{__MODULE__}"
- end
- end
- else
- def uri(term) do
- RDF.Vocabulary.term_to_uri(@base_uri, term)
- end
- end
- end
- end
-
- @doc """
- Defines an URI via a term concatenated to the `base_uri` of the vocabulary
- module.
- """
- defmacro defuri(term) when is_atom(term) do
- quote do
- @terms unquote(term)
-
- if Atom.to_string(unquote(term)) =~ ~r/^\p{Ll}/u do
-# TODO: the URI should be built at compile-time
- # uri = RDF.Vocabulary.term_to_uri(@base_uri, unquote(term))
- def unquote(term)() do
- URI.parse(__base_uri__() <> to_string(unquote(term)))
- end
- end
- end
- end
-
- @doc false
- def term_to_uri(base_uri, term) do
- URI.parse(base_uri <> to_string(term))
- end
-
- @doc false
- def __uri__(uri = %URI{}), do: uri
- def __uri__(namespaced_atom) when is_atom(namespaced_atom) do
- case namespaced_atom
- |> to_string
- |> String.reverse
- |> String.split(".", parts: 2)
- |> Enum.map(&String.reverse/1)
- |> Enum.map(&String.to_existing_atom/1) do
- [term, vocabulary] -> vocabulary.uri(term)
- _ -> raise RDF.Vocabulary.InvalidTermError, ""
- end
-
- end
-
-end
diff --git a/lib/rdf/vocabulary_namespace.ex b/lib/rdf/vocabulary_namespace.ex
new file mode 100644
index 0000000..74c6c7d
--- /dev/null
+++ b/lib/rdf/vocabulary_namespace.ex
@@ -0,0 +1,256 @@
+defmodule RDF.Vocabulary.Namespace do
+ @moduledoc """
+ Defines a RDF Vocabulary as a `RDF.Namespace`.
+
+
+ ## Strict vocabularies
+
+ What is a strict vocabulary and why should I use them over non-strict
+ vocabularies and define all terms ...
+
+
+ ## Defining a vocabulary
+
+ There are two basic ways to define a vocabulary:
+
+ 1. You can define all terms manually.
+ 2. You can load all terms from a specified namespace in a given dataset or
+ graph.
+
+ Either way, you'll first have to define a new module for your vocabulary:
+
+ defmodule Example do
+ use RDF.Vocabulary.Namespace
+
+ defvocab EX,
+ base_uri: "http://www.example.com/ns/",
+ terms: ~w[Foo bar]
+
+ # Your term definitions
+ end
+
+ The `base_uri` argument with the URI prefix of all the terms in the defined
+ vocabulary is required and expects a valid URI ending with either a `"/"` or
+ a `"#"`.
+
+
+ ## Reflection
+
+ `__base_uri__` and `__terms__` ...
+
+ """
+
+ @vocabs_dir "priv/vocabs"
+
+ defmacro __using__(_opts) do
+ quote do
+ import unquote(__MODULE__)
+
+# Module.register_attribute __MODULE__, :vocabs_acc, accumulate: true
+#
+# @before_compile unquote(__MODULE__)
+ end
+ end
+
+# defmacro __before_compile__(_env) do
+# quote do
+# @__vocabs__ normalize_vocabs(
+# Module.delete_attribute(__MODULE__, :vocabs_acc), __MODULE__)
+# def __all__, do: @__vocabs__
+# end
+# end
+#
+# def normalize_vocabs(vocabs, parent_module) do
+# Enum.reduce vocabs, %{}, fn ({name, opts}, vocabs) ->
+# Map.put(vocabs, name, normalize_vocab_opts(name, opts, parent_module))
+# end
+# end
+#
+# defp normalize_vocab_opts(name, opts, parent_module) do
+# Keyword.put_new(opts, :module, Module.safe_concat([parent_module, name]))
+# end
+
+ @doc """
+ Defines a `RDF.Namespace` module for a RDF vocabulary.
+ """
+ defmacro defvocab({:__aliases__, _, [name_atom]} = name, opts) do
+ base_uri = base_uri!(opts)
+ file = file!(opts)
+ terms = terms!(opts)
+ strict = Keyword.get(opts, :strict, true)
+ case_separated_terms = group_terms_by_case(terms)
+ lowercased_terms = Map.get(case_separated_terms, :lowercased, [])
+ capitalized_terms = Map.get(case_separated_terms, :capitalized, [])
+
+ quote do
+# @vocabs_acc {unquote(name_atom), unquote(opts)}
+
+ vocabdoc = Module.delete_attribute(__MODULE__, :vocabdoc)
+
+ defmodule unquote(name) do
+ @moduledoc vocabdoc
+
+ @behaviour RDF.Namespace
+
+ if unquote(file) do
+ @external_resource unquote(file)
+ end
+
+ @base_uri unquote(base_uri)
+ def __base_uri__, do: @base_uri
+
+ @strict unquote(strict)
+ def __strict__, do: @strict
+
+ @lowercased_terms unquote(lowercased_terms |> Enum.map(&String.to_atom/1))
+ @capitalized_terms unquote(capitalized_terms |> Enum.map(&String.to_atom/1))
+ @terms @lowercased_terms ++ @capitalized_terms
+ def __terms__, do: @terms
+
+ define_vocab_terms unquote(lowercased_terms), unquote(base_uri)
+
+ if @strict do
+ def __resolve_term__(term) do
+ if Enum.member?(@capitalized_terms, term) do
+ term_to_uri(@base_uri, term)
+ else
+ raise RDF.Namespace.UndefinedTermError,
+ "undefined term #{term} in strict vocabulary #{__MODULE__}"
+ end
+ end
+ else
+ def __resolve_term__(term) do
+ term_to_uri(@base_uri, term)
+ end
+
+ def unquote(:"$handle_undefined_function")(term, args) do
+ term_to_uri(@base_uri, term)
+ end
+ end
+
+ Module.delete_attribute(__MODULE__, :tmp_uri)
+ end
+ end
+ end
+
+ defp base_uri!(opts) do
+ base_uri = Keyword.fetch!(opts, :base_uri)
+ unless String.ends_with?(base_uri, ["/", "#"]) do
+ raise RDF.Namespace.InvalidVocabBaseURIError,
+ "a base_uri without a trailing '/' or '#' is invalid"
+ else
+ base_uri
+ end
+ end
+
+ def terms!(opts) do
+ cond do
+ Keyword.has_key?(opts, :file) ->
+ opts
+ |> Keyword.delete(:file)
+ |> Keyword.put(:data, load_file(file!(opts)))
+ |> terms!
+ data = Keyword.get(opts, :data) ->
+ # TODO: support also RDF.Datasets ...
+ data = unless match?(%RDF.Graph{}, data) do
+ # TODO: find an alternative to Code.eval_quoted
+ {data, _ } = Code.eval_quoted(data, [], data_env())
+ data
+ else
+ data
+ end
+ data_vocab_terms(data, Keyword.fetch!(opts, :base_uri))
+ terms = Keyword.get(opts, :terms) ->
+ # TODO: find an alternative to Code.eval_quoted - We want to support that the terms can be given as sigils ...
+ {terms, _ } = Code.eval_quoted(terms, [], data_env())
+ terms
+ |> Enum.map(&to_string/1)
+ true ->
+ raise KeyError, key: ~w[terms data file], term: opts
+ end
+ end
+
+ def file!(opts) do
+ if file = Keyword.get(opts, :file) do
+ cond do
+ File.exists?(file) ->
+ file
+ File.exists?(expanded_file = Path.expand(file, @vocabs_dir)) ->
+ expanded_file
+ true ->
+ raise File.Error, path: file, action: "find", reason: :enoent
+ end
+ end
+ end
+
+ defp load_file(file) do
+ RDF.NTriples.Reader.read_file!(file)
+ end
+
+ defp data_env do
+ __ENV__
+ end
+
+
+ defmacro define_vocab_terms(terms, base_uri) do
+ Enum.map terms, fn term ->
+# TODO: Why does this way of precompiling the URI not work? We're getting an "invalid quoted expression: %URI{...}"
+# uri = term_to_uri(base_uri, term)
+# quote bind_quoted: [uri: Macro.escape(uri), term: String.to_atom(term)] do
+## @doc "<#{@tmp_uri}>"
+# def unquote(term)() do
+# unquote(uri)
+# end
+# end
+# Temporary workaround:
+ quote do
+ @tmp_uri term_to_uri(@base_uri, unquote(term))
+ @doc "<#{@tmp_uri}>"
+ def unquote(term |> String.to_atom)(), do: @tmp_uri
+ end
+ end
+ end
+
+ defp data_vocab_terms(data, base_uri) do
+ data
+ |> RDF.Graph.resources # TODO: support also RDF.Datasets ...
+ # filter URIs
+ |> Stream.filter(fn
+ %URI{} -> true
+ _ -> false
+ end)
+ |> Stream.map(&to_string/1)
+ |> Stream.map(&(strip_base_uri(&1, base_uri)))
+ |> Enum.filter(&vocab_term?/1)
+ end
+
+ defp group_terms_by_case(terms) do
+ Enum.group_by terms, fn term ->
+ if lowercase?(term),
+ do: :lowercased,
+ else: :capitalized
+ end
+ end
+
+ defp lowercase?(term) when is_atom(term),
+ do: Atom.to_string(term) |> lowercase?
+ defp lowercase?(term),
+ do: term =~ ~r/^\p{Ll}/u
+
+ defp strip_base_uri(uri, base_uri) do
+ if String.starts_with?(uri, base_uri) do
+ String.replace_prefix(uri, base_uri, "")
+ end
+ end
+
+ defp vocab_term?(term) when is_binary(term) do
+ not String.contains?(term, "/")
+ end
+ defp vocab_term?(_), do: false
+
+ @doc false
+ def term_to_uri(base_uri, term) do
+ URI.parse(base_uri <> to_string(term))
+ end
+
+end
diff --git a/priv/vocabs/owl.nt b/priv/vocabs/owl.nt
new file mode 100644
index 0000000..27f2bc1
--- /dev/null
+++ b/priv/vocabs/owl.nt
@@ -0,0 +1,450 @@
+ .
+ "DatatypeProperty" .
+ .
+ "The class of data properties." .
+ .
+ .
+ "IrreflexiveProperty" .
+ .
+ "The class of irreflexive properties." .
+ .
+ .
+ .
+ "maxQualifiedCardinality" .
+ .
+ "The property that determines the cardinality of a maximum qualified cardinality restriction." .
+ .
+ .
+ "Nothing" .
+ .
+ "This is the empty class." .
+ .
+ .
+ "AllDifferent" .
+ .
+ "The class of collections of pairwise different individuals." .
+ .
+ .
+ "Axiom" .
+ .
+ "The class of annotated axioms for which the RDF serialization consists of an annotated subject, predicate and object." .
+ .
+ .
+ "AllDisjointClasses" .
+ .
+ "The class of collections of pairwise disjoint classes." .
+ .
+ .
+ "DeprecatedProperty" .
+ .
+ "The class of deprecated properties." .
+ .
+ .
+ "OntologyProperty" .
+ .
+ "The class of ontology properties." .
+ .
+ .
+ "DeprecatedClass" .
+ .
+ "The class of deprecated classes." .
+ .
+ .
+ "AnnotationProperty" .
+ .
+ "The class of annotation properties." .
+ .
+ .
+ .
+ "deprecated" .
+ .
+ "The annotation property that indicates that a given entity has been deprecated." .
+ .
+ .
+ .
+ "maxCardinality" .
+ .
+ "The property that determines the cardinality of a maximum cardinality restriction." .
+ .
+ .
+ "AsymmetricProperty" .
+ .
+ "The class of asymmetric properties." .
+ .
+ .
+ "DataRange" .
+ .
+ "The class of OWL data ranges, which are special kinds of datatypes. Note: The use of the IRI owl:DataRange has been deprecated as of OWL 2. The IRI rdfs:Datatype SHOULD be used instead." .
+ .
+ .
+ .
+ "priorVersion" .
+ .
+ .
+ "The annotation property that indicates the predecessor ontology of a given ontology." .
+ .
+ .
+ .
+ "bottomObjectProperty" .
+ .
+ "The object property that does not relate any two individuals." .
+ .
+ .
+ "FunctionalProperty" .
+ .
+ "The class of functional properties." .
+ .
+