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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6cc5d6fb36d7bd99ed84e9f141f1e169ab492096bbf53ea978955efdf17fb39
4
- data.tar.gz: 97838f4eab1bf562050e629dc27b34fc7c82c714589f0a6efabce2873a2872fc
3
+ metadata.gz: 47de1e3c1d07adb1a2ae440c1c53f72ceeb2c7b4e33d947739474225c802ed50
4
+ data.tar.gz: 611468fcef85b3a812ae184759d3c9806106ac2d15611f63cdbbe08fe194202b
5
5
  SHA512:
6
- metadata.gz: 86c5d742c3b08343123c7c8f4f4d898b15f1cccce163a21a31a89fbea9b50d22eb4f309c22ec8e402e5027d4b88f56490820340265a1ea419fb9c96863017ca1
7
- data.tar.gz: c0ee051dd41ce7064e48ad352b074ec73054909b56516e4fcf44f07f885ffcaf38e4cf1a11bd1d60f2759e005c62450b8f9e4788539361852b22445a76223380
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://travis-ci.org/gkellogg/earl-report.png?branch=master)](http://travis-ci.org/gkellogg/earl-report)
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 <http://greggkellogg.net/foaf#me>;
21
- earl:subject <http://rubygems.org/gems/rdf-turtle>;
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
- <http://rubygems.org/gems/rdf-turtle> a doap:Project, earl:TestSubject, earl:Software ;
32
+ <https://rubygems.org/gems/rdf-turtle> a doap:Project, earl:TestSubject, earl:Software ;
33
33
  doap:name "RDF::Turtle" ;
34
- doap:developer <http://greggkellogg.net/foaf#me> ;
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
- <http://greggkellogg.net/foaf#me> foaf:name "Gregg Kellogg" .
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 <http://greggkellogg.net/foaf#me>;
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 <http://rubygems.org/gems/rdf-turtle>;
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](http://github.com/gkellogg) - <http://greggkellogg.net/>
135
+ * [Gregg Kellogg](https://github.com/gkellogg) - <https://greggkellogg.net/>
131
136
 
132
137
  ## License
133
138
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.8
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 [ doap:revision ?revision] .}
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
- assertion_graph = RDF::Graph.new
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 " loaded #{doap_graph.count} triples"
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 " loaded #{foaf_graph.count} triples"
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
- rev = RDF::Node.new
298
- graph << RDF::Statement(solution[:uri], RDF::Vocab::DOAP.release, rev)
299
- graph << RDF::Statement(rev, RDF::Vocab::DOAP.revision, (solution[:revision] || "unknown"))
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
- assertion_graph << file_graph
303
- end
304
- end
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
- # Make sure that each assertion matches a test and add reference from test to assertion
307
- found_solutions = {}
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
- # Initialize test assertions with an entry for each test subject
310
- test_assertion_lists = {}
311
- test_assertion_lists = tests.keys.inject({}) do |memo, test|
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
- status "query assertions"
316
- assertion_stats = {}
317
- SPARQL.execute(ASSERTION_QUERY, assertion_graph).each do |solution|
318
- subject = solution[:subject]
319
- unless tests[solution[:test]]
320
- assertion_stats["Skipped"] = assertion_stats["Skipped"].to_i + 1
321
- $stderr.puts "Skipping result for #{solution[:test]} for #{subject}, which is not defined in manifests"
322
- next
323
- end
324
- unless subjects[subject]
325
- assertion_stats["Missing Subject"] = assertion_stats["Missing Subject"].to_i + 1
326
- $stderr.puts "No test result subject found for #{subject}: in #{subjects.keys.join(', ')}"
327
- next
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
- @prefix dc: <http://purl.org/dc/terms/> .
379
- @prefix doap: <http://usefulinc.com/ns/doap#> .
380
- @prefix earl: <http://www.w3.org/ns/earl#> .
381
- @prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
382
- @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
383
-
384
- <> a earl:Software, doap:Project;
385
- doap:name #{quoted(@options.fetch(:name, 'Unknown'))};
386
- dc:bibliographicCitation "#{@options.fetch(:bibRef, 'Unknown reference')}";
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, expanded: true, embed: '@never')
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
- top_level = graph.first_subject(predicate: EARL.generatedBy)
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
- writer.send(:preprocess)
509
- writer.send(:start_document)
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
- # Write top-level object referencing manifests and subjects
512
- writer.send(:statement, top_level)
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
- # Write each manifest
515
- io.puts "\n# Manifests"
516
- RDF::List.new(subject: graph.first_object(subject: top_level, predicate: MF[:entries]), graph: graph).each do |manifest|
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
- # Write each test case
520
- RDF::List.new(subject: graph.first_object(subject: manifest, predicate: MF[:entries]), graph: graph).each do |tc|
521
- writer.send(:statement, tc)
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
- # Write test subjects
526
- io.puts "\n# Test Subjects"
527
- graph.query(subject: top_level, predicate: EARL.testSubjects).each do |s|
528
- writer.send(:statement, s.object)
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
- # Write each developer
531
- graph.query(subject: s.object, predicate: RDF::Vocab::DOAP.developer).each do |d|
532
- writer.send(:statement, d.object)
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
- # Write generator
537
- io.puts "\n# Report Generation Software"
538
- writer.send(:statement, RDF::URI("http://rubygems.org/gems/earl-report"))
539
- writer.send(:statement, RDF::URI("https://github.com/gkellogg/earl-report/tree/#{VERSION}"))
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)