Add ProtocolEx-based custom datatype registration
This commit is contained in:
parent
4fedb2cfc0
commit
faaebb2de2
33 changed files with 182 additions and 60 deletions
|
@ -209,10 +209,7 @@ defmodule RDF do
|
||||||
defdelegate literal(value), to: Literal, as: :new
|
defdelegate literal(value), to: Literal, as: :new
|
||||||
defdelegate literal(value, opts), to: Literal, as: :new
|
defdelegate literal(value, opts), to: Literal, as: :new
|
||||||
|
|
||||||
def literal?(%Literal{}), do: true
|
defdelegate literal?(literal), to: Literal.Datatype.Registry
|
||||||
def literal?(%RDF.Literal.Generic{}), do: true
|
|
||||||
def literal?(%datatype{}), do: Literal.Datatype.Registry.datatype?(datatype)
|
|
||||||
def literal?(_), do: false
|
|
||||||
|
|
||||||
defdelegate triple(s, p, o), to: Triple, as: :new
|
defdelegate triple(s, p, o), to: Triple, as: :new
|
||||||
defdelegate triple(tuple), to: Triple, as: :new
|
defdelegate triple(tuple), to: Triple, as: :new
|
||||||
|
|
|
@ -92,12 +92,6 @@ defmodule RDF.Literal do
|
||||||
|
|
||||||
def coerce(%__MODULE__{} = literal), do: literal
|
def coerce(%__MODULE__{} = literal), do: literal
|
||||||
|
|
||||||
Enum.each(Datatype.Registry.datatypes(), fn datatype ->
|
|
||||||
def coerce(%unquote(datatype){} = literal) do
|
|
||||||
%__MODULE__{literal: literal}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
def coerce(value) when is_binary(value), do: RDF.XSD.String.new(value)
|
def coerce(value) when is_binary(value), do: RDF.XSD.String.new(value)
|
||||||
def coerce(value) when is_boolean(value), do: RDF.XSD.Boolean.new(value)
|
def coerce(value) when is_boolean(value), do: RDF.XSD.Boolean.new(value)
|
||||||
def coerce(value) when is_integer(value), do: RDF.XSD.Integer.new(value)
|
def coerce(value) when is_integer(value), do: RDF.XSD.Integer.new(value)
|
||||||
|
@ -108,7 +102,22 @@ defmodule RDF.Literal do
|
||||||
def coerce(%DateTime{} = value), do: RDF.XSD.DateTime.new(value)
|
def coerce(%DateTime{} = value), do: RDF.XSD.DateTime.new(value)
|
||||||
def coerce(%NaiveDateTime{} = value), do: RDF.XSD.DateTime.new(value)
|
def coerce(%NaiveDateTime{} = value), do: RDF.XSD.DateTime.new(value)
|
||||||
def coerce(%URI{} = value), do: RDF.XSD.AnyURI.new(value)
|
def coerce(%URI{} = value), do: RDF.XSD.AnyURI.new(value)
|
||||||
def coerce(_), do: nil
|
|
||||||
|
# Although the following catch-all-clause for all structs could handle the core datatypes
|
||||||
|
# we're generating dedicated clauses for them here, as they are approx. 15% faster
|
||||||
|
Enum.each(Datatype.Registry.core_datatypes(), fn datatype ->
|
||||||
|
def coerce(%unquote(datatype){} = datatype_literal) do
|
||||||
|
%__MODULE__{literal: datatype_literal}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
def coerce(%_datatype{} = datatype_literal) do
|
||||||
|
if Datatype.Registry.literal?(datatype_literal) do
|
||||||
|
%__MODULE__{literal: datatype_literal}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def coerce(_), do: nil
|
||||||
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -140,6 +140,17 @@ defmodule RDF.Literal.Datatype do
|
||||||
defmacro __using__(opts) do
|
defmacro __using__(opts) do
|
||||||
name = Keyword.fetch!(opts, :name)
|
name = Keyword.fetch!(opts, :name)
|
||||||
id = Keyword.fetch!(opts, :id)
|
id = Keyword.fetch!(opts, :id)
|
||||||
|
do_register = Keyword.get(opts, :register, not is_nil(id))
|
||||||
|
datatype = __CALLER__.module
|
||||||
|
|
||||||
|
# TODO: find an alternative to Code.eval_quoted - We want to support that id can be passed via a function call
|
||||||
|
unquoted_id =
|
||||||
|
if do_register do
|
||||||
|
id
|
||||||
|
|> Code.eval_quoted([], __ENV__)
|
||||||
|
|> elem(0)
|
||||||
|
|> to_string()
|
||||||
|
end
|
||||||
|
|
||||||
quote do
|
quote do
|
||||||
@behaviour unquote(__MODULE__)
|
@behaviour unquote(__MODULE__)
|
||||||
|
@ -258,6 +269,15 @@ defmodule RDF.Literal.Datatype do
|
||||||
literal.__struct__.lexical(literal)
|
literal.__struct__.lexical(literal)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if unquote(do_register) do
|
||||||
|
import ProtocolEx
|
||||||
|
|
||||||
|
defimpl_ex Registration, unquote(unquoted_id),
|
||||||
|
for: RDF.Literal.Datatype.Registry.Registration do
|
||||||
|
def datatype(id), do: unquote(datatype)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,33 +1,45 @@
|
||||||
# TODO: This registry should be managed automatically/dynamically and be extendable, to allow user-defined datatypes ...
|
|
||||||
defmodule RDF.Literal.Datatype.Registry do
|
defmodule RDF.Literal.Datatype.Registry do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
alias RDF.{Literal, IRI, XSD}
|
alias RDF.{Literal, IRI, XSD}
|
||||||
|
alias RDF.Literal.Datatype.Registry.Registration
|
||||||
|
|
||||||
@datatypes [RDF.LangString | Enum.to_list(XSD.datatypes())]
|
import RDF.Guards
|
||||||
|
|
||||||
@mapping Map.new(@datatypes, fn datatype -> {IRI.new(datatype.id), datatype} end)
|
@core_datatypes [RDF.LangString | Enum.to_list(XSD.datatypes())]
|
||||||
|
@mapping Map.new(@core_datatypes, fn datatype -> {IRI.new(datatype.id), datatype} end)
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
The mapping of IRIs of datatypes to their `RDF.Literal.Datatype`.
|
The IRIs of all core `RDF.Literal.Datatype`s.
|
||||||
"""
|
"""
|
||||||
@spec mapping :: %{IRI.t => Literal.Datatype.t}
|
@spec core_ids :: [IRI.t]
|
||||||
def mapping, do: @mapping
|
def core_ids, do: Map.keys(@mapping)
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
The IRIs of all datatypes with a `RDF.Literal.Datatype` defined.
|
All core `RDF.Literal.Datatype` modules.
|
||||||
"""
|
"""
|
||||||
@spec ids :: [IRI.t]
|
@spec core_datatypes :: Enum.t
|
||||||
def ids, do: Map.keys(@mapping)
|
def core_datatypes, do: @core_datatypes
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
All defined `RDF.Literal.Datatype` modules.
|
Checks if the given module is core datatype.
|
||||||
"""
|
"""
|
||||||
@spec datatypes :: Enum.t
|
@spec core_datatype?(module) :: boolean
|
||||||
def datatypes, do: @datatypes
|
def core_datatype?(module), do: module in @core_datatypes
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Checks if the given module is a core datatype or a registered custom datatype implementing the `RDF.Literal.Datatype` behaviour.
|
||||||
|
"""
|
||||||
@spec datatype?(module) :: boolean
|
@spec datatype?(module) :: boolean
|
||||||
def datatype?(module), do: module in @datatypes
|
def datatype?(module) do
|
||||||
|
core_datatype?(module) or implements_datatype_behaviour?(module)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec literal?(module) :: boolean
|
||||||
|
def literal?(%Literal{}), do: true
|
||||||
|
def literal?(%Literal.Generic{}), do: true
|
||||||
|
def literal?(%datatype{}), do: datatype?(datatype)
|
||||||
|
def literal?(_), do: false
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the `RDF.Literal.Datatype` for a directly datatype IRI or the datatype IRI of a `RDF.Literal`.
|
Returns the `RDF.Literal.Datatype` for a directly datatype IRI or the datatype IRI of a `RDF.Literal`.
|
||||||
|
@ -35,5 +47,19 @@ defmodule RDF.Literal.Datatype.Registry do
|
||||||
@spec get(Literal.t | IRI.t | String.t) :: Literal.Datatype.t
|
@spec get(Literal.t | IRI.t | String.t) :: Literal.Datatype.t
|
||||||
def get(%Literal{} = literal), do: Literal.datatype(literal)
|
def get(%Literal{} = literal), do: Literal.datatype(literal)
|
||||||
def get(id) when is_binary(id), do: id |> IRI.new() |> get()
|
def get(id) when is_binary(id), do: id |> IRI.new() |> get()
|
||||||
def get(id), do: @mapping[id]
|
def get(id) when maybe_ns_term(id), do: id |> IRI.new() |> get()
|
||||||
|
def get(id), do: @mapping[id] || get_custom_datatype(id)
|
||||||
|
|
||||||
|
defp get_custom_datatype(id) do
|
||||||
|
id
|
||||||
|
|> to_string()
|
||||||
|
|> Registration.datatype()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp implements_datatype_behaviour?(module) do
|
||||||
|
module.module_info[:attributes]
|
||||||
|
|> Keyword.get_values(:behaviour)
|
||||||
|
|> List.flatten()
|
||||||
|
|> Enum.member?(RDF.Literal.Datatype)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
5
lib/rdf/literal/datatype/registry/protocol.ex
Normal file
5
lib/rdf/literal/datatype/registry/protocol.ex
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import ProtocolEx
|
||||||
|
|
||||||
|
defprotocol_ex RDF.Literal.Datatype.Registry.Registration do
|
||||||
|
def datatype(id), do: nil
|
||||||
|
end
|
|
@ -7,7 +7,8 @@ defmodule RDF.LangString do
|
||||||
|
|
||||||
use RDF.Literal.Datatype,
|
use RDF.Literal.Datatype,
|
||||||
name: "langString",
|
name: "langString",
|
||||||
id: RDF.Utils.Bootstrapping.rdf_iri("langString")
|
id: RDF.Utils.Bootstrapping.rdf_iri("langString"),
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
alias RDF.Literal.Datatype
|
alias RDF.Literal.Datatype
|
||||||
alias RDF.Literal
|
alias RDF.Literal
|
||||||
|
|
|
@ -11,7 +11,8 @@ defmodule RDF.XSD.AnyURI do
|
||||||
|
|
||||||
use RDF.XSD.Datatype.Primitive,
|
use RDF.XSD.Datatype.Primitive,
|
||||||
name: "anyURI",
|
name: "anyURI",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("anyURI")
|
id: RDF.Utils.Bootstrapping.xsd_iri("anyURI"),
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
@impl RDF.XSD.Datatype
|
@impl RDF.XSD.Datatype
|
||||||
@spec lexical_mapping(String.t(), Keyword.t()) :: valid_value
|
@spec lexical_mapping(String.t(), Keyword.t()) :: valid_value
|
||||||
|
|
|
@ -8,7 +8,8 @@ defmodule RDF.XSD.Boolean do
|
||||||
|
|
||||||
use RDF.XSD.Datatype.Primitive,
|
use RDF.XSD.Datatype.Primitive,
|
||||||
name: "boolean",
|
name: "boolean",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("boolean")
|
id: RDF.Utils.Bootstrapping.xsd_iri("boolean"),
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
@impl RDF.XSD.Datatype
|
@impl RDF.XSD.Datatype
|
||||||
def lexical_mapping(lexical, _) do
|
def lexical_mapping(lexical, _) do
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.Byte do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "byte",
|
name: "byte",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("byte"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("byte"),
|
||||||
base: RDF.XSD.Short
|
base: RDF.XSD.Short,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MinInclusive, -128
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, -128
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 127
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 127
|
||||||
|
|
|
@ -11,7 +11,8 @@ defmodule RDF.XSD.Date do
|
||||||
|
|
||||||
use RDF.XSD.Datatype.Primitive,
|
use RDF.XSD.Datatype.Primitive,
|
||||||
name: "date",
|
name: "date",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("date")
|
id: RDF.Utils.Bootstrapping.xsd_iri("date"),
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
|
|
||||||
# TODO: Are GMT/UTC actually allowed? Maybe because it is supported by Elixir's Datetime ...
|
# TODO: Are GMT/UTC actually allowed? Maybe because it is supported by Elixir's Datetime ...
|
||||||
|
|
|
@ -7,7 +7,8 @@ defmodule RDF.XSD.DateTime do
|
||||||
|
|
||||||
use RDF.XSD.Datatype.Primitive,
|
use RDF.XSD.Datatype.Primitive,
|
||||||
name: "dateTime",
|
name: "dateTime",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("dateTime")
|
id: RDF.Utils.Bootstrapping.xsd_iri("dateTime"),
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
@impl RDF.XSD.Datatype
|
@impl RDF.XSD.Datatype
|
||||||
def lexical_mapping(lexical, opts) do
|
def lexical_mapping(lexical, opts) do
|
||||||
|
|
|
@ -7,7 +7,8 @@ defmodule RDF.XSD.Decimal do
|
||||||
|
|
||||||
use RDF.XSD.Datatype.Primitive,
|
use RDF.XSD.Datatype.Primitive,
|
||||||
name: "decimal",
|
name: "decimal",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("decimal")
|
id: RDF.Utils.Bootstrapping.xsd_iri("decimal"),
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
alias Elixir.Decimal, as: D
|
alias Elixir.Decimal, as: D
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,8 @@ defmodule RDF.XSD.Double do
|
||||||
|
|
||||||
use RDF.XSD.Datatype.Primitive,
|
use RDF.XSD.Datatype.Primitive,
|
||||||
name: "double",
|
name: "double",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("double")
|
id: RDF.Utils.Bootstrapping.xsd_iri("double"),
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
@special_values ~W[positive_infinity negative_infinity nan]a
|
@special_values ~W[positive_infinity negative_infinity nan]a
|
||||||
|
|
||||||
|
|
|
@ -9,5 +9,6 @@ defmodule RDF.XSD.Float do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "float",
|
name: "float",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("float"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("float"),
|
||||||
base: RDF.XSD.Double
|
base: RDF.XSD.Double,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.Int do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "int",
|
name: "int",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("int"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("int"),
|
||||||
base: RDF.XSD.Long
|
base: RDF.XSD.Long,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MinInclusive, -2_147_483_648
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, -2_147_483_648
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 2_147_483_647
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 2_147_483_647
|
||||||
|
|
|
@ -10,7 +10,8 @@ defmodule RDF.XSD.Integer do
|
||||||
|
|
||||||
use RDF.XSD.Datatype.Primitive,
|
use RDF.XSD.Datatype.Primitive,
|
||||||
name: "integer",
|
name: "integer",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("integer")
|
id: RDF.Utils.Bootstrapping.xsd_iri("integer"),
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_applicable_facet RDF.XSD.Facets.MinInclusive
|
def_applicable_facet RDF.XSD.Facets.MinInclusive
|
||||||
def_applicable_facet RDF.XSD.Facets.MaxInclusive
|
def_applicable_facet RDF.XSD.Facets.MaxInclusive
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.Long do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "long",
|
name: "long",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("long"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("long"),
|
||||||
base: RDF.XSD.Integer
|
base: RDF.XSD.Integer,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MinInclusive, -9_223_372_036_854_775_808
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, -9_223_372_036_854_775_808
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 9_223_372_036_854_775_807
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 9_223_372_036_854_775_807
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.NegativeInteger do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "negativeInteger",
|
name: "negativeInteger",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("negativeInteger"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("negativeInteger"),
|
||||||
base: RDF.XSD.NonPositiveInteger
|
base: RDF.XSD.NonPositiveInteger,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, -1
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, -1
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.NonNegativeInteger do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "nonNegativeInteger",
|
name: "nonNegativeInteger",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("nonNegativeInteger"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("nonNegativeInteger"),
|
||||||
base: RDF.XSD.Integer
|
base: RDF.XSD.Integer,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.NonPositiveInteger do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "nonPositiveInteger",
|
name: "nonPositiveInteger",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("nonPositiveInteger"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("nonPositiveInteger"),
|
||||||
base: RDF.XSD.Integer
|
base: RDF.XSD.Integer,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 0
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 0
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.PositiveInteger do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "positiveInteger",
|
name: "positiveInteger",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("positiveInteger"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("positiveInteger"),
|
||||||
base: RDF.XSD.NonNegativeInteger
|
base: RDF.XSD.NonNegativeInteger,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MinInclusive, 1
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, 1
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.Short do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "short",
|
name: "short",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("short"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("short"),
|
||||||
base: RDF.XSD.Int
|
base: RDF.XSD.Int,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MinInclusive, -32768
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, -32768
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 32767
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 32767
|
||||||
|
|
|
@ -7,7 +7,8 @@ defmodule RDF.XSD.String do
|
||||||
|
|
||||||
use RDF.XSD.Datatype.Primitive,
|
use RDF.XSD.Datatype.Primitive,
|
||||||
name: "string",
|
name: "string",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("string")
|
id: RDF.Utils.Bootstrapping.xsd_iri("string"),
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
@impl RDF.XSD.Datatype
|
@impl RDF.XSD.Datatype
|
||||||
@spec lexical_mapping(String.t(), Keyword.t()) :: valid_value
|
@spec lexical_mapping(String.t(), Keyword.t()) :: valid_value
|
||||||
|
|
|
@ -7,7 +7,8 @@ defmodule RDF.XSD.Time do
|
||||||
|
|
||||||
use RDF.XSD.Datatype.Primitive,
|
use RDF.XSD.Datatype.Primitive,
|
||||||
name: "time",
|
name: "time",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("time")
|
id: RDF.Utils.Bootstrapping.xsd_iri("time"),
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
# TODO: Are GMT/UTC actually allowed? Maybe because it is supported by Elixir's Datetime ...
|
# TODO: Are GMT/UTC actually allowed? Maybe because it is supported by Elixir's Datetime ...
|
||||||
@grammar ~r/\A(\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z/
|
@grammar ~r/\A(\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z/
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.UnsignedByte do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "unsignedByte",
|
name: "unsignedByte",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("unsignedByte"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("unsignedByte"),
|
||||||
base: RDF.XSD.UnsignedShort
|
base: RDF.XSD.UnsignedShort,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 255
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 255
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.UnsignedInt do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "unsignedInt",
|
name: "unsignedInt",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("unsignedInt"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("unsignedInt"),
|
||||||
base: RDF.XSD.UnsignedLong
|
base: RDF.XSD.UnsignedLong,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 4_294_967_295
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 4_294_967_295
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.UnsignedLong do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "unsignedLong",
|
name: "unsignedLong",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("unsignedLong"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("unsignedLong"),
|
||||||
base: RDF.XSD.NonNegativeInteger
|
base: RDF.XSD.NonNegativeInteger,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 18_446_744_073_709_551_615
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 18_446_744_073_709_551_615
|
||||||
|
|
|
@ -2,7 +2,8 @@ defmodule RDF.XSD.UnsignedShort do
|
||||||
use RDF.XSD.Datatype.Restriction,
|
use RDF.XSD.Datatype.Restriction,
|
||||||
name: "unsignedShort",
|
name: "unsignedShort",
|
||||||
id: RDF.Utils.Bootstrapping.xsd_iri("unsignedShort"),
|
id: RDF.Utils.Bootstrapping.xsd_iri("unsignedShort"),
|
||||||
base: RDF.XSD.UnsignedInt
|
base: RDF.XSD.UnsignedInt,
|
||||||
|
register: false # core datatypes don't need to be registered
|
||||||
|
|
||||||
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
def_facet_constraint RDF.XSD.Facets.MinInclusive, 0
|
||||||
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 65535
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 65535
|
||||||
|
|
2
mix.exs
2
mix.exs
|
@ -14,6 +14,7 @@ defmodule RDF.Mixfile do
|
||||||
start_permanent: Mix.env == :prod,
|
start_permanent: Mix.env == :prod,
|
||||||
deps: deps(),
|
deps: deps(),
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
|
compilers: Mix.compilers ++ [:protocol_ex],
|
||||||
|
|
||||||
# Dialyzer
|
# Dialyzer
|
||||||
dialyzer: dialyzer(),
|
dialyzer: dialyzer(),
|
||||||
|
@ -68,6 +69,7 @@ defmodule RDF.Mixfile do
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:decimal, "~> 1.5"},
|
{:decimal, "~> 1.5"},
|
||||||
|
{:protocol_ex, "~> 0.4"},
|
||||||
|
|
||||||
{:credo, "~> 1.3", only: [:dev, :test], runtime: false},
|
{:credo, "~> 1.3", only: [:dev, :test], runtime: false},
|
||||||
{:dialyxir, "~> 1.0.0-rc.7", only: :dev, runtime: false},
|
{:dialyxir, "~> 1.0.0-rc.7", only: :dev, runtime: false},
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -20,6 +20,7 @@
|
||||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
|
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
|
||||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
||||||
|
"protocol_ex": {:hex, :protocol_ex, "0.4.3", "4acbe35da85109dc40315c1139bb7a65ebc7fc102d384cd8b3038384fbb9b282", [:mix], [], "hexpm", "6ca5ddb3505c9c86f17cd3f19838b34bf89966ae17078f79f81983b6a4391fe9"},
|
||||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"},
|
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"},
|
||||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
|
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
|
||||||
}
|
}
|
||||||
|
|
10
test/support/test_datatypes.ex
Normal file
10
test/support/test_datatypes.ex
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
defmodule RDF.TestDatatypes do
|
||||||
|
defmodule Age do
|
||||||
|
use RDF.XSD.Datatype.Restriction,
|
||||||
|
name: "age",
|
||||||
|
id: "http://example.com/Age",
|
||||||
|
base: RDF.XSD.PositiveInteger
|
||||||
|
|
||||||
|
def_facet_constraint RDF.XSD.Facets.MaxInclusive, 150
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule RDF.Literal.Datatype.RegistryTest do
|
defmodule RDF.Literal.Datatype.RegistryTest do
|
||||||
use ExUnit.Case
|
use RDF.Test.Case
|
||||||
|
|
||||||
|
alias RDF.TestDatatypes.Age
|
||||||
alias RDF.Literal.Datatype
|
alias RDF.Literal.Datatype
|
||||||
alias RDF.NS
|
alias RDF.NS
|
||||||
|
|
||||||
|
@ -36,20 +37,40 @@ defmodule RDF.Literal.Datatype.RegistryTest do
|
||||||
|
|
||||||
|
|
||||||
describe "get/1" do
|
describe "get/1" do
|
||||||
test "IRIs of supported datatypes from the XSD namespace" do
|
test "core datatypes" do
|
||||||
Enum.each(@supported_xsd_datatypes, fn xsd_datatype_iri ->
|
Enum.each(Datatype.Registry.core_datatypes(), fn datatype ->
|
||||||
assert xsd_datatype = Datatype.Registry.get(xsd_datatype_iri)
|
assert datatype == Datatype.Registry.get(datatype.id)
|
||||||
assert xsd_datatype == Datatype.Registry.get(to_string(xsd_datatype_iri))
|
assert datatype == Datatype.Registry.get(to_string(datatype.id))
|
||||||
assert RDF.iri(xsd_datatype.id) == xsd_datatype_iri
|
|
||||||
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "IRIs of unsupported datatypes from the XSD namespace" do
|
test "supported datatypes from the XSD namespace" do
|
||||||
|
Enum.each(@supported_xsd_datatypes, fn xsd_datatype_iri ->
|
||||||
|
assert xsd_datatype = Datatype.Registry.get(xsd_datatype_iri)
|
||||||
|
assert xsd_datatype.id == xsd_datatype_iri
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "unsupported datatypes from the XSD namespace" do
|
||||||
Enum.each(@unsupported_xsd_datatypes, fn xsd_datatype_iri ->
|
Enum.each(@unsupported_xsd_datatypes, fn xsd_datatype_iri ->
|
||||||
refute Datatype.Registry.get(xsd_datatype_iri)
|
refute Datatype.Registry.get(xsd_datatype_iri)
|
||||||
refute Datatype.Registry.get(to_string(xsd_datatype_iri))
|
refute Datatype.Registry.get(to_string(xsd_datatype_iri))
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "with IRI of custom datatype" do
|
||||||
|
assert Age == Datatype.Registry.get(Age.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with namespace terms" do
|
||||||
|
assert Age == Datatype.Registry.get(EX.Age)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "core datatypes are handled differently and should not be registered (for performance reasons)" do
|
||||||
|
Enum.each(Datatype.Registry.core_datatypes(), fn datatype ->
|
||||||
|
refute Datatype.Registry.Registration.datatype(datatype.id)
|
||||||
|
refute Datatype.Registry.Registration.datatype(to_string(datatype.id))
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
defmodule RDF.LiteralTest do
|
defmodule RDF.LiteralTest do
|
||||||
use ExUnit.Case
|
use RDF.Test.Case
|
||||||
|
|
||||||
import RDF.Sigils
|
|
||||||
import RDF.TestLiterals
|
import RDF.TestLiterals
|
||||||
|
|
||||||
alias RDF.{Literal, XSD, LangString}
|
alias RDF.{Literal, XSD, LangString}
|
||||||
|
@ -31,13 +30,18 @@ defmodule RDF.LiteralTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with typed literals" do
|
test "with core datatype literals" do
|
||||||
Enum.each Datatype.Registry.datatypes(), fn datatype ->
|
Enum.each Datatype.Registry.core_datatypes(), fn datatype ->
|
||||||
datatype_literal = datatype.new("foo").literal
|
datatype_literal = datatype.new("foo").literal
|
||||||
assert %Literal{literal: ^datatype_literal} = Literal.new(datatype_literal)
|
assert %Literal{literal: ^datatype_literal} = Literal.new(datatype_literal)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "with custom datatype literals" do
|
||||||
|
datatype_literal = RDF.TestDatatypes.Age.new(42).literal
|
||||||
|
assert %Literal{literal: typed_literal} = Literal.new(datatype_literal)
|
||||||
|
end
|
||||||
|
|
||||||
test "when options without datatype given" do
|
test "when options without datatype given" do
|
||||||
assert Literal.new(true, []) == XSD.Boolean.new(true)
|
assert Literal.new(true, []) == XSD.Boolean.new(true)
|
||||||
assert Literal.new(42, []) == XSD.Integer.new(42)
|
assert Literal.new(42, []) == XSD.Integer.new(42)
|
||||||
|
@ -80,6 +84,10 @@ defmodule RDF.LiteralTest do
|
||||||
assert Literal.new("foo", datatype: NS.XSD.string) == XSD.String.new("foo")
|
assert Literal.new("foo", datatype: NS.XSD.string) == XSD.String.new("foo")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "registered custom datatype" do
|
||||||
|
assert Literal.new(42, datatype: EX.Age) == RDF.TestDatatypes.Age.new(42)
|
||||||
|
end
|
||||||
|
|
||||||
test "unmapped/unknown datatype" do
|
test "unmapped/unknown datatype" do
|
||||||
assert Literal.new("custom typed value", datatype: "http://example/dt") ==
|
assert Literal.new("custom typed value", datatype: "http://example/dt") ==
|
||||||
Generic.new("custom typed value", datatype: "http://example/dt")
|
Generic.new("custom typed value", datatype: "http://example/dt")
|
||||||
|
|
Loading…
Reference in a new issue