From 774b3f911c4064f72e1dc1e2723b708f8b26777c Mon Sep 17 00:00:00 2001 From: tpluscode Date: Tue, 30 Jun 2020 11:23:11 +0200 Subject: [PATCH 1/8] feat: filtering literals by language tags --- docs/api.md | 8 +- lib/Clownface.js | 6 +- lib/Context.js | 16 +++- lib/languageTag.js | 47 ++++++++++ lib/namespace.js | 1 - package.json | 3 + test/Clownface/out.js | 186 +++++++++++++++++++++++++++++++++++++- test/support/namespace.js | 4 +- test/support/parse.js | 14 +++ 9 files changed, 274 insertions(+), 11 deletions(-) create mode 100644 lib/languageTag.js create mode 100644 test/support/parse.js diff --git a/docs/api.md b/docs/api.md index 0cd58fb..4d06baf 100644 --- a/docs/api.md +++ b/docs/api.md @@ -51,7 +51,7 @@ A graph pointer object, which points at 0..N nodes within a dataset * [.literal(values, [languageOrDatatype])](#Clownface+literal) ⇒ [Clownface](#Clownface) * [.namedNode(values)](#Clownface+namedNode) ⇒ [Clownface](#Clownface) * [.in(predicates)](#Clownface+in) ⇒ [Clownface](#Clownface) - * [.out(predicates)](#Clownface+out) ⇒ [Clownface](#Clownface) + * [.out(predicates, [options])](#Clownface+out) ⇒ [Clownface](#Clownface) * [.has(predicates, [objects])](#Clownface+has) ⇒ [Clownface](#Clownface) * [.addIn(predicates, subjects, [callback])](#Clownface+addIn) ⇒ [Clownface](#Clownface) * [.addOut(predicates, objects, [callback])](#Clownface+addOut) ⇒ [Clownface](#Clownface) @@ -279,7 +279,7 @@ Creates a graph pointer to nodes which are linked to the current pointer by `pre -### clownface.out(predicates) ⇒ [Clownface](#Clownface) +### clownface.out(predicates, [options]) ⇒ [Clownface](#Clownface) Creates a graph pointer to nodes which link the current pointer by `predicates` **Kind**: instance method of [Clownface](#Clownface) @@ -293,6 +293,10 @@ Creates a graph pointer to nodes which link the current pointer by `predicates` predicatesTerm | Array.<Term> | Clownface | Array.<Clownface>

one or more RDF/JS term identifying a property

+ + [options]object + + [options.language]string | Array.<string> | undefined diff --git a/lib/Clownface.js b/lib/Clownface.js index bea84a7..f7bcf68 100644 --- a/lib/Clownface.js +++ b/lib/Clownface.js @@ -252,12 +252,14 @@ class Clownface { /** * Creates a graph pointer to nodes which link the current pointer by `predicates` * @param {Term|Term[]|Clownface|Clownface[]} predicates one or more RDF/JS term identifying a property + * @param {object} [options] + * @param {string | string[] | undefined} [options.language] * @returns {Clownface} */ - out (predicates) { + out (predicates, options) { predicates = this._toTermArray(predicates) - const context = this._context.reduce((all, current) => all.concat(current.out(predicates)), []) + const context = this._context.reduce((all, current) => all.concat(current.out(predicates, options)), []) return Clownface.fromContext(context) } diff --git a/lib/Context.js b/lib/Context.js index 5851ce0..372f366 100644 --- a/lib/Context.js +++ b/lib/Context.js @@ -1,6 +1,7 @@ const inArray = require('./inArray') const term = require('./term') const toArray = require('./toArray') +const { createLanguageMapper } = require('../lib/languageTag') class Context { constructor ({ dataset, graph, value, factory, namespace }) { @@ -27,9 +28,18 @@ class Context { }) } - out (predicate) { - return this.matchProperty(toArray(this.term), predicate, null, toArray(this.graph), 'object').map(subject => { - return this.clone({ value: subject }) + out (predicate, options) { + let objects = this.matchProperty(toArray(this.term), predicate, null, toArray(this.graph), 'object') + + if (options && typeof options.language !== 'undefined') { + const languages = (typeof options.language === 'string' ? [options.language] : options.language) + const getLiteralsForLanguage = createLanguageMapper(objects) + + objects = languages.map(getLiteralsForLanguage).find(Boolean) || [] + } + + return objects.map(object => { + return this.clone({ value: object }) }) } diff --git a/lib/languageTag.js b/lib/languageTag.js new file mode 100644 index 0000000..9ed9b20 --- /dev/null +++ b/lib/languageTag.js @@ -0,0 +1,47 @@ +const { namedNode } = require('@rdfjs/data-model') + +const langString = namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#langString') +const xsdString = namedNode('http://www.w3.org/2001/XMLSchema#string') + +function mapLiteralsByLanguage(map, current) { + const notLiteral = current.termType !== 'Literal' + const notStringLiteral = langString.equals(current.datatype) || xsdString.equals(current.datatype) + + if (notLiteral || !notStringLiteral) return map + + const language = current.language.toLowerCase() + + if (map.has(language)) { + map.get(language).push(current) + } else{ + map.set(language, [current]) + } + + return map +} + +function createLanguageMapper(objects) { + const literalsByLanguage = objects.reduce(mapLiteralsByLanguage, new Map()) + const langMapEntries = [...literalsByLanguage.entries()] + + return language => { + const languageLowerCase = language.toLowerCase() + + if (languageLowerCase === '*') { + return langMapEntries[0] && langMapEntries[0][1] + } + + const exactMatch = literalsByLanguage.get(languageLowerCase) + if (!exactMatch) { + const secondaryMatches = langMapEntries.find(([entryLanguage]) => entryLanguage.startsWith(languageLowerCase)) + + return secondaryMatches && secondaryMatches[1] + } + + return exactMatch + } +} + +module.exports = { + createLanguageMapper +} diff --git a/lib/namespace.js b/lib/namespace.js index bff0c18..d5cd2be 100644 --- a/lib/namespace.js +++ b/lib/namespace.js @@ -1,4 +1,3 @@ - const ns = (factory) => ({ first: factory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#first'), nil: factory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'), diff --git a/package.json b/package.json index e81cb05..6b01867 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,8 @@ }, "devDependencies": { "@rdfjs/parser-n3": "^1.1.2", + "@tpluscode/rdf-ns-builders": "^0.3.6", + "@tpluscode/rdf-string": "^0.2.15", "docsify-cli": "^4.4.0", "husky": "^4.2.5", "jsdoc-to-markdown": "^5.0.3", @@ -39,6 +41,7 @@ "rdf-ext": "^1.3.0", "rimraf": "^3.0.2", "standard": "^12.0.1", + "string-to-stream": "^3.0.1", "tbbt-ld": "^1.1.0" }, "nyc": { diff --git a/test/Clownface/out.js b/test/Clownface/out.js index be2ce50..7713022 100644 --- a/test/Clownface/out.js +++ b/test/Clownface/out.js @@ -1,10 +1,13 @@ -/* global describe, it */ - +const { describe, it } = require('mocha') const assert = require('assert') +const { turtle } = require('@tpluscode/rdf-string') +const { rdfs } = require('@tpluscode/rdf-ns-builders') const clownface = require('../..') const loadExample = require('../support/example') const rdf = require('../support/factory') const Clownface = require('../../lib/Clownface') +const { ex } = require('../support/namespace') +const parse = require('../support/parse') describe('.out', () => { it('should be a function', () => { @@ -65,4 +68,183 @@ describe('.out', () => { assert.strictEqual(result._context.length, 2) }) + + describe('with language option', () => { + const testData = turtle`${ex.ananas} + ${rdfs.label} "Pineapple" ; + ${rdfs.label} "Ananas"@pl ; + ${rdfs.label} "Ananas"@de ; + ${rdfs.label} "Ananász"@hu ; + ${rdfs.label} "Ananas"@sr-Latn ; + ${rdfs.label} "Ананас"@sr-Cyrl ; + . + + ${ex.noLabels} ${rdfs.label} _:foo , ${ex.bar}, 41 . + + ${ex.apple} + ${rdfs.label} "Apple"@en ; + ${rdfs.label} "Apfel"@de ; + ${rdfs.label} "Јабука"@sr-Cyrl . + + ${ex.carrot} + ${rdfs.label} "Karotte"@de ; + ${rdfs.label} "Karotte"@de-AT ; + ${rdfs.label} "Rüebli"@de-CH ; + . + + ${ex.eggplant} + ${rdfs.label} "Psianka podłużna"@pl, "Bakłażan"@pl, "Oberżyna"@pl . + + ${ex.kongressstrasse} + ${rdfs.label} "Kongressstraße"@de ; + ${rdfs.label} "Kongreßstraße"@de-DE-1901 ; + .`.toString() + + describe('filtered by single language parameter', () => { + it('should not return non-literals and non-string literals when language parameter is defined', async () => { + // given + const apple = (await parse(testData)).node(ex.noLabels) + + // when + const label = apple.out(rdfs.label, { language: '' }) + + // then + assert.strictEqual(label.terms.length, 0) + }) + + it('should return exact match for given language', async () => { + // given + const apple = (await parse(testData)).node(ex.apple) + + // when + const label = apple.out(rdfs.label, { language: 'de' }) + + // then + assert(label.term.equals(rdf.literal('Apfel', 'de'))) + }) + + it('should return plain string when language is empty string', async () => { + // given + const apple = (await parse(testData)).node(ex.ananas) + + // when + const label = apple.out(rdfs.label, { language: '' }) + + // then + assert(label.term.equals(rdf.literal('Pineapple'))) + }) + + it('should skip pointers which do not have matching language', async () => { + // given + const apple = (await parse(testData)).node(ex.ananas) + + // when + const label = apple.out(rdfs.label, { language: 'en' }) + + // then + assert.strictEqual(label.values.length, 0) + }) + + it('should return any result for wildcard language', async () => { + // given + const apple = (await parse(testData)).node(ex.ananas) + + // when + const label = apple.out(rdfs.label, { language: '*' }) + + // then + assert.ok(label.term) + }) + + it('should return all matching literals for a language', async () => { + // given + const apple = (await parse(testData)).node(ex.eggplant) + + // when + const label = apple.out(rdfs.label, { language: 'pl' }) + + // then + assert.strictEqual(label.terms.length, 3) + }) + + it('should return all matching literals for a wildcard language', async () => { + // given + const apple = (await parse(testData)).node(ex.eggplant) + + // when + const label = apple.out(rdfs.label, { language: '*' }) + + // then + assert.strictEqual(label.terms.length, 3) + }) + + it('should be case-insensitive', async () => { + // given + const apple = (await parse(testData)).node(ex.apple) + + // when + const label = apple.out(rdfs.label, { language: 'SR-cyrl' }) + + // then + assert(label.term.equals(rdf.literal('Јабука', 'sr-Cyrl'))) + }) + + it('should match secondary language tag by primary', async () => { + // given + const apple = (await parse(testData)).node(ex.apple) + + // when + const label = apple.out(rdfs.label, { language: 'sr' }) + + // then + assert(label.term.equals(rdf.literal('Јабука', 'sr-Cyrl'))) + }) + + it('should match secondary language tag by primary regardless of case', async () => { + // given + const apple = (await parse(testData)).node(ex.apple) + + // when + const label = apple.out(rdfs.label, { language: 'SR' }) + + // then + assert(label.term.equals(rdf.literal('Јабука', 'sr-Cyrl'))) + }) + + it('should match tertiary tag by secondary language', async () => { + // given + const apple = (await parse(testData)).node(ex.kongressstrasse) + + // when + const label = apple.out(rdfs.label, { language: 'de-DE' }) + + // then + assert(label.term.equals(rdf.literal('Kongreßstraße', 'de-DE-1901'))) + }) + }) + + describe('filtered by multiple languages', () => { + it('should choose first match', async () => { + // given + const apple = (await parse(testData)).node(ex.apple) + + // when + const label = apple.out(rdfs.label, { language: ['fr', 'no', 'be', 'en', 'de'] }) + + // then + assert(label.term.equals(rdf.literal('Apple', 'en'))) + }) + + it('should choose exact match over secondary language', async () => { + // given + const apple = (await parse(testData)).node(ex.carrot) + + // when + const label = apple.out(rdfs.label, { language: ['de-1901', 'de'] }) + + // then + assert(label.term.equals(rdf.literal('Karotte', 'de'))) + }) + }) + }) }) diff --git a/test/support/namespace.js b/test/support/namespace.js index 37f69f7..616d83f 100644 --- a/test/support/namespace.js +++ b/test/support/namespace.js @@ -1,10 +1,12 @@ const rdf = require('rdf-ext') +const namespace = require('@rdfjs/namespace') const ns = { first: rdf.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#first'), list: rdf.namedNode('http://example.org/list'), nil: rdf.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'), - rest: rdf.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#rest') + rest: rdf.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#rest'), + ex: namespace('http://example.org/') } module.exports = ns diff --git a/test/support/parse.js b/test/support/parse.js new file mode 100644 index 0000000..d1a407b --- /dev/null +++ b/test/support/parse.js @@ -0,0 +1,14 @@ +const $rdf = require('rdf-ext') +const toStream = require('string-to-stream') +const Parser = require('@rdfjs/parser-n3') +const cf = require('../../') + +const parser = new Parser() + +async function parse (string) { + const dataset = await $rdf.dataset().import(parser.import(toStream(string))) + + return cf({ dataset }) +} + +module.exports = parse From 270c9cc42a6979347a984766d676e6f7899af2ed Mon Sep 17 00:00:00 2001 From: tpluscode Date: Tue, 30 Jun 2020 16:10:18 +0200 Subject: [PATCH 2/8] docs: add page for the language tag feature --- docs/_sidebar.md | 1 + docs/tagged-literals.md | 137 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 docs/tagged-literals.md diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 5e96be5..be973a8 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -7,6 +7,7 @@ * [Manipulating data](manipulation.md) * [Working with named graphs](named-graphs.md) * [RDF Lists](rdf-lists.md) + * [Tagged literals](tagged-literals.md) * Reference * [JSDoc](api.md) * [TypeScript](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/clownface) diff --git a/docs/tagged-literals.md b/docs/tagged-literals.md new file mode 100644 index 0000000..2bb5b6d --- /dev/null +++ b/docs/tagged-literals.md @@ -0,0 +1,137 @@ +# Literals with language tags + +?> New in 1.0 + +Using the `.out()` method it is possible to only find literals in specific languages by passing a second `{ language }` parameter to the method. + +When that parameter is defined, only string literal nodes will be returned. + +For any given subject, all strings in the chosen language will be returned. + +## Finding specific language + +To find string literal in a given language, pass a second object argument with a string `language` key. + + + +```js +const cf = require('clownface') +const RDF = require('@rdfjs/dataset') +const { literal } = require('@rdfjs/data-model') +const { rdf, rdfs } = require('@tpluscode/rdf-ns-builders') + +// create two labels for a resource +const apple = cf({ dataset: RDF.dataset() }) + .node(rdf.Resource) + .addOut(rdfs.label, literal('apple', 'en')) + .addOut(rdfs.label, literal('Apfel', 'de')) + +// find German label +apple.out(rdfs.label, { language: 'de' }).value +``` + + + +## Finding plain literals + +Using an empty string for the `language` parameter will find strings without a language. + + + +```js +const cf = require('clownface') +const RDF = require('@rdfjs/dataset') +const { literal } = require('@rdfjs/data-model') +const { rdf, rdfs } = require('@tpluscode/rdf-ns-builders') + +// create two labels for a resource +const apple = cf({ dataset: RDF.dataset() }) + .node(rdf.Resource) + .addOut(rdfs.label, literal('apple')) + .addOut(rdfs.label, literal('Apfel', 'de')) + +// find literal without language tag +apple.out(rdfs.label, { language: '' }).value +``` + + + +## Finding from a choice of potential languages + +It is possible to look up the literals in multiple alternatives byt providing an array of languages instead. The first language which gets matched to the literals will be used. + + + +```js +const cf = require('clownface') +const RDF = require('@rdfjs/dataset') +const { literal } = require('@rdfjs/data-model') +const { rdf, rdfs } = require('@tpluscode/rdf-ns-builders') + +// create two labels for a resource +const apple = cf({ dataset: RDF.dataset() }) + .node(rdf.Resource) + .addOut(rdfs.label, literal('apple', 'en')) + .addOut(rdfs.label, literal('Apfel', 'de')) + +// there is no French translation so English will be returned +apple.out(rdfs.label, { language: ['fr', 'en'] }).value +``` + + + +A wildcard (asterisk) can also be used to choose any other (random) literal if the preceding choices did not yield any results. It would look similarly to previous example. + +```js +apple.out(rdfs.label, { language: ['fr', '*'] }).value +``` + +!> The result can be either English or German with equal probability. + +## Matching subtags + +In specific cases [subtags](https://tools.ietf.org/html/bcp47#section-2.2), such as `de-CH` can be matched to a given language. By analogy, it is also possible to find a subtag of any length by applying a "starts with" match. + +For example, in the snippet below the more specific subtag `de-CH-1996` will indeed be matched to the more general Swiss German `de-CH` + + + +```js +const cf = require('clownface') +const RDF = require('@rdfjs/dataset') +const { literal } = require('@rdfjs/data-model') +const { rdf, rdfs } = require('@tpluscode/rdf-ns-builders') + +// create two labels for a resource +const bicycle = cf({ dataset: RDF.dataset() }) + .node(rdf.Resource) + .addOut(rdfs.label, literal('Fahrrad', 'de')) + .addOut(rdfs.label, literal('Velo', 'de-CH-1996')) + +// finds a Swiss translation +bicycle.out(rdfs.label, { language: 'de-CH' }).value +``` + + + +!> However, any exact match will always take precedence before the subtag match + + + +```js +const cf = require('clownface') +const RDF = require('@rdfjs/dataset') +const { literal } = require('@rdfjs/data-model') +const { rdf, rdfs } = require('@tpluscode/rdf-ns-builders') + +// create two labels for a resource +const bicycle = cf({ dataset: RDF.dataset() }) + .node(rdf.Resource) + .addOut(rdfs.label, literal('Fahrrad', 'de')) + .addOut(rdfs.label, literal('Velo', 'de-CH-1996')) + +// finds the standard German label +bicycle.out(rdfs.label, { language: 'de' }).value +``` + + From a118063fcbfc81d5b76760612ad078e81c0435d3 Mon Sep 17 00:00:00 2001 From: tpluscode Date: Tue, 30 Jun 2020 16:19:07 +0200 Subject: [PATCH 3/8] refactor: flip if statement to reduce nesting --- lib/languageTag.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/languageTag.js b/lib/languageTag.js index 9ed9b20..d74c06d 100644 --- a/lib/languageTag.js +++ b/lib/languageTag.js @@ -32,13 +32,13 @@ function createLanguageMapper(objects) { } const exactMatch = literalsByLanguage.get(languageLowerCase) - if (!exactMatch) { - const secondaryMatches = langMapEntries.find(([entryLanguage]) => entryLanguage.startsWith(languageLowerCase)) - - return secondaryMatches && secondaryMatches[1] + if (exactMatch) { + return exactMatch } - return exactMatch + const secondaryMatches = langMapEntries.find(([entryLanguage]) => entryLanguage.startsWith(languageLowerCase)) + + return secondaryMatches && secondaryMatches[1] } } From e5be5719d3e5645595b725b3a393b26cb01e1507 Mon Sep 17 00:00:00 2001 From: tpluscode Date: Tue, 30 Jun 2020 16:20:59 +0200 Subject: [PATCH 4/8] style fix --- lib/languageTag.js | 54 +++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/languageTag.js b/lib/languageTag.js index d74c06d..cdbbf47 100644 --- a/lib/languageTag.js +++ b/lib/languageTag.js @@ -3,45 +3,45 @@ const { namedNode } = require('@rdfjs/data-model') const langString = namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#langString') const xsdString = namedNode('http://www.w3.org/2001/XMLSchema#string') -function mapLiteralsByLanguage(map, current) { - const notLiteral = current.termType !== 'Literal' - const notStringLiteral = langString.equals(current.datatype) || xsdString.equals(current.datatype) +function mapLiteralsByLanguage (map, current) { + const notLiteral = current.termType !== 'Literal' + const notStringLiteral = langString.equals(current.datatype) || xsdString.equals(current.datatype) - if (notLiteral || !notStringLiteral) return map + if (notLiteral || !notStringLiteral) return map - const language = current.language.toLowerCase() + const language = current.language.toLowerCase() - if (map.has(language)) { - map.get(language).push(current) - } else{ - map.set(language, [current]) - } + if (map.has(language)) { + map.get(language).push(current) + } else { + map.set(language, [current]) + } - return map + return map } -function createLanguageMapper(objects) { - const literalsByLanguage = objects.reduce(mapLiteralsByLanguage, new Map()) - const langMapEntries = [...literalsByLanguage.entries()] +function createLanguageMapper (objects) { + const literalsByLanguage = objects.reduce(mapLiteralsByLanguage, new Map()) + const langMapEntries = [...literalsByLanguage.entries()] - return language => { - const languageLowerCase = language.toLowerCase() + return language => { + const languageLowerCase = language.toLowerCase() - if (languageLowerCase === '*') { - return langMapEntries[0] && langMapEntries[0][1] - } + if (languageLowerCase === '*') { + return langMapEntries[0] && langMapEntries[0][1] + } - const exactMatch = literalsByLanguage.get(languageLowerCase) - if (exactMatch) { - return exactMatch - } + const exactMatch = literalsByLanguage.get(languageLowerCase) + if (exactMatch) { + return exactMatch + } - const secondaryMatches = langMapEntries.find(([entryLanguage]) => entryLanguage.startsWith(languageLowerCase)) + const secondaryMatches = langMapEntries.find(([entryLanguage]) => entryLanguage.startsWith(languageLowerCase)) - return secondaryMatches && secondaryMatches[1] - } + return secondaryMatches && secondaryMatches[1] + } } module.exports = { - createLanguageMapper + createLanguageMapper } From ba6041409e6333325a4ff849345fa84d29346aee Mon Sep 17 00:00:00 2001 From: tpluscode Date: Wed, 1 Jul 2020 08:21:51 +0200 Subject: [PATCH 5/8] refactor: destructure options argument --- lib/Clownface.js | 2 +- lib/Context.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Clownface.js b/lib/Clownface.js index f7bcf68..410b09a 100644 --- a/lib/Clownface.js +++ b/lib/Clownface.js @@ -256,7 +256,7 @@ class Clownface { * @param {string | string[] | undefined} [options.language] * @returns {Clownface} */ - out (predicates, options) { + out (predicates, options = {}) { predicates = this._toTermArray(predicates) const context = this._context.reduce((all, current) => all.concat(current.out(predicates, options)), []) diff --git a/lib/Context.js b/lib/Context.js index 372f366..698e2c3 100644 --- a/lib/Context.js +++ b/lib/Context.js @@ -28,11 +28,11 @@ class Context { }) } - out (predicate, options) { + out (predicate, { language }) { let objects = this.matchProperty(toArray(this.term), predicate, null, toArray(this.graph), 'object') - if (options && typeof options.language !== 'undefined') { - const languages = (typeof options.language === 'string' ? [options.language] : options.language) + if (typeof language !== 'undefined') { + const languages = (typeof language === 'string' ? [language] : language) const getLiteralsForLanguage = createLanguageMapper(objects) objects = languages.map(getLiteralsForLanguage).find(Boolean) || [] From 0a9610e97e8ca9357174e07042813b12b7cb1855 Mon Sep 17 00:00:00 2001 From: tpluscode Date: Wed, 1 Jul 2020 08:26:23 +0200 Subject: [PATCH 6/8] remove comments in unit tests --- test/Clownface/out.js | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/test/Clownface/out.js b/test/Clownface/out.js index 7713022..1bcd092 100644 --- a/test/Clownface/out.js +++ b/test/Clownface/out.js @@ -102,147 +102,108 @@ describe('.out', () => { describe('filtered by single language parameter', () => { it('should not return non-literals and non-string literals when language parameter is defined', async () => { - // given const apple = (await parse(testData)).node(ex.noLabels) - // when const label = apple.out(rdfs.label, { language: '' }) - // then assert.strictEqual(label.terms.length, 0) }) it('should return exact match for given language', async () => { - // given const apple = (await parse(testData)).node(ex.apple) - // when const label = apple.out(rdfs.label, { language: 'de' }) - // then assert(label.term.equals(rdf.literal('Apfel', 'de'))) }) it('should return plain string when language is empty string', async () => { - // given const apple = (await parse(testData)).node(ex.ananas) - // when const label = apple.out(rdfs.label, { language: '' }) - // then assert(label.term.equals(rdf.literal('Pineapple'))) }) it('should skip pointers which do not have matching language', async () => { - // given const apple = (await parse(testData)).node(ex.ananas) - // when const label = apple.out(rdfs.label, { language: 'en' }) - // then assert.strictEqual(label.values.length, 0) }) it('should return any result for wildcard language', async () => { - // given const apple = (await parse(testData)).node(ex.ananas) - // when const label = apple.out(rdfs.label, { language: '*' }) - // then assert.ok(label.term) }) it('should return all matching literals for a language', async () => { - // given const apple = (await parse(testData)).node(ex.eggplant) - // when const label = apple.out(rdfs.label, { language: 'pl' }) - // then assert.strictEqual(label.terms.length, 3) }) it('should return all matching literals for a wildcard language', async () => { - // given const apple = (await parse(testData)).node(ex.eggplant) - // when const label = apple.out(rdfs.label, { language: '*' }) - // then assert.strictEqual(label.terms.length, 3) }) it('should be case-insensitive', async () => { - // given const apple = (await parse(testData)).node(ex.apple) - // when const label = apple.out(rdfs.label, { language: 'SR-cyrl' }) - // then assert(label.term.equals(rdf.literal('Јабука', 'sr-Cyrl'))) }) it('should match secondary language tag by primary', async () => { - // given const apple = (await parse(testData)).node(ex.apple) - // when const label = apple.out(rdfs.label, { language: 'sr' }) - // then assert(label.term.equals(rdf.literal('Јабука', 'sr-Cyrl'))) }) it('should match secondary language tag by primary regardless of case', async () => { - // given const apple = (await parse(testData)).node(ex.apple) - // when const label = apple.out(rdfs.label, { language: 'SR' }) - // then assert(label.term.equals(rdf.literal('Јабука', 'sr-Cyrl'))) }) it('should match tertiary tag by secondary language', async () => { - // given const apple = (await parse(testData)).node(ex.kongressstrasse) - // when const label = apple.out(rdfs.label, { language: 'de-DE' }) - // then assert(label.term.equals(rdf.literal('Kongreßstraße', 'de-DE-1901'))) }) }) describe('filtered by multiple languages', () => { it('should choose first match', async () => { - // given const apple = (await parse(testData)).node(ex.apple) - // when const label = apple.out(rdfs.label, { language: ['fr', 'no', 'be', 'en', 'de'] }) - // then assert(label.term.equals(rdf.literal('Apple', 'en'))) }) it('should choose exact match over secondary language', async () => { - // given const apple = (await parse(testData)).node(ex.carrot) - // when const label = apple.out(rdfs.label, { language: ['de-1901', 'de'] }) - // then assert(label.term.equals(rdf.literal('Karotte', 'de'))) }) }) From b4e8695345ba00362306e029a1fc5a1c8e7fa099 Mon Sep 17 00:00:00 2001 From: tpluscode Date: Tue, 7 Jul 2020 16:52:36 +0200 Subject: [PATCH 7/8] refactor: have only one location for namespaces --- lib/fromPrimitive.js | 4 ++-- lib/languageTag.js | 8 ++++---- lib/namespace.js | 19 ++++++++++++++----- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/fromPrimitive.js b/lib/fromPrimitive.js index c454d66..ad37bf7 100644 --- a/lib/fromPrimitive.js +++ b/lib/fromPrimitive.js @@ -1,7 +1,7 @@ const rdf = require('@rdfjs/data-model') -const namespace = require('@rdfjs/namespace') +const namespace = require('./namespace') -const xsd = namespace('http://www.w3.org/2001/XMLSchema#') +const { xsd } = namespace(rdf) function booleanToLiteral (value, factory = rdf) { if (typeof value !== 'boolean') { diff --git a/lib/languageTag.js b/lib/languageTag.js index cdbbf47..8f3804a 100644 --- a/lib/languageTag.js +++ b/lib/languageTag.js @@ -1,11 +1,11 @@ -const { namedNode } = require('@rdfjs/data-model') +const RDF = require('@rdfjs/data-model') +const namespace = require('./namespace') -const langString = namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#langString') -const xsdString = namedNode('http://www.w3.org/2001/XMLSchema#string') +const ns = namespace(RDF) function mapLiteralsByLanguage (map, current) { const notLiteral = current.termType !== 'Literal' - const notStringLiteral = langString.equals(current.datatype) || xsdString.equals(current.datatype) + const notStringLiteral = ns.langString.equals(current.datatype) || ns.xsd.string.equals(current.datatype) if (notLiteral || !notStringLiteral) return map diff --git a/lib/namespace.js b/lib/namespace.js index d5cd2be..781c27e 100644 --- a/lib/namespace.js +++ b/lib/namespace.js @@ -1,7 +1,16 @@ -const ns = (factory) => ({ - first: factory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#first'), - nil: factory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#nil'), - rest: factory.namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#rest') -}) +const namespace = require('@rdfjs/namespace') + +const ns = (factory) => { + const xsd = namespace('http://www.w3.org/2001/XMLSchema#', { factory }) + const rdf = namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#', { factory }) + + return { + first: rdf.first, + nil: rdf.nil, + rest: rdf.rest, + langString: rdf.langString, + xsd + } +} module.exports = ns From 1458822c151dd775f247bbf87b1dd9ec0fa19ca4 Mon Sep 17 00:00:00 2001 From: Tomasz Pluskiewicz Date: Wed, 8 Jul 2020 15:33:19 +0200 Subject: [PATCH 8/8] Update tagged-literals.md --- docs/tagged-literals.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/tagged-literals.md b/docs/tagged-literals.md index 2bb5b6d..de9639b 100644 --- a/docs/tagged-literals.md +++ b/docs/tagged-literals.md @@ -1,7 +1,5 @@ # Literals with language tags -?> New in 1.0 - Using the `.out()` method it is possible to only find literals in specific languages by passing a second `{ language }` parameter to the method. When that parameter is defined, only string literal nodes will be returned.