Add RDF.Resource.Generator

This commit is contained in:
Marcel Otto 2022-03-01 23:13:50 +01:00
parent b89b5d34d2
commit db007641e2
9 changed files with 124 additions and 3 deletions

View file

@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and
### Added
- `RDF.Resource.Generator`s which can be used to generate configurable ids
- `:implicit_base` option on the `RDF.Turtle.Encoder`
- `:base_description` option on the `RDF.Turtle.Encoder`
- `RDF.Resource.t` type

View file

@ -41,10 +41,11 @@ defmodule RDF do
"""
alias RDF.{
Resource,
IRI,
Namespace,
Literal,
BlankNode,
Literal,
Namespace,
Description,
Graph,
Dataset,
@ -191,6 +192,9 @@ defmodule RDF do
def resource?(_), do: false
defdelegate resource(), to: Resource, as: :new
defdelegate resource(args), to: Resource, as: :new
@doc """
Checks if the given value is a RDF term.

View file

@ -13,6 +13,8 @@ defmodule RDF.BlankNode do
@enforce_keys [:value]
defstruct [:value]
use RDF.Resource.Generator
@doc """
Creates a `RDF.BlankNode`.
"""
@ -44,6 +46,9 @@ defmodule RDF.BlankNode do
"""
def value(%__MODULE__{} = bnode), do: bnode.value
@impl RDF.Resource.Generator
def generate(), do: new()
@doc """
Tests for value equality of blank nodes.

View file

@ -4,6 +4,7 @@ defmodule RDF.BlankNode.Generator do
"""
use GenServer
use RDF.Resource.Generator
# Client API ###############################################################
@ -64,10 +65,16 @@ defmodule RDF.BlankNode.Generator do
@doc """
Generates a new blank node according to the `RDF.BlankNode.Generator.Algorithm` set up.
"""
@impl RDF.Resource.Generator
def generate(pid) do
GenServer.call(pid, :generate)
end
@impl RDF.Resource.Generator
def generate do
raise ArgumentError, "required pid of RDF.BlankNode.Generator missing"
end
@doc """
Generates a blank node for a given value according to the `RDF.BlankNode.Generator.Algorithm` set up.
"""

View file

@ -1,5 +1,12 @@
defmodule RDF.Resource do
alias RDF.{IRI, BlankNode}
alias RDF.Resource.Generator
@type t :: IRI.t() | BlankNode.t()
@default_generator (Application.get_env(:rdf, :resource) || [generator: BlankNode])
|> Generator.config()
def new(), do: Generator.generate(@default_generator, nil)
def new(args), do: Generator.generate(@default_generator, args)
end

View file

@ -0,0 +1,57 @@
defmodule RDF.Resource.Generator do
defmodule Config do
@enforce_keys [:generator]
defstruct [:generator, :arguments]
@type t :: %__MODULE__{generator: module, arguments: any}
end
@callback generator_config() :: Config.t()
@callback generator_config(args :: any) :: Config.t()
@callback generator_arguments(args :: any, defaults :: any) :: any
@callback generate() :: RDF.Resource.t()
@callback generate(args :: any) :: RDF.Resource.t()
defmacro __using__(_opts) do
quote do
@behaviour RDF.Resource.Generator
@impl RDF.Resource.Generator
def generator_config(defaults \\ nil) do
%RDF.Resource.Generator.Config{
generator: __MODULE__,
arguments: generator_arguments(defaults, nil)
}
end
@impl RDF.Resource.Generator
def generator_arguments(args, defaults) when is_list(args) and is_list(defaults),
do: Keyword.merge(defaults, args)
def generator_arguments(nil, defaults), do: defaults
def generator_arguments(args, defaults), do: args
@impl RDF.Resource.Generator
def generate(_args), do: generate()
defoverridable generate: 1, generator_config: 1, generator_arguments: 2
end
end
@doc false
def config(config) do
{generator, args} = Keyword.pop!(config, :generator)
default_args = unless Enum.empty?(args), do: args
generator.generator_config(default_args)
end
@doc false
def generate(%Config{generator: generator, arguments: defaults}, args),
do: do_generate(generator, generator.generator_arguments(args, defaults))
defp do_generate(generator, nil), do: generator.generate()
defp do_generate(generator, args), do: generator.generate(args)
end

View file

@ -12,7 +12,7 @@ defmodule RDF.Test.Case do
using do
quote do
alias RDF.{Dataset, Graph, Description, IRI, XSD, PrefixMap, PropertyMap, NS}
alias RDF.{Dataset, Graph, Description, IRI, BlankNode, XSD, PrefixMap, PropertyMap, NS}
alias RDF.NS.{RDFS, OWL}
alias unquote(__MODULE__).{EX, FOAF}

View file

@ -0,0 +1,29 @@
defmodule RDF.ResourceId.GeneratorTest do
use RDF.Test.Case
doctest RDF.Resource.Generator
alias RDF.Resource.Generator
test "RDF.BlankNode as a generator" do
assert %BlankNode{} = bnode1 = Generator.generate(BlankNode.generator_config(), nil)
assert %BlankNode{} = bnode2 = Generator.generate(BlankNode.generator_config(), nil)
assert bnode1 != bnode2
end
test "RDF.BlankNode.Generator as a generator" do
{:ok, generator} = start_supervised({RDF.BlankNode.Generator, RDF.BlankNode.Increment})
assert RDF.BlankNode.Generator.generator_config(generator)
|> Generator.generate(nil) ==
RDF.bnode(0)
assert RDF.BlankNode.Generator.generator_config(generator)
|> Generator.generate(nil) ==
RDF.bnode(1)
assert RDF.BlankNode.Generator.generator_config(:foo)
|> Generator.generate(generator) ==
RDF.bnode(2)
end
end

View file

@ -0,0 +1,11 @@
defmodule RDF.ResourceTest do
use RDF.Test.Case
doctest RDF.Resource
alias RDF.Resource
test "new/0" do
assert %BlankNode{} = Resource.new()
end
end