├── .gitignore
├── .travis.yml
├── README.md
├── index.js
├── lib
├── find.js
├── scope.js
└── tag.js
├── package.json
├── spec
├── support
│ └── jasmine.json
└── tag.spec.js
├── tag.min.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # https://git-scm.com/docs/gitignore
2 | # https://help.github.com/articles/ignoring-files
3 | # Example .gitignore files: https://github.com/github/gitignore
4 | /node_modules/
5 | .DS_Store
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '4.1'
4 | - '4.0'
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Labelmaker
2 | ==========
3 | Organize your data by tags
4 |
5 | > Labelmaker is made for [gunDB](https://github.com/amark/gun/)
6 |
7 | ## What it is
8 | If you've found yourself wanting a tagging system for gun, this is what you've been looking for. Labelmaker allows you to organize and retrieve your data by any of it's associated tags/groups.
9 |
10 | > **Note:** you cannot tag primitives. For that, use `.key`.
11 |
12 | ## How to use it
13 | **Node.js**
14 |
15 | ```bash
16 | npm install labelmaker
17 | ```
18 | to install it, then you can require it from your app:
19 | ```javascript
20 | var labler = require('labelmaker')
21 | ```
22 |
23 | Labelmaker works with both gun version `0.2` and `0.3`. Since it doesn't assume your version, you need to tell it explicitly by giving it the constructor you're using:
24 |
25 | ```javascript
26 | var Gun = require('gun')
27 | labler(Gun)
28 | // You now have tag support!
29 | ```
30 |
31 | **Browser**
32 |
33 | For the browser, it's much simpler, since your version of gun is exported as a global. Just include it as a script tag, and labelmaker takes care of the rest.
34 |
35 | ```html
36 |
37 |
38 | ```
39 | ### API
40 | Two methods are exposed for your gun instances:
41 |
42 | - `.tag`
43 | - `.tagged`
44 |
45 | #### gun.tag(name[, name...])
46 | You can pass `.tag` multiple names to index a node under. When called, it will try to read out your current context, index it under each tag, and then place each tag under a master list of every tag ever used.
47 |
48 | ```javascript
49 | gun.put({
50 | name: 'Bob',
51 | profession: 'developer'
52 | }).tag(
53 | 'members',
54 | 'javascript developers',
55 | 'gunDB developers',
56 | 'examples'
57 | )
58 | ```
59 |
60 | #### gun.tagged(name[, callback])
61 | Iterate over the list of nodes under that tag name.
62 | ```javascript
63 | gun.tagged('members', function (member, ID) {
64 | view.show.user(member, ID)
65 | })
66 | ```
67 |
68 | ### Roadmap
69 | Here are some goals for labelmaker:
70 |
71 | - option to disable implicit mapping
72 | - ability to remove a node from a tag
73 |
74 | Contributions are welcome!
75 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /*jslint node: true*/
2 | 'use strict';
3 |
4 | var tagger = require('./lib/tag');
5 | var finder = require('./lib/find');
6 |
7 |
8 | function attach(Gun) {
9 | Gun.chain.init = Gun.chain.init || Gun.chain.set;
10 | tagger(Gun);
11 | finder(Gun);
12 |
13 | return Gun;
14 | }
15 |
16 | if (typeof window !== 'undefined' && window.Gun) {
17 | attach(window.Gun);
18 | }
19 |
20 | module.exports = attach;
21 |
--------------------------------------------------------------------------------
/lib/find.js:
--------------------------------------------------------------------------------
1 | /*jslint node: true*/
2 | 'use strict';
3 |
4 | var scope = require('./scope');
5 |
6 | module.exports = function find(Gun) {
7 |
8 | Gun.chain.tagged = function (name, cb) {
9 | if (arguments.length === 0) {
10 | return this.get(scope + 'all tags');
11 | }
12 | if (!name && typeof name !== 'string') {
13 | return this;
14 | }
15 | return this.get(scope + name).map(cb, true);
16 | };
17 |
18 | };
19 |
--------------------------------------------------------------------------------
/lib/scope.js:
--------------------------------------------------------------------------------
1 | /*jslint node: true*/
2 | module.exports = 'xWVyuulvIIHyTO08nHHWrqta:';
3 |
--------------------------------------------------------------------------------
/lib/tag.js:
--------------------------------------------------------------------------------
1 | /*jslint node: true, nomen: true*/
2 | 'use strict';
3 |
4 | var scope = require('./scope');
5 |
6 | // check compatibility
7 | function invalid(value) {
8 | if (typeof value !== 'object') {
9 | console.log('Only objects can be tagged');
10 | return true;
11 | }
12 | }
13 |
14 | /*
15 | Keep a master collection
16 | of every tag used.
17 | */
18 | function pushTag(gun, tag) {
19 | gun.get(scope + tag).val(function (group) {
20 | gun.get(scope + 'all tags').init().path(tag).put(group);
21 | });
22 | }
23 |
24 | /*
25 | Take a list of tags and send
26 | them through, one at a time.
27 | */
28 | function serialize(gun, args) {
29 | // turn them into an array
30 | args = Array.prototype.slice.call(args);
31 |
32 | args.forEach(function (tag) {
33 | // run each tag
34 | gun.tag(tag);
35 | });
36 |
37 | return gun;
38 | }
39 |
40 | module.exports = function tag(Gun) {
41 |
42 | Gun.chain.tag = function (tag) {
43 |
44 | if (arguments.length !== 1) {
45 | return serialize(this, arguments);
46 | }
47 |
48 | return this.val(function (obj) {
49 | // filter non-objects
50 | if (invalid(obj)) {
51 | return this;
52 | }
53 |
54 | // place the object under the tag
55 | this.get(scope + tag).init()
56 | .path(obj._['#']).put(obj);
57 |
58 |
59 | // place that tag under a master list of tags
60 | pushTag(this, tag);
61 |
62 | });
63 | };
64 | };
65 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "labelmaker",
3 | "version": "0.1.0",
4 | "description": "Bring tags to gunDB",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "jasmine",
8 | "build": "webpack --watch"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/PsychoLlama/labelmaker.git"
13 | },
14 | "keywords": [
15 | "gun",
16 | "gunDB",
17 | "tags",
18 | "tagging",
19 | "tag",
20 | "group",
21 | "category",
22 | "label",
23 | "labels",
24 | "categories",
25 | "groups"
26 | ],
27 | "author": "Jesse Gibson (http://techllama.com)",
28 | "license": "Zlib OR MIT OR Apache-2.0",
29 | "devDependencies": {
30 | "gun": "^0.3.1",
31 | "jasmine": "^2.4.1",
32 | "webpack": "^1.12.11"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/spec/support/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "spec",
3 | "spec_files": [
4 | "**/*[sS]pec.js"
5 | ],
6 | "helpers": [
7 | "helpers/**/*.js"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/spec/tag.spec.js:
--------------------------------------------------------------------------------
1 | /*global describe, it, expect, jasmine, beforeEach, afterAll*/
2 | /*jslint node: true, nomen: true*/
3 | 'use strict';
4 |
5 | /*
6 | Warning:
7 | These tests are written for
8 | gun@v0.3, and may not work
9 | for older versions.
10 | */
11 |
12 | // Squelch gun peer connection warnings
13 | console.log = (function () {
14 | var log = console.log;
15 | return function (name) {
16 |
17 | if (name && name.match(/Warning!/i)) {
18 | return;
19 | }
20 |
21 | log.apply(console, arguments);
22 | };
23 | }());
24 |
25 |
26 | // set the default timeout
27 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
28 |
29 | var tagger = require('../index');
30 | var scope = require('../lib/scope');
31 | var Gun = require('gun');
32 | tagger(Gun);
33 |
34 | describe('The tagger function', function () {
35 | var gun, tag;
36 |
37 | beforeEach(function () {
38 | // generate a new tag each test
39 | tag = Gun.text.random(10);
40 |
41 | // make a new gun instance
42 | gun = new Gun({
43 | file: 'tag.spec-data.json'
44 | });
45 | });
46 |
47 | it('should export a "tag" method', function () {
48 | expect(Gun.chain.tag).toEqual(jasmine.any(Function));
49 | });
50 |
51 | it('should export a "tagged" method', function () {
52 | expect(Gun.chain.tagged).toEqual(jasmine.any(Function));
53 | });
54 |
55 |
56 | describe('tag method', function () {
57 |
58 | it('should index data', function (done) {
59 | gun.put({
60 | name: 'success'
61 | }).tag(tag).get(scope + tag).map().val(done);
62 | });
63 |
64 | it('should not pseudo-merge nodes', function (done) {
65 | gun.put({
66 | data: true
67 | }).tag(tag).get(scope + tag).map().val(function (val) {
68 | expect(val.name).toBe(undefined);
69 | expect(val.data).toBe(true);
70 | done();
71 | });
72 | });
73 |
74 | it('should be able to take multiple tags', function (done) {
75 | gun.put({
76 | test: true
77 | }).tag(tag, 'custom');
78 | gun.tagged('custom').val(done);
79 | });
80 |
81 | });
82 |
83 | describe('tagged method', function () {
84 |
85 | it('should be able to find tags', function (done) {
86 | gun.put({
87 | data: true
88 | }).tag(tag);
89 | gun.tagged(tag).val(done);
90 | });
91 |
92 | it('should be able to find every tag', function (done) {
93 | gun.tagged().map().val(done);
94 | });
95 |
96 | });
97 |
98 | });
99 |
100 | afterAll(function () {
101 | require('fs').unlink(__dirname + '/../tag.spec-data.json');
102 | });
103 |
--------------------------------------------------------------------------------
/tag.min.js:
--------------------------------------------------------------------------------
1 | !function(t){function n(e){if(i[e])return i[e].exports;var o=i[e]={exports:{},id:e,loaded:!1};return t[e].call(o.exports,o,o.exports,n),o.loaded=!0,o.exports}var i={};return n.m=t,n.c=i,n.p="",n(0)}([function(t,n,i){"use strict";function e(t){return t.chain.init=t.chain.init||t.chain.set,o(t),r(t),t}var o=i(1),r=i(3);"undefined"!=typeof window&&window.Gun&&e(window.Gun),t.exports=e},function(t,n,i){"use strict";function e(t){return"object"!=typeof t?(console.log("Only objects can be tagged"),!0):void 0}function o(t,n){t.get(u+n).val(function(i){t.get(u+"all tags").init().path(n).put(i)})}function r(t,n){return n=Array.prototype.slice.call(n),n.forEach(function(n){t.tag(n)}),t}var u=i(2);t.exports=function(t){t.chain.tag=function(t){return 1!==arguments.length?r(this,arguments):this.val(function(n){return e(n)?this:(this.get(u+t).init().path(n._["#"]).put(n),void o(this,t))})}}},function(t,n){t.exports="xWVyuulvIIHyTO08nHHWrqta:"},function(t,n,i){"use strict";var e=i(2);t.exports=function(t){t.chain.tagged=function(t,n){return 0===arguments.length?this.get(e+"all tags"):t||"string"==typeof t?this.get(e+t).map(n,!0):this}}}]);
2 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | /*jslint node: true*/
2 |
3 | var webpack = require('webpack');
4 | module.exports = {
5 | entry: './index.js',
6 | output: {
7 | path: './',
8 | filename: 'tag.min.js'
9 | },
10 | plugins: [
11 | new webpack.optimize.UglifyJsPlugin({
12 | minimize: true
13 | })
14 | ]
15 | };
16 |
--------------------------------------------------------------------------------