Support more values for the :base_iri in defvocab

This commit is contained in:
Marcel Otto 2022-06-05 03:09:10 +02:00
parent 9449fce988
commit f8517de119
3 changed files with 45 additions and 20 deletions

View file

@ -18,15 +18,21 @@ The generated namespaces are much more flexible now and compile faster.
### Changed
- The `:base_iri` specified in `defvocab` can now be given in any form supported
by `RDF.IRI.new/1`. There are also no longer restrictions on the expression
of this value. While previously the value had to be provided as a literal value,
now any expression returning a value accepted by `RDF.IRI.new/1` can be given
(e.g. function calls, module attributes etc.).
The `:base_iri` also no longer has to end with a `/` or `#`.
- Aliases on a `RDF.Vocabulary.Namespace` can now be specified directly in the
`:terms` list
`:terms` list.
- When defining an alias for a term of vocabulary which would be invalid as an
Elixir term, the original term is now implicitly ignored and won't any longer
be returned by the `__terms__/0` function of a `RDF.Vocabulary.Namespace`.
- `RDF.Data.merge/2` and `RDF.Data.equal?/2` are now commutative, i.e. structs
which implement the `RDF.Data` protocol can be given also as the second argument
(previously custom structs with `RDF.Data` protocol implementations always
had to be given as the first argument)
had to be given as the first argument).
- several performance improvements

View file

@ -9,7 +9,7 @@ defmodule RDF.Vocabulary.Namespace do
the `RDF.NS` module.
"""
alias RDF.{Description, Graph, Dataset, Vocabulary, Namespace}
alias RDF.{Description, Graph, Dataset, Vocabulary, Namespace, IRI}
import RDF.Vocabulary.Namespace.{TermMapping, CaseValidation}
import RDF.Vocabulary, only: [term_to_iri: 2, extract_terms: 2]
@ -254,14 +254,25 @@ defmodule RDF.Vocabulary.Namespace do
end
end
defp normalize_base_uri(base_uri) do
unless is_binary(base_uri) and String.ends_with?(base_uri, ~w[/ # .]) do
raise RDF.Namespace.InvalidVocabBaseIRIError, "invalid base IRI: #{inspect(base_uri)}"
else
defp normalize_base_uri(%IRI{} = base_iri), do: IRI.to_string(base_iri)
defp normalize_base_uri(base_uri) when is_binary(base_uri) do
if IRI.valid?(base_uri) do
base_uri
else
raise RDF.Namespace.InvalidVocabBaseIRIError, "invalid base IRI: #{inspect(base_uri)}"
end
end
defp normalize_base_uri(base_uri) do
base_uri |> IRI.new() |> normalize_base_uri()
rescue
[Namespace.UndefinedTermError, IRI.InvalidError, FunctionClauseError] ->
reraise RDF.Namespace.InvalidVocabBaseIRIError,
"invalid base IRI: #{inspect(base_uri)}",
__STACKTRACE__
end
@doc false
@spec vocabulary_namespace?(module) :: boolean
def vocabulary_namespace?(name) do

View file

@ -42,6 +42,15 @@ defmodule RDF.Vocabulary.NamespaceTest do
base_iri: "http://example.com/strict#",
terms: ~w[foo bar]
@base_iri "http://example.com/"
defvocab ExampleWithBaseFromModuleAttribute,
base_iri: @base_iri,
terms: ~w[foo Bar]a
defvocab ExampleWithBaseFromIRI,
base_iri: ~I<http://example.com/>,
terms: ~w[foo Bar]a
defvocab ExampleFromGraph,
base_iri: "http://example.com/from_graph#",
data:
@ -139,18 +148,6 @@ defmodule RDF.Vocabulary.NamespaceTest do
end
end
test "when the base_iri doesn't end with '/' or '#', an error is raised" do
assert_raise RDF.Namespace.InvalidVocabBaseIRIError, fn ->
defmodule NSWithInvalidBaseIRI1 do
use RDF.Vocabulary.Namespace
defvocab Example,
base_iri: "http://example.com/base_iri4",
terms: []
end
end
end
test "when the base_iri isn't a valid IRI, an error is raised" do
assert_raise RDF.Namespace.InvalidVocabBaseIRIError, fn ->
defmodule NSWithInvalidBaseIRI2 do
@ -957,9 +954,13 @@ defmodule RDF.Vocabulary.NamespaceTest do
test "__base_iri__ returns the base_iri" do
alias TestNS.ExampleFromGraph, as: HashVocab
alias TestNS.ExampleFromNTriplesFile, as: SlashVocab
alias TestNS.ExampleWithBaseFromModuleAttribute, as: BaseFromModuleAttribute
alias TestNS.ExampleWithBaseFromIRI, as: BaseFromIRI
assert HashVocab.__base_iri__() == "http://example.com/from_graph#"
assert SlashVocab.__base_iri__() == "http://example.com/from_ntriples/"
assert BaseFromModuleAttribute.__base_iri__() == "http://example.com/"
assert BaseFromIRI.__base_iri__() == "http://example.com/"
end
test "__iris__ returns all IRIs of the vocabulary" do
@ -1040,7 +1041,9 @@ defmodule RDF.Vocabulary.NamespaceTest do
EXS,
ExampleFromGraph,
ExampleFromNTriplesFile,
StrictExampleFromTerms
StrictExampleFromTerms,
ExampleWithBaseFromIRI,
ExampleWithBaseFromModuleAttribute
}
test "undefined terms" do
@ -1088,12 +1091,17 @@ defmodule RDF.Vocabulary.NamespaceTest do
assert StrictExampleFromTerms.foo() == ~I<http://example.com/strict_from_terms#foo>
assert RDF.iri(StrictExampleFromTerms.foo()) == ~I<http://example.com/strict_from_terms#foo>
assert ExampleWithBaseFromIRI.foo() == ~I<http://example.com/foo>
assert ExampleWithBaseFromModuleAttribute.foo() == ~I<http://example.com/foo>
end
test "capitalized terms" do
assert RDF.iri(ExampleFromGraph.Bar) == ~I<http://example.com/from_graph#Bar>
assert RDF.iri(ExampleFromNTriplesFile.Bar) == ~I<http://example.com/from_ntriples/Bar>
assert RDF.iri(StrictExampleFromTerms.Bar) == ~I<http://example.com/strict_from_terms#Bar>
assert RDF.iri(ExampleWithBaseFromIRI.Bar) == ~I<http://example.com/Bar>
assert RDF.iri(ExampleWithBaseFromModuleAttribute.Bar) == ~I<http://example.com/Bar>
end
test "terms starting with an underscore" do