├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── lib ├── hydra.js ├── index.js └── jsonld-dsl.js ├── node_modules └── jsonld-dsl ├── package.json ├── src ├── hydra.js ├── index.js └── jsonld-dsl.js └── test ├── example.test.js ├── hydra.test.js ├── jsonld-dsl.test.js └── module-import.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs"] 2 | path = docs 3 | url = git@github.com:ericmoritz/node-jsonld-dsl.git 4 | branch = gh-pages 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Eric Moritz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: compile test doc 2 | 3 | SRC = $(wildcard src/*.js) 4 | LIB = $(SRC:src/%.js=lib/%.js) 5 | 6 | all: lib test doc 7 | 8 | 9 | lib: $(LIB) 10 | lib/%.js: src/%.js 11 | mkdir -p $(@D) 12 | babel $< -o $@ 13 | 14 | 15 | 16 | 17 | doc: 18 | ./node_modules/.bin/docco test/example.test.js 19 | mv docs/example.test.html docs/index.html 20 | 21 | test: 22 | npm test 23 | 24 | deploy: 25 | git -C docs push origin 26 | git push origin master 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsonld-dsl 2 | 3 | `jsonld-dsl` is a DSL for building JSON-LD powered hypermedia services. 4 | 5 | * [documentation](https://ericmoritz.github.io/node-jsonld-dsl/) 6 | * [npm](https://www.npmjs.com/package/jsonld-dsl) 7 | 8 | ```js 9 | var schema = Namespace( 10 | Class('Thing'), 11 | Class('Blog'), 12 | Class('BlogPosting'), 13 | Property('name'), 14 | Property('url'), 15 | Property('blogPost'), 16 | Property('articleBody') 17 | ) 18 | 19 | var post = schema.BlogPosting( 20 | URI('/entries/hydra-lite.json'), 21 | schema.name('Hydra Lite'), 22 | schema.url('http://eric.themoritzfamily.com/hydra-lite.html'), 23 | schema.articleBody('This is the article body...') 24 | ) 25 | ``` 26 | 27 | ## hydra support 28 | 29 | This module declares the [hydra]() namespace for you. See the 30 | [documentation](https://ericmoritz.github.io/node-jsonld-dsl/) for 31 | more information 32 | 33 | ```js 34 | var hydra = require('jsonld-dsl').hydra 35 | ``` 36 | -------------------------------------------------------------------------------- /lib/hydra.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | /* -*- mode: javascript -*- */ 7 | 8 | var _Namespace$Class$Property$Prefix = require('./jsonld-dsl'); 9 | 10 | var _Set = require('immutable'); 11 | 12 | var ns = _Namespace$Class$Property$Prefix.Namespace(_Namespace$Class$Property$Prefix.Property('apiDocumentation'), _Namespace$Class$Property$Prefix.Property('description'), _Namespace$Class$Property$Prefix.Property('entrypoint'), _Namespace$Class$Property$Prefix.Property('expects'), _Namespace$Class$Property$Prefix.Property('firstPage'), _Namespace$Class$Property$Prefix.Property('freetextQuery'), _Namespace$Class$Property$Prefix.Property('itemsPerPage'), _Namespace$Class$Property$Prefix.Property('lastPage'), _Namespace$Class$Property$Prefix.Property('mapping'), _Namespace$Class$Property$Prefix.Property('member'), _Namespace$Class$Property$Prefix.Property('method'), _Namespace$Class$Property$Prefix.Property('nextPage'), _Namespace$Class$Property$Prefix.Property('operation'), _Namespace$Class$Property$Prefix.Property('possibleStatus'), _Namespace$Class$Property$Prefix.Property('previousPage'), _Namespace$Class$Property$Prefix.Property('property'), _Namespace$Class$Property$Prefix.Property('readable'), _Namespace$Class$Property$Prefix.Property('required'), _Namespace$Class$Property$Prefix.Property('returns'), _Namespace$Class$Property$Prefix.Property('search'), _Namespace$Class$Property$Prefix.Property('statusCode'), _Namespace$Class$Property$Prefix.Property('supportedProperty'), _Namespace$Class$Property$Prefix.Property('supportedOperation'), _Namespace$Class$Property$Prefix.Property('supportedProperty'), _Namespace$Class$Property$Prefix.Property('template'), _Namespace$Class$Property$Prefix.Property('title'), _Namespace$Class$Property$Prefix.Property('totalItems'), _Namespace$Class$Property$Prefix.Property('variable'), _Namespace$Class$Property$Prefix.Property('variableRepresentation'), _Namespace$Class$Property$Prefix.Property('writeable'), _Namespace$Class$Property$Prefix.Class('ApiDocumentation'), _Namespace$Class$Property$Prefix.Class('Class'), _Namespace$Class$Property$Prefix.Class('Collection'), _Namespace$Class$Property$Prefix.Class('CreateResourceOperation'), _Namespace$Class$Property$Prefix.Class('DeleteResourceOperation'), _Namespace$Class$Property$Prefix.Class('Error'), _Namespace$Class$Property$Prefix.Class('IriTemplate'), _Namespace$Class$Property$Prefix.Class('IriTemplateMapping'), _Namespace$Class$Property$Prefix.Class('Link'), _Namespace$Class$Property$Prefix.Class('Operation'), _Namespace$Class$Property$Prefix.Class('PagedCollection'), _Namespace$Class$Property$Prefix.Class('ReplaceResourceOperation'), _Namespace$Class$Property$Prefix.Class('Resource'), _Namespace$Class$Property$Prefix.Class('Status'), _Namespace$Class$Property$Prefix.Class('SupportedProperty'), _Namespace$Class$Property$Prefix.Class('TemplatedLink'), _Namespace$Class$Property$Prefix.Class('VariableRepresentation')); 13 | 14 | var operationFactory = function operationFactory(method) { 15 | return function () { 16 | for (var _len = arguments.length, properties = Array(_len), _key = 0; _key < _len; _key++) { 17 | properties[_key] = arguments[_key]; 18 | } 19 | 20 | return ns.operation(_Set.Set.of(ns.Operation.apply(ns, [ns.method(method)].concat(properties)))); 21 | }; 22 | }; 23 | 24 | ns.PUT = operationFactory('PUT'); 25 | ns.POST = operationFactory('POST'); 26 | ns.DELETE = operationFactory('DELETE'); 27 | ns.context = _Namespace$Class$Property$Prefix.Prefix('hydra', 'http://www.w3.org/ns/hydra/core#', ns); 28 | exports['default'] = ns; 29 | module.exports = exports['default']; 30 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _interopRequireWildcard = function (obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }; 4 | 5 | Object.defineProperty(exports, '__esModule', { 6 | value: true 7 | }); 8 | 9 | var _import = require('./jsonld-dsl'); 10 | 11 | var jsonldMod = _interopRequireWildcard(_import); 12 | 13 | var _hydraMod = require('./hydra'); 14 | 15 | var _hydraMod2 = _interopRequireWildcard(_hydraMod); 16 | 17 | jsonldMod.hydra = _hydraMod2['default']; 18 | exports['default'] = jsonldMod; 19 | module.exports = exports['default']; 20 | -------------------------------------------------------------------------------- /lib/jsonld-dsl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _bind = Function.prototype.bind; 4 | var _slice = Array.prototype.slice; 5 | 6 | var _inherits = function (subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; }; 7 | 8 | var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }; 9 | 10 | Object.defineProperty(exports, '__esModule', { 11 | value: true 12 | }); 13 | 14 | var _im$Map$Set$List = require('immutable'); 15 | 16 | /////////////////////////////////////////////////////////////////////////////// 17 | // The Namespace constructor 18 | /////////////////////////////////////////////////////////////////////////////// 19 | /* 20 | * Generates a Namespace based on the Class() and Property() 21 | * definitions passed in. 22 | * 23 | * Usage: 24 | * 25 | * var ns = Namespace( 26 | * Class('Class1'), 27 | * Property('prop1') 28 | * ) 29 | * ns.Class1( 30 | * ns.prop1("prop1's value") 31 | * ) 32 | * 33 | */ 34 | var Namespace = function Namespace() { 35 | for (var _len = arguments.length, definitions = Array(_len), _key = 0; _key < _len; _key++) { 36 | definitions[_key] = arguments[_key]; 37 | } 38 | 39 | return definitions.reduce(function (accum, x) { 40 | var typeURI = x.get('@id'); 41 | var resourceType = x.get('@type', _im$Map$Set$List.Set()); 42 | var factoryFun = resourceType.contains('rdfs:Class') ? resourceFactory(typeURI) : resourceType.contains('rdfs:Property') ? propertyFactory(typeURI) : undefined; 43 | accum[typeURI] = factoryFun; 44 | accum['@graph'] = accum['@graph'].push(x); 45 | return accum; 46 | }, new NamespaceClass()); 47 | }; 48 | 49 | exports.Namespace = Namespace; 50 | 51 | var NamespaceClass = function NamespaceClass() { 52 | _classCallCheck(this, NamespaceClass); 53 | 54 | this['@graph'] = _im$Map$Set$List.List(); 55 | }; 56 | 57 | exports.NamespaceClass = NamespaceClass; 58 | 59 | /////////////////////////////////////////////////////////////////////////////// 60 | // The Prefix constructor 61 | /////////////////////////////////////////////////////////////////////////////// 62 | /* 63 | * includes the @context for a Namespace into the resource 64 | * 65 | * Usage: 66 | * 67 | * var ns = Namespace( 68 | * Class('Class1'), 69 | * Property('prop1') 70 | * ) 71 | * 72 | * ns.Class1( 73 | * Prefix('vocab', 'http://example.com/vocab#', ns), 74 | * ns.prop1('value1') 75 | * ) // returns a Class1 instance with the @context of ns 76 | */ 77 | 78 | var Prefix = function Prefix(prefix, uri, namespace) { 79 | var context = arguments[3] === undefined ? {} : arguments[3]; 80 | 81 | var contextMap = _im$Map$Set$List.fromJS(context); 82 | return new ResourceClass(namespace['@graph'].reduce(function (accum, x) { 83 | var label = x.get('@id'); 84 | var uri = prefix + ':' + label; 85 | var value = contextMap.has(label) ? contextMap.get(label).set('@id', uri) : uri; 86 | return accum.updateIn(['@context'], function (context) { 87 | return context.set(label, value); 88 | }); 89 | }, _im$Map$Set$List.Map({ 90 | '@context': _im$Map$Set$List.Map({ 91 | rdfs: 'http://www.w3.org/2000/01/rdf-schema#' 92 | }).set(prefix, uri) 93 | }))); 94 | }; 95 | 96 | exports.Prefix = Prefix; 97 | /////////////////////////////////////////////////////////////////////////////// 98 | // The Resource constructor 99 | /////////////////////////////////////////////////////////////////////////////// 100 | /* 101 | * Composes a JSON-LD Resource as a Immutable.Map() 102 | * 103 | * Usage 104 | * 105 | * 106 | * var schema = Namespace( 107 | * Class('Thing'), 108 | * Property('image'), 109 | * Class('Person'), 110 | * Property('familyName'), 111 | * Property('givenName'), 112 | * ) 113 | * 114 | * var resource = Resource( 115 | * URI('#ericmoritz'), 116 | * Prefix('schema', 'http://schema.org/', schema), 117 | * schema.Thing( 118 | * schema.image("http://www.gravatar.com/avatar/4839d0678248e68eaeed5084e788210b.png") 119 | * ), 120 | * schema.Person( 121 | * schema.givenName('Eric'), 122 | * schema.familyName('Moritz') 123 | * ) 124 | * ) 125 | * 126 | */ 127 | var Resource = function Resource() { 128 | for (var _len2 = arguments.length, properties = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 129 | properties[_key2] = arguments[_key2]; 130 | } 131 | 132 | return new ResourceClass(properties.reduce(function (x, y) { 133 | return _im$Map$Set$List.fromJS(x).mergeDeep(_im$Map$Set$List.fromJS(y)); 134 | })); 135 | }; 136 | 137 | exports.Resource = Resource; 138 | 139 | var ResourceClass = (function (_Map) { 140 | function ResourceClass() { 141 | _classCallCheck(this, ResourceClass); 142 | 143 | if (_Map != null) { 144 | var _this = new (_bind.apply(_Map, [null].concat(_slice.call(arguments))))(); 145 | 146 | _this.__proto__ = ResourceClass.prototype; 147 | return _this; 148 | } 149 | 150 | return _this; 151 | } 152 | 153 | _inherits(ResourceClass, _Map); 154 | 155 | return ResourceClass; 156 | })(_im$Map$Set$List.Map); 157 | 158 | exports.ResourceClass = ResourceClass; 159 | 160 | /////////////////////////////////////////////////////////////////////////////// 161 | // A convenience function for the '@id' field 162 | /////////////////////////////////////////////////////////////////////////////// 163 | var URI = function URI(uri) { 164 | return new ResourceClass({ 165 | '@id': uri 166 | }); 167 | }; 168 | 169 | exports.URI = URI; 170 | /////////////////////////////////////////////////////////////////////////////// 171 | // A convenience function for the '@type' field 172 | /////////////////////////////////////////////////////////////////////////////// 173 | var type = function type(uri) { 174 | return new ResourceClass({ 175 | '@type': _im$Map$Set$List.Set([uri]) 176 | }); 177 | }; 178 | 179 | exports.type = type; 180 | /////////////////////////////////////////////////////////////////////////////// 181 | // A convenience function for defining classes 182 | /////////////////////////////////////////////////////////////////////////////// 183 | var Class = function Class(label) { 184 | for (var _len3 = arguments.length, properties = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { 185 | properties[_key3 - 1] = arguments[_key3]; 186 | } 187 | 188 | return Resource.apply(undefined, [URI(label), type('rdfs:Class')].concat(properties)); 189 | }; 190 | 191 | exports.Class = Class; 192 | /////////////////////////////////////////////////////////////////////////////// 193 | // A convenience function for defining Properties 194 | /////////////////////////////////////////////////////////////////////////////// 195 | var Property = function Property(label) { 196 | for (var _len4 = arguments.length, properties = Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { 197 | properties[_key4 - 1] = arguments[_key4]; 198 | } 199 | 200 | return Resource.apply(undefined, [URI(label), type('rdfs:Property')].concat(properties)); 201 | }; 202 | 203 | exports.Property = Property; 204 | var Vocab = function Vocab() { 205 | for (var _len5 = arguments.length, namespaces = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { 206 | namespaces[_key5] = arguments[_key5]; 207 | } 208 | 209 | return new ResourceClass(namespaces.reduce(function (accum, y) { 210 | return accum.updateIn(['@graph'], function (graph) { 211 | return graph.concat(y['@graph']); 212 | }); 213 | }, _im$Map$Set$List.Map({ '@graph': _im$Map$Set$List.List() }))); 214 | }; 215 | exports.Vocab = Vocab; 216 | /////////////////////////////////////////////////////////////////////////////// 217 | // Internal 218 | /////////////////////////////////////////////////////////////////////////////// 219 | 220 | // A function that creates a namespace's resource constructor 221 | var resourceFactory = function resourceFactory(typeURI) { 222 | return function () { 223 | for (var _len6 = arguments.length, properties = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { 224 | properties[_key6] = arguments[_key6]; 225 | } 226 | 227 | return Resource.apply(undefined, [type(typeURI)].concat(properties)); 228 | }; 229 | }; 230 | 231 | var propertyFactory = function propertyFactory(propURI) { 232 | return function (value) { 233 | return new ResourceClass().set(propURI, _im$Map$Set$List.fromJS(value)); 234 | }; 235 | }; 236 | -------------------------------------------------------------------------------- /node_modules/jsonld-dsl: -------------------------------------------------------------------------------- 1 | .. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jsonld-dsl", 3 | "description": "A DSL for building JSON-LD resources", 4 | "version": "0.0.7", 5 | "homepage": "https://ericmoritz.github.io/node-jsonld-dsl/", 6 | "license": "MIT", 7 | "author": "Eric Moritz", 8 | "main": "lib/index", 9 | "directories": { 10 | "test": "test" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/ericmoritz/node-jsonld-dsl.git" 15 | }, 16 | "dependencies": { 17 | "immutable": "^3.7.2", 18 | "jsonld": "^0.3.22" 19 | }, 20 | "devDependencies": { 21 | "babel": "^5.2.6", 22 | "docco": "^0.7.0", 23 | "expect": "^1.6.0", 24 | "mocha": "^2.2.4" 25 | }, 26 | "scripts": { 27 | "test": "mocha --compilers js:babel/register" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/hydra.js: -------------------------------------------------------------------------------- 1 | /* -*- mode: javascript -*- */ 2 | import {Namespace, Class, Property, Prefix} from './jsonld-dsl' 3 | import {Set} from 'immutable' 4 | 5 | const ns = Namespace( 6 | Property("apiDocumentation"), 7 | Property("description"), 8 | Property("entrypoint"), 9 | Property("expects"), 10 | Property("firstPage"), 11 | Property("freetextQuery"), 12 | Property("itemsPerPage"), 13 | Property("lastPage"), 14 | Property("mapping"), 15 | Property("member"), 16 | Property("method"), 17 | Property("nextPage"), 18 | Property("operation"), 19 | Property("possibleStatus"), 20 | Property("previousPage"), 21 | Property("property"), 22 | Property("readable"), 23 | Property("required"), 24 | Property("returns"), 25 | Property("search"), 26 | Property("statusCode"), 27 | Property("supportedProperty"), 28 | Property("supportedOperation"), 29 | Property("supportedProperty"), 30 | Property("template"), 31 | Property("title"), 32 | Property("totalItems"), 33 | Property("variable"), 34 | Property("variableRepresentation"), 35 | Property("writeable"), 36 | Class("ApiDocumentation"), 37 | Class("Class"), 38 | Class("Collection"), 39 | Class("CreateResourceOperation"), 40 | Class("DeleteResourceOperation"), 41 | Class("Error"), 42 | Class("IriTemplate"), 43 | Class("IriTemplateMapping"), 44 | Class("Link"), 45 | Class("Operation"), 46 | Class("PagedCollection"), 47 | Class("ReplaceResourceOperation"), 48 | Class("Resource"), 49 | Class("Status"), 50 | Class("SupportedProperty"), 51 | Class("TemplatedLink"), 52 | Class("VariableRepresentation") 53 | ) 54 | 55 | const operationFactory = (method) => (...properties) => ns.operation( 56 | Set.of( 57 | ns.Operation( 58 | ns.method(method), 59 | ...properties 60 | ) 61 | ) 62 | ) 63 | 64 | ns.PUT = operationFactory('PUT') 65 | ns.POST = operationFactory('POST') 66 | ns.DELETE = operationFactory('DELETE') 67 | ns.context = Prefix( 68 | 'hydra', 'http://www.w3.org/ns/hydra/core#', ns 69 | ) 70 | export default ns 71 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 2 | import * as jsonldMod from './jsonld-dsl' 3 | import hydraMod from './hydra' 4 | 5 | jsonldMod.hydra = hydraMod 6 | export default jsonldMod 7 | 8 | -------------------------------------------------------------------------------- /src/jsonld-dsl.js: -------------------------------------------------------------------------------- 1 | import {fromJS as im, Map, Set, List} from 'immutable' 2 | 3 | /////////////////////////////////////////////////////////////////////////////// 4 | // The Namespace constructor 5 | /////////////////////////////////////////////////////////////////////////////// 6 | /* 7 | * Generates a Namespace based on the Class() and Property() 8 | * definitions passed in. 9 | * 10 | * Usage: 11 | * 12 | * var ns = Namespace( 13 | * Class('Class1'), 14 | * Property('prop1') 15 | * ) 16 | * ns.Class1( 17 | * ns.prop1("prop1's value") 18 | * ) 19 | * 20 | */ 21 | export const Namespace = 22 | (...definitions) => definitions.reduce( 23 | (accum, x) => { 24 | let typeURI = x.get('@id') 25 | let resourceType = x.get('@type', Set()) 26 | let factoryFun = ( 27 | resourceType.contains('rdfs:Class') 28 | ? resourceFactory(typeURI) 29 | : resourceType.contains('rdfs:Property') 30 | ? propertyFactory(typeURI) 31 | : undefined 32 | ) 33 | accum[typeURI] = factoryFun 34 | accum['@graph'] = accum['@graph'].push(x) 35 | return accum 36 | }, 37 | new NamespaceClass() 38 | ) 39 | 40 | export class NamespaceClass { 41 | constructor() { 42 | this['@graph'] = List() 43 | } 44 | } 45 | /////////////////////////////////////////////////////////////////////////////// 46 | // The Prefix constructor 47 | /////////////////////////////////////////////////////////////////////////////// 48 | /* 49 | * includes the @context for a Namespace into the resource 50 | * 51 | * Usage: 52 | * 53 | * var ns = Namespace( 54 | * Class('Class1'), 55 | * Property('prop1') 56 | * ) 57 | * 58 | * ns.Class1( 59 | * Prefix('vocab', 'http://example.com/vocab#', ns), 60 | * ns.prop1('value1') 61 | * ) // returns a Class1 instance with the @context of ns 62 | */ 63 | 64 | export const Prefix = (prefix, uri, namespace, context={}) => { 65 | let contextMap = im(context) 66 | return new ResourceClass( 67 | namespace['@graph'].reduce( 68 | (accum, x) => { 69 | let label = x.get('@id') 70 | let uri = prefix + ':' + label 71 | let value = ( 72 | contextMap.has(label) 73 | ? contextMap.get(label).set('@id', uri) 74 | : uri 75 | ) 76 | return accum.updateIn( 77 | ['@context'], 78 | context => context.set( 79 | label, value 80 | ) 81 | ) 82 | }, 83 | Map({ 84 | '@context': Map( 85 | { 86 | 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#' 87 | } 88 | ).set( 89 | prefix, uri 90 | ) 91 | }) 92 | ) 93 | ) 94 | } 95 | 96 | 97 | /////////////////////////////////////////////////////////////////////////////// 98 | // The Resource constructor 99 | /////////////////////////////////////////////////////////////////////////////// 100 | /* 101 | * Composes a JSON-LD Resource as a Immutable.Map() 102 | * 103 | * Usage 104 | * 105 | * 106 | * var schema = Namespace( 107 | * Class('Thing'), 108 | * Property('image'), 109 | * Class('Person'), 110 | * Property('familyName'), 111 | * Property('givenName'), 112 | * ) 113 | * 114 | * var resource = Resource( 115 | * URI('#ericmoritz'), 116 | * Prefix('schema', 'http://schema.org/', schema), 117 | * schema.Thing( 118 | * schema.image("http://www.gravatar.com/avatar/4839d0678248e68eaeed5084e788210b.png") 119 | * ), 120 | * schema.Person( 121 | * schema.givenName('Eric'), 122 | * schema.familyName('Moritz') 123 | * ) 124 | * ) 125 | * 126 | */ 127 | export const Resource = (...properties) => 128 | new ResourceClass( 129 | properties.reduce( 130 | (x, y) => im(x).mergeDeep(im(y)) 131 | ) 132 | ) 133 | 134 | export class ResourceClass extends Map {} 135 | 136 | /////////////////////////////////////////////////////////////////////////////// 137 | // A convenience function for the '@id' field 138 | /////////////////////////////////////////////////////////////////////////////// 139 | export const URI = (uri) => 140 | new ResourceClass({ 141 | '@id': uri 142 | }) 143 | 144 | 145 | /////////////////////////////////////////////////////////////////////////////// 146 | // A convenience function for the '@type' field 147 | /////////////////////////////////////////////////////////////////////////////// 148 | export const type = (uri) => 149 | new ResourceClass({ 150 | '@type': Set([uri]) 151 | }) 152 | 153 | 154 | /////////////////////////////////////////////////////////////////////////////// 155 | // A convenience function for defining classes 156 | /////////////////////////////////////////////////////////////////////////////// 157 | export const Class = (label, ...properties) => 158 | Resource( 159 | URI(label), 160 | type('rdfs:Class'), 161 | ...properties 162 | ) 163 | 164 | 165 | /////////////////////////////////////////////////////////////////////////////// 166 | // A convenience function for defining Properties 167 | /////////////////////////////////////////////////////////////////////////////// 168 | export const Property = (label, ...properties) => 169 | Resource( 170 | URI(label), 171 | type('rdfs:Property'), 172 | ...properties 173 | ) 174 | 175 | export const Vocab = (...namespaces) => 176 | new ResourceClass( 177 | namespaces.reduce( 178 | (accum, y) => accum.updateIn( 179 | ['@graph'], 180 | graph => graph.concat( 181 | y['@graph'] 182 | ) 183 | ), 184 | Map({'@graph': List()}) 185 | ) 186 | ) 187 | /////////////////////////////////////////////////////////////////////////////// 188 | // Internal 189 | /////////////////////////////////////////////////////////////////////////////// 190 | 191 | // A function that creates a namespace's resource constructor 192 | const resourceFactory = typeURI => (...properties) => 193 | Resource(type(typeURI), ...properties) 194 | 195 | 196 | const propertyFactory = propURI => value => 197 | new ResourceClass().set(propURI, im(value)) 198 | 199 | -------------------------------------------------------------------------------- /test/example.test.js: -------------------------------------------------------------------------------- 1 | // # jsonld-dsl 2 | 3 | // [jsonld-dsl](http://github.com/ericmoritz/node-jsonld-dsl/) is a 4 | // DSL for building JSON-LD powered hypermedia services. This module 5 | // will serve as the view layer for a service. 6 | 7 | // Building JSON-LD services by hand often leads to inconsistencies 8 | // between the context and the resources that is hard to detect. 9 | 10 | // This DSL will help you build JSON-LD services that keep their 11 | // context's consistent. 12 | import expect from 'expect' 13 | import {DSL, URI, Namespace, Class, Property, Prefix, Resource, Vocab} from 'jsonld-dsl' 14 | 15 | 16 | // ## Namespace 17 | // 18 | // First, we'll look at how to declare a namespace for your service. 19 | // 20 | const xhtml = Namespace( 21 | Property('up') 22 | ) 23 | 24 | const schema = Namespace( 25 | Class('Thing'), 26 | Class('Blog'), 27 | Class('BlogPosting'), 28 | Property('name'), 29 | Property('url'), 30 | Property('blogPost'), 31 | Property('articleBody') 32 | ) 33 | 34 | it( 35 | 'should produce a Immutable.Map() of the BlogPosting() resource', 36 | () => { 37 | expect( 38 | 39 | // 40 | // This schema allows you to declare the classes and properties that 41 | // your resources will use. 42 | // 43 | // To generate a BlogPost resource is easy: 44 | // 45 | schema.BlogPosting( 46 | URI('/entries/hydra-lite.json'), 47 | schema.name('Hydra Lite'), 48 | schema.url('http://eric.themoritzfamily.com/hydra-lite.html'), 49 | schema.articleBody('This is the article body...') 50 | ).toJSON() 51 | 52 | ).toEqual( 53 | // 54 | // This will produce a `ResourceClass` instance which is a sub-class of 55 | // [Immutable.Map()](http://facebook.github.io/immutable-js/docs/#/Map) 56 | // instance that represents your resource. 57 | // 58 | { 59 | "@id": "/entries/hydra-lite.json", 60 | "@type": ["BlogPosting"], 61 | "name": "Hydra Lite", 62 | "url": "http://eric.themoritzfamily.com/hydra-lite.html", 63 | "articleBody": "This is the article body..." 64 | } 65 | ) 66 | } 67 | ) 68 | // 69 | // Because of the `Immutable.Map#toJSON` method, rendering 70 | // the resource as JSON is as easy as calling `JSON.stringify(entry)` 71 | // 72 | // The use of `Immutable.Map()` allows us to easily and efficiently 73 | // compose resources together. Any instance of a `ResourceClass` can 74 | // be composed with `Resource()` 75 | // 76 | 77 | it( 78 | 'Resource() allows you to compose class instances into a single resource', 79 | () => { 80 | expect( 81 | // 82 | // `Resource()` allows us render a resource that is the composition 83 | // multiple properties and classes. 84 | // 85 | // For instance to compose a resource of a `schema.Thing()` and 86 | // a `schema.BlogPosting()`. You would do the following 87 | // 88 | Resource( 89 | URI('/entries/hydra-lite.json'), 90 | schema.Thing( 91 | schema.name('Hydra Lite'), 92 | schema.url( 93 | 'http://eric.themoritzfamily.com/hydra-lite.html' 94 | ) 95 | ), 96 | schema.BlogPosting( 97 | schema.articleBody('This is the article body...') 98 | ) 99 | ).toJSON() 100 | ).toEqual( 101 | // 102 | // Rendered as JSON-LD, this is a really minor change: 103 | // 104 | { 105 | "@id": "/entries/hydra-lite.json", 106 | "@type": ["Thing", "BlogPosting"], 107 | "name": "Hydra Lite", 108 | "url": "http://eric.themoritzfamily.com/hydra-lite.html", 109 | "articleBody": "This is the article body..." 110 | } 111 | ) 112 | } 113 | // 114 | // Did you notice the difference? That's right, it simply adds 115 | // `"Thing"` to the `@type` field. 116 | // 117 | ) 118 | 119 | // 120 | // ## Context 121 | // 122 | // 123 | it('should render a context correctly', () => { 124 | // 125 | // You have two options when it comes to a JSON-LD you can embed the 126 | // context or you can refer to it as a URL. 127 | // 128 | // Let us start with how you would render a context resource: 129 | // 130 | // 131 | let context = Resource( 132 | Prefix( 133 | // This is the namespace's prefix. 134 | 'schema', 135 | // This is the namespace's URI 136 | 'http://schema.org/', 137 | schema, 138 | // This is an optional context overlay 139 | {'blogPost': {'@container': '@list'}} 140 | ), 141 | Prefix('xhtml', 'http://www.w3.org/1999/xhtml#', xhtml) 142 | ) 143 | 144 | expect( 145 | context.toJSON() 146 | ).toEqual( 147 | // As JSON this `context` will look like: 148 | { 149 | '@context': { 150 | 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#', 151 | 'Blog': 'schema:Blog', 152 | 'BlogPosting': 'schema:BlogPosting', 153 | 'Thing': 'schema:Thing', 154 | 'articleBody': 'schema:articleBody', 155 | 'blogPost': {'@id': 'schema:blogPost', '@container': '@list'}, 156 | 'name': 'schema:name', 157 | 'schema': 'http://schema.org/', 158 | 'up': 'xhtml:up', 159 | 'url': 'schema:url', 160 | 'xhtml': 'http://www.w3.org/1999/xhtml#', 161 | } 162 | } 163 | ) 164 | 165 | 166 | expect( 167 | // Resources can then link to the context like so: 168 | schema.BlogPosting( 169 | {'@context': '/context.jsonld'}, 170 | URI('/entries/hydra-lite.json'), 171 | schema.BlogPosting( 172 | schema.name('Hydra Lite'), 173 | schema.url( 174 | 'http://eric.themoritzfamily.com/hydra-lite.html' 175 | ), 176 | schema.articleBody('This is the article body...') 177 | ) 178 | ).toJSON() 179 | ).toEqual( 180 | // Which will result in JSON that looks like so 181 | { 182 | "@context": "/context.jsonld", 183 | "@id": "/entries/hydra-lite.json", 184 | "@type": ["BlogPosting"], 185 | "name": "Hydra Lite", 186 | "url": "http://eric.themoritzfamily.com/hydra-lite.html", 187 | "articleBody": "This is the article body..." 188 | } 189 | ) 190 | 191 | expect( 192 | // To Embed the context, this is really simple: 193 | schema.BlogPosting( 194 | context, 195 | URI('/entries/hydra-lite.json'), 196 | schema.BlogPosting( 197 | schema.name('Hydra Lite'), 198 | schema.url( 199 | 'http://eric.themoritzfamily.com/hydra-lite.html' 200 | ), 201 | schema.articleBody('This is the article body...') 202 | ) 203 | ).toJSON() 204 | ).toEqual( 205 | // 206 | // This results in JSON that looks like so: 207 | // 208 | { 209 | '@context': { 210 | 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#', 211 | 'Blog': 'schema:Blog', 212 | 'BlogPosting': 'schema:BlogPosting', 213 | 'Thing': 'schema:Thing', 214 | 'articleBody': 'schema:articleBody', 215 | 'blogPost': {'@id': 'schema:blogPost', '@container': '@list'}, 216 | 'name': 'schema:name', 217 | 'schema': 'http://schema.org/', 218 | 'up': 'xhtml:up', 219 | 'url': 'schema:url', 220 | 'xhtml': 'http://www.w3.org/1999/xhtml#', 221 | }, 222 | "@id": "/entries/hydra-lite.json", 223 | "@type": ["BlogPosting"], 224 | "name": "Hydra Lite", 225 | "url": "http://eric.themoritzfamily.com/hydra-lite.html", 226 | "articleBody": "This is the article body..." 227 | } 228 | ) 229 | }) 230 | 231 | 232 | // ## Vocabulary 233 | // 234 | it('should render a vocabulary correctly', () => { 235 | expect( 236 | // 237 | // If you need to automatically generate a vocabulary for your 238 | // service, this couldn't be any simpler: 239 | // 240 | Vocab(schema, xhtml).toJSON() 241 | ).toEqual( 242 | { 243 | "@graph": [ 244 | { 245 | "@id": "Thing", 246 | "@type": ["rdfs:Class"] 247 | }, 248 | { 249 | "@id": "Blog", 250 | "@type": ["rdfs:Class"] 251 | }, 252 | { 253 | "@id": "BlogPosting", 254 | "@type": ["rdfs:Class"] 255 | }, 256 | { 257 | "@id": "name", 258 | "@type": ["rdfs:Property"] 259 | }, 260 | { 261 | "@id": "url", 262 | "@type": ["rdfs:Property"] 263 | }, 264 | { 265 | "@id": "blogPost", 266 | "@type": ["rdfs:Property"] 267 | }, 268 | { 269 | "@id": "articleBody", 270 | "@type": ["rdfs:Property"] 271 | }, 272 | { 273 | "@id": "up", 274 | "@type": ["rdfs:Property"] 275 | } 276 | ] 277 | } 278 | ) 279 | }) 280 | 281 | // 282 | // ## Annotating your Properties and Classes 283 | // 284 | it('should allow annotation of Property and Classes', () => { 285 | 286 | // `Property()` and `Class()` results are `Resources()` like any other 287 | // so they allow you to annotate them just like any other resource 288 | 289 | let rdfs = Namespace( 290 | Property('comment'), 291 | Property('range') 292 | ) 293 | 294 | let hydra = Namespace( 295 | Class('Link') 296 | ) 297 | 298 | let ns = Namespace( 299 | Class('SearchResults'), 300 | Property( 301 | 'search', 302 | hydra.Link(), 303 | rdfs.comment('This is the search link'), 304 | rdfs.range(URI('SearchResults')) 305 | ) 306 | ) 307 | 308 | expect( 309 | // 310 | // The vocabulary of this namespace will include these annotations 311 | // 312 | Resource( 313 | Prefix('rdfs', 'http://www.w3.org/2000/01/rdf-schema#', rdfs), 314 | Prefix('hydra', 'http://www.w3.org/ns/hydra/core#', hydra), 315 | Prefix('ns', 'http://example.com/vocab#', ns), 316 | Vocab(ns) 317 | ).toJSON() 318 | ).toEqual( 319 | { 320 | "@context": { 321 | "rdfs": "http://www.w3.org/2000/01/rdf-schema#", 322 | "comment": "rdfs:comment", 323 | "range": "rdfs:range", 324 | "hydra": "http://www.w3.org/ns/hydra/core#", 325 | "Link": "hydra:Link", 326 | "SearchResults": "ns:SearchResults", 327 | "search": "ns:search", 328 | "ns": "http://example.com/vocab#" 329 | }, 330 | "@graph": [ 331 | { 332 | "@id": "SearchResults", 333 | "@type": ["rdfs:Class"] 334 | }, 335 | // 336 | // The search property is a `Link` as well as a `rdfs:Property` 337 | // 338 | { 339 | "@id": "search", 340 | "@type": ["rdfs:Property", "Link"], 341 | "comment": "This is the search link", 342 | "range": { "@id": "SearchResults"} 343 | } 344 | ] 345 | } 346 | ) 347 | }) 348 | 349 | // # hydra support 350 | import {hydra} from 'jsonld-dsl' 351 | 352 | // hydra is a part of the jsonld-dsl module. It defines 353 | // all the hydra classes and properties for you 354 | 355 | // For instance, here is a hydra collection that 356 | // has POST, PUT, and DELETE operations declared 357 | it('allows you to add hydra properties and classes', () => { 358 | expect( 359 | Resource( 360 | schema.Blog(), 361 | hydra.POST( 362 | hydra.statusCode([303, 400]), 363 | hydra.expects('BlogPosting') 364 | ), 365 | hydra.Collection( 366 | hydra.member([ 367 | Resource( 368 | URI('/entries/hydra-lite.json'), 369 | hydra.PUT( 370 | hydra.statusCode([204, 400]), 371 | hydra.expects('BlogPosting') 372 | ), 373 | hydra.DELETE(), 374 | schema.Thing( 375 | schema.name('Hydra Lite'), 376 | schema.url('http://eric.themoritzfamily.com/hydra-lite.html') 377 | ), 378 | schema.BlogPosting( 379 | schema.articleBody('This is the article body...') 380 | ) 381 | ) 382 | ]) 383 | ) 384 | ).toJSON() 385 | ).toEqual( 386 | { 387 | '@type': ['Blog', 'Collection'], 388 | 'operation': [ 389 | {'@type': ['Operation'], 'method': 'POST', 'expects': 'BlogPosting', 'statusCode': [303, 400]} 390 | ], 391 | 'member': [ 392 | { 393 | '@id': '/entries/hydra-lite.json', 394 | 'operation': [ 395 | {'@type': ['Operation'], 'method': 'PUT', 'expects': 'BlogPosting', 'statusCode': [204, 400]}, 396 | {'@type': ['Operation'], 'method': 'DELETE'} 397 | ], 398 | '@type': ['Thing', 'BlogPosting'], 399 | 'articleBody': 'This is the article body...', 400 | 'name': 'Hydra Lite', 401 | 'url': 'http://eric.themoritzfamily.com/hydra-lite.html' 402 | } 403 | ] 404 | } 405 | ) 406 | }) 407 | -------------------------------------------------------------------------------- /test/hydra.test.js: -------------------------------------------------------------------------------- 1 | import {Resource, hydra} from '../src/index' 2 | import expect from 'expect' 3 | 4 | describe('hydra support', () => { 5 | describe('operation shortcuts', () => { 6 | it('should support operation composition', () => { 7 | expect( 8 | Resource( 9 | hydra.PUT(), 10 | hydra.DELETE() 11 | ).toJSON() 12 | ).toEqual( 13 | { 14 | 'operation': [ 15 | {'@type': ['Operation'], 'method': 'PUT'}, 16 | {'@type': ['Operation'], 'method': 'DELETE'} 17 | ] 18 | } 19 | ) 20 | }) 21 | 22 | it('should support PUT', () => { 23 | expect( 24 | hydra.PUT().toJSON() 25 | ).toEqual( 26 | { 27 | 'operation': [ 28 | {'@type': ['Operation'], 'method': 'PUT'} 29 | ] 30 | } 31 | ) 32 | }) 33 | 34 | it('should support POST', () => { 35 | expect( 36 | hydra.POST().toJSON() 37 | ).toEqual( 38 | { 39 | 'operation': [ 40 | {'@type': ['Operation'], 'method': 'POST'} 41 | ] 42 | } 43 | ) 44 | }) 45 | 46 | it('should support DELETE', () => { 47 | expect( 48 | hydra.DELETE().toJSON() 49 | ).toEqual( 50 | { 51 | 'operation': [ 52 | {'@type': ['Operation'], 'method': 'DELETE'} 53 | ] 54 | } 55 | ) 56 | }) 57 | 58 | 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /test/jsonld-dsl.test.js: -------------------------------------------------------------------------------- 1 | import {NamespaceClass, ResourceClass, Namespace, URI, Class, Property, type, Resource, Vocab, Context, Prefix} from '../src/index' 2 | import expect from 'expect' 3 | import {Map, Set, List} from 'immutable' 4 | 5 | 6 | describe('URI', () => { 7 | it('should be a ResourceClass instance', () => { 8 | expect(URI('test')).toBeA(ResourceClass) 9 | }) 10 | it('should create the correct map', () => { 11 | expect( 12 | URI('test').toJSON() 13 | ).toEqual( 14 | { 15 | '@id': 'test' 16 | } 17 | ) 18 | }) 19 | }) 20 | 21 | describe('Vocab', () => { 22 | it('should be a ResourceClass instance', () => { 23 | let ns1 = Namespace( 24 | Class('Class1') 25 | ) 26 | expect(Vocab(ns1)).toBeA(ResourceClass) 27 | }) 28 | 29 | it('should create the correct map', () => { 30 | const ns1 = Namespace( 31 | Class('Class1'), 32 | Property('prop1') 33 | ) 34 | 35 | const ns2 = Namespace( 36 | Class('Class2'), 37 | Property('prop2') 38 | ) 39 | 40 | expect( 41 | Vocab(ns1, ns2).toJSON() 42 | ).toEqual( 43 | { 44 | '@graph': [ 45 | { 46 | "@id": "Class1", 47 | "@type": ["rdfs:Class"] 48 | }, 49 | { 50 | "@id": "prop1", 51 | "@type": ["rdfs:Property"] 52 | }, 53 | { 54 | "@id": "Class2", 55 | "@type": ["rdfs:Class"] 56 | }, 57 | { 58 | "@id": "prop2", 59 | "@type": ["rdfs:Property"] 60 | } 61 | ] 62 | } 63 | ) 64 | 65 | }) 66 | 67 | it('should support property supclassing', function() { 68 | let hydra = Namespace( 69 | Class('Link') 70 | ) 71 | var search = Property('search', hydra.Link()) 72 | 73 | expect( 74 | search.toJSON() 75 | ).toEqual( 76 | { 77 | '@id': 'search', 78 | '@type': ['rdfs:Property', 'Link'] 79 | } 80 | ) 81 | }) 82 | }) 83 | 84 | 85 | describe('type', () => { 86 | it('should be a ResourceClass instance', () => { 87 | expect(type('test')).toBeA(ResourceClass) 88 | }) 89 | it('should create a type set', () => { 90 | expect( 91 | type('test').toJSON() 92 | ).toEqual( 93 | { 94 | '@type': ['test'] 95 | } 96 | ) 97 | }) 98 | }) 99 | 100 | 101 | describe('Class', () => { 102 | it('should be a ResourceClass instance', () => { 103 | expect(Class('TestClass')).toBeA(ResourceClass) 104 | }) 105 | 106 | it('should create a Class resource', () => { 107 | expect( 108 | Class('TestClass').toJSON() 109 | ).toEqual( 110 | { 111 | '@id': 'TestClass', 112 | '@type': ['rdfs:Class'] 113 | } 114 | ) 115 | }) 116 | }) 117 | 118 | 119 | describe('Property', () => { 120 | it('should be a ResourceClass instance', () => { 121 | expect(Property('testProperty')).toBeA(ResourceClass) 122 | }) 123 | 124 | it('should create a Property resource', () => { 125 | expect( 126 | Property('testProperty').toJSON() 127 | ).toEqual( 128 | { 129 | '@id': 'testProperty', 130 | '@type': ['rdfs:Property'] 131 | } 132 | ) 133 | }) 134 | 135 | it('should allow additional properties', () => { 136 | expect( 137 | Property('testProperty').toJSON() 138 | ).toEqual( 139 | { 140 | '@id': 'testProperty', 141 | '@type': ['rdfs:Property'], 142 | } 143 | ) 144 | }) 145 | }) 146 | 147 | 148 | describe('Resource', () => { 149 | it('should be a ResourceClass instance', () => { 150 | expect(Resource(URI('/'))).toBeA(ResourceClass) 151 | }) 152 | 153 | it('should generate a resource correctly', () => { 154 | let res = Resource( 155 | URI('/'), 156 | type('Type1'), 157 | type('Type2') 158 | ) 159 | expect(res.toJSON()) 160 | .toEqual({ 161 | '@id': '/', 162 | '@type': ['Type1', 'Type2'] 163 | }) 164 | }) 165 | }) 166 | 167 | 168 | describe('Namespace', () => { 169 | it('should be a NamespaceClass instance', () => { 170 | expect(Namespace(Class('Class1'))).toBeA(NamespaceClass) 171 | }) 172 | 173 | it('should create a namespace correctly', () => { 174 | 175 | const ns = Namespace( 176 | Class('Class1'), 177 | Property('prop1') 178 | ) 179 | 180 | expect( 181 | ns.Class1(URI('/')).toJSON() 182 | ).toEqual( 183 | { 184 | '@id': '/', 185 | '@type': ['Class1'] 186 | } 187 | ) 188 | 189 | expect( 190 | ns.prop1('test').toJSON() 191 | ).toEqual( 192 | { 193 | 'prop1': 'test' 194 | } 195 | ) 196 | }) 197 | }) 198 | 199 | describe('Prefix', () => { 200 | it('should be a ResourceClass instance', () => { 201 | const ns = Namespace( 202 | Class('Class1') 203 | ) 204 | expect(Prefix('ns', 'http://example.com/', ns)).toBeA(ResourceClass) 205 | }) 206 | 207 | it('should generate the context correctly', () => { 208 | const ns = Namespace( 209 | Class('Class1'), 210 | Property('prop1') 211 | ) 212 | 213 | const prefix = Prefix( 214 | 'vocab', 'http://example.com/vocab#', ns 215 | ) 216 | 217 | expect( 218 | prefix.toJSON() 219 | ).toEqual({ 220 | '@context': { 221 | 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#', 222 | 'vocab': 'http://example.com/vocab#', 223 | 'Class1': 'vocab:Class1', 224 | 'prop1': 'vocab:prop1' 225 | } 226 | }) 227 | }) 228 | 229 | it('should support context overrides', () => { 230 | const ns = Namespace( 231 | Property('member'), 232 | Property('up') 233 | ) 234 | 235 | const prefix = Prefix( 236 | 'vocab', 'http://example.com/vocab#', ns, 237 | { 238 | 'member': {'@container': '@list'}, 239 | 'up': {'@type': '@id'} 240 | } 241 | ) 242 | expect( 243 | prefix.toJSON() 244 | ).toEqual( 245 | { 246 | '@context': { 247 | 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#', 248 | 'vocab': 'http://example.com/vocab#', 249 | 'member': {'@id': 'vocab:member', '@container': '@list'}, 250 | 'up': {'@id': 'vocab:up', '@type': '@id'} 251 | } 252 | } 253 | ) 254 | }) 255 | }) 256 | -------------------------------------------------------------------------------- /test/module-import.test.js: -------------------------------------------------------------------------------- 1 | import DSLModule from 'jsonld-dsl' 2 | import IndexModule from '../src/index' 3 | import expect from 'expect' 4 | 5 | it('should support importing as jsonld-dsl', () => { 6 | expect( 7 | DSLModule 8 | ).toEqual( 9 | IndexModule 10 | ) 11 | }) 12 | --------------------------------------------------------------------------------