I know other people have other tools for RDF, but I'm kinda happy with Donatello now 🙂
For instance, if I have an entity with a url of and my prefix ex is mapped accordingly, I can represent it with the keyword :ex/_1
If I have a set of properties on it, then I can just put those into a map:
{:a :ex/MyClass
:rdfs/label "An instance of the class"
:ex/fib [1 1 2 3 5 8]}
I can write that with write-triples-map!
(ttl/write-triples-map!
out
:ex/_1
{:a :ex/MyClass
:rdfs/label "An instance of the class"
:ex/fib [1 1 2 3 5 8]})Of course, I can also write triples directly:
(ttl/write-triple! out :ex/_1 :a :ex/MyClass)
(ttl/write-triple! out (URI. "") :rdfs/label "An instance of the class") And it handles anonymous nodes as well:
(ttl/write-triple! out
{:rdfs/label "An anonymous instance"
:ex/fib [1 1 2 3 5 8]} :a :ex/Class)
It writes all of this pretty much as you'd expect, complete with indenting.
e.g. one of my test cases looks like:
(ttl/write-triple! out
{:a :data/Class
:b/data :data/_123
:b/more {:a :data/Inner :b/list [1 2 3]}}
:data/rel
{:p1 #{"data a" "data b"}
(URI. " ") 5})
and the output is:
[a data:Class;
b:data data:_123;
b:more [a data:Inner;
b:list (1 2 3)]] data:rel [:p1 "data a", "data b";
< > 5].
Feeling good about it, and wanted to show it off 😄
thanks
Really nice @quoll! I like the readable way blank nodes are outputted, rather than them being enumerated.
@quoll I've been looking for a Turtle autoformatter, i.e. given a .ttl file, that it deterministically autoformats it. This would make collaboration in our team a lot nicer. If I'm not mistaken, if Donatello supported .ttl files as input as well, it could basically serve as an autoformatter. Correct? I don't know if this is something you think Donatello should support, but I'd love to see something like this
This is what Raphael is for
Of course I could easily compose Raphael and Donatello, (i.e. basically summon the TMNTs 😉) to get that done. Perhaps I can run clj such that it runs Raphael for a given ttl file and output that in the shell
Yes I was just realizing!
I built them to complement each other
An in-memory Asami graph is just a nested map like what you give to Donatello to output. Raphael takes TTL input and gives you a stream of triples, but those are easy to insert into a nested map
I see what you're saying
This is really nice 😄. I'm so annoyed by inconsistent and noisy Turtle code messy people are outputting. This will put an end to that 🙂. Thanks!
(it's also really nice for doing version control)
When I say “easy”, I just meant a small amount of code. This is what Asami grew out of, back-in-the-day: https://github.com/threatgrid/naga/blob/d149904da8ecb510ddbbb5b73824c74da4b05d77/src/naga/storage/memory/index.clj#L9
It uses Plumatic Schema (hence the unusual typing syntax)
Sorry, about to start driving again, so I can’t respond for the next hour or more
Np. I've got plenty to look at
I’ll respond as soon as I can though!
Thanks!
I’m actually in the process of updating Raphael so that it no longer does look-ahead (this is only in 3 places)
Why? Well, every function accepts a string and an offset as the first 2 parameters. It calls get-char to get the character at whatever the current offset is, and then increments the offset. This pair could be replaced with a StringReader, and everything could then just call getChar on the reader. Meaning that it wouldn’t need to be a StringReader… it could just be a Reader
Well, it would actually be my own protocol, and I could use it to wrap a Reader. That way it will stay ClojureScript compatible
But that’s just a technicality
However, it’s a technicality that means that I want to avoid a PushbackReader (yes, I could write this for ClojureScript, but the correct approach is to avoid look-ahead)
I pulled it out of 2 of the 3 places yesterday, though I haven’t tested it yet
I'm afraid this goes straight over my head. What does look-ahead (and the avoidance thereof) mean in this context?
I do a subs at the front of the string, and if it doesn’t match what I’m looking for, I go to the other thing it could match. ie. I look several characters into the stream for something, and if it doesn’t match, I rewind and match it to something else
eg. If I see a line starting with:
prefix
Then it could be the start of a prefix operation:
prefix ex: .
Or it could be the start of a triple:
prefix:xyz rdf:value 5 .
So I need to look at the first 6 characters to match to prefix without knowing what kind of line I’m parsing. If the next character is a whitespace then it’s a directive. Otherwise it’s prefixed name like prefixes:abcThe parser can’t know what kind of line it’s looking at until it’s looked ahead by 7 characters
Ah, yes that clears things up!