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 ### 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 - 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 - 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 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`. 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 - `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 which implement the `RDF.Data` protocol can be given also as the second argument
(previously custom structs with `RDF.Data` protocol implementations always (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 - several performance improvements

View file

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

View file

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