rdf-tabular 3.1.0 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +139 -149
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/etc/doap.csv +1 -1
- data/etc/doap.csv-metadata.json +1 -1
- data/etc/doap.ttl +16 -18
- data/etc/earl.html +650 -650
- data/etc/earl.jsonld +692 -692
- data/etc/earl.ttl +847 -847
- data/lib/rdf/tabular/csvw.rb +500 -90
- data/lib/rdf/tabular/format.rb +2 -2
- data/lib/rdf/tabular/metadata.rb +10 -5
- data/lib/rdf/tabular/reader.rb +16 -6
- data/lib/rdf/tabular/uax35.rb +1 -1
- data/lib/rdf/tabular.rb +3 -3
- data/spec/metadata_spec.rb +83 -0
- data/spec/reader_spec.rb +2 -2
- data/spec/spec_helper.rb +20 -8
- metadata +103 -86
data/lib/rdf/tabular/format.rb
CHANGED
@@ -22,7 +22,7 @@ module RDF::Tabular
|
|
22
22
|
# @example Obtaining serialization format file extension mappings
|
23
23
|
# RDF::Format.file_extensions #=> {:csv => "text/csv"}
|
24
24
|
#
|
25
|
-
# @see
|
25
|
+
# @see https://www.w3.org/TR/rdf-testcases/#ntriples
|
26
26
|
class Format < RDF::Format
|
27
27
|
content_type 'text/csv;q=0.4',
|
28
28
|
extensions: [:csv, :tsv],
|
@@ -62,7 +62,7 @@ module RDF::Tabular
|
|
62
62
|
raise ArgumentError, "Outputting Tabular JSON only allowed when input format is tabular." unless opts[:format] == :tabular
|
63
63
|
out = opts[:output] || $stdout
|
64
64
|
out.set_encoding(Encoding::UTF_8) if RUBY_PLATFORM == "java"
|
65
|
-
RDF::CLI.parse(argv, opts) do |reader|
|
65
|
+
RDF::CLI.parse(argv, **opts) do |reader|
|
66
66
|
out.puts reader.to_json
|
67
67
|
end
|
68
68
|
end
|
data/lib/rdf/tabular/metadata.rb
CHANGED
@@ -16,7 +16,7 @@ require 'yaml' # used by BCP47, which should have required it.
|
|
16
16
|
# * Return Column-level annotations
|
17
17
|
# * Return row iterator with column information
|
18
18
|
#
|
19
|
-
# @author [Gregg Kellogg](
|
19
|
+
# @author [Gregg Kellogg](https://greggkellogg.net/)
|
20
20
|
module RDF::Tabular
|
21
21
|
class Metadata
|
22
22
|
include RDF::Util::Logger
|
@@ -142,7 +142,7 @@ module RDF::Tabular
|
|
142
142
|
'Accept' => 'application/ld+json, application/json'
|
143
143
|
}
|
144
144
|
)
|
145
|
-
path = "file
|
145
|
+
path = "file:#{path}" if RDF::URI(path).relative?
|
146
146
|
RDF::Util::File.open_file(path, **options) do |file|
|
147
147
|
self.new(file, **options.merge(base: path, filenames: path))
|
148
148
|
end
|
@@ -209,7 +209,9 @@ module RDF::Tabular
|
|
209
209
|
log_debug("for_input", **options) {"templates: #{templates.map(&:to_s).inspect}"}
|
210
210
|
locs = templates.map do |template|
|
211
211
|
t = Addressable::Template.new(template)
|
212
|
-
|
212
|
+
mapped = t.expand(url: base).to_s
|
213
|
+
mapped = RDF::URI.decode(mapped) if options[:decode_uri]
|
214
|
+
RDF::URI(base).join(mapped)
|
213
215
|
end
|
214
216
|
log_debug("for_input", **options) {"locs: #{locs.map(&:to_s).inspect}"}
|
215
217
|
|
@@ -314,6 +316,8 @@ module RDF::Tabular
|
|
314
316
|
# Context used for this metadata. Taken from input if not provided
|
315
317
|
# @option options [RDF::URI] :base
|
316
318
|
# The Base URL to use when expanding the document. This overrides the value of `input` if it is a URL. If not specified and `input` is not an URL, the base URL defaults to the current document URL if in a browser context, or the empty string if there is no document context.
|
319
|
+
# @option options [Boolean] :decode_uri
|
320
|
+
# Decode %-encodings in the result of a URI Template operation.
|
317
321
|
# @option options [Boolean] :normalize normalize the object
|
318
322
|
# @option options [Boolean] :validate Strict metadata validation
|
319
323
|
# @raise [Error]
|
@@ -1947,13 +1951,14 @@ module RDF::Tabular
|
|
1947
1951
|
class Row
|
1948
1952
|
# Class for returning values
|
1949
1953
|
Cell = Struct.new(:table, :column, :row, :stringValue, :aboutUrl, :propertyUrl, :valueUrl, :value, :errors) do
|
1950
|
-
def set_urls(mapped_values)
|
1954
|
+
def set_urls(mapped_values, decode_uri)
|
1951
1955
|
%w(aboutUrl propertyUrl valueUrl).each do |prop|
|
1952
1956
|
# If the cell value is nil, and it is not a virtual column
|
1953
1957
|
next if prop == "valueUrl" && value.nil? && !column.virtual
|
1954
1958
|
if v = column.send(prop.to_sym)
|
1955
1959
|
t = Addressable::Template.new(v)
|
1956
1960
|
mapped = t.expand(mapped_values).to_s
|
1961
|
+
mapped = RDF::URI.decode(mapped) if decode_uri
|
1957
1962
|
# FIXME: don't expand here, do it in CSV2RDF
|
1958
1963
|
url = row.context.expand_iri(mapped, documentRelative: true)
|
1959
1964
|
self.send("#{prop}=".to_sym, url)
|
@@ -2114,7 +2119,7 @@ module RDF::Tabular
|
|
2114
2119
|
"_column" => cell.column.number,
|
2115
2120
|
"_sourceColumn" => cell.column.sourceNumber
|
2116
2121
|
)
|
2117
|
-
cell.set_urls(mapped_values)
|
2122
|
+
cell.set_urls(mapped_values, options[:decode_uri])
|
2118
2123
|
end
|
2119
2124
|
end
|
2120
2125
|
|
data/lib/rdf/tabular/reader.rb
CHANGED
@@ -5,7 +5,7 @@ module RDF::Tabular
|
|
5
5
|
##
|
6
6
|
# A Tabular Data to RDF parser in Ruby.
|
7
7
|
#
|
8
|
-
# @author [Gregg Kellogg](
|
8
|
+
# @author [Gregg Kellogg](https://greggkellogg.net/)
|
9
9
|
class Reader < RDF::Reader
|
10
10
|
format Format
|
11
11
|
include RDF::Util::Logger
|
@@ -22,7 +22,7 @@ module RDF::Tabular
|
|
22
22
|
|
23
23
|
##
|
24
24
|
# Writer options
|
25
|
-
# @see
|
25
|
+
# @see https://ruby-rdf.github.io/rdf/RDF/Writer#options-class_method
|
26
26
|
def self.options
|
27
27
|
super + [
|
28
28
|
RDF::CLI::Option.new(
|
@@ -43,6 +43,13 @@ module RDF::Tabular
|
|
43
43
|
control: :checkbox,
|
44
44
|
on: ["--no-prov"],
|
45
45
|
description: "do not output optional provenance information.") {true},
|
46
|
+
RDF::CLI::Option.new(
|
47
|
+
symbol: :decode_uri,
|
48
|
+
datatype: TrueClass,
|
49
|
+
control: :checkbox,
|
50
|
+
on: ["--decode-uri"],
|
51
|
+
description: "decode %-encodings in the result of a URI Template operation."
|
52
|
+
)
|
46
53
|
]
|
47
54
|
end
|
48
55
|
|
@@ -54,11 +61,13 @@ module RDF::Tabular
|
|
54
61
|
# or an Array used as an internalized array of arrays
|
55
62
|
# @param [Hash{Symbol => Object}] options
|
56
63
|
# any additional options (see `RDF::Reader#initialize`)
|
64
|
+
# @option options [Boolean] :decode_uri
|
65
|
+
# Decode %-encodings in the result of a URI Template operation.
|
66
|
+
# @option options [Array<Hash>] :fks_referencing_table
|
67
|
+
# When called with Table metadata, a list of the foreign keys referencing this table
|
57
68
|
# @option options [Metadata, Hash, String, RDF::URI] :metadata user supplied metadata, merged on top of extracted metadata. If provided as a URL, Metadata is loade from that location
|
58
69
|
# @option options [Boolean] :minimal includes only the information gleaned from the cells of the tabular data
|
59
70
|
# @option options [Boolean] :noProv do not output optional provenance information
|
60
|
-
# @option optinons [Array<Hash>] :fks_referencing_table
|
61
|
-
# When called with Table metadata, a list of the foreign keys referencing this table
|
62
71
|
# @yield [reader] `self`
|
63
72
|
# @yieldparam [RDF::Reader] reader
|
64
73
|
# @yieldreturn [void] ignored
|
@@ -71,7 +80,8 @@ module RDF::Tabular
|
|
71
80
|
@options[:base] ||= input.path if input.respond_to?(:path)
|
72
81
|
@options[:base] ||= input.filename if input.respond_to?(:filename)
|
73
82
|
if RDF::URI(@options[:base]).relative? && File.exist?(@options[:base].to_s)
|
74
|
-
|
83
|
+
file_uri = "file:" + File.expand_path(@options[:base])
|
84
|
+
@options[:base] = RDF::URI(file_uri.to_s).normalize
|
75
85
|
end
|
76
86
|
|
77
87
|
log_debug("Reader#initialize") {"input: #{input.inspect}, base: #{@options[:base]}"}
|
@@ -225,7 +235,7 @@ module RDF::Tabular
|
|
225
235
|
activity = RDF::Node.new
|
226
236
|
add_statement(0, table_group, RDF::Vocab::PROV.wasGeneratedBy, activity)
|
227
237
|
add_statement(0, activity, RDF.type, RDF::Vocab::PROV.Activity)
|
228
|
-
add_statement(0, activity, RDF::Vocab::PROV.wasAssociatedWith, RDF::URI("
|
238
|
+
add_statement(0, activity, RDF::Vocab::PROV.wasAssociatedWith, RDF::URI("https://rubygems.org/gems/rdf-tabular"))
|
229
239
|
add_statement(0, activity, RDF::Vocab::PROV.startedAtTime, RDF::Literal::DateTime.new(start_time))
|
230
240
|
add_statement(0, activity, RDF::Vocab::PROV.endedAtTime, RDF::Literal::DateTime.new(Time.now))
|
231
241
|
|
data/lib/rdf/tabular/uax35.rb
CHANGED
data/lib/rdf/tabular.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
$:.unshift(File.expand_path("..", __FILE__))
|
2
|
-
require 'rdf' # @see
|
2
|
+
require 'rdf' # @see https://rubygems.org/gems/rdf
|
3
3
|
require 'csv'
|
4
4
|
|
5
5
|
module RDF
|
6
6
|
##
|
7
7
|
# **`RDF::Tabular`** is a Tabular/CSV extension for RDF.rb.
|
8
8
|
#
|
9
|
-
# @see
|
9
|
+
# @see https://w3c.github.io/csvw/
|
10
10
|
#
|
11
|
-
# @author [Gregg Kellogg](
|
11
|
+
# @author [Gregg Kellogg](https://greggkellogg.net/)
|
12
12
|
module Tabular
|
13
13
|
require 'rdf/tabular/format'
|
14
14
|
autoload :Column, 'rdf/tabular/metadata'
|
data/spec/metadata_spec.rb
CHANGED
@@ -1082,6 +1082,89 @@ describe RDF::Tabular::Metadata do
|
|
1082
1082
|
end
|
1083
1083
|
end
|
1084
1084
|
end
|
1085
|
+
|
1086
|
+
context "virtual columns" do
|
1087
|
+
subject {
|
1088
|
+
described_class.new(JSON.parse(%({
|
1089
|
+
"@context": "http://www.w3.org/ns/csvw",
|
1090
|
+
"url": "https://example.org/countries.csv",
|
1091
|
+
"aboutUrl": "https://example.org/countries",
|
1092
|
+
"@type": "Table",
|
1093
|
+
"tableSchema": {
|
1094
|
+
"@type": "Schema",
|
1095
|
+
"columns": [{
|
1096
|
+
"name": "countryCode",
|
1097
|
+
"titles": "countryCode",
|
1098
|
+
"propertyUrl": "https://example.org/countries.csv#countryCode"
|
1099
|
+
}, {
|
1100
|
+
"name": "latitude",
|
1101
|
+
"titles": "latitude",
|
1102
|
+
"propertyUrl": "https://example.org/countries.csv#latitude"
|
1103
|
+
}, {
|
1104
|
+
"name": "longitude",
|
1105
|
+
"titles": "longitude",
|
1106
|
+
"propertyUrl": "https://example.org/countries.csv#longitude"
|
1107
|
+
}, {
|
1108
|
+
"name": "name",
|
1109
|
+
"titles": "name",
|
1110
|
+
"propertyUrl": "https://example.org/countries.csv#name"
|
1111
|
+
}, {
|
1112
|
+
"virtual": true,
|
1113
|
+
"propertyUrl": "https://example.org/countries.csv#virt1",
|
1114
|
+
"valueUrl": "https://example.org/countries.csv#virt1"
|
1115
|
+
}, {
|
1116
|
+
"virtual": true,
|
1117
|
+
"propertyUrl": "https://example.org/countries.csv#virt2",
|
1118
|
+
"default": "default",
|
1119
|
+
"datatype": "string"
|
1120
|
+
}]
|
1121
|
+
}
|
1122
|
+
})), base: RDF::URI("http://example.org/base"), logger: logger)
|
1123
|
+
}
|
1124
|
+
let(:input) {RDF::Util::File.open_file("https://example.org/countries.csv")}
|
1125
|
+
let(:rows) {subject.to_enum(:each_row, input).to_a}
|
1126
|
+
|
1127
|
+
it "has expected aboutUrls" do
|
1128
|
+
subject.each_row(input) do |row|
|
1129
|
+
expect(row.values[0].aboutUrl).to eq "https://example.org/countries"
|
1130
|
+
expect(row.values[1].aboutUrl).to eq "https://example.org/countries"
|
1131
|
+
expect(row.values[2].aboutUrl).to eq "https://example.org/countries"
|
1132
|
+
expect(row.values[3].aboutUrl).to eq "https://example.org/countries"
|
1133
|
+
expect(row.values[4].aboutUrl).to eq "https://example.org/countries"
|
1134
|
+
expect(row.values[5].aboutUrl).to eq "https://example.org/countries"
|
1135
|
+
end
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
it "has expected propertyUrls" do
|
1139
|
+
subject.each_row(input) do |row|
|
1140
|
+
expect(row.values[0].propertyUrl).to eq "https://example.org/countries.csv#countryCode"
|
1141
|
+
expect(row.values[1].propertyUrl).to eq "https://example.org/countries.csv#latitude"
|
1142
|
+
expect(row.values[2].propertyUrl).to eq "https://example.org/countries.csv#longitude"
|
1143
|
+
expect(row.values[3].propertyUrl).to eq "https://example.org/countries.csv#name"
|
1144
|
+
expect(row.values[4].propertyUrl).to eq "https://example.org/countries.csv#virt1"
|
1145
|
+
expect(row.values[5].propertyUrl).to eq "https://example.org/countries.csv#virt2"
|
1146
|
+
end
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
it "has expected valueUrls" do
|
1150
|
+
subject.each_row(input) do |row|
|
1151
|
+
expect(row.values[0].valueUrl).to be_nil
|
1152
|
+
expect(row.values[1].valueUrl).to be_nil
|
1153
|
+
expect(row.values[2].valueUrl).to be_nil
|
1154
|
+
expect(row.values[3].valueUrl).to be_nil
|
1155
|
+
expect(row.values[4].valueUrl).to eq "https://example.org/countries.csv#virt1"
|
1156
|
+
expect(row.values[5].valueUrl).to be_nil
|
1157
|
+
end
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
it "has expected values" do
|
1161
|
+
rows = subject.to_enum(:each_row, input).to_a
|
1162
|
+
expect(rows[0].values.map(&:to_s)).to produce(%w(AD 42.546245 1.601554 Andorra).push("", "default"), logger)
|
1163
|
+
expect(rows[1].values.map(&:to_s)).to produce((%w(AE 23.424076 53.847818).push("United Arab Emirates", "", "default")), logger)
|
1164
|
+
expect(rows[2].values.map(&:to_s)).to produce(%w(AF 33.93911 67.709953 Afghanistan).push("", "default"), logger)
|
1165
|
+
end
|
1166
|
+
end
|
1167
|
+
|
1085
1168
|
end
|
1086
1169
|
|
1087
1170
|
context "datatypes" do
|
data/spec/reader_spec.rb
CHANGED
@@ -308,7 +308,7 @@ describe RDF::Tabular::Reader do
|
|
308
308
|
ASK WHERE {
|
309
309
|
[ prov:wasGeneratedBy [
|
310
310
|
a prov:Activity;
|
311
|
-
prov:wasAssociatedWith <
|
311
|
+
prov:wasAssociatedWith <https://rubygems.org/gems/rdf-tabular>;
|
312
312
|
prov:startedAtTime ?start;
|
313
313
|
prov:endedAtTime ?end;
|
314
314
|
prov:qualifiedUsage [
|
@@ -331,7 +331,7 @@ describe RDF::Tabular::Reader do
|
|
331
331
|
ASK WHERE {
|
332
332
|
[ prov:wasGeneratedBy [
|
333
333
|
a prov:Activity;
|
334
|
-
prov:wasAssociatedWith <
|
334
|
+
prov:wasAssociatedWith <https://rubygems.org/gems/rdf-tabular>;
|
335
335
|
prov:startedAtTime ?start;
|
336
336
|
prov:endedAtTime ?end;
|
337
337
|
prov:qualifiedUsage [
|
data/spec/spec_helper.rb
CHANGED
@@ -13,14 +13,26 @@ require 'json'
|
|
13
13
|
require 'webmock/rspec'
|
14
14
|
require 'matchers'
|
15
15
|
require 'suite_helper'
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
|
17
|
+
begin
|
18
|
+
require 'simplecov'
|
19
|
+
require 'simplecov-lcov'
|
20
|
+
|
21
|
+
SimpleCov::Formatter::LcovFormatter.config do |config|
|
22
|
+
#Coveralls is coverage by default/lcov. Send info results
|
23
|
+
config.report_with_single_file = true
|
24
|
+
config.single_report_path = 'coverage/lcov.info'
|
25
|
+
end
|
26
|
+
|
27
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
|
28
|
+
SimpleCov::Formatter::HTMLFormatter,
|
29
|
+
SimpleCov::Formatter::LcovFormatter
|
30
|
+
])
|
31
|
+
SimpleCov.start do
|
32
|
+
add_filter "/spec/"
|
33
|
+
end
|
34
|
+
rescue LoadError => e
|
35
|
+
STDERR.puts "Coverage Skipped: #{e.message}"
|
24
36
|
end
|
25
37
|
|
26
38
|
require 'rdf/tabular'
|