Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@YuukanOO
Last active June 23, 2020 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save YuukanOO/f5fbe36f757123976d3f5251fbdd8c6d to your computer and use it in GitHub Desktop.
Save YuukanOO/f5fbe36f757123976d3f5251fbdd8c6d to your computer and use it in GitHub Desktop.
Solid sandbox
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();
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