rdf-tabular 0.1.3.1 → 0.2.0
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 +4 -3
- data/VERSION +1 -1
- data/etc/README +4 -0
- data/etc/csvw.jsonld +1 -1
- data/etc/earl.html +10544 -0
- data/etc/earl.jsonld +17068 -0
- data/etc/earl.ttl +7114 -0
- data/etc/template.haml +205 -0
- data/etc/well-known +4 -0
- data/lib/rdf/tabular.rb +10 -4
- data/lib/rdf/tabular/format.rb +11 -7
- data/lib/rdf/tabular/metadata.rb +761 -314
- data/lib/rdf/tabular/reader.rb +276 -144
- data/spec/format_spec.rb +11 -8
- data/spec/matchers.rb +4 -4
- data/spec/metadata_spec.rb +120 -36
- data/spec/reader_spec.rb +56 -18
- data/spec/spec_helper.rb +10 -2
- data/spec/suite_helper.rb +35 -18
- data/spec/suite_spec.rb +26 -24
- metadata +15 -3
data/spec/format_spec.rb
CHANGED
@@ -4,19 +4,22 @@ require 'spec_helper'
|
|
4
4
|
require 'rdf/spec/format'
|
5
5
|
|
6
6
|
describe RDF::Tabular::Format do
|
7
|
-
|
8
|
-
|
7
|
+
it_behaves_like 'an RDF::Format' do
|
8
|
+
let(:format_class) {RDF::Tabular::Format}
|
9
9
|
end
|
10
10
|
|
11
|
-
include RDF_Format
|
12
|
-
|
13
11
|
describe ".for" do
|
14
12
|
formats = [
|
15
13
|
:tabular,
|
16
14
|
'etc/doap.csv',
|
17
|
-
|
18
|
-
{:
|
19
|
-
{:
|
15
|
+
'etc/doap.tsv',
|
16
|
+
{file_name: 'etc/doap.csv'},
|
17
|
+
{file_name: 'etc/doap.tsv'},
|
18
|
+
{file_extension: 'csv'},
|
19
|
+
{file_extension: 'tsv'},
|
20
|
+
{content_type: 'text/csv'},
|
21
|
+
{content_type: 'text/tab-separated-values'},
|
22
|
+
{content_type: 'application/csvm+json'},
|
20
23
|
].each do |arg|
|
21
24
|
it "discovers with #{arg.inspect}" do
|
22
25
|
expect(RDF::Tabular::Format).to include RDF::Format.for(arg)
|
@@ -25,6 +28,6 @@ describe RDF::Tabular::Format do
|
|
25
28
|
end
|
26
29
|
|
27
30
|
describe "#to_sym" do
|
28
|
-
specify {expect(
|
31
|
+
specify {expect(described_class.to_sym).to eq :tabular}
|
29
32
|
end
|
30
33
|
end
|
data/spec/matchers.rb
CHANGED
@@ -47,8 +47,8 @@ RSpec::Matchers.define :be_equivalent_graph do |expected, info|
|
|
47
47
|
else
|
48
48
|
"Graph differs\n"
|
49
49
|
end +
|
50
|
-
"Expected:\n#{@expected.dump(:ttl, standard_prefixes: true, prefixes: prefixes)}" +
|
51
|
-
"Results:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: prefixes)}" +
|
50
|
+
"Expected:\n#{@expected.dump(:ttl, standard_prefixes: true, prefixes: prefixes, literal_shorthand: false)}" +
|
51
|
+
"Results:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: prefixes, literal_shorthand: false)}" +
|
52
52
|
(@info.metadata ? "\nMetadata:\n#{@info.metadata.to_json(JSON_STATE)}\n" : "") +
|
53
53
|
(@info.metadata && !@info.metadata.errors.empty? ? "\nMetadata Errors:\n#{@info.metadata.errors.join("\n")}\n" : "") +
|
54
54
|
(@info.debug ? "\nDebug:\n#{@info.debug}" : "")
|
@@ -85,7 +85,7 @@ RSpec::Matchers.define :pass_query do |expected, info|
|
|
85
85
|
"Query returned true (expected #{@info.result})"
|
86
86
|
end +
|
87
87
|
"\n#{@expected}" +
|
88
|
-
"\nResults:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: {'' => @info.action + '#'})}" +
|
88
|
+
"\nResults:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: {'' => @info.action + '#'}, literal_shorthand: false)}" +
|
89
89
|
(@info.metadata ? "\nMetadata:\n#{@info.metadata.to_json(JSON_STATE)}\n" : "") +
|
90
90
|
(@info.metadata && !@info.metadata.errors.empty? ? "\nMetadata Errors:\n#{@info.metadata.errors.join("\n")}\n" : "") +
|
91
91
|
"\nDebug:\n#{@info.debug}"
|
@@ -103,7 +103,7 @@ RSpec::Matchers.define :pass_query do |expected, info|
|
|
103
103
|
"Query returned true (expected #{@info.result})"
|
104
104
|
end +
|
105
105
|
"\n#{@expected}" +
|
106
|
-
"\nResults:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: {'' => @info.action + '#'})}" +
|
106
|
+
"\nResults:\n#{@actual.dump(:ttl, standard_prefixes: true, prefixes: {'' => @info.action + '#'}, literal_shorthand: false)}" +
|
107
107
|
(@info.metadata ? "\nMetadata:\n#{@info.metadata.to_json(JSON_STATE)}\n" : "") +
|
108
108
|
(@info.metadata && !@info.metadata.errors.empty? ? "\nMetadata Errors:\n#{@info.metadata.errors.join("\n")}\n" : "") +
|
109
109
|
"\nDebug:\n#{@info.debug}"
|
data/spec/metadata_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe RDF::Tabular::Metadata do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
case file
|
17
|
-
when "metadata.json", "country-codes-and-names.csv-metadata.json"
|
17
|
+
when "csv-metadata.json", "country-codes-and-names.csv-metadata.json"
|
18
18
|
{status: 401}
|
19
19
|
else
|
20
20
|
{
|
@@ -37,7 +37,15 @@ describe RDF::Tabular::Metadata do
|
|
37
37
|
valid: (%w(anyAtomicType string token language Name NCName boolean gYear number binary datetime any xml html json) +
|
38
38
|
[{"base" => "string"}]
|
39
39
|
),
|
40
|
-
invalid: [1, true, "
|
40
|
+
invalid: [1, true, "http://example.org/",
|
41
|
+
{"base" => "foo"},
|
42
|
+
{"base" => "anyType"},
|
43
|
+
{"base" => "anySimpleType"},
|
44
|
+
{"base" => "IDREFS"},
|
45
|
+
],
|
46
|
+
errors: [{"@id" => "_:foo"},
|
47
|
+
{"@id" => "http://www.w3.org/2001/XMLSchema#string"},
|
48
|
+
]
|
41
49
|
},
|
42
50
|
default: {
|
43
51
|
valid: ["foo"],
|
@@ -68,8 +76,8 @@ describe RDF::Tabular::Metadata do
|
|
68
76
|
invalid: [nil, "foo", 1, 0, "true", "false", "TrUe", "fAlSe", "1", "0"],
|
69
77
|
},
|
70
78
|
separator: {
|
71
|
-
valid: %w(, a | :) + [nil],
|
72
|
-
invalid: [1, false]
|
79
|
+
valid: %w(, a | : foo ::) + [nil],
|
80
|
+
invalid: [1, false]
|
73
81
|
},
|
74
82
|
"textDirection" => {
|
75
83
|
valid: %w(rtl ltr),
|
@@ -87,19 +95,25 @@ describe RDF::Tabular::Metadata do
|
|
87
95
|
context prop.to_s do
|
88
96
|
if allowed
|
89
97
|
it "validates" do
|
90
|
-
params
|
98
|
+
params.fetch(:valid, {}).each do |v|
|
91
99
|
subject.send("#{prop}=".to_sym, v)
|
92
100
|
expect(subject.errors).to be_empty
|
93
101
|
expect(subject.warnings).to be_empty
|
94
102
|
end
|
95
103
|
end
|
96
104
|
it "invalidates" do
|
97
|
-
params
|
105
|
+
params.fetch(:invalid, {}).each do |v|
|
98
106
|
subject.send("#{prop}=".to_sym, v)
|
99
107
|
expect(subject.errors).to be_empty
|
100
108
|
expect(subject.warnings).not_to be_empty
|
101
109
|
end
|
102
110
|
end
|
111
|
+
it "errors" do
|
112
|
+
params.fetch(:error, {}).each do |v|
|
113
|
+
subject.send("#{prop}=".to_sym, v)
|
114
|
+
expect(subject.errors).not_to be_empty
|
115
|
+
end
|
116
|
+
end
|
103
117
|
else
|
104
118
|
it "does not allow" do
|
105
119
|
params[:valid].each do |v|
|
@@ -588,12 +602,12 @@ describe RDF::Tabular::Metadata do
|
|
588
602
|
invalid: [1, true, nil]
|
589
603
|
},
|
590
604
|
tableDirection: {
|
591
|
-
valid: %w(rtl ltr
|
605
|
+
valid: %w(rtl ltr auto),
|
592
606
|
warning: %w(foo true 1)
|
593
607
|
},
|
594
608
|
transformations: {
|
595
|
-
valid: [[RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")]],
|
596
|
-
warning: [RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")] +
|
609
|
+
valid: [[RDF::Tabular::Transformation.new({url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/"}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"))]],
|
610
|
+
warning: [RDF::Tabular::Transformation.new({url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/"}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"))] +
|
597
611
|
%w(foo true 1)
|
598
612
|
},
|
599
613
|
dialect: {
|
@@ -643,7 +657,7 @@ describe RDF::Tabular::Metadata do
|
|
643
657
|
warning: [1, true, nil]
|
644
658
|
},
|
645
659
|
tableDirection: {
|
646
|
-
valid: %w(rtl ltr
|
660
|
+
valid: %w(rtl ltr auto),
|
647
661
|
warning: %w(foo true 1)
|
648
662
|
},
|
649
663
|
dialect: {
|
@@ -651,8 +665,8 @@ describe RDF::Tabular::Metadata do
|
|
651
665
|
warning: [1]
|
652
666
|
},
|
653
667
|
transformations: {
|
654
|
-
valid: [[RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")]],
|
655
|
-
warning: [RDF::Tabular::Transformation.new(url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/")] +
|
668
|
+
valid: [[RDF::Tabular::Transformation.new({url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/"}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"))]],
|
669
|
+
warning: [RDF::Tabular::Transformation.new({url: "http://example", targetFormat: "http://example", scriptFormat: "http://example/"}, context: "http://www.w3.org/ns/csvw", base: RDF::URI("http://example.org/base"))] +
|
656
670
|
%w(foo true 1)
|
657
671
|
},
|
658
672
|
notes: {
|
@@ -1014,6 +1028,7 @@ describe RDF::Tabular::Metadata do
|
|
1014
1028
|
# Strings
|
1015
1029
|
"string with no constraints" => {base: "string", value: "foo", result: "foo"},
|
1016
1030
|
"string with matching length" => {base: "string", value: "foo", length: 3, result: "foo"},
|
1031
|
+
"string matching null when required" => {base: "string", value: "NULL", null: "NULL", required: true},
|
1017
1032
|
"string with wrong length" => {
|
1018
1033
|
base: "string",
|
1019
1034
|
value: "foo",
|
@@ -1040,36 +1055,31 @@ describe RDF::Tabular::Metadata do
|
|
1040
1055
|
},
|
1041
1056
|
"decimal with matching pattern" => {
|
1042
1057
|
base: "decimal",
|
1043
|
-
format: {pattern
|
1058
|
+
format: {"pattern" => '000'},
|
1044
1059
|
value: "123"
|
1045
1060
|
},
|
1046
1061
|
"decimal with wrong pattern" => {
|
1047
1062
|
base: "decimal",
|
1048
|
-
format: {pattern
|
1063
|
+
format: {"pattern" => '0000'},
|
1049
1064
|
value: "123",
|
1050
1065
|
errors: [/123 does not match pattern/]
|
1051
1066
|
},
|
1052
|
-
"decimal with implicit groupChar" => {
|
1053
|
-
base: "decimal",
|
1054
|
-
value: %("123,456.789"),
|
1055
|
-
result: "123456.789"
|
1056
|
-
},
|
1057
1067
|
"decimal with explicit groupChar" => {
|
1058
1068
|
base: "decimal",
|
1059
|
-
format: {groupChar
|
1069
|
+
format: {"groupChar" => ";"},
|
1060
1070
|
value: "123;456.789",
|
1061
1071
|
result: "123456.789"
|
1062
1072
|
},
|
1063
1073
|
"decimal with repeated groupChar" => {
|
1064
1074
|
base: "decimal",
|
1065
|
-
format: {groupChar
|
1075
|
+
format: {"groupChar" => ";"},
|
1066
1076
|
value: "123;;456.789",
|
1067
1077
|
result: "123;;456.789",
|
1068
1078
|
errors: [/has repeating/]
|
1069
1079
|
},
|
1070
1080
|
"decimal with explicit decimalChar" => {
|
1071
1081
|
base: "decimal",
|
1072
|
-
format: {decimalChar
|
1082
|
+
format: {"decimalChar" => ";"},
|
1073
1083
|
value: "123456;789",
|
1074
1084
|
result: "123456.789"
|
1075
1085
|
},
|
@@ -1081,11 +1091,13 @@ describe RDF::Tabular::Metadata do
|
|
1081
1091
|
},
|
1082
1092
|
"decimal with percent" => {
|
1083
1093
|
base: "decimal",
|
1094
|
+
format: {"groupChar" => ","},
|
1084
1095
|
value: "123456.789%",
|
1085
1096
|
result: "1234.56789"
|
1086
1097
|
},
|
1087
1098
|
"decimal with per-mille" => {
|
1088
1099
|
base: "decimal",
|
1100
|
+
format: {"groupChar" => ","},
|
1089
1101
|
value: "123456.789‰",
|
1090
1102
|
result: "123.456789"
|
1091
1103
|
},
|
@@ -1121,7 +1133,7 @@ describe RDF::Tabular::Metadata do
|
|
1121
1133
|
"NaN number" => {base: "number", value: "NaN"},
|
1122
1134
|
"INF number" => {base: "number", value: "INF"},
|
1123
1135
|
"-INF number" => {base: "number", value: "-INF"},
|
1124
|
-
"valid float" => {base: "float", value: "1234.
|
1136
|
+
"valid float" => {base: "float", value: "1234.456E7"},
|
1125
1137
|
"invalid float" => {base: "float", value: "1z", errors: ["1z is not a valid float"]},
|
1126
1138
|
"NaN float" => {base: "float", value: "NaN"},
|
1127
1139
|
"INF float" => {base: "float", value: "INF"},
|
@@ -1152,6 +1164,7 @@ describe RDF::Tabular::Metadata do
|
|
1152
1164
|
"validate date M.d.yyyy" => {base: "date", value: "3.22.2015", format: "M.d.yyyy", result: "2015-03-22"},
|
1153
1165
|
|
1154
1166
|
# Times
|
1167
|
+
"valid time HH:mm:ss.S" => {base: "time", value: "15:02:37.1", format: "HH:mm:ss.S", result: "15:02:37.1"},
|
1155
1168
|
"valid time HH:mm:ss" => {base: "time", value: "15:02:37", format: "HH:mm:ss", result: "15:02:37"},
|
1156
1169
|
"valid time HHmmss" => {base: "time", value: "150237", format: "HHmmss", result: "15:02:37"},
|
1157
1170
|
"valid time HH:mm" => {base: "time", value: "15:02", format: "HH:mm", result: "15:02:00"},
|
@@ -1159,6 +1172,7 @@ describe RDF::Tabular::Metadata do
|
|
1159
1172
|
|
1160
1173
|
# DateTimes
|
1161
1174
|
"valid dateTime yyyy-MM-ddTHH:mm:ss" => {base: "dateTime", value: "2015-03-15T15:02:37", format: "yyyy-MM-ddTHH:mm:ss", result: "2015-03-15T15:02:37"},
|
1175
|
+
"valid dateTime yyyy-MM-ddTHH:mm:ss.S" => {base: "dateTime", value: "2015-03-15T15:02:37.1", format: "yyyy-MM-ddTHH:mm:ss.S", result: "2015-03-15T15:02:37.1"},
|
1162
1176
|
"valid dateTime yyyy-MM-dd HH:mm:ss" => {base: "dateTime", value: "2015-03-15 15:02:37", format: "yyyy-MM-dd HH:mm:ss", result: "2015-03-15T15:02:37"},
|
1163
1177
|
"valid dateTime yyyyMMdd HHmmss" => {base: "dateTime", value: "20150315 150237", format: "yyyyMMdd HHmmss", result: "2015-03-15T15:02:37"},
|
1164
1178
|
"valid dateTime dd-MM-yyyy HH:mm" => {base: "dateTime", value: "15-03-2015 15:02", format: "dd-MM-yyyy HH:mm", result: "2015-03-15T15:02:00"},
|
@@ -1210,17 +1224,6 @@ describe RDF::Tabular::Metadata do
|
|
1210
1224
|
"valid Name" => {base: "Name", value: "someThing", result: RDF::Literal("someThing", datatype: RDF::XSD.Name)},
|
1211
1225
|
"valid NMTOKEN" => {base: "NMTOKEN", value: "someThing", result: RDF::Literal("someThing", datatype: RDF::XSD.NMTOKEN)},
|
1212
1226
|
|
1213
|
-
# Unsupported datatypes
|
1214
|
-
"anyType not allowed" => {base: "anyType", value: "some thing", errors: [/unsupported datatype/]},
|
1215
|
-
"anySimpleType not allowed" => {base: "anySimpleType", value: "some thing", errors: [/unsupported datatype/]},
|
1216
|
-
"ENTITIES not allowed" => {base: "ENTITIES", value: "some thing", errors: [/unsupported datatype/]},
|
1217
|
-
"IDREFS not allowed" => {base: "IDREFS", value: "some thing", errors: [/unsupported datatype/]},
|
1218
|
-
"NMTOKENS not allowed" => {base: "NMTOKENS", value: "some thing", errors: [/unsupported datatype/]},
|
1219
|
-
"ENTITY not allowed" => {base: "ENTITY", value: "something", errors: [/unsupported datatype/]},
|
1220
|
-
"ID not allowed" => {base: "ID", value: "something", errors: [/unsupported datatype/]},
|
1221
|
-
"IDREF not allowed" => {base: "IDREF", value: "something", errors: [/unsupported datatype/]},
|
1222
|
-
"NOTATION not allowed" => {base: "NOTATION", value: "some:thing", errors: [/unsupported datatype/]},
|
1223
|
-
|
1224
1227
|
# Aliases
|
1225
1228
|
"number is alias for double" => {base: "number", value: "1234.456E789", result: RDF::Literal("1234.456E789", datatype: RDF::XSD.double)},
|
1226
1229
|
"binary is alias for base64Binary" => {base: "binary", value: "Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g", result: RDF::Literal("Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g", datatype: RDF::XSD.base64Binary)},
|
@@ -1270,6 +1273,72 @@ describe RDF::Tabular::Metadata do
|
|
1270
1273
|
specify {expect(subject.value).to eql result}
|
1271
1274
|
end
|
1272
1275
|
end
|
1276
|
+
|
1277
|
+
context "Unsupported datatypes" do
|
1278
|
+
%w(anyType anySimpleType ENTITIES IDREFS NMTOKENS ENTITY ID IDREF NOTATAION foo).each do |base|
|
1279
|
+
it "detects #{base} as unsupported" do
|
1280
|
+
md = RDF::Tabular::Table.new({
|
1281
|
+
url: "http://example.com/table.csv",
|
1282
|
+
tableSchema: {
|
1283
|
+
columns: [{
|
1284
|
+
name: "name",
|
1285
|
+
datatype: base
|
1286
|
+
}]
|
1287
|
+
}
|
1288
|
+
})
|
1289
|
+
expect(md.warnings).not_to be_empty
|
1290
|
+
end
|
1291
|
+
end
|
1292
|
+
end
|
1293
|
+
end
|
1294
|
+
end
|
1295
|
+
|
1296
|
+
describe "#build_number_re" do
|
1297
|
+
subject {RDF::Tabular::Datatype.new({})}
|
1298
|
+
{
|
1299
|
+
'#,##0.##' => /^\d{1,}\.\d{,2}$/,
|
1300
|
+
'#,##0.###' => /^\d{1,}\.\d{,3}$/,
|
1301
|
+
'###0.#####' => /^\d{1,}\.\d{,5}$/,
|
1302
|
+
'###0.0000#' => /^\d{1,}\.\d{4,5}$/,
|
1303
|
+
'00000.0000' => /^\d{5}\.\d{4}$/,
|
1304
|
+
|
1305
|
+
'0' => /^\d{1}$/,
|
1306
|
+
'00' => /^\d{2}$/,
|
1307
|
+
'#' => /^\d*$/,
|
1308
|
+
'##' => /^\d*$/,
|
1309
|
+
|
1310
|
+
'.0' => /^\.\d{1}$/,
|
1311
|
+
'.00' => /^\.\d{2}$/,
|
1312
|
+
'.#' => /^\.\d{,1}$/,
|
1313
|
+
'.##' => /^\.\d{,2}$/,
|
1314
|
+
|
1315
|
+
'+0' => /^+\d{1}$/,
|
1316
|
+
'-0' => /^-\d{1}$/,
|
1317
|
+
'%0' => /^%\d{1}$/,
|
1318
|
+
'‰0' => /^‰\d{1}$/,
|
1319
|
+
'0%' => /^\d{1}%$/,
|
1320
|
+
'0‰' => /^\d{1}‰$/,
|
1321
|
+
'0.0%' => /^\d{1}\.\d{1}%$/,
|
1322
|
+
|
1323
|
+
'#0.0#E#0' => /^\d{1,}\.\d{1,2}E\d{1,2}$/,
|
1324
|
+
'#0.0#E+#' => /^\d{1,}\.\d{1,2}E+\d{,1}$/,
|
1325
|
+
'#0.0#E-00' => /^\d{1,}\.\d{1,2}E-\d{2}$/,
|
1326
|
+
'#0.0#E#0%' => /^\d{1,}\.\d{1,2}E\d{1,2}%$/,
|
1327
|
+
}.each do |pattern,regexp|
|
1328
|
+
it "generates #{regexp} for #{pattern}" do
|
1329
|
+
expect(subject.build_number_re(pattern, ",", ".")).to eql regexp
|
1330
|
+
end
|
1331
|
+
end
|
1332
|
+
|
1333
|
+
%W{
|
1334
|
+
+%0
|
1335
|
+
0#
|
1336
|
+
0E0
|
1337
|
+
0-
|
1338
|
+
}.each do |pattern|
|
1339
|
+
it "recognizes bad pattern #{pattern}" do
|
1340
|
+
expect{subject.build_number_re(pattern, ",", ".")}.to raise_error(ArgumentError)
|
1341
|
+
end
|
1273
1342
|
end
|
1274
1343
|
end
|
1275
1344
|
|
@@ -1511,11 +1580,26 @@ describe RDF::Tabular::Metadata do
|
|
1511
1580
|
"url": "http://example.org/table1",
|
1512
1581
|
"tableSchema": {"columns": [{"titles": "foo"}]}
|
1513
1582
|
}),
|
1514
|
-
R:
|
1583
|
+
R: false
|
1584
|
+
},
|
1585
|
+
"tables with mismatch columns on name/titles" => {
|
1586
|
+
A: %({
|
1587
|
+
"@context": "http://www.w3.org/ns/csvw",
|
1588
|
+
"@type": "Table",
|
1589
|
+
"url": "http://example.org/table1",
|
1590
|
+
"tableSchema": {"columns": [{"name": "foo"}]}
|
1591
|
+
}),
|
1592
|
+
B: %({
|
1593
|
+
"@context": "http://www.w3.org/ns/csvw",
|
1594
|
+
"@type": "Table",
|
1595
|
+
"url": "http://example.org/table1",
|
1596
|
+
"tableSchema": {"columns": [{"titles": "bar"}]}
|
1597
|
+
}),
|
1598
|
+
R: false
|
1515
1599
|
},
|
1516
1600
|
}.each do |name, props|
|
1517
1601
|
it name do
|
1518
|
-
a = described_class.new(::JSON.parse(props[:A]))
|
1602
|
+
a = described_class.new(::JSON.parse(props[:A]), validate: true)
|
1519
1603
|
b = described_class.new(::JSON.parse(props[:B]))
|
1520
1604
|
if props[:R]
|
1521
1605
|
expect {a.verify_compatible!(b)}.not_to raise_error
|
data/spec/reader_spec.rb
CHANGED
@@ -3,12 +3,10 @@ require File.join(File.dirname(__FILE__), 'spec_helper')
|
|
3
3
|
require 'rdf/spec/reader'
|
4
4
|
|
5
5
|
describe RDF::Tabular::Reader do
|
6
|
-
let!(:doap) {File.expand_path("../../etc/doap.
|
7
|
-
let!(:doap_count) {
|
6
|
+
let!(:doap) {File.expand_path("../../etc/doap.csv", __FILE__)}
|
7
|
+
let!(:doap_count) {9}
|
8
8
|
|
9
9
|
before(:each) do
|
10
|
-
@reader = RDF::Tabular::Reader.new(StringIO.new(""), base_uri: "file:#{File.expand_path("..", __FILE__)}")
|
11
|
-
|
12
10
|
WebMock.stub_request(:any, %r(.*example.org.*)).
|
13
11
|
to_return(lambda {|request|
|
14
12
|
file = request.uri.to_s.split('/').last
|
@@ -18,15 +16,15 @@ describe RDF::Tabular::Reader do
|
|
18
16
|
else 'text/plain'
|
19
17
|
end
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
{status: 401}
|
24
|
-
else
|
19
|
+
path = File.expand_path("../data/#{file}", __FILE__)
|
20
|
+
if File.exist?(path)
|
25
21
|
{
|
26
|
-
body: File.read(
|
22
|
+
body: File.read(path),
|
27
23
|
status: 200,
|
28
24
|
headers: {'Content-Type' => content_type}
|
29
25
|
}
|
26
|
+
else
|
27
|
+
{status: 401}
|
30
28
|
end
|
31
29
|
})
|
32
30
|
|
@@ -34,7 +32,12 @@ describe RDF::Tabular::Reader do
|
|
34
32
|
end
|
35
33
|
|
36
34
|
# @see lib/rdf/spec/reader.rb in rdf-spec
|
37
|
-
#
|
35
|
+
# two failures specific to the way @input is handled in rdf-tabular make this a problem
|
36
|
+
#it_behaves_like 'an RDF::Reader' do
|
37
|
+
# let(:reader_input) {doap}
|
38
|
+
# let(:reader) {RDF::Tabular::Reader.new(StringIO.new(""))}
|
39
|
+
# let(:reader_count) {doap_count}
|
40
|
+
#end
|
38
41
|
|
39
42
|
it "should be discoverable" do
|
40
43
|
readers = [
|
@@ -67,7 +70,7 @@ describe RDF::Tabular::Reader do
|
|
67
70
|
expect_any_instance_of(RDF::Tabular::Dialect).to receive(:encoding=).with("ISO-8859-4")
|
68
71
|
RDF::Tabular::Reader.new(input) {|r| r.each_statement {}}
|
69
72
|
end
|
70
|
-
it "sets lang to de in metadata given Content-Language=de" do
|
73
|
+
it "sets lang to de in metadata given Content-Language=de", pending: "affecting some RSpec matcher" do
|
71
74
|
input = double("input", content_type: "text/csv", headers: {content_language: "de"}, charset: nil)
|
72
75
|
expect_any_instance_of(RDF::Tabular::Metadata).to receive(:lang=).with("de")
|
73
76
|
RDF::Tabular::Reader.new(input) {|r| r.each_statement {}}
|
@@ -82,12 +85,12 @@ describe RDF::Tabular::Reader do
|
|
82
85
|
context "non-file input" do
|
83
86
|
let(:expected) {
|
84
87
|
JSON.parse(%({
|
85
|
-
"
|
88
|
+
"tables": [
|
86
89
|
{
|
87
|
-
"url": "
|
90
|
+
"url": "",
|
88
91
|
"row": [
|
89
92
|
{
|
90
|
-
"url": "
|
93
|
+
"url": "#row=2",
|
91
94
|
"rownum": 1,
|
92
95
|
"describes": [
|
93
96
|
{
|
@@ -97,7 +100,7 @@ describe RDF::Tabular::Reader do
|
|
97
100
|
]
|
98
101
|
},
|
99
102
|
{
|
100
|
-
"url": "
|
103
|
+
"url": "#row=3",
|
101
104
|
"rownum": 2,
|
102
105
|
"describes": [
|
103
106
|
{
|
@@ -107,7 +110,7 @@ describe RDF::Tabular::Reader do
|
|
107
110
|
]
|
108
111
|
},
|
109
112
|
{
|
110
|
-
"url": "
|
113
|
+
"url": "#row=4",
|
111
114
|
"rownum": 3,
|
112
115
|
"describes": [
|
113
116
|
{
|
@@ -117,7 +120,7 @@ describe RDF::Tabular::Reader do
|
|
117
120
|
]
|
118
121
|
},
|
119
122
|
{
|
120
|
-
"url": "
|
123
|
+
"url": "#row=5",
|
121
124
|
"rownum": 4,
|
122
125
|
"describes": [
|
123
126
|
{
|
@@ -168,7 +171,7 @@ describe RDF::Tabular::Reader do
|
|
168
171
|
|
169
172
|
it "standard mode" do
|
170
173
|
expected = File.expand_path("../data/#{ttl}", __FILE__)
|
171
|
-
RDF::Reader.open(input, format: :tabular, base_uri: about, noProv: true,
|
174
|
+
RDF::Reader.open(input, format: :tabular, base_uri: about, noProv: true, debug: @debug) do |reader|
|
172
175
|
graph = RDF::Graph.new << reader
|
173
176
|
graph2 = RDF::Graph.load(expected, base_uri: about)
|
174
177
|
expect(graph).to be_equivalent_graph(graph2,
|
@@ -258,6 +261,41 @@ describe RDF::Tabular::Reader do
|
|
258
261
|
end
|
259
262
|
end
|
260
263
|
|
264
|
+
context "Primary Keys" do
|
265
|
+
it "has expected primary keys" do
|
266
|
+
RDF::Reader.open("http://example.org/countries.json", format: :tabular, validate: true) do |reader|
|
267
|
+
reader.each_statement {}
|
268
|
+
pks = reader.metadata.tables.first.object[:rows].map(&:primaryKey)
|
269
|
+
|
270
|
+
# Each entry is an array of cells
|
271
|
+
expect(pks.map {|r| r.map(&:value).join(",")}).to eql %w(AD AE AF)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
it "errors on duplicate primary keys" do
|
276
|
+
RDF::Reader.open("http://example.org/test232-metadata.json", format: :tabular, validate: true, errors: []) do |reader|
|
277
|
+
expect {reader.validate!}.to raise_error(RDF::Tabular::Error)
|
278
|
+
|
279
|
+
pks = reader.metadata.tables.first.object[:rows].map(&:primaryKey)
|
280
|
+
|
281
|
+
# Each entry is an array of cells
|
282
|
+
expect(pks.map {|r| r.map(&:value).join(",")}).to eql %w(1 1)
|
283
|
+
|
284
|
+
expect(reader.options[:errors]).to eq ["Table http://example.org/test232.csv has duplicate primary key 1"]
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context "Foreign Keys" do
|
290
|
+
let(:path) {File.expand_path("../data/countries.json", __FILE__)}
|
291
|
+
it "validates consistent foreign keys" do
|
292
|
+
RDF::Reader.open(path, format: :tabular, validate: true, warnings: []) do |reader|
|
293
|
+
reader.each_statement {}
|
294
|
+
expect(reader.options[:warnings]).to be_empty
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
261
299
|
context "Provenance" do
|
262
300
|
{
|
263
301
|
"country-codes-and-names.csv" => %(
|