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.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`
|
||||
|
||||
### 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 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 RDF.Sigils
|
||||
|
|
|
@ -15,7 +15,7 @@ defmodule RDF.DatasetTest do
|
|||
|
||||
test "creating an empty dataset with a coercible dataset name" do
|
||||
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))
|
||||
end
|
||||
|
|
|
@ -23,7 +23,7 @@ defmodule RDF.GraphTest do
|
|||
|
||||
test "creating an empty graph with a coercible graph name" do
|
||||
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))
|
||||
end
|
||||
|
|
|
@ -9,8 +9,6 @@ defmodule RDF.ListTest do
|
|||
|
||||
use RDF.Vocabulary.Namespace
|
||||
|
||||
defvocab EX, base_iri: "http://example.org/#", terms: [], strict: false
|
||||
|
||||
setup do
|
||||
{:ok,
|
||||
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
|
||||
assert ~I<http://example.com/f\no> == RDF.iri("http://example.com/f\\no")
|
||||
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
|
||||
|
||||
describe "~i sigil" do
|
||||
|
|
Loading…
Reference in a new issue