Change Inspect form for RDF data structures to be Turtle-based
This commit is contained in:
parent
929e2a8c81
commit
8d98461e0b
3 changed files with 168 additions and 72 deletions
|
@ -15,10 +15,12 @@ This project adheres to [Semantic Versioning](http://semver.org/) and
|
|||
|
||||
### Changed
|
||||
|
||||
- the Inspect form of the RDF data structures are now Turtle-based and respect
|
||||
the usual `:limit` behaviour
|
||||
- more compact Inspect form for `RDF.PrefixMap`
|
||||
- the `RDF.Turtle.Encoder` accepts `RDF.Vocabulary.Namespace` modules as `base`
|
||||
- the performance of the `RDF.Turtle.Encoder` was improved (by using a more
|
||||
efficient method for resolving IRIs to prefixed names for most use cases)
|
||||
- the performance of the `RDF.Turtle.Encoder` was improved (by using a for most
|
||||
use cases more efficient method for resolving IRIs to prefixed names)
|
||||
- `RDF.BlankNode.new/0` creates integer-based blank nodes, which is much more
|
||||
efficient in terms of performance and memory consumption than the previous
|
||||
ref-based blank nodes
|
||||
|
|
|
@ -1,43 +1,3 @@
|
|||
defmodule RDF.InspectHelper do
|
||||
@moduledoc false
|
||||
|
||||
import Inspect.Algebra
|
||||
|
||||
def objects_doc(objects, opts) do
|
||||
objects
|
||||
|> Enum.map(fn {object, _} -> to_doc(object, opts) end)
|
||||
|> fold_doc(fn object, acc -> line(object, acc) end)
|
||||
end
|
||||
|
||||
def predications_doc(predications, opts) do
|
||||
predications
|
||||
|> Enum.map(fn {predicate, objects} ->
|
||||
to_doc(predicate, opts)
|
||||
|> line(objects_doc(objects, opts))
|
||||
|> nest(4)
|
||||
end)
|
||||
|> fold_doc(fn predication, acc ->
|
||||
line(predication, acc)
|
||||
end)
|
||||
end
|
||||
|
||||
def descriptions_doc(descriptions, opts) do
|
||||
descriptions
|
||||
|> Enum.map(fn {subject, description} ->
|
||||
to_doc(subject, opts)
|
||||
|> line(predications_doc(description.predications, opts))
|
||||
|> nest(4)
|
||||
end)
|
||||
|> fold_doc(fn predication, acc ->
|
||||
line(predication, acc)
|
||||
end)
|
||||
end
|
||||
|
||||
def surround_doc(left, doc, right) do
|
||||
concat(concat(left, nest(doc, 1)), right)
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Inspect, for: RDF.IRI do
|
||||
def inspect(%RDF.IRI{value: value}, _opts) do
|
||||
"~I<#{value}>"
|
||||
|
@ -57,49 +17,100 @@ defimpl Inspect, for: RDF.Literal do
|
|||
end
|
||||
|
||||
defimpl Inspect, for: RDF.Description do
|
||||
import Inspect.Algebra
|
||||
import RDF.InspectHelper
|
||||
def inspect(description, opts) do
|
||||
if opts.structs do
|
||||
try do
|
||||
limit = opts.limit < RDF.Description.statement_count(description)
|
||||
|
||||
def inspect(%RDF.Description{subject: subject, predications: predications}, opts) do
|
||||
doc =
|
||||
space("subject:", to_doc(subject, opts))
|
||||
|> line(predications_doc(predications, opts))
|
||||
|> nest(4)
|
||||
description =
|
||||
if limit do
|
||||
description.subject
|
||||
|> RDF.Description.new(init: Enum.take(description, opts.limit))
|
||||
else
|
||||
description
|
||||
end
|
||||
|
||||
surround_doc("#RDF.Description{", doc, "}")
|
||||
body =
|
||||
description
|
||||
|> RDF.Turtle.write_string!(only: :triples)
|
||||
|> String.trim_trailing()
|
||||
|
||||
"#RDF.Description\n#{body}#{if limit, do: "..\n..."}"
|
||||
rescue
|
||||
caught_exception ->
|
||||
message =
|
||||
"got #{inspect(caught_exception.__struct__)} with message " <>
|
||||
"#{inspect(Exception.message(caught_exception))} while inspecting RDF.Description #{
|
||||
description.subject
|
||||
}"
|
||||
|
||||
exception = Inspect.Error.exception(message: message)
|
||||
|
||||
if opts.safe do
|
||||
Inspect.inspect(exception, opts)
|
||||
else
|
||||
reraise(exception, __STACKTRACE__)
|
||||
end
|
||||
end
|
||||
else
|
||||
Inspect.Map.inspect(description, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Inspect, for: RDF.Graph do
|
||||
import Inspect.Algebra
|
||||
import RDF.InspectHelper
|
||||
def inspect(graph, opts) do
|
||||
if opts.structs do
|
||||
try do
|
||||
limit = opts.limit < RDF.Graph.statement_count(graph)
|
||||
|
||||
def inspect(%RDF.Graph{name: name, descriptions: descriptions}, opts) do
|
||||
doc =
|
||||
space("name:", to_doc(name, opts))
|
||||
|> line(descriptions_doc(descriptions, opts))
|
||||
|> nest(4)
|
||||
graph =
|
||||
if limit do
|
||||
graph
|
||||
|> RDF.Graph.clear()
|
||||
|> RDF.Graph.add(Enum.take(graph, opts.limit))
|
||||
else
|
||||
graph
|
||||
end
|
||||
|
||||
surround_doc("#RDF.Graph{", doc, "}")
|
||||
header = "#RDF.Graph name: #{inspect(graph.name)}"
|
||||
|
||||
body =
|
||||
graph
|
||||
|> RDF.Turtle.write_string!()
|
||||
|> String.trim_trailing()
|
||||
|
||||
"#{header}\n#{body}#{if limit, do: "..\n..."}"
|
||||
rescue
|
||||
caught_exception ->
|
||||
message =
|
||||
"got #{inspect(caught_exception.__struct__)} with message " <>
|
||||
"#{inspect(Exception.message(caught_exception))} while inspecting RDF.Graph #{
|
||||
graph.name
|
||||
}"
|
||||
|
||||
exception = Inspect.Error.exception(message: message)
|
||||
|
||||
if opts.safe do
|
||||
Inspect.inspect(exception, opts)
|
||||
else
|
||||
reraise(exception, __STACKTRACE__)
|
||||
end
|
||||
end
|
||||
else
|
||||
Inspect.Map.inspect(graph, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Inspect, for: RDF.Dataset do
|
||||
import Inspect.Algebra
|
||||
import RDF.InspectHelper
|
||||
|
||||
def inspect(%RDF.Dataset{name: name} = dataset, opts) do
|
||||
doc =
|
||||
space("name:", to_doc(name, opts))
|
||||
|> line(graphs_doc(RDF.Dataset.graphs(dataset), opts))
|
||||
|> nest(4)
|
||||
|
||||
surround_doc("#RDF.Dataset{", doc, "}")
|
||||
end
|
||||
|
||||
defp graphs_doc(graphs, opts) do
|
||||
graphs
|
||||
|> Enum.map(fn graph -> to_doc(graph, opts) end)
|
||||
|> fold_doc(fn graph, acc -> line(graph, acc) end)
|
||||
def inspect(dataset, opts) do
|
||||
map = [name: dataset.name, graph_names: Map.keys(dataset.graphs)]
|
||||
open = color("%RDF.Dataset{", :map, opts)
|
||||
sep = color(",", :map, opts)
|
||||
close = color("}", :map, opts)
|
||||
container_doc(open, map, close, opts, &Inspect.List.keyword/2, separator: sep, break: :strict)
|
||||
end
|
||||
end
|
||||
|
|
83
test/unit/inspect_test.exs
Normal file
83
test/unit/inspect_test.exs
Normal file
|
@ -0,0 +1,83 @@
|
|||
defmodule RDF.InspectTest do
|
||||
use RDF.Test.Case
|
||||
|
||||
alias RDF.Turtle
|
||||
alias RDF.NS.RDFS
|
||||
|
||||
@test_description EX.S
|
||||
|> RDF.type(RDFS.Class)
|
||||
|> EX.p("foo", 42)
|
||||
|
||||
@test_graph Graph.new(
|
||||
[
|
||||
EX.S1
|
||||
|> EX.p1(EX.O1)
|
||||
|> EX.p2("foo", 42),
|
||||
EX.S2
|
||||
|> EX.p3(EX.O3)
|
||||
],
|
||||
prefixes: [ex: EX]
|
||||
)
|
||||
|
||||
describe "RDF.Description" do
|
||||
test "it includes a header" do
|
||||
{header, _} = inspect_parts(@test_description)
|
||||
assert header == "#RDF.Description"
|
||||
end
|
||||
|
||||
test "it encodes the description in Turtle" do
|
||||
{_, body} = inspect_parts(@test_description)
|
||||
|
||||
assert body ==
|
||||
Turtle.write_string!(@test_description, only: :triples) |> String.trim()
|
||||
end
|
||||
|
||||
test ":limit option" do
|
||||
{_, triples} = inspect_parts(@test_description, limit: 2)
|
||||
|
||||
assert triples ==
|
||||
(EX.S
|
||||
|> EX.p("foo", 42)
|
||||
|> Turtle.write_string!(only: :triples)
|
||||
|> String.trim()) <>
|
||||
"..\n..."
|
||||
end
|
||||
end
|
||||
|
||||
describe "RDF.Graph" do
|
||||
test "it includes a header with the graph name" do
|
||||
{header, _} = inspect_parts(@test_graph)
|
||||
assert header == "#RDF.Graph name: nil"
|
||||
|
||||
graph_name = RDF.iri(EX.Graph)
|
||||
{header, _} = @test_graph |> Graph.change_name(graph_name) |> inspect_parts()
|
||||
assert header == "#RDF.Graph name: #{inspect(graph_name)}"
|
||||
end
|
||||
|
||||
test "it encodes the graph in Turtle" do
|
||||
{_, body} = inspect_parts(@test_graph)
|
||||
assert body == Turtle.write_string!(@test_graph) |> String.trim()
|
||||
end
|
||||
|
||||
test ":limit option" do
|
||||
{_, body} = inspect_parts(@test_graph, limit: 2)
|
||||
|
||||
assert body ==
|
||||
(Graph.new(
|
||||
EX.S1
|
||||
|> EX.p1(EX.O1)
|
||||
|> EX.p2(42),
|
||||
prefixes: [ex: EX]
|
||||
)
|
||||
|> Turtle.write_string!()
|
||||
|> String.trim()) <>
|
||||
"..\n..."
|
||||
end
|
||||
end
|
||||
|
||||
def inspect_parts(graph, opts \\ []) do
|
||||
inspect_form = inspect(graph, opts)
|
||||
[header, body] = String.split(inspect_form, "\n", parts: 2)
|
||||
{header, body}
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue