Add RDF.Namespace.IRI.iri/1 macro
This commit is contained in:
parent
adf3905ee6
commit
1a51aea606
8 changed files with 123 additions and 5 deletions
|
@ -14,6 +14,8 @@ The generated namespaces are much more flexible now and compile faster.
|
||||||
|
|
||||||
- `RDF.Namespace` builders `defnamespace/3` and `create/4`
|
- `RDF.Namespace` builders `defnamespace/3` and `create/4`
|
||||||
- `RDF.Vocabulary.Namespace.create/5` for dynamic creation of `RDF.Vocabulary.Namespace`s
|
- `RDF.Vocabulary.Namespace.create/5` for dynamic creation of `RDF.Vocabulary.Namespace`s
|
||||||
|
- macro `RDF.Namespace.IRI.iri/1` which allows to resolve `RDF.Namespace` terms
|
||||||
|
inside of pattern matches
|
||||||
- `RDF.IRI.starts_with?/2` and `RDF.IRI.ends_with?/2`
|
- `RDF.IRI.starts_with?/2` and `RDF.IRI.ends_with?/2`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
54
lib/rdf/namespace/iri.ex
Normal file
54
lib/rdf/namespace/iri.ex
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
defmodule RDF.Namespace.IRI do
|
||||||
|
@moduledoc """
|
||||||
|
Provides the `iri/1` macro to resolve IRI values inside of pattern matches.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
A macro which allows to resolve IRI values inside of pattern matches.
|
||||||
|
|
||||||
|
Terms of a `RDF.Namespace` (which includes terms of `RDF.Vocabulary.Namespace`)
|
||||||
|
can't be resolved in pattern matches. This macro allows just that, by wrapping
|
||||||
|
the terms in a pattern match with a call of this macro.
|
||||||
|
|
||||||
|
Note: Only literal values are allowed as arguments of this macro, since the argument
|
||||||
|
expression needs to be evaluated at compile-time.
|
||||||
|
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
import RDF.Namespace.IRI
|
||||||
|
|
||||||
|
case expr do
|
||||||
|
iri(EX.Foo) -> ...
|
||||||
|
iri(EX.bar()) -> ...
|
||||||
|
...
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
defmacro iri({{:., _, [{:__aliases__, _, _} = module_alias, _fun_name]}, _, []} = expr) do
|
||||||
|
{module, _} = Code.eval_quoted(module_alias, [], __CALLER__)
|
||||||
|
|
||||||
|
if RDF.Namespace.namespace?(module) do
|
||||||
|
resolve_to_iri(expr, __CALLER__)
|
||||||
|
else
|
||||||
|
forbidden_iri_expr(expr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defmacro iri({:__aliases__, _, _} = expr), do: resolve_to_iri(expr, __CALLER__)
|
||||||
|
|
||||||
|
defmacro iri(expr), do: forbidden_iri_expr(expr)
|
||||||
|
|
||||||
|
defp resolve_to_iri(expr, env) do
|
||||||
|
{value, _} = Code.eval_quoted(expr, [], env)
|
||||||
|
iri = RDF.IRI.new(value)
|
||||||
|
|
||||||
|
quote do
|
||||||
|
unquote(Macro.escape(iri))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp forbidden_iri_expr(expr) do
|
||||||
|
raise ArgumentError, "forbidden expression in RDF.Guard.iri/1 call: #{Macro.to_string(expr)}"
|
||||||
|
end
|
||||||
|
end
|
|
@ -16,7 +16,8 @@ defmodule RDF.Test.Case do
|
||||||
alias RDF.NS.{RDFS, OWL}
|
alias RDF.NS.{RDFS, OWL}
|
||||||
alias unquote(__MODULE__).{EX, FOAF}
|
alias unquote(__MODULE__).{EX, FOAF}
|
||||||
|
|
||||||
import RDF, only: [iri: 1, literal: 1, bnode: 1]
|
import RDF, only: [literal: 1, bnode: 1]
|
||||||
|
import RDF.Namespace.IRI
|
||||||
import unquote(__MODULE__)
|
import unquote(__MODULE__)
|
||||||
|
|
||||||
import RDF.Sigils
|
import RDF.Sigils
|
||||||
|
|
|
@ -15,7 +15,7 @@ defmodule RDF.DatasetTest do
|
||||||
|
|
||||||
test "creating an empty dataset with a coercible dataset name" do
|
test "creating an empty dataset with a coercible dataset name" do
|
||||||
assert named_dataset("http://example.com/DatasetName")
|
assert named_dataset("http://example.com/DatasetName")
|
||||||
|> named_dataset?(iri("http://example.com/DatasetName"))
|
|> named_dataset?(~I<http://example.com/DatasetName>)
|
||||||
|
|
||||||
assert named_dataset(EX.Foo) |> named_dataset?(iri(EX.Foo))
|
assert named_dataset(EX.Foo) |> named_dataset?(iri(EX.Foo))
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,7 +23,7 @@ defmodule RDF.GraphTest do
|
||||||
|
|
||||||
test "creating an empty graph with a coercible graph name" do
|
test "creating an empty graph with a coercible graph name" do
|
||||||
assert named_graph("http://example.com/graph/GraphName")
|
assert named_graph("http://example.com/graph/GraphName")
|
||||||
|> named_graph?(iri("http://example.com/graph/GraphName"))
|
|> named_graph?(~I<http://example.com/graph/GraphName>)
|
||||||
|
|
||||||
assert named_graph(EX.Foo) |> named_graph?(iri(EX.Foo))
|
assert named_graph(EX.Foo) |> named_graph?(iri(EX.Foo))
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,8 +9,6 @@ defmodule RDF.ListTest do
|
||||||
|
|
||||||
use RDF.Vocabulary.Namespace
|
use RDF.Vocabulary.Namespace
|
||||||
|
|
||||||
defvocab EX, base_iri: "http://example.org/#", terms: [], strict: false
|
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
{:ok,
|
{:ok,
|
||||||
empty: RDF.List.new(RDF.nil(), Graph.new()),
|
empty: RDF.List.new(RDF.nil(), Graph.new()),
|
||||||
|
|
56
test/unit/namespace/iri_test.exs
Normal file
56
test/unit/namespace/iri_test.exs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
defmodule RDF.Namespace.IRITest do
|
||||||
|
use RDF.Test.Case
|
||||||
|
|
||||||
|
doctest RDF.Namespace.IRI
|
||||||
|
|
||||||
|
import RDF.Namespace.IRI
|
||||||
|
|
||||||
|
describe "iri/1" do
|
||||||
|
test "with a property function from a vocabulary namespace" do
|
||||||
|
assert iri(EX.foo()) == EX.foo()
|
||||||
|
assert iri(RDF.NS.OWL.sameAs()) == RDF.NS.OWL.sameAs()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with a term atom from a vocabulary namespace" do
|
||||||
|
assert iri(EX.Foo) == RDF.iri(EX.Foo)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "constant function calls from non-vocabulary namespace module results in a compile error" do
|
||||||
|
assert_raise ArgumentError, ~r[forbidden expression in RDF.Guard.iri/1], fn ->
|
||||||
|
ast =
|
||||||
|
quote do
|
||||||
|
import RDF.Guards
|
||||||
|
|
||||||
|
iri(Mix.env())
|
||||||
|
end
|
||||||
|
|
||||||
|
Code.eval_quoted(ast, [], __ENV__)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "other forms result in a compile error" do
|
||||||
|
assert_raise ArgumentError, ~r[forbidden expression in RDF.Guard.iri/1], fn ->
|
||||||
|
ast =
|
||||||
|
quote do
|
||||||
|
import RDF.Guards
|
||||||
|
var = ~I<http://example.com>
|
||||||
|
iri(var)
|
||||||
|
end
|
||||||
|
|
||||||
|
Code.eval_quoted(ast, [], __ENV__)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "in pattern matches" do
|
||||||
|
assert (case EX.foo() do
|
||||||
|
iri(EX.foo()) -> "match"
|
||||||
|
_ -> {:mismatch, iri(EX.foo())}
|
||||||
|
end) == "match"
|
||||||
|
|
||||||
|
assert (case RDF.iri(EX.Bar) do
|
||||||
|
iri(EX.Bar) -> "match"
|
||||||
|
_ -> {:mismatch, iri(EX.Bar)}
|
||||||
|
end) == "match"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -13,6 +13,13 @@ defmodule RDF.SigilsTest do
|
||||||
test "escaping" do
|
test "escaping" do
|
||||||
assert ~I<http://example.com/f\no> == RDF.iri("http://example.com/f\\no")
|
assert ~I<http://example.com/f\no> == RDF.iri("http://example.com/f\\no")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "in pattern matches" do
|
||||||
|
assert (case RDF.iri("http://example.com/foo") do
|
||||||
|
~I<http://example.com/foo> -> "match"
|
||||||
|
_ -> :mismatch
|
||||||
|
end) == "match"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "~i sigil" do
|
describe "~i sigil" do
|
||||||
|
|
Loading…
Reference in a new issue