earl-report 0.4.8 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +15 -10
- data/VERSION +1 -1
- data/lib/earl_report.rb +199 -115
- data/lib/earl_report/views/earl_report.html.haml +17 -14
- data/spec/earl_report_spec.rb +15 -15
- data/spec/spec_helper.rb +1 -0
- data/spec/test-files/doap.ttl +11 -11
- data/spec/test-files/foaf.ttl +2 -2
- data/spec/test-files/report-complete.ttl +15 -15
- data/spec/test-files/report-no-doap.ttl +4 -4
- data/spec/test-files/report-no-foaf.ttl +14 -14
- data/spec/test-files/results.html +29 -31
- data/spec/test-files/results.jsonld +95 -95
- data/spec/test-files/results.ttl +62 -75
- metadata +37 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47de1e3c1d07adb1a2ae440c1c53f72ceeb2c7b4e33d947739474225c802ed50
|
4
|
+
data.tar.gz: 611468fcef85b3a812ae184759d3c9806106ac2d15611f63cdbbe08fe194202b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e023c3c5a1c42da0dbfc976149e50e590b3c9b770e0e66372794f68b2e881de064a515d0c6d8f73ac7a2acf1920cc95e0b35735a3a4897742ac6cb9d3385d8fa
|
7
|
+
data.tar.gz: e1c6bc8836bc9629fd36c0bc67e2f5dd30694a28f64da9ee06d864e29b0ce3d1636a83e755f878cb756d424f93b739f01246e08156085d73e8ccc48867c8bc09
|
data/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
Ruby gem to consolidate multiple EARL report and generate a rollup conformance report.
|
3
3
|
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/earl-report.png)](http://badge.fury.io/rb/earl-report)
|
5
|
-
[![Build Status](https://
|
6
|
-
[![Coverage Status](https://coveralls.io/repos/gkellogg/earl-report/badge.svg)](https://coveralls.io/r/gkellogg/earl-report)
|
5
|
+
[![Build Status](https://github.com/gkellogg/earl-report/workflows/CI/badge.svg?branch=develop)](https://github.com/gkellogg/earl-report/actions?query=workflow%3ACI)
|
6
|
+
[![Coverage Status](https://coveralls.io/repos/gkellogg/earl-report/badge.svg?branch=develop)](https://coveralls.io/r/gkellogg/earl-report?branch=develop)
|
7
7
|
|
8
8
|
## Description
|
9
9
|
Reads a test manifest in the
|
@@ -17,8 +17,8 @@ may be specified in an any compatible RDF serialization. The report is composed
|
|
17
17
|
in the following form:
|
18
18
|
|
19
19
|
[ a earl:Assertion;
|
20
|
-
earl:assertedBy <
|
21
|
-
earl:subject <
|
20
|
+
earl:assertedBy <https://greggkellogg.net/foaf#me>;
|
21
|
+
earl:subject <https://rubygems.org/gems/rdf-turtle>;
|
22
22
|
earl:test <http://dvcs.w3.org/hg/rdf/raw-file/default/rdf-turtle/tests-ttl/manifest.ttl#turtle-syntax-file-01>;
|
23
23
|
earl:result [
|
24
24
|
a earl:TestResult;
|
@@ -29,11 +29,16 @@ in the following form:
|
|
29
29
|
Additionally, `earl:subject` is expected to reference a [DOAP]() description
|
30
30
|
of the reported software, in the following form:
|
31
31
|
|
32
|
-
<
|
32
|
+
<https://rubygems.org/gems/rdf-turtle> a doap:Project, earl:TestSubject, earl:Software ;
|
33
33
|
doap:name "RDF::Turtle" ;
|
34
|
-
doap:developer <
|
34
|
+
doap:developer <https://greggkellogg.net/foaf#me> ;
|
35
35
|
doap:homepage <http://ruby-rdf.github.com/rdf-turtle> ;
|
36
36
|
doap:description "RDF::Turtle is an Turtle reader/writer for the RDF.rb library suite."@en ;
|
37
|
+
doap:release [
|
38
|
+
doap:name "RDF::Turtle 3.1.0" ;
|
39
|
+
doap:created "2015-09-27"^^xsd:date ;
|
40
|
+
doap:revision "3.1.0"
|
41
|
+
] ;
|
37
42
|
doap:programming-language "Ruby" .
|
38
43
|
|
39
44
|
The [DOAP]() description may be included in the [EARL]() report. If not found,
|
@@ -44,7 +49,7 @@ The `doap:developer` is expected to reference a [FOAF]() profile for the agent
|
|
44
49
|
(user or organization) responsible for the test subject. It is expected to be
|
45
50
|
of the following form:
|
46
51
|
|
47
|
-
<
|
52
|
+
<https://greggkellogg.net/foaf#me> foaf:name "Gregg Kellogg" .
|
48
53
|
|
49
54
|
If not found, the IRI identified by `doap:developer`
|
50
55
|
will be dereferenced and is presumed to provide a [FOAF]() profile of the developer.
|
@@ -91,14 +96,14 @@ Version 0.3 and prior re-constructed the test manifest used to create the body o
|
|
91
96
|
mf:result <http://example/test-00.out>;
|
92
97
|
earl:assertions ([
|
93
98
|
a earl:Assertion;
|
94
|
-
earl:assertedBy <
|
99
|
+
earl:assertedBy <https://greggkellogg.net/foaf#me>;
|
95
100
|
earl:mode earl:automatic;
|
96
101
|
earl:result [
|
97
102
|
a earl:TestResult;
|
98
103
|
dc:date "2012-11-06T19:23:29-08:00"^^xsd:dateTime;
|
99
104
|
earl:outcome earl:passed
|
100
105
|
];
|
101
|
-
earl:subject <
|
106
|
+
earl:subject <https://rubygems.org/gems/rdf-turtle>;
|
102
107
|
earl:test <http://example/manifest.ttl#testeval00>
|
103
108
|
]) .
|
104
109
|
|
@@ -127,7 +132,7 @@ Generally, creating a `json` format first is more efficient. Subsequent invocati
|
|
127
132
|
When run, `earl-report` attempts to open the file `.earl` in the current directory. This file is in [YAML][] format with entries for each option. Use the `--rc` option to automatically generate it.
|
128
133
|
|
129
134
|
## Author
|
130
|
-
* [Gregg Kellogg](
|
135
|
+
* [Gregg Kellogg](https://github.com/gkellogg) - <https://greggkellogg.net/>
|
131
136
|
|
132
137
|
## License
|
133
138
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.3
|
data/lib/earl_report.rb
CHANGED
@@ -33,13 +33,14 @@ class EarlReport
|
|
33
33
|
PREFIX doap: <http://usefulinc.com/ns/doap#>
|
34
34
|
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
|
35
35
|
|
36
|
-
SELECT DISTINCT ?uri ?name ?doapDesc ?revision ?homepage ?language ?developer ?devName ?devType ?devHomepage
|
36
|
+
SELECT DISTINCT ?uri ?name ?doapDesc ?release ?revision ?homepage ?language ?developer ?devName ?devType ?devHomepage
|
37
37
|
WHERE {
|
38
38
|
?uri a doap:Project; doap:name ?name; doap:developer ?developer .
|
39
39
|
OPTIONAL { ?uri doap:homepage ?homepage . }
|
40
40
|
OPTIONAL { ?uri doap:description ?doapDesc . }
|
41
41
|
OPTIONAL { ?uri doap:programming-language ?language . }
|
42
|
-
OPTIONAL { ?uri doap:release
|
42
|
+
OPTIONAL { ?uri doap:release ?release . }
|
43
|
+
OPTIONAL { ?release doap:revision ?revision .}
|
43
44
|
OPTIONAL { ?developer a ?devType .}
|
44
45
|
OPTIONAL { ?developer foaf:name ?devName .}
|
45
46
|
OPTIONAL { ?developer foaf:homepage ?devHomepage .}
|
@@ -151,6 +152,34 @@ class EarlReport
|
|
151
152
|
}]
|
152
153
|
}.freeze
|
153
154
|
|
155
|
+
TURTLE_PREFIXES = %(@prefix dc: <http://purl.org/dc/terms/> .
|
156
|
+
@prefix doap: <http://usefulinc.com/ns/doap#> .
|
157
|
+
@prefix earl: <http://www.w3.org/ns/earl#> .
|
158
|
+
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
|
159
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
160
|
+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
161
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
162
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
163
|
+
).gsub(/^ /, '')
|
164
|
+
|
165
|
+
TURTLE_SOFTWARE = %(
|
166
|
+
# Report Generation Software
|
167
|
+
<https://rubygems.org/gems/earl-report> a earl:Software, doap:Project;
|
168
|
+
doap:name "earl-report";
|
169
|
+
doap:shortdesc "Earl Report summary generator"@en;
|
170
|
+
doap:description "EarlReport generates HTML+RDFa rollups of multiple EARL reports"@en;
|
171
|
+
doap:homepage <https://github.com/gkellogg/earl-report>;
|
172
|
+
doap:programming-language "Ruby";
|
173
|
+
doap:license <http://unlicense.org>;
|
174
|
+
doap:release <https://github.com/gkellogg/earl-report/tree/#{VERSION}>;
|
175
|
+
doap:developer <https://greggkellogg.net/foaf#me> .
|
176
|
+
|
177
|
+
<https://github.com/gkellogg/earl-report/tree/#{VERSION}> a doap:Version;
|
178
|
+
doap:name "earl-report-#{VERSION}";
|
179
|
+
doap:created "#{File.mtime(File.expand_path('../../VERSION', __FILE__)).strftime('%Y-%m-%d')}"^^xsd:date;
|
180
|
+
doap:revision "#{VERSION}" .
|
181
|
+
).gsub(/^ /, '')
|
182
|
+
|
154
183
|
# Convenience vocabularies
|
155
184
|
class EARL < RDF::Vocabulary("http://www.w3.org/ns/earl#"); end
|
156
185
|
class MF < RDF::Vocabulary("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#"); end
|
@@ -220,7 +249,14 @@ class EarlReport
|
|
220
249
|
test_resources = tests.values.map {|v| v[:uri]}.uniq.compact
|
221
250
|
subjects = {}
|
222
251
|
|
223
|
-
|
252
|
+
# Initialize test assertions with an entry for each test subject
|
253
|
+
test_assertion_lists = {}
|
254
|
+
test_assertion_lists = tests.keys.inject({}) do |memo, test|
|
255
|
+
memo.merge(test => [])
|
256
|
+
end
|
257
|
+
|
258
|
+
assertion_stats = {}
|
259
|
+
|
224
260
|
# Read test assertion files into assertion graph
|
225
261
|
files.flatten.each do |file|
|
226
262
|
status "read #{file}"
|
@@ -237,10 +273,10 @@ class EarlReport
|
|
237
273
|
|
238
274
|
# Load DOAP definitions
|
239
275
|
unless solution[:name] # not loaded
|
240
|
-
status "read doap description for #{subject}"
|
276
|
+
status " read doap description for #{subject}"
|
241
277
|
begin
|
242
278
|
doap_graph = RDF::Graph.load(subject)
|
243
|
-
status "
|
279
|
+
status " loaded #{doap_graph.count} triples"
|
244
280
|
file_graph << doap_graph.to_a
|
245
281
|
rescue
|
246
282
|
warn "\nfailed to load DOAP from #{subject}: #{$!}"
|
@@ -260,10 +296,10 @@ class EarlReport
|
|
260
296
|
if !solutions.first[:developer]
|
261
297
|
warn "\nNo developer identified for #{solutions.first[:uri]}"
|
262
298
|
elsif !solutions.first[:devName]
|
263
|
-
status "read description for developer #{solutions.first[:developer].inspect}"
|
299
|
+
status " read description for developer #{solutions.first[:developer].inspect}"
|
264
300
|
begin
|
265
301
|
foaf_graph = RDF::Graph.load(solutions.first[:developer])
|
266
|
-
status "
|
302
|
+
status " loaded #{foaf_graph.count} triples"
|
267
303
|
file_graph << foaf_graph.to_a
|
268
304
|
# Reload solutions
|
269
305
|
solutions = SPARQL.execute(TEST_SUBJECT_QUERY, file_graph)
|
@@ -272,6 +308,7 @@ class EarlReport
|
|
272
308
|
end
|
273
309
|
end
|
274
310
|
|
311
|
+
release = nil
|
275
312
|
solutions.each do |solution|
|
276
313
|
# Kepp track of subjects
|
277
314
|
subjects[solution[:uri]] = RDF::URI(file)
|
@@ -294,59 +331,60 @@ class EarlReport
|
|
294
331
|
graph << RDF::Statement(solution[:developer], RDF::Vocab::FOAF.name, devName) if devName
|
295
332
|
graph << RDF::Statement(solution[:developer], RDF::Vocab::FOAF.homepage, solution[:devHomepage]) if solution[:devHomepage]
|
296
333
|
|
297
|
-
|
298
|
-
|
299
|
-
|
334
|
+
# Make sure BNode identifiers don't leak
|
335
|
+
release ||= if !solution[:release] || solution[:release].node?
|
336
|
+
RDF::Node.new
|
337
|
+
else
|
338
|
+
solution[:release]
|
339
|
+
end
|
340
|
+
graph << RDF::Statement(solution[:uri], RDF::Vocab::DOAP.release, release)
|
341
|
+
graph << RDF::Statement(release, RDF::Vocab::DOAP.revision, (solution[:revision] || "unknown"))
|
300
342
|
end
|
301
343
|
|
302
|
-
|
303
|
-
|
304
|
-
|
344
|
+
# Make sure that each assertion matches a test and add reference from test to assertion
|
345
|
+
found_solutions = false
|
346
|
+
subject = nil
|
305
347
|
|
306
|
-
|
307
|
-
|
348
|
+
status " query assertions"
|
349
|
+
SPARQL.execute(ASSERTION_QUERY, file_graph).each do |solution|
|
350
|
+
subject = solution[:subject]
|
351
|
+
unless tests[solution[:test]]
|
352
|
+
assertion_stats["Skipped"] = assertion_stats["Skipped"].to_i + 1
|
353
|
+
$stderr.puts "Skipping result for #{solution[:test]} for #{subject}, which is not defined in manifests"
|
354
|
+
next
|
355
|
+
end
|
356
|
+
unless subjects[subject]
|
357
|
+
assertion_stats["Missing Subject"] = assertion_stats["Missing Subject"].to_i + 1
|
358
|
+
$stderr.puts "No test result subject found for #{subject}: in #{subjects.keys.join(', ')}"
|
359
|
+
next
|
360
|
+
end
|
361
|
+
found_solutions ||= true
|
362
|
+
assertion_stats["Found"] = assertion_stats["Found"].to_i + 1
|
308
363
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
memo.merge(test => Array.new(subjects.length))
|
313
|
-
end
|
364
|
+
# Add this solution at the appropriate index within that list
|
365
|
+
ndx = subjects.keys.find_index(subject)
|
366
|
+
ary = test_assertion_lists[solution[:test]]
|
314
367
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
368
|
+
ary[ndx] = a = RDF::Node.new
|
369
|
+
graph << RDF::Statement(a, RDF.type, EARL.Assertion)
|
370
|
+
graph << RDF::Statement(a, EARL.subject, subject)
|
371
|
+
graph << RDF::Statement(a, EARL.test, solution[:test])
|
372
|
+
graph << RDF::Statement(a, EARL.assertedBy, solution[:by])
|
373
|
+
graph << RDF::Statement(a, EARL.mode, solution[:mode]) if solution[:mode]
|
374
|
+
r = RDF::Node.new
|
375
|
+
graph << RDF::Statement(a, EARL.result, r)
|
376
|
+
graph << RDF::Statement(r, RDF.type, EARL.TestResult)
|
377
|
+
graph << RDF::Statement(r, EARL.outcome, solution[:outcome])
|
378
|
+
end
|
379
|
+
|
380
|
+
# See if subject did not report results, which may indicate a formatting error in the EARL source
|
381
|
+
$stderr.puts "No results found for #{subject} using #{ASSERTION_QUERY}" unless found_solutions
|
328
382
|
end
|
329
|
-
found_solutions[subject] = true
|
330
|
-
assertion_stats["Found"] = assertion_stats["Found"].to_i + 1
|
331
|
-
|
332
|
-
# Add this solution at the appropriate index within that list
|
333
|
-
ndx = subjects.keys.find_index(subject)
|
334
|
-
ary = test_assertion_lists[solution[:test]] ||= []
|
335
|
-
|
336
|
-
ary[ndx] = a = RDF::Node.new
|
337
|
-
graph << RDF::Statement(a, RDF.type, EARL.Assertion)
|
338
|
-
graph << RDF::Statement(a, EARL.subject, subject)
|
339
|
-
graph << RDF::Statement(a, EARL.test, solution[:test])
|
340
|
-
graph << RDF::Statement(a, EARL.assertedBy, solution[:by])
|
341
|
-
graph << RDF::Statement(a, EARL.mode, solution[:mode]) if solution[:mode]
|
342
|
-
r = RDF::Node.new
|
343
|
-
graph << RDF::Statement(a, EARL.result, r)
|
344
|
-
graph << RDF::Statement(r, RDF.type, EARL.TestResult)
|
345
|
-
graph << RDF::Statement(r, EARL.outcome, solution[:outcome])
|
346
383
|
end
|
347
384
|
|
348
385
|
# Add ordered assertions for each test
|
349
386
|
test_assertion_lists.each do |test, ary|
|
387
|
+
ary[subjects.length - 1] ||= nil # extend for all subjects
|
350
388
|
# Fill any missing entries with an untested outcome
|
351
389
|
ary.each_with_index do |a, ndx|
|
352
390
|
unless a
|
@@ -368,42 +406,17 @@ class EarlReport
|
|
368
406
|
|
369
407
|
assertion_stats.each {|stat, count| status("Assertions #{stat}: #{count}")}
|
370
408
|
|
371
|
-
# See if any subject did not report results, which may indicate a formatting error in the EARL source
|
372
|
-
subjects.reject {|s| found_solutions[s]}.each do |sub|
|
373
|
-
$stderr.puts "No results found for #{sub} using #{ASSERTION_QUERY}"
|
374
|
-
end
|
375
|
-
|
376
409
|
# Add report wrapper to graph
|
377
|
-
ttl = %(
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
earl:generatedBy <http://rubygems.org/gems/earl-report>;
|
388
|
-
earl:assertions #{subjects.values.map {|f| f.to_ntriples}.join(",\n ")};
|
389
|
-
earl:testSubjects #{subjects.keys.map {|f| f.to_ntriples}.join(",\n ")};
|
390
|
-
mf:entries (#{man_uris.map {|f| f.to_ntriples}.join("\n ")}) .
|
391
|
-
|
392
|
-
<http://rubygems.org/gems/earl-report> a earl:Software, doap:Project;
|
393
|
-
doap:name "earl-report";
|
394
|
-
doap:shortdesc "Earl Report summary generator"@en;
|
395
|
-
doap:description "EarlReport generates HTML+RDFa rollups of multiple EARL reports"@en;
|
396
|
-
doap:homepage <https://github.com/gkellogg/earl-report>;
|
397
|
-
doap:programming-language "Ruby";
|
398
|
-
doap:license <http://unlicense.org>;
|
399
|
-
doap:release <https://github.com/gkellogg/earl-report/tree/#{VERSION}>;
|
400
|
-
doap:developer <http://greggkellogg.net/foaf#me> .
|
401
|
-
|
402
|
-
<https://github.com/gkellogg/earl-report/tree/#{VERSION}> a doap:Version;
|
403
|
-
doap:name "earl-report-#{VERSION}";
|
404
|
-
doap:created "#{File.mtime(File.expand_path('../../VERSION', __FILE__)).strftime('%Y-%m-%d')}"^^xsd:date;
|
405
|
-
doap:revision "#{VERSION}" .
|
406
|
-
).gsub(/^ /, '')
|
410
|
+
ttl = TURTLE_PREFIXES + %(
|
411
|
+
<> a earl:Software, doap:Project;
|
412
|
+
doap:name #{quoted(@options.fetch(:name, 'Unknown'))};
|
413
|
+
dc:bibliographicCitation "#{@options.fetch(:bibRef, 'Unknown reference')}";
|
414
|
+
earl:generatedBy <https://rubygems.org/gems/earl-report>;
|
415
|
+
earl:assertions #{subjects.values.map {|f| f.to_ntriples}.join(",\n ")};
|
416
|
+
earl:testSubjects #{subjects.keys.map {|f| f.to_ntriples}.join(",\n ")};
|
417
|
+
mf:entries (#{man_uris.map {|f| f.to_ntriples}.join("\n ")}) .
|
418
|
+
).gsub(/^ /, '') +
|
419
|
+
TURTLE_SOFTWARE
|
407
420
|
RDF::Turtle::Reader.new(ttl) {|r| graph << r}
|
408
421
|
|
409
422
|
# Each manifest is an earl:Report
|
@@ -482,7 +495,20 @@ class EarlReport
|
|
482
495
|
@json_hash ||= begin
|
483
496
|
# Customized JSON-LD output
|
484
497
|
result = JSON::LD::API.fromRDF(graph) do |expanded|
|
485
|
-
JSON::LD::API.frame(expanded, TEST_FRAME,
|
498
|
+
framed = JSON::LD::API.frame(expanded, TEST_FRAME,
|
499
|
+
expanded: true,
|
500
|
+
embed: '@never',
|
501
|
+
pruneBlankNodeIdentifiers: false)
|
502
|
+
# Reorder test subjects by @id
|
503
|
+
framed['testSubjects'] = Array(framed['testSubjects']).sort_by {|t| t['@id']}
|
504
|
+
|
505
|
+
# Reorder test assertions to make them consistent with subject order
|
506
|
+
Array(framed['entries']).each do |manifest|
|
507
|
+
manifest['entries'].each do |test|
|
508
|
+
test['assertions'] = test['assertions'].sort_by {|a| a['subject']}
|
509
|
+
end
|
510
|
+
end
|
511
|
+
framed
|
486
512
|
end
|
487
513
|
unless result.is_a?(Hash)
|
488
514
|
raise "Expected JSON result to have a single entry, it had #{result.length rescue 'unknown'} entries"
|
@@ -497,50 +523,108 @@ class EarlReport
|
|
497
523
|
# @option options [IO, StringIO] :io
|
498
524
|
# @return [String]
|
499
525
|
def earl_turtle(options)
|
526
|
+
context = JSON::LD::Context.parse(json_hash['@context'])
|
500
527
|
io = options[:io]
|
528
|
+
io.write(TURTLE_PREFIXES + "\n")
|
501
529
|
|
502
|
-
|
503
|
-
|
504
|
-
# Write starting with the entire graph to get preamble
|
505
|
-
writer = RDF::Turtle::Writer.new(io, standard_prefixes: true)
|
506
|
-
writer << graph
|
530
|
+
# Write project header
|
531
|
+
ttl_entity(io, json_hash, context)
|
507
532
|
|
508
|
-
|
509
|
-
|
533
|
+
# Write out each manifest entry
|
534
|
+
io.puts("# Manifests")
|
535
|
+
json_hash['entries'].each do |man|
|
536
|
+
ttl_entity(io, man, context)
|
510
537
|
|
511
|
-
|
512
|
-
|
538
|
+
# Output each test entry with assertions
|
539
|
+
man['entries'].each do |entry|
|
540
|
+
ttl_entity(io, entry, context)
|
541
|
+
end
|
542
|
+
end
|
513
543
|
|
514
|
-
#
|
515
|
-
|
516
|
-
|
517
|
-
writer.send(:statement, manifest)
|
544
|
+
# Output each DOAP
|
545
|
+
json_hash['testSubjects'].each do |doap|
|
546
|
+
ttl_entity(io, doap, context)
|
518
547
|
|
519
|
-
#
|
520
|
-
|
521
|
-
|
548
|
+
# FOAF
|
549
|
+
dev = doap['developer']
|
550
|
+
dev = [dev] unless dev.is_a?(Array)
|
551
|
+
dev.each do |foaf|
|
552
|
+
ttl_entity(io, foaf, context)
|
522
553
|
end
|
523
554
|
end
|
555
|
+
|
556
|
+
io.write(TURTLE_SOFTWARE)
|
557
|
+
end
|
524
558
|
|
525
|
-
|
526
|
-
io.
|
527
|
-
|
528
|
-
|
559
|
+
def ttl_entity(io, entity, context)
|
560
|
+
io.write(ttl_value(entity) + " " + entity.map do |dk, dv|
|
561
|
+
case dk
|
562
|
+
when '@context', '@id'
|
563
|
+
nil
|
564
|
+
when '@type'
|
565
|
+
"a " + ttl_value(dv)
|
566
|
+
when 'assertions'
|
567
|
+
"earl:assertions #{dv.map {|a| ttl_assertion(a)}.join(", ")}"
|
568
|
+
when 'entries'
|
569
|
+
"mf:entries #{ttl_value({'@list' => dv}, whitespace: "\n ")}"
|
570
|
+
when 'release'
|
571
|
+
"doap:release [doap:revision #{quoted(dv['revision'])}]"
|
572
|
+
else
|
573
|
+
dv = [dv] unless dv.is_a?(Array)
|
574
|
+
dv = dv.map {|v| v.is_a?(Hash) ? v : context.expand_value(dk, v)}
|
575
|
+
"#{ttl_value(dk)} #{ttl_value(dv, whitespace: "\n ")}"
|
576
|
+
end
|
577
|
+
end.compact.join(" ;\n ") + " .\n\n")
|
578
|
+
end
|
529
579
|
|
530
|
-
|
531
|
-
|
532
|
-
|
580
|
+
def ttl_value(value, whitespace: " ")
|
581
|
+
if value.is_a?(Array)
|
582
|
+
value.map {|v| ttl_value(v)}.join(",#{whitespace}")
|
583
|
+
elsif value.is_a?(Hash)
|
584
|
+
if value.key?('@list')
|
585
|
+
"(#{value['@list'].map {|vv| ttl_value(vv)}.join(whitespace)})"
|
586
|
+
elsif value.key?('@value')
|
587
|
+
quoted(value['@value'], language: value['@language'], datatype: value['@type'])
|
588
|
+
elsif value.key?('@id')
|
589
|
+
ttl_value(value['@id'])
|
590
|
+
else
|
591
|
+
"[]"
|
533
592
|
end
|
593
|
+
elsif value.start_with?(/https?/) || value.start_with?('/')
|
594
|
+
"<#{value}>"
|
595
|
+
elsif value.include?(':')
|
596
|
+
value
|
597
|
+
elsif json_hash['@context'][value].is_a?(Hash)
|
598
|
+
json_hash['@context'][value].fetch('@id', "earl:#{value}")
|
599
|
+
elsif value.empty?
|
600
|
+
"<>"
|
601
|
+
else
|
602
|
+
"earl:#{value}"
|
534
603
|
end
|
604
|
+
end
|
535
605
|
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
606
|
+
def ttl_assertion(value)
|
607
|
+
return ttl_value(value) if value.is_a?(String)
|
608
|
+
block = [
|
609
|
+
"[",
|
610
|
+
" a earl:Assertion ;",
|
611
|
+
" earl:test #{ttl_value(value['test'])} ;",
|
612
|
+
" earl:subject #{ttl_value(value['subject'])} ;",
|
613
|
+
" earl:result [",
|
614
|
+
" a earl:TestResult ;",
|
615
|
+
" earl:outcome #{ttl_value(value['result']['outcome'])}",
|
616
|
+
" ] ;",
|
617
|
+
]
|
618
|
+
block << " earl:assertedBy #{ttl_value(value['assertedBy'])} ;" if value['assertedBy']
|
619
|
+
|
620
|
+
block.join("\n") + "\n ]"
|
540
621
|
end
|
541
622
|
|
542
|
-
def quoted(string)
|
543
|
-
(@turtle_writer ||= RDF::Turtle::Writer.new).send(:quoted, string)
|
623
|
+
def quoted(string, language: nil, datatype: nil)
|
624
|
+
str = (@turtle_writer ||= RDF::Turtle::Writer.new).send(:quoted, string)
|
625
|
+
str += "@#{language}" if language
|
626
|
+
str += "^^#{ttl_value(datatype)}" if datatype
|
627
|
+
str
|
544
628
|
end
|
545
629
|
|
546
630
|
def warn(message)
|