Refactor PluginAdapter abstraction (#512)
- PluginAdapters no longer expose a Renderer; instead, the render methods are inlined on the PluginAdapter. The extra abstraction didn't provide any lift in the current architecture. - The edgeVerb function has been removed. - PluginAdapters now enumerate EdgeTypes. Each has a prefix, and a forward and a backward name. Test plan: `yarn travis`, plus manual testing of the frontend and the weight config.
This commit is contained in:
parent
28100275c4
commit
3c14ef8a43
|
@ -32,7 +32,7 @@ export function nodeDescription(
|
|||
}
|
||||
|
||||
try {
|
||||
return adapter.renderer().nodeDescription(address);
|
||||
return adapter.nodeDescription(address);
|
||||
} catch (e) {
|
||||
const result = NodeAddress.toString(address);
|
||||
console.error(`Error getting description for ${result}: ${e.message}`);
|
||||
|
@ -54,13 +54,15 @@ function edgeVerb(
|
|||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
return adapter.renderer().edgeVerb(address, direction);
|
||||
} catch (e) {
|
||||
const edgeType = adapter
|
||||
.edgeTypes()
|
||||
.find(({prefix}) => EdgeAddress.hasPrefix(address, prefix));
|
||||
if (edgeType == null) {
|
||||
const result = EdgeAddress.toString(address);
|
||||
console.error(`Error getting description for ${result}: ${e.message}`);
|
||||
console.warn(`No edge type for ${result}`);
|
||||
return result;
|
||||
}
|
||||
return direction === "FORWARD" ? edgeType.forwardName : edgeType.backwardName;
|
||||
}
|
||||
|
||||
function scoreDisplay(probability: number) {
|
||||
|
|
|
@ -61,57 +61,66 @@ function example() {
|
|||
throw new Error("unused");
|
||||
},
|
||||
renderer: () => ({
|
||||
nodeDescription: (x) => `foo: ${NodeAddress.toString(x)}`,
|
||||
edgeVerb: (_unused_e, direction) =>
|
||||
direction === "FORWARD" ? "foos" : "is fooed by",
|
||||
}),
|
||||
nodeDescription: (x) => `foo: ${NodeAddress.toString(x)}`,
|
||||
nodePrefix: () => NodeAddress.fromParts(["foo"]),
|
||||
edgePrefix: () => EdgeAddress.fromParts(["foo"]),
|
||||
nodeTypes: () => [
|
||||
{name: "alpha", prefix: NodeAddress.fromParts(["foo", "a"])},
|
||||
{name: "beta", prefix: NodeAddress.fromParts(["foo", "b"])},
|
||||
],
|
||||
edgeTypes: () => [
|
||||
{
|
||||
prefix: EdgeAddress.fromParts(["foo"]),
|
||||
forwardName: "foos",
|
||||
backwardName: "is fooed by",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: () => "bar",
|
||||
graph: () => {
|
||||
throw new Error("unused");
|
||||
},
|
||||
renderer: () => ({
|
||||
nodeDescription: (x) => `bar: ${NodeAddress.toString(x)}`,
|
||||
edgeVerb: (_unused_e, direction) =>
|
||||
direction === "FORWARD" ? "bars" : "is barred by",
|
||||
}),
|
||||
nodeDescription: (x) => `bar: ${NodeAddress.toString(x)}`,
|
||||
nodePrefix: () => NodeAddress.fromParts(["bar"]),
|
||||
edgePrefix: () => EdgeAddress.fromParts(["bar"]),
|
||||
nodeTypes: () => [
|
||||
{name: "alpha", prefix: NodeAddress.fromParts(["bar", "a"])},
|
||||
],
|
||||
edgeTypes: () => [
|
||||
{
|
||||
prefix: EdgeAddress.fromParts(["bar"]),
|
||||
forwardName: "bars",
|
||||
backwardName: "is barred by",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: () => "xox",
|
||||
graph: () => {
|
||||
throw new Error("unused");
|
||||
},
|
||||
renderer: () => ({
|
||||
nodeDescription: (_unused_arg) => `xox node!`,
|
||||
edgeVerb: (_unused_e, _unused_direction) => `xox'd`,
|
||||
}),
|
||||
nodeDescription: (_unused_arg) => `xox node!`,
|
||||
nodePrefix: () => NodeAddress.fromParts(["xox"]),
|
||||
edgePrefix: () => EdgeAddress.fromParts(["xox"]),
|
||||
nodeTypes: () => [],
|
||||
edgeTypes: () => [],
|
||||
},
|
||||
{
|
||||
name: () => "unused",
|
||||
graph: () => {
|
||||
throw new Error("unused");
|
||||
},
|
||||
renderer: () => {
|
||||
throw new Error("Impossible!");
|
||||
nodeDescription: () => {
|
||||
throw new Error("Unused");
|
||||
},
|
||||
nodePrefix: () => NodeAddress.fromParts(["unused"]),
|
||||
edgePrefix: () => EdgeAddress.fromParts(["unused"]),
|
||||
nodeTypes: () => [],
|
||||
edgeTypes: () => [],
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -2,19 +2,19 @@
|
|||
|
||||
import type {Graph, NodeAddressT, EdgeAddressT} from "../core/graph";
|
||||
|
||||
export interface Renderer {
|
||||
nodeDescription(NodeAddressT): string;
|
||||
edgeVerb(EdgeAddressT, "FORWARD" | "BACKWARD"): string;
|
||||
}
|
||||
|
||||
export interface PluginAdapter {
|
||||
name(): string;
|
||||
graph(): Graph;
|
||||
renderer(): Renderer;
|
||||
nodePrefix(): NodeAddressT;
|
||||
edgePrefix(): EdgeAddressT;
|
||||
nodeTypes(): Array<{|
|
||||
+name: string,
|
||||
+prefix: NodeAddressT,
|
||||
|}>;
|
||||
nodeDescription(NodeAddressT): string;
|
||||
edgeTypes(): Array<{|
|
||||
+forwardName: string,
|
||||
+backwardName: string,
|
||||
+prefix: EdgeAddressT,
|
||||
|}>;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
// @flow
|
||||
import type {
|
||||
PluginAdapter as IPluginAdapter,
|
||||
Renderer as IRenderer,
|
||||
} from "../../app/pluginAdapter";
|
||||
import type {PluginAdapter as IPluginAdapter} from "../../app/pluginAdapter";
|
||||
import {Graph} from "../../core/graph";
|
||||
import * as N from "./nodes";
|
||||
import * as E from "./edges";
|
||||
import {description, edgeVerb} from "./render";
|
||||
import {description} from "./render";
|
||||
|
||||
export async function createPluginAdapter(
|
||||
repoOwner: string,
|
||||
|
@ -33,15 +30,18 @@ class PluginAdapter implements IPluginAdapter {
|
|||
graph() {
|
||||
return this._graph;
|
||||
}
|
||||
renderer() {
|
||||
return new Renderer();
|
||||
}
|
||||
nodePrefix() {
|
||||
return N._Prefix.base;
|
||||
}
|
||||
edgePrefix() {
|
||||
return E._Prefix.base;
|
||||
}
|
||||
nodeDescription(node) {
|
||||
// This cast is unsound, and might throw at runtime, but won't have
|
||||
// silent failures or cause problems down the road.
|
||||
const address = N.fromRaw((node: any));
|
||||
return description(address);
|
||||
}
|
||||
nodeTypes() {
|
||||
return [
|
||||
{name: "Blob", prefix: N._Prefix.blob},
|
||||
|
@ -50,16 +50,33 @@ class PluginAdapter implements IPluginAdapter {
|
|||
{name: "Tree entry", prefix: N._Prefix.treeEntry},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class Renderer implements IRenderer {
|
||||
nodeDescription(node) {
|
||||
// This cast is unsound, and might throw at runtime, but won't have
|
||||
// silent failures or cause problems down the road.
|
||||
const address = N.fromRaw((node: any));
|
||||
return description(address);
|
||||
}
|
||||
edgeVerb(edgeAddress, direction) {
|
||||
return edgeVerb(E.fromRaw((edgeAddress: any)), direction);
|
||||
edgeTypes() {
|
||||
return [
|
||||
{
|
||||
forwardName: "has tree",
|
||||
backwardName: "owned by",
|
||||
prefix: E._Prefix.hasTree,
|
||||
},
|
||||
{
|
||||
forwardName: "has parent",
|
||||
backwardName: "is parent of",
|
||||
prefix: E._Prefix.hasParent,
|
||||
},
|
||||
{
|
||||
forwardName: "includes",
|
||||
backwardName: "is included by",
|
||||
prefix: E._Prefix.includes,
|
||||
},
|
||||
{
|
||||
forwardName: "evolves to",
|
||||
backwardName: "evolves from",
|
||||
prefix: E._Prefix.becomes,
|
||||
},
|
||||
{
|
||||
forwardName: "has contents",
|
||||
backwardName: "is contents of",
|
||||
prefix: E._Prefix.hasContents,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// @flow
|
||||
|
||||
import * as N from "./nodes";
|
||||
import * as E from "./edges";
|
||||
|
||||
export function description(address: N.StructuredAddress) {
|
||||
switch (address.type) {
|
||||
|
@ -19,24 +18,3 @@ export function description(address: N.StructuredAddress) {
|
|||
throw new Error(`unknown type: ${(address.type: empty)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function edgeVerb(
|
||||
address: E.StructuredAddress,
|
||||
direction: "FORWARD" | "BACKWARD"
|
||||
) {
|
||||
const forward = direction === "FORWARD";
|
||||
switch (address.type) {
|
||||
case "HAS_TREE":
|
||||
return forward ? "has tree" : "owned by";
|
||||
case "HAS_PARENT":
|
||||
return forward ? "has parent" : "is parent of";
|
||||
case "INCLUDES":
|
||||
return forward ? "includes" : "is included by";
|
||||
case "BECOMES":
|
||||
return forward ? "evolves to" : "evolves from";
|
||||
case "HAS_CONTENTS":
|
||||
return forward ? "has contents" : "is contents of";
|
||||
default:
|
||||
throw new Error(`unknown type: ${(address.type: empty)}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
// @flow
|
||||
import type {
|
||||
PluginAdapter as IPluginAdapter,
|
||||
Renderer as IRenderer,
|
||||
} from "../../app/pluginAdapter";
|
||||
import type {PluginAdapter as IPluginAdapter} from "../../app/pluginAdapter";
|
||||
import {type Graph, NodeAddress} from "../../core/graph";
|
||||
import {createGraph} from "./createGraph";
|
||||
import * as N from "./nodes";
|
||||
import * as E from "./edges";
|
||||
import {RelationalView} from "./relationalView";
|
||||
import {description, edgeVerb} from "./render";
|
||||
import {description} from "./render";
|
||||
|
||||
export async function createPluginAdapter(
|
||||
repoOwner: string,
|
||||
|
@ -35,12 +32,19 @@ class PluginAdapter implements IPluginAdapter {
|
|||
name() {
|
||||
return "GitHub";
|
||||
}
|
||||
nodeDescription(node) {
|
||||
// This cast is unsound, and might throw at runtime, but won't have
|
||||
// silent failures or cause problems down the road.
|
||||
const address = N.fromRaw((node: any));
|
||||
const entity = this._view.entity(address);
|
||||
if (entity == null) {
|
||||
throw new Error(`unknown entity: ${NodeAddress.toString(node)}`);
|
||||
}
|
||||
return description(entity);
|
||||
}
|
||||
graph() {
|
||||
return this._graph;
|
||||
}
|
||||
renderer() {
|
||||
return new Renderer(this._view);
|
||||
}
|
||||
nodePrefix() {
|
||||
return N._Prefix.base;
|
||||
}
|
||||
|
@ -57,24 +61,28 @@ class PluginAdapter implements IPluginAdapter {
|
|||
{name: "User", prefix: N._Prefix.userlike},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class Renderer implements IRenderer {
|
||||
+_view: RelationalView;
|
||||
constructor(view) {
|
||||
this._view = view;
|
||||
}
|
||||
nodeDescription(node) {
|
||||
// This cast is unsound, and might throw at runtime, but won't have
|
||||
// silent failures or cause problems down the road.
|
||||
const address = N.fromRaw((node: any));
|
||||
const entity = this._view.entity(address);
|
||||
if (entity == null) {
|
||||
throw new Error(`unknown entity: ${NodeAddress.toString(node)}`);
|
||||
}
|
||||
return description(entity);
|
||||
}
|
||||
edgeVerb(edgeAddress, direction) {
|
||||
return edgeVerb(E.fromRaw((edgeAddress: any)), direction);
|
||||
edgeTypes() {
|
||||
return [
|
||||
{
|
||||
forwardName: "authors",
|
||||
backwardName: "is authored by",
|
||||
prefix: E._Prefix.authors,
|
||||
},
|
||||
{
|
||||
forwardName: "has parent",
|
||||
backwardName: "has child",
|
||||
prefix: E._Prefix.hasParent,
|
||||
},
|
||||
{
|
||||
forwardName: "merges",
|
||||
backwardName: "is merged by",
|
||||
prefix: E._Prefix.mergedAs,
|
||||
},
|
||||
{
|
||||
forwardName: "references",
|
||||
backwardName: "is referenced by",
|
||||
prefix: E._Prefix.references,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// @flow
|
||||
|
||||
import * as R from "./relationalView";
|
||||
import * as E from "./edges";
|
||||
|
||||
export function description(e: R.Entity) {
|
||||
const withAuthors = (x: R.AuthoredEntity) => {
|
||||
|
@ -25,22 +24,3 @@ export function description(e: R.Entity) {
|
|||
};
|
||||
return R.match(handlers, e);
|
||||
}
|
||||
|
||||
export function edgeVerb(
|
||||
e: E.StructuredAddress,
|
||||
direction: "FORWARD" | "BACKWARD"
|
||||
) {
|
||||
const forward = direction === "FORWARD";
|
||||
switch (e.type) {
|
||||
case "AUTHORS":
|
||||
return forward ? "authors" : "is authored by";
|
||||
case "MERGED_AS":
|
||||
return forward ? "merges" : "is merged by";
|
||||
case "HAS_PARENT":
|
||||
return forward ? "has parent" : "has child";
|
||||
case "REFERENCES":
|
||||
return forward ? "references" : "is referenced by";
|
||||
default:
|
||||
throw new Error(`Unexpected type ${(e.type: empty)}`);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue