Last active
June 23, 2020 14:06
-
-
Save YuukanOO/f5fbe36f757123976d3f5251fbdd8c6d to your computer and use it in GitHub Desktop.
Solid sandbox
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createDocument, fetchDocument, TripleSubject } from "tripledoc"; | |
interface SchemaDefinition { | |
write(subject: TripleSubject, value: any): void; | |
read(subject: TripleSubject): any; | |
} | |
class StringDefinition implements SchemaDefinition { | |
private readonly predicates: string[]; | |
constructor(...predicates: string[]) { | |
this.predicates = predicates; | |
} | |
write(subject: TripleSubject, value: any): void { | |
this.predicates.forEach((p) => subject.addString(p, value)); | |
} | |
read(subject: TripleSubject): any { | |
// TODO: find the first predicate matching one | |
return subject.getString(this.predicates[0]); | |
} | |
} | |
type Schema<T> = { [key in keyof Partial<T>]: SchemaDefinition }; | |
abstract class SolidRepository<T> { | |
protected constructor( | |
private readonly ctor: new (...args: any[]) => T, | |
private readonly type: string, | |
private readonly schema: Schema<T> | |
) {} | |
async save(data: T) { | |
const doc = await fetchDocument( | |
"https://yuukanoo.solid.community/public/bookmarks.ttl" | |
); | |
// Create a new subject record | |
const subject = doc.addSubject(); | |
subject.addRef( | |
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type", | |
this.type | |
); | |
// And add all defined schema members | |
for (let key in this.schema) { | |
this.schema[key].write(subject, data[key]); | |
} | |
await doc.save(); | |
} | |
async all(): Promise<T[]> { | |
const doc = await fetchDocument( | |
"https://yuukanoo.solid.community/public/bookmarks.ttl" | |
); | |
const subjects = doc.getAllSubjectsOfType(this.type); | |
return subjects.map((s) => { | |
const props: any = {}; | |
for (let k in this.schema) { | |
props[k] = this.schema[k].read(s); | |
} | |
Object.setPrototypeOf(props, this.ctor.prototype); | |
return props; | |
}); | |
} | |
} | |
class BookmarksRepository extends SolidRepository<Bookmark> { | |
constructor() { | |
super(Bookmark, "https://www.w3.org/2002/01/bookmark#Bookmark", { | |
title: new StringDefinition("http://purl.org/dc/elements/1.1/title"), | |
url: new StringDefinition("https://www.w3.org/2002/01/bookmark#recalls"), | |
}); | |
} | |
} | |
class Bookmark { | |
constructor(public title: string, public url: string) {} | |
pretty(): string { | |
return `this is the bookmark "${this.title}"`; | |
} | |
} | |
async function main() { | |
const b = new Bookmark("My google bookmark", "https://google.com"); | |
const repo = new BookmarksRepository(); | |
// await repo.save(b); | |
const bookmarks = await repo.all(); | |
console.log(bookmarks); | |
} | |
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as $rdf from "rdflib"; | |
/** | |
* Let's starts with sprinkle basic features. Your problem is simple: you got a bunch | |
* of local object you want to save on a user's pod. | |
*/ | |
class Bookmark { | |
constructor(public title: string, public url: string, public id?: string) {} | |
pretty() { | |
return `${this.title} @ ${this.url}`; | |
} | |
} | |
interface Options<T> { | |
/** | |
* Hold the class URI of what is stored in a repository. | |
*/ | |
class: string; | |
/** | |
* Property name which represents the identity (ie. URI) of a resource. | |
*/ | |
identityKey: keyof T | string; | |
/** | |
* Document URI where data is stored. | |
*/ | |
source: string; | |
/** | |
* Holds the mapping between your model and the Linked Data representation. This | |
* is where you should really take your time to choose the appropriate vocabulary | |
* to add meanings to your objects. | |
*/ | |
schema: { [key in keyof Partial<T>]: string } | { [key: string]: string }; | |
} | |
type Tracked<T> = T & { __changes: { [key: string]: any }; __schema: string[] }; | |
/** | |
* And so you need a repository to save and retrieve this kind of data. It will be | |
* the single entry point to the user pod. | |
*/ | |
class Repository<T extends object> { | |
static store: $rdf.IndexedFormula; | |
static fetcher: $rdf.Fetcher; | |
private readonly watchProperties: string[]; | |
/** | |
* Here we are, the repository constructor where you should take care of defining | |
* WHAT you are storing, WHERE you expect it to be and HOW properties are representing | |
* in the Linked Data universe. | |
* | |
* The klass argument is the class function used to define the propotype when | |
* retrieving data on the web and reconstructing them using the provided schema. | |
*/ | |
constructor( | |
private readonly options: Options<T>, | |
private readonly klass?: { new (...args: any[]): T } | |
) { | |
// Let's make sure the store and the fetcher are initialized. | |
Repository.store = Repository.store ?? $rdf.graph(); | |
Repository.fetcher = | |
Repository.fetcher ?? new $rdf.Fetcher(Repository.store); | |
this.watchProperties = Object.keys(this.options.schema); | |
} | |
/** | |
* Save some data in this repository and returns a proxified object which will | |
* keep track of changes during the object lifecycle. | |
* | |
* If it's a new object constructed from outside the repository, you must use | |
* the returned object so that updates will work correctly. | |
*/ | |
async save(data: T): Promise<T> { | |
const tdata = this.track(data, true); | |
console.log("must do something with those changes", tdata.__changes); | |
return tdata; | |
} | |
async all(): Promise<T[]> { | |
await Repository.fetcher.load(this.options.source); | |
const data = Repository.store.match( | |
undefined, | |
undefined, | |
undefined, | |
$rdf.sym(this.options.source) // Filter on the why which represents the source document | |
); | |
return []; | |
} | |
protected track(data: T, newObject: boolean = false): Tracked<T> { | |
// Check if already a proxy with a specific key | |
if ((<any>data).__changes !== undefined) { | |
return data as Tracked<T>; | |
} | |
const proxy = new Proxy(data, { | |
set(target: any, p, value, receiver) { | |
if (target.__schema && target.__schema.includes(p)) { | |
target.__changes[p] = value; | |
} | |
// Check if in schema | |
return Reflect.set(target, p, value, receiver); | |
}, | |
}) as Tracked<T>; | |
proxy.__changes = newObject | |
? this.watchProperties.reduce((changes, prop) => { | |
changes[prop] = (<any>data)[prop]; | |
return changes; | |
}, {} as { [key: string]: any }) | |
: {}; | |
proxy.__schema = this.watchProperties; | |
return proxy; | |
} | |
} | |
(async function () { | |
const repo = new Repository( | |
{ | |
source: "https://yuukanoo.solid.community/public/bookmarks.ttl", | |
class: "https://www.w3.org/2002/01/bookmark#Bookmark", | |
identityKey: "id", | |
schema: { | |
title: "", | |
url: "", | |
}, | |
}, | |
Bookmark | |
); | |
// await repo.all(); | |
let bookmark = new Bookmark("mon premier lien", "https://julien.leicher.me"); | |
bookmark = await repo.save(bookmark); | |
// const p = new Proxy(bookmark, { | |
// get(target, p, receiver) { | |
// console.log("getting a value for", p); | |
// return Reflect.get(target, p, receiver); | |
// }, | |
// }); | |
// Object.setPrototypeOf(bookmark, p); | |
// bookmark.title = "changing to hello"; | |
bookmark.title = "or something else"; | |
console.log(bookmark.pretty(), bookmark); | |
// await repo.save(bookmark); | |
// const bookmarkProxy = new Proxy(bookmark, { | |
// set(target, p: keyof Bookmark, value, receiver) { | |
// console.log("setting value", p, "was", target[p], "now", value); | |
// return Reflect.set(target, p, value, receiver); | |
// }, | |
// }); | |
// bookmarkProxy.title = "john doe"; | |
// console.log(bookmarkProxy.title); | |
// const store = $rdf.graph(); | |
// const fetcher = new $rdf.Fetcher(store, { timeout: 5000 }); | |
// await fetcher.load("https://yuukanoo.solid.community/public/bookmarks.ttl"); | |
// const BOOK = $rdf.Namespace("https://www.w3.org/2002/01/bookmark#"); | |
// const DC = $rdf.Namespace("http://purl.org/dc/elements/1.1/"); | |
// const RDF = $rdf.Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#"); | |
// const q = store.match( | |
// undefined, | |
// $rdf.sym("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"), | |
// BOOK("Bookmark") | |
// ); | |
// console.log(store.any(q[0].subject, DC("title"))); | |
// const r = $rdf.sym( | |
// store.each(undefined, RDF("type"), BOOK("Bookmark"))[7].value | |
// ); | |
// console.log(store.any(r, DC("title"), undefined)); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment