Add a babelified test file, Webpack the add-on, and make the JS API work.
We concatenate a simple setTimeout monkeypatch onto the add-on itself.
This commit is contained in:
parent
360f7622e8
commit
17d7eaec7b
16 changed files with 317 additions and 55 deletions
27
.babelrc
Normal file
27
.babelrc
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"env": {
|
||||
"production": {
|
||||
"presets": ["react", "react-optimize"]
|
||||
},
|
||||
"development": {
|
||||
"presets": ["react"]
|
||||
},
|
||||
"test": {
|
||||
"presets": ["react"]
|
||||
}
|
||||
},
|
||||
"only": [
|
||||
"test/js/**"
|
||||
],
|
||||
"plugins": [
|
||||
"transform-es2015-destructuring",
|
||||
"transform-es2015-parameters",
|
||||
"transform-es2015-modules-commonjs",
|
||||
"transform-async-to-generator",
|
||||
"transform-object-rest-spread",
|
||||
"transform-class-properties",
|
||||
"transform-runtime"
|
||||
],
|
||||
"sourceMaps": "inline",
|
||||
"retainLines": true
|
||||
}
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -28,5 +28,8 @@ pom.xml.asc
|
|||
/release-node
|
||||
/release-node/datomish.js
|
||||
/release-node/datomish.bare.js
|
||||
/addon/datomish-test.xpi
|
||||
/addon/datomish.js
|
||||
/addon/built/index.js
|
||||
/addon/node_modules/
|
||||
/addon/release/datomish-test.xpi
|
||||
/addon/release/datomish.js
|
||||
/addon/release/index.js
|
||||
|
|
6
addon/.babelrc
Normal file
6
addon/.babelrc
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"presets": ["es2015"],
|
||||
"plugins": [
|
||||
"transform-async-to-generator"
|
||||
]
|
||||
}
|
2
addon/build.sh
Executable file
2
addon/build.sh
Executable file
|
@ -0,0 +1,2 @@
|
|||
node_modules/.bin/webpack -p
|
||||
cat src/wrapper.prefix built/index.js > release/index.js
|
|
@ -1,15 +0,0 @@
|
|||
var self = require("sdk/self");
|
||||
|
||||
console.log("Datomish Test");
|
||||
console.log("This: " + this);
|
||||
|
||||
var datomish = require("datomish.js");
|
||||
datomish.open("/tmp/foobar.db").then(function (db) {
|
||||
console.log("Got " + db);
|
||||
try {
|
||||
db.close();
|
||||
console.log("Closed.");
|
||||
} catch (e) {
|
||||
console.log("Couldn't close: " + e);
|
||||
}
|
||||
});
|
|
@ -1,15 +1,23 @@
|
|||
{
|
||||
"title": "Datomish Test",
|
||||
"name": "datomish-test",
|
||||
"version": "0.0.1",
|
||||
"description": "An example add-on that loads Datomish on top of Sqlite.jsm.",
|
||||
"name": "datomish-example",
|
||||
"version": "1.0.0",
|
||||
"description": "A test add-on for Datomish and Firefox.",
|
||||
"main": "index.js",
|
||||
"author": "Richard Newman <rnewman@mozilla.com>",
|
||||
"engines": {
|
||||
"firefox": ">=48.0a1"
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MPL-2.0",
|
||||
"keywords": [
|
||||
"jetpack"
|
||||
]
|
||||
"devDependencies": {
|
||||
"babel": "^6.5.2",
|
||||
"babel-cli": "^6.14.0",
|
||||
"babel-core": "^6.14.0",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-plugin-transform-async-to-generator": "^6.8.0",
|
||||
"babel-preset-es2015": "^6.14.0",
|
||||
"webpack": "^1.13.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.13.0"
|
||||
}
|
||||
}
|
||||
|
|
BIN
addon/release/data/datomish-48.png
Normal file
BIN
addon/release/data/datomish-48.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
15
addon/release/package.json
Normal file
15
addon/release/package.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"title": "Datomish Test",
|
||||
"name": "datomish-test",
|
||||
"version": "0.0.1",
|
||||
"description": "An example add-on that loads Datomish on top of Sqlite.jsm.",
|
||||
"main": "index.js",
|
||||
"author": "Richard Newman <rnewman@mozilla.com>",
|
||||
"engines": {
|
||||
"firefox": ">=48.0a1"
|
||||
},
|
||||
"license": "MPL-2.0",
|
||||
"keywords": [
|
||||
"jetpack"
|
||||
]
|
||||
}
|
1
addon/release/run.sh
Executable file
1
addon/release/run.sh
Executable file
|
@ -0,0 +1 @@
|
|||
jpm run -b /Applications/FirefoxNightly.app/
|
101
addon/src/index.js
Normal file
101
addon/src/index.js
Normal file
|
@ -0,0 +1,101 @@
|
|||
var self = require("sdk/self");
|
||||
var buttons = require('sdk/ui/button/action');
|
||||
var tabs = require('sdk/tabs');
|
||||
|
||||
var datomish = require("datomish.js");
|
||||
|
||||
var schema = {
|
||||
"name": "pages",
|
||||
"attributes": [
|
||||
{"name": "page/url",
|
||||
"type": "string",
|
||||
"cardinality": "one",
|
||||
"unique": "identity",
|
||||
"doc": "A page's URL."},
|
||||
{"name": "page/title",
|
||||
"type": "string",
|
||||
"cardinality": "one",
|
||||
"fulltext": true,
|
||||
"doc": "A page's title."},
|
||||
{"name": "page/content",
|
||||
"type": "string",
|
||||
"cardinality": "one", // Simple for now.
|
||||
"fulltext": true,
|
||||
"doc": "A snapshot of the page's content. Should be plain text."},
|
||||
]
|
||||
};
|
||||
|
||||
async function initDB(path) {
|
||||
let db = await datomish.open(path);
|
||||
await db.ensureSchema(schema);
|
||||
return db;
|
||||
}
|
||||
|
||||
async function findURLs(db) {
|
||||
let query = `[:find ?page ?url ?title :in $ :where [?page :page/url ?url][(get-else $ ?page :page/title "") ?title]]`;
|
||||
let options = new Object();
|
||||
options["limit"] = 10;
|
||||
return datomish.q(db.db(), query, options);
|
||||
}
|
||||
|
||||
async function findPagesMatching(db, string) {
|
||||
let query =
|
||||
`[:find ?url ?title
|
||||
:in $ ?str
|
||||
:where
|
||||
[(fulltext $ :any ?str) [[?page]]]
|
||||
[?page :page/url ?url]
|
||||
[(get-else $ ?page :page/title "") ?title]]`;
|
||||
return datomish.q(db.db(), query, {"limit": 10, "inputs": {"str": string}});
|
||||
}
|
||||
|
||||
async function savePage(db, url, title, content) {
|
||||
let txResult = await db.transact([{"db/id": 55,
|
||||
"page/title": title,
|
||||
"page/url": url
|
||||
// "page/starred": true
|
||||
}]);
|
||||
return txResult;
|
||||
}
|
||||
|
||||
async function handleClick(state) {
|
||||
let db = await datomish.open("/tmp/testing.db");
|
||||
await db.ensureSchema(schema);
|
||||
let txResult = await savePage(db, tabs.activeTab.url, tabs.activeTab.title, "Content goes here");
|
||||
console.log("Transaction returned " + JSON.stringify(txResult));
|
||||
console.log("Transaction instant: " + txResult.txInstant);
|
||||
let results = await findURLs(db); //datomish.q(db.db(), "[:find ?url :in $ :where [?e :page/url ?url]]");
|
||||
results = results.map(r => r[1]);
|
||||
console.log("Query results: " + JSON.stringify(results));
|
||||
let pages = await findPagesMatching(db, "goes");
|
||||
console.log("Pages: " + JSON.stringify(pages));
|
||||
await db.close();
|
||||
}
|
||||
|
||||
/*
|
||||
async function handleClick(state) {
|
||||
console.log("Handling click: " + state);
|
||||
let tab = tabs.activeTab;
|
||||
console.log("Active tab: " + tab);
|
||||
console.log("Active tab: " + tab.url);
|
||||
console.log("Active tab: " + tab.title);
|
||||
let db = await initDB("/tmp/datomish.db");
|
||||
console.log("Opened DB: " + db);
|
||||
await savePage(db, tab.url, tab.title, "Content goes here.");
|
||||
console.log("Saved page.");
|
||||
|
||||
let urls = await findURLs(db);
|
||||
console.log("URLs: " + JSON.stringify(urls));
|
||||
|
||||
let results = await findPagesMatching(db, "goes");
|
||||
console.log("Pages: " + JSON.stringify(results));
|
||||
await db.close();
|
||||
}
|
||||
*/
|
||||
|
||||
var button = buttons.ActionButton({
|
||||
id: "datomish-save",
|
||||
label: "Save Page",
|
||||
icon: "./datomish-48.png",
|
||||
onClick: handleClick
|
||||
});
|
4
addon/src/wrapper.prefix
Normal file
4
addon/src/wrapper.prefix
Normal file
|
@ -0,0 +1,4 @@
|
|||
// Monkeypatch.
|
||||
var { setTimeout } = require("sdk/timers");
|
||||
this.setTimeout = setTimeout;
|
||||
|
21
addon/webpack.config.js
Normal file
21
addon/webpack.config.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
module.exports = {
|
||||
entry: ['babel-polyfill', './src/index.js'],
|
||||
output: {
|
||||
filename: 'built/index.js'
|
||||
},
|
||||
target: 'webworker',
|
||||
externals: {
|
||||
'datomish.js': 'commonjs datomish.js',
|
||||
'sdk/self': 'commonjs sdk/self',
|
||||
'sdk/ui/button/action': 'commonjs sdk/ui/button/action',
|
||||
'sdk/tabs': 'commonjs sdk/tabs'
|
||||
},
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /\.js?$/,
|
||||
exclude: /(node_modules)|(wrapper.prefix)/,
|
||||
loader: 'babel'
|
||||
}]
|
||||
}
|
||||
}
|
23
package.json
23
package.json
|
@ -13,7 +13,26 @@
|
|||
"thenify-all": "^1.6.0",
|
||||
"ws": "1.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "babel-node test/js/tests.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.14.0",
|
||||
"babel-core": "6.14.0",
|
||||
"babel-eslint": "6.1.2",
|
||||
"babel-loader": "6.2.5",
|
||||
"babel-plugin-transform-async-to-generator": "6.8.0",
|
||||
"babel-plugin-transform-class-properties": "6.11.5",
|
||||
"babel-plugin-transform-es2015-destructuring": "6.9.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "6.11.5",
|
||||
"babel-plugin-transform-es2015-parameters": "6.11.4",
|
||||
"babel-plugin-transform-object-rest-spread": "6.8.0",
|
||||
"babel-plugin-transform-runtime": "6.12.0",
|
||||
"babel-polyfill": "6.13.0",
|
||||
"babel-preset-react": "6.11.1",
|
||||
"babel-preset-react-optimize": "1.0.1",
|
||||
"babel-register": "6.14.0",
|
||||
"babel-runtime": "6.11.6",
|
||||
"tmp": "0.0.28"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -27,5 +46,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/mozilla/datomish#readme",
|
||||
"main": "./datomish.js",
|
||||
"files": ["datomish.js"]
|
||||
"files": [
|
||||
"datomish.js"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[cljs.core.async :as a :refer [take! <! >!]]
|
||||
[cljs.reader]
|
||||
[cljs-promises.core :refer [promise]]
|
||||
[datomish.cljify :refer [cljify]]
|
||||
[datomish.db :as db]
|
||||
[datomish.db-factory :as db-factory]
|
||||
[datomish.pair-chan]
|
||||
|
@ -18,14 +19,17 @@
|
|||
[datomish.js-sqlite :as js-sqlite]
|
||||
[datomish.transact :as transact]))
|
||||
|
||||
(defn- take-pair-as-promise! [ch]
|
||||
(defn- take-pair-as-promise! [ch f]
|
||||
;; Just like take-as-promise!, but aware that it's handling a pair channel.
|
||||
;; Also converts values, if desired.
|
||||
(promise
|
||||
(fn [resolve reject]
|
||||
(letfn [(split-pair [[v e]]
|
||||
(if e
|
||||
(reject e)
|
||||
(resolve v)))]
|
||||
(do
|
||||
(println "Got error:" e)
|
||||
(reject e))
|
||||
(resolve (f v))))]
|
||||
(cljs.core.async/take! ch split-pair)))))
|
||||
|
||||
;; Public API.
|
||||
|
@ -35,32 +39,46 @@
|
|||
|
||||
(defn ^:export q [db find options]
|
||||
(let [find (cljs.reader/read-string find)
|
||||
options (js->clj options)]
|
||||
opts (cljify options)]
|
||||
(println "Running query " (pr-str find) (pr-str {:foo find}) (pr-str opts))
|
||||
(take-pair-as-promise!
|
||||
(db/<?q db find options))))
|
||||
(go-pair
|
||||
(let [res (<? (db/<?q db find opts))]
|
||||
(println "Got results: " (pr-str res))
|
||||
(clj->js res)))
|
||||
identity)))
|
||||
|
||||
(defn ^:export ensure-schema [conn simple-schema]
|
||||
(let [simple-schema (js->clj simple-schema)]
|
||||
(println "simple-schema: " (pr-str simple-schema))
|
||||
(let [simple-schema (cljify simple-schema)
|
||||
datoms (simple-schema/simple-schema->schema simple-schema)]
|
||||
(println "Transacting schema datoms" (pr-str datoms))
|
||||
(take-pair-as-promise!
|
||||
(transact/<transact!
|
||||
conn
|
||||
(simple-schema/simple-schema->schema simple-schema)))))
|
||||
datoms)
|
||||
clj->js)))
|
||||
|
||||
(defn js->tx-data [tx-data]
|
||||
;; Objects to maps.
|
||||
;; Arrays to arrays.
|
||||
;; RHS strings… well, some of them will be richer types.
|
||||
;; TODO
|
||||
(println "Converting" (pr-str tx-data) "to" (pr-str (js->clj tx-data :keywordize-keys true)))
|
||||
(println "Converting" (pr-str tx-data) "to" (pr-str (js->clj tx-data :keywordize-keys true)))
|
||||
(js->clj tx-data))
|
||||
(def js->tx-data cljify)
|
||||
|
||||
(def ^:export tempid (partial db/id-literal :db.part/user))
|
||||
|
||||
(defn ^:export transact [conn tx-data]
|
||||
;; Expects a JS array as input.
|
||||
(let [tx-data (js->tx-data tx-data)]
|
||||
(take-pair-as-promise!
|
||||
(transact/<transact! conn tx-data))))
|
||||
(try
|
||||
(let [tx-data (js->tx-data tx-data)]
|
||||
(println "Transacting:" (pr-str tx-data))
|
||||
(take-pair-as-promise!
|
||||
(go-pair
|
||||
(let [tx-result (<? (transact/<transact! conn tx-data))]
|
||||
(select-keys tx-result
|
||||
[:tempids
|
||||
:added-idents
|
||||
:added-attributes
|
||||
:tx
|
||||
:txInstant])))
|
||||
clj->js))
|
||||
(catch js/Error e
|
||||
(println "Error in transact:" e))))
|
||||
|
||||
(defn ^:export open [path]
|
||||
;; Eventually, URI. For now, just a plain path (no file://).
|
||||
|
@ -69,11 +87,15 @@
|
|||
(let [conn (<? (sqlite/<sqlite-connection path))
|
||||
db (<? (db-factory/<db-with-sqlite-connection conn))]
|
||||
(let [c (transact/connection-with-db db)]
|
||||
(clj->js
|
||||
{:conn c
|
||||
:ensureSchema (fn [simple-schema] (ensure-schema c simple-schema))
|
||||
:transact (fn [tx-data] (transact c tx-data))
|
||||
:q (fn [find options] (q (transact/db c) find options))
|
||||
:close (fn [] (db/close-db db))
|
||||
:toString (fn [] (str "#<DB " path ">"))
|
||||
:path path}))))))
|
||||
(clj->js
|
||||
;; We pickle the connection as a thunk here so it roundtrips through JS
|
||||
;; without incident.
|
||||
{:conn (fn [] c)
|
||||
:roundtrip (fn [x] (clj->js (cljify x)))
|
||||
:db (fn [] (transact/db c))
|
||||
:ensureSchema (fn [simple-schema] (ensure-schema c simple-schema))
|
||||
:transact (fn [tx-data] (transact c tx-data))
|
||||
:close (fn [] (db/close-db db))
|
||||
:toString (fn [] (str "#<DB " path ">"))
|
||||
:path path}))))
|
||||
identity))
|
||||
|
|
46
test/js/tests.js
Normal file
46
test/js/tests.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
var datomish = require("../../release-node/datomish.js");
|
||||
|
||||
var schema = {
|
||||
"name": "pages",
|
||||
"attributes": [
|
||||
{"name": "page/url",
|
||||
"type": "string",
|
||||
"cardinality": "one",
|
||||
"unique": "identity",
|
||||
"doc": "A page's URL."},
|
||||
{"name": "page/title",
|
||||
"type": "string",
|
||||
"cardinality": "one",
|
||||
"doc": "A page's title."},
|
||||
{"name": "page/starred",
|
||||
"type": "boolean",
|
||||
"cardinality": "one",
|
||||
"doc": "Whether the page is starred."},
|
||||
{"name": "page/visit",
|
||||
"type": "ref",
|
||||
"cardinality": "many",
|
||||
"doc": "A visit to the page."}
|
||||
]
|
||||
};
|
||||
|
||||
async function testOpen() {
|
||||
let db = await datomish.open("/tmp/testing.db");
|
||||
await db.ensureSchema(schema);
|
||||
let txResult = await db.transact([{"db/id": 55,
|
||||
"page/url": "http://foo.com/bar",
|
||||
"page/starred": true}]);
|
||||
console.log("Transaction returned " + JSON.stringify(txResult));
|
||||
console.log("Transaction instant: " + txResult.txInstant);
|
||||
let results = await datomish.q(db.db(), "[:find ?url :in $ :where [?e :page/url ?url]]")
|
||||
results = results.map(r => r[0]);
|
||||
console.log("Query results: " + JSON.stringify(results));
|
||||
await db.close();
|
||||
}
|
||||
|
||||
testOpen()
|
||||
.then((r) => console.log("Done."))
|
||||
.catch((e) => console.log("Failure: " + e.stack));
|
Loading…
Reference in a new issue